C# 复杂树数据结构
我正在为一款游戏设计一个道具系统,我们正在制作一款类似于古老的经典生化危机游戏的道具。目前,我正在实现项目组合,您可以将不同的项目相互组合以获得新的内容。复杂的情况来自于这样一个事实,即存在具有多个转换级别的项目,并且每个级别有多个配偶。请允许我澄清一下,让我们假设我们有一种绿色、红色和蓝色的草药。你不能把红色和蓝色结合起来,但是你可以把G+B结合起来你会得到一个像绿蓝药草一样的东西,或者G+R得到绿红药草,如果你把这两个结果中的任何一个结合到一个蓝色药草上,你会得到一个绿绿药草。正如你在这个例子中看到的,绿色药草有两个级别的转化,为了达到第一个级别,有两个可能的配偶(红色|蓝色),从这一点到第二个级别,只有一个配偶(蓝色)C# 复杂树数据结构,c#,algorithm,data-structures,tree,C#,Algorithm,Data Structures,Tree,我正在为一款游戏设计一个道具系统,我们正在制作一款类似于古老的经典生化危机游戏的道具。目前,我正在实现项目组合,您可以将不同的项目相互组合以获得新的内容。复杂的情况来自于这样一个事实,即存在具有多个转换级别的项目,并且每个级别有多个配偶。请允许我澄清一下,让我们假设我们有一种绿色、红色和蓝色的草药。你不能把红色和蓝色结合起来,但是你可以把G+B结合起来你会得到一个像绿蓝药草一样的东西,或者G+R得到绿红药草,如果你把这两个结果中的任何一个结合到一个蓝色药草上,你会得到一个绿绿药草。正如你在这个例
所以我想出了一个有趣的树,涵盖了所有的可能性:<强> nSeals>强,不只是2,树的层次越复杂,看看这个3个层次的例子,你在中间看到的三角形形状代表一个项目,它周围的其他彩色形状表示它可能达到下一个层次:
有很多不同的组合,我可以先把我的物品和蓝色的结合起来,然后是红色的,然后是绿色的,或者是绿色的,然后是红色的,然后是蓝色的,等等,以达到我的最终水平。 我找到了这棵树,代表了所有可能的组合: (右边的数字是#级别,左边是每个级别的#节点),但正如您所看到的,它是多余的。如果您查看结束节点,它们应该都是一个,因为它们都会导致相同的最终结果,即G+R+B。实际上,这种情况下总共有7种可能的状态,下面是正确的树: 这很有意义,注意节点数量上的巨大差异 现在我的问题是,什么是适合这个的数据结构我很确定这个没有内置的,所以我要做我自己的定制的,我确实做了,并设法让它工作,但有一个问题。(值得一提的是,我从一个XML文件中获取节点信息,我所说的信息是指达到一个节点/级别所需的项,以及在该节点上我的项的名称,例如:绿色药草要达到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...
}
}
下面是一个XML示例,可以让一切变得清晰:
ItemName->Level->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>
我首先连接节点,然后删除标高?为什么,因为如果我没有,那么每个节点都有两个对它的引用,一个来自其父节点,另一个来自当前级别。
现在,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));
}
}
}
每个“1”表示一个实际项目,“0”表示为空。在我的树上,'001'=蓝色,'010'=绿色,'100'=红色,'011'表示绿色+蓝色,'111'=灰色(最终等级)
所以现在我解释了一切,首先:我的方法正确吗?如果不是,那是什么?
如果是,那么我可以使用/制作什么样的数据结构来实现这一点?如果我提出的数据结构在它们的位置上,我如何将XML文件中的数据存储到我的数据结构中,从而将节点连接在一起,这样每当我取出一个节点时,它就会取出它的子节点
非常感谢您的帮助和耐心:)
编辑:有趣的是,整个系统只针对在整个游戏中只出现一次的项目(拾取一次)。这就是为什么每当我选择一条路径时,我会将其从memeory中删除,每当我选择一个项目时,我会将其条目从数据库中删除,因为我不会再遇到它
编辑:请注意,我不仅仅通过字符串来表示我的项目,它们还有很多其他属性。但在这种情况下,我只关心它们的名称,这就是我处理字符串的原因。我不喜欢这个解决方案的原因:
- 简单的解决方案是最好的解决方案
- 很难维护,因为xml是基于图形的
- 不要真的利用OOP
- 漏洞的来源
- 可能使用
解决小问题(我说小是因为如果你玩这样的游戏,你将面临更多的困难和问题;)。这意味着不必要的复杂性反射
- 你刚刚完全理解了这个问题。每个项都有一个与其他一些对象的转换列表。现在的问题是如何表示它(而不是存储它)
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());
}
}
公共类InventoryObject
{
受保护的词典_组合=新词典();
公共InventoryObject(){}
公共库存对象组合(库存对象o)
{
foreach(组合中的var c)
if(类型(o)==c.键)
返回c.值
抛出新异常(“这些对象不可组合”);
}
}
公共类BlueHerb:InventoryObject
{
公共药草()
{
_添加(RedHerb,newblueredherb());
_添加(青草,新青草());
}
}
公共类BlueRedHerb:InventoryObject
{
紫草
// 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));
}
}
}
001
010
100
011
101
110
111
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());
}
}
public struct TransData
{
public TransData(string itemName, List <string> itemsRequired)
{
this.itemName = itemName;
this.itemsRequired = itemsRequired;
}
public string itemName;
public List <string> itemsRequired;
}
public TransNode(string itemName, List <string> itemsRequired)
{
data = new TransData(itemName, itemsRequired);
}
Necklace L.1_A: Requires BlueGem
Necklace L.1_B: Requires GreenGem
Necklace L.1_C: Requires RedGem
Necklace L.2_A: Requires BlueGem AND GreenGem
Necklace L.2_B: Requires GreenGem AND RedGem
Necklace L.2_C: Requires RedGem AND BlueGem
Necklace L.3_A: Requires RedGem AND BlueGem AND GreenGem
<IOUTransformableItemsDatabaseManager>
<Necklace>
<Level_0>
<Path_0>
<NewName>RedNecklace</NewName>
<ItemsRequired>
<Item_0>Red</Item_0>
</ItemsRequired>
</Path_0>
<Path_1>
.......
</Level_0>
<Level_1>
<Path_0>
<NewName>RedGreenNecklace</NewName>
<ItemsRequired>
<Item_0>Red</Item_0>
<Item_1>Green</Item_1>
</ItemsRequired>
</Path_0>
<Path_1>
.....
</Level_1>
<Level_2>
<Path_0>
<NewName>RedGreenBlueNecklace</NewName>
<ItemsRequired>
<Item_0>Red</Item_0>
<Item_1>Green</Item_1>
<Item_2>Blue</Item_2>
</ItemsRequired>
</Path_0>
</Level_2>
</Necklace>
</IOUTransformableItemsDatabaseManager>
public class TransTree
{
//public string itemName;
//public List <TransNode> nodes;
public List <TransLevel> levels = new List<TransLevel>();
public TransNode root { private set; get; }
public TransTree(string itemName)
{
// this.itemName = itemName;
root = new TransNode(itemName, null);
}
}
public void ConnectNodes()
{
for (int i = 0; i < levels.Count - 1; i++)
ConnectNodes(i, i + 1);
ConnectRoot();
}
private void ConnectNodes(int level1, int level2)
{
int level1_nNodes = levels[level1].nodes.Count;
int level2_nNodes = levels[level2].nodes.Count;
for (int i = 0; i < level1_nNodes; i++)
{
var node1 = levels[level1].nodes[i];
for (int j = 0; j < level2_nNodes; j++)
{
var node2 = levels[level2].nodes[j];
foreach (var itemReq in node1.data.itemsRequired)
{
if (node2.data.itemsRequired.Contains(itemReq))
{
node1.nodes.Add(node2);
break;
}
}
}
}
}
ConnectRoot();
public void ConnectRoot()
{
foreach (var node in levels[0].nodes)
root.nodes.Add(node);
}
// fetching the data from the xml file
tree.ConnectNodes();
tree.RemoveLevels();
tree.SetRoot(nodeTakenToTransform);
public void Notify_ItemTransformed(TransTree itemTree, TransNode nodeTaken)
{
var key = new Tuple<string, string>(itemTree.root.data.itemName, nodeTaken.data.itemsRequired[0]);
itemTree.SetRoot(nodeTaken);
itemTree.UpdateNodesRequirements(itemTree.root); // take note here
RemoveKey(key);
RegisterItem(itemTree);
}
tree.SetRoot(nodeTaken);
tree.UpdateNodesRequirements(tree.root);
public void UpdateNodesRequirements(TransNode node)
{
foreach (var n in node.nodes)
{
if (n.data.itemsRequired.Contains(root.data.itemsRequired[0]))
n.data.itemsRequired.Remove(root.data.itemsRequired[0]);
if (n.nodes != null && n.nodes.Count > 0)
UpdateNodesRequirements(n);
}
}