复杂树数据结构 - c#

我正在为一款与旧的经典《生化危机》游戏类似的游戏制作道具系统。当前,我正在实现项目合并,在这里您将不同的项目彼此合并以获得新的东西。复杂性来自这样一个事实,即存在的项目具有不止一个转换级别,并且每个转换级别都有多个匹配对象。让我澄清一下,假设我们有绿色,红色和蓝色的药草。您不能将红色+蓝色组合在一起,但是可以将G + B组合在一起,得到的是GreenBlueHerb,或者G + R组合起来可以得到GreenRedHerb,现在,如果将这些结果中的任意一个与蓝色药草组合,您将得到一个灰色的草药。从本示例中可以看到,绿色药草有2个转换级别,要达到第一个级别,有两个可能的配偶(红色|蓝色),从这一点到第二级,只有一个配偶(蓝色)。

因此,我想出了一棵有趣的树,它涵盖了下降到nLevels的所有可能性,而不仅仅是2个,级别越高,树越复杂,看看这个3个级别的示例,您在中间看到的三角形代表一个项,其周围的其他彩色形状表示可能的配对,使其达到下一个层次:

有很多不同的组合,我可以先将我的商品与蓝色的商品,然后是红色的,然后是绿色的,或者是绿色的,然后是红色,然后是蓝色的,等等。要达到我的最终水平。
我想出了代表所有可能组合的树:

(右边的数字是#level,左边的数字是每个级别的#node)。但是,正如您所看到的,这是多余的。如果查看末端节点,它们应该全部为1,因为它们都导致相同的最终结果,即G + R + B。对于这种情况,实际上总共有7种可能的状态,这是正确的树:

这很有意义,请注意节点数量上的巨大差异。

现在我的问题是,什么是正确的数据结构? -我很确定没有内置的功能,所以我必须做一个我自己的自定义功能,我确实做了,并且设法使它工作但有问题。 (值得一提的是,我是从XML文件中获取节点信息的,信息是指itemRequirement必须达到一个节点/级别,该节点上我的项目的名称是什么,例如:对于绿色药草要达到RedGreenHerb状态,它“需要”一个RedHerb,当这种组合发生时,名称“ GreenHerb”将更改为“ RedGreenHerb”,如果您想知道RedHerb发生了什么,它就消失了,我不需要它),这是我的数据结构:

public struct TransData
{
    public TransData(string transItemName, string itemRequired)
    {
        this.transItemName = transItemName;
        this.itemRequired = itemRequired;
    }
    public string transItemName;
    public string itemRequired;
}

public class TransNode
{
    public List<TransNode> nodes = new List<TransNode>();
    public TransData data;
    public TransNode(TransNode node): this(node.data.transItemName, node.data.itemRequired) { }
    public TransNode(string itemName, string itemRequired)
    {
       data = new TransData(itemName, itemRequired);
    }
}

public class TransLevel
{
    public List<TransNode> nodes = new List<TransNode>();
    public TransNode NextNode { get { return nodes[cnt++ % nodes.Count]; } }
    int cnt;
}

public class TransTree
{    
    public TransTree(string itemName)
    {
        this.itemName = itemName;
    }
    public string itemName;
    public TransNode[] nodes;
    public List <TransLevel> levels = new List<TransLevel>();
    // other stuff...
}

让我解释一下:TransTree实际上是基础节点,它的开头具有项的名称(例如GreenHerb),树具有多个级别(您在图片中看到的黑色线条),每个级别都有一个节点数,每个节点都带有一个新的项目数据,以及许多要指向的节点(子节点)。现在您可能会问,需要在TransTree类中放置节点列表吗? -在向您展示如何从XML文件获取数据后,我将回答:

public TransTree GetTransItemData(string itemName)
{
    var doc = new XmlDocument();
    var tree = new TransTree(itemName);
    doc.LoadXml(databasePath.text);

    var itemNode = doc.DocumentElement.ChildNodes[GetIndex(itemName)];
    int nLevels = itemNode.ChildNodes.Count;
    for (int i = 0; i < nLevels; i++) {
       var levelNode = itemNode.ChildNodes[i];
       tree.levels.Add(new TransLevel());
       int nPaths = levelNode.ChildNodes.Count;
       for (int j = 0; j < nPaths; j++) {
            var pathNode = levelNode.ChildNodes[j];
        string newName = pathNode.SelectSingleNode("NewName").InnerText;
        string itemRequired = pathNode.SelectSingleNode("ItemRequired").InnerText;
        tree.levels[i].nodes.Add(new TransNode(newName, itemRequired));
       }
    }
    tree.ConnectNodes(); // pretend these two
    tree.RemoveLevels(); // lines don't exist for now
    return tree;

}

这是一个XML示例,可以使所有内容变得清晰:
ItemName-> Level-> Path(仅是节点)-> Path数据

<IOUTransformableItemsDatabaseManager>
  <GreenHerb>
    <Level_0>
      <Path_0>
        <NewName>RedGreenHerb</NewName>
        <ItemRequired>RedHerb</ItemRequired>
      </Path_0>
      <Path_1>
        <NewName>BlueGreenHerb</NewName>
        <ItemRequired>BlueHerb</ItemRequired>
      </Path_1>
    </Level_0>
    <Level_1>
      <Path_0>
        <NewName>GreyHerb</NewName>
        <ItemRequired>BlueHerb</ItemRequired>
      </Path_0>
    </Level_1>
  </GreenHerb>
</IOUTransformableItemsDatabaseManager>

现在,这样做的问题在于节点之间没有相互连接,那又意味着什么呢?好吧,如果某个项目采用某个路径到达某个特定级别,那么我们当然不需要继续存储其他路径,而这些路径并没有占用,那么为什么要将它们保留在内存中? (没有回滚的路径,一旦您走了一条路,就是您必须沿着那条路走,再也不回头了)我想拥有的是,当我取出一个节点时,它下面的所有其他节点也会下降,这是有道理的,但是目前我到目前为止的操作方式是这样的:

如您所见,节点未连接,保持它们的是关卡!这意味着,当前我无法取出节点,而取出其所有子节点。 (在没有连接节点的事实的情况下这样做确实很困难,并且会大大降低性能),这使我们能够:

tree.ConnectNodes(); 
tree.RemoveLevels();

我首先连接节点,然后删除级别?为什么,因为如果我不这样做,那么每个节点都有对其的两个引用,一个来自其父节点,另一个来自当前级别。
现在ConnectNode实际上是针对我显示的一棵长树,而不是针对具有7种状态的优化树:

    // this is an overloaded version I use inside a for loop in ConnectNodes()
    private void ConnectNodes(int level1, int level2)
    {
        int level1_nNodes = levels[level1].nodes.Count;
        int level2_nNodes = levels[level2].nodes.Count;

        // the result of the division gives us the number of nodes in level2,
        // that should connect to each node in level1. 12/4 = 3, means that each
        // node from level1 will connect to 3 nodes from level2;
        int nNdsToAtch = level2_nNodes / level1_nNodes;
        for (int i = 0, j = 0; i < level2_nNodes; j++)
        {
            var level1_nextNode = levels[level1].nodes[j];
            for (int cnt = 0; cnt < nNdsToAtch; cnt++, i++)
            {
                var level2_nextNode = levels[level2].nodes[i];
                level1_nextNode.nodes.Add(new TransNode(level2_nextNode));
            }
        }
   }

最终这就是我想在另一棵树上拥有的东西,但是我不知道该怎么做。我想连接节点并形成我显示的第二棵树,它比ex的4个层次相对简单,我什至无法连接画中的节点! (当我尝试4个级别时)

如果您盯着它看,会发现与二进制数字有些相似,这是我的二进制形式的树:

001
010
100
011
101
110
111

每个“ 1”代表一个实际项目,“ 0”表示空。在我的树中,“ 001” =蓝色,“ 010” =绿色,“ 100” =红色,“ 011”表示绿色+蓝色,...“ 111” =灰色(最终级别)

因此,现在我对所有事情都进行了解释,首先:我的方法正确吗?如果不是,那是什么?
如果是这样,那么我可以使用/制作的数据结构是什么?如果我想出的数据结构在原位,那么如何将XML文件中的数据存储到我的数据结构中,将节点连接在一起,所以每当取出一个节点时,它就会取出其子节点用它?

在此先感谢您的帮助和耐心:)

编辑:有趣的是,这整个系统适用于在整个游戏中仅出现一次的物品(被拾取一次)。这就是为什么每当我走一条路径时,都会从内存中删除它,并且每当我拿起一个项目时,都会从数据库中删除它的条目,因为我不会再遇到它了。

编辑:请注意,我不仅通过字符串表示我的项目,它们还有很多其他属性。但是在这种情况下,我只关心它们的名称,这就是为什么我要处理字符串。

参考方案

在此解决方案中,我不喜欢的是:

简单的解决方案是最好的解决方案
由于您的xml基于图形,因此难以维护。
不要真正利用OOP
错误来源
可能将Reflection用于小问题(我之所以这么说,是因为如果您做这样的游戏,您将面临更多困难的问题;))。这意味着不必要的复杂性。

我在此解决方案中喜欢什么:

您刚刚完全理解了问题。每个项目都有与其他对象的转换列表。现在的问题是如何表示(而不是存储)它

我将要做什么(恕我直言,您的解决方案也不错):在仅节点的角度上使用OOP。因此,如果要将树附加到数据结构,则树将成为状态机(正如您所说的path;))。

public class InventoryObject
{
    protected Dictionnary<Type, InventoryObject> _combinations = new Dictionnary<Type, InventoryObject>();

    public InventoryObject() {}       

    public InventoryObject Combine(InventoryObject o)
    {
       foreach (var c in _combinations)
          if (typeof(o) == c.Key)
            return c.Value

       throw new Exception("These objects aren't combinable");
    }
}

public class BlueHerb : InventoryObject
{
    public Herb()
    {
       _combinations.Add(RedHerb, new BlueRedHerb());
       _combinations.Add(GreenHerb, new BlueGreenHerb());
    }
}

public class BlueRedHerb: InventoryObject
{
    public BlueRedHerb()
    {
       _combinations.Add(GreenHerb, new GreyHerb());
    }
}

然后只需调用BlueHerb.Combine(myRedHerb);即可得到结果。您还可以执行BlueHerb.Combine(myStone);并轻松调试。

我尝试使我的示例尽可能简单。为了点亮代码,可以进行很多修改(类Herb,类CombinedHerb,使用LINQ查询等)。

将谓词<T>转换为Func <T,bool> - c#

我有一个包含成员Predicate的类,希望在Linq表达式中使用该类:using System.Linq; class MyClass { public bool DoAllHaveSomeProperty() { return m_instrumentList.All(m_filterExpression); } private IEnumerable&…

合并List <T>和List <Optional <T >> - java

鉴于: List<Integer> integers = new ArrayList<>(Arrays.asList( 10, 12 )); List<Optional<Integer>> optionalIntegers = Arrays.asList( Optional.of(5), Optional.em…

无法从ArrayList <String>转换为List <Comparable> - java

当我写下面的代码时,编译器说 无法从ArrayList<String>转换为List<Comparable>private List<Comparable> get(){ return new ArrayList<String>(); } 但是当我用通配符编写返回类型时,代码会编译。private List&l…

客户端反序列化为数组序列化字典<string,string>数据 - c#

我有一个字典,该字典使用C#中的JavaScriptSerializer进行了序列化。在客户端,我有:"{"dd049eda-e289-4ca2-8841-4908f94d5b65":"2","ab969ac2-320e-42e1-b759-038eb7f57178":"5�…

OpenShift构建错误:无法在多模块Maven Spring启动项目的父模块中导入子模块类 - java

我有一个使用spring的多模块Maven项目。通用模块类用作业务模块项目中的直接导入。我可以在本地PC上编译并成功运行它们。当我在OpenShift中部署相同的模块时,出现错误,无法在业务模块中导入通用模块类。项目结构可以总结如下:项目根 通用模块 src pom.xml 业务模块 src pom.xml pom.xml父POM:<?xml vers…