创建所需项目的树

时间:2013-07-12 19:58:19

标签: c#

当我正在开发我的小游戏时,我已经取得了很多进展,但对许多事情感到沮丧。最新的事情是creating a list of required items,并且您了解我将为您提供我创建的Explanation以及Code,但显然无效...

我 - 解释

为了让玩家建造一座建筑物,他必须完成一些必要的研究,每项研究都需要更多的研究才能研究......就像一棵研究树,玩家将通过探索它来经历它们。游戏和做一些任务......

所以想象一下,你可以更准确地看一下我的小代码

II - 代码

    //Available Main Elements
    var carbon = new Element {Name = "Carbon"};
    var hydrogen = new Element {Name = "Hydrogen"};
    var oxygen = new Element {Name = "Oxygen"};
    var nitrogen = new Element {Name = "Nitrogen"};

        //Example Research
var steam = new Research(name : "Steam", requiredElements: null, requiredResearches: /*Fire*/ & /*Water*/ & /*Iron*/);

所以从最后一段代码[只是为了解释],玩家想要研究Steam,例如需要另外3个研究才能被研究......其中一个{{1}还需要再研究1个研究等等[可能更少或许更多或根本没有要求] ......

结论是问题是:我怎样才能创建这样的嵌套,以便当玩家尝试进行研究时,系统会快速查看他所做的研究以及他想做的研究[包括它的嵌套研究] ]如果玩家不符合要求,它只会返回一棵树,上面有他想要实现的东西

毕竟,我只想提前感谢你,我正在等待你非常宝贵的支持......

7 个答案:

答案 0 :(得分:5)

我会从Research对象本身中删除需求逻辑。简单来说就是这样:

public class Research
{
    public string Name { get; set; }
}

然后,我会在Dictonary中保留一份要求列表,其中每个Research包含一桶其他Researches

Dictionary<Research, List<Research>> requiredResearches = 
    new Dictionary<Research, List<Research>>();

// the list of Researches the player has completed
List<Research> playersResearched = new List<Research>;

例如,“Steam”将包含“Fire”,“Water”和“Iron”。也许“铁”包含“锡”。

接下来,给定一项研究,我们可以查看所有需求,包括要求的要求:

// e.g. research is "Steam" and returns "Fire", "Water", "Iron", "Tin"
var chainOfRequirements = GetReq(requiredResearches, research);

调用这样的递归函数:

public IList<Research> GetReq(Dictionary<Research, List<Research>> reqs, 
                              Research target)
{
    var chain = new List<Research>();
    if(reqs.ContainsKey(target))
    {
        foreach(var item in reqs[target])
        {
            chain.Add(item);
            chain.AddRange(GetReq(reqs, item));
        }
    }
    return chain;
}

您返回的是一系列要求(包括要求要求)。此时,针对Researches的玩家列表的一点查询可以向您返回哪些缺失:

var missing = chainOfRequirements.Where (c => 
                  playerResearches.Where (r => r == c).Any () == false).Distinct();

使用“NAME”

启用比较字典
public sealed class NameEqualityComparer : IEqualityComparer<Research>
{
    public bool Equals(Research x, Research y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (ReferenceEquals(x, null)) return false;
        if (ReferenceEquals(y, null)) return false;
        if (x.GetType() != y.GetType()) return false;
        return string.Equals(x.Name, y.Name);
    }

    public int GetHashCode(Research obj)
    {
        return (obj.Name != null ? obj.Name.GetHashCode() : 0);
    }
}

Here is proof of concept.

答案 1 :(得分:1)

我不清楚C#提供代码,但您可以让每个Research都有Array类型Research,这样就可以保存Research必须完成才能生成实际的Research,然后您只需要迭代Array检查是否所有这些都已完成。

您甚至不需要检查要求的要求,就好像它们已经完成,它们已经完成了所有要求。

如果Research没有任何要求,您只需将Array清空。

答案 2 :(得分:1)

一种可能性是将bool completed添加到ElementResearch类。然后,您可以使用一个函数来检查completed == false的任何子研究。

bool CanCompleteResearch(Research r)
{
    for (Research sub in r.requiredResearches)
    {
        // No need to recursively check 'sub'; in order for 'sub' to be marked as
        // completed, its sub-researches must've been already completed.            
        if (!sub.completed)
            return false;
    }
    return true;
}

我确信你可以用LINQ做一些单线技巧。

编辑:这仅适用于一次不在多个地方使用研究树的情况(即如果您在多个播放器实例中共享研究对象)。

编辑2:为了找出所有缺失的研究,您可以使用简单的递归函数:

void ListMissingResearch(Research r)
{
    for (Research sub in r.requiredResearches)
    {
        if (!sub.completed)
        {
            // print message saying "Research <r> requires research <sub>"
            ListMissingResearch(sub); // list any missing sub-sub-research
        }
    }
}

答案 3 :(得分:1)

我建议您创建自己的requirementsTree课程,该课程必须是至少包含以下成员的链接列表:

  • 一个requirementsTree[] prerequisiteResearch属性,包含该特定项目所需的所有研究。
  • 一个bool isResearched属性,表示玩家是否实际上已经研究过这个项目。
  • 一种方法,比如bool isEligibleToResearch(),它循环遍历prerequisiteResearch中的所有项目,以检查玩家是否已经对它们进行了研究。

有了这个,我认为你有一个结构良好且可扩展的需求类。

答案 4 :(得分:1)

我无法想到会阻止这种事情发生的任何具体事情,但我可以理解为什么你会犹豫不决,只是在没有仔细思考的情况下跳进编码。为了尝试给出一些指示,这里有一些我想象你到底有的功能:

// Having a function to retrieve a unique variable by its name (backed by a Dictionary<string, Research>)
// is often handy for me, especially if you decide to script the requirements tree in a text file.
// If you have enough references passed around that you can do this without a static method, all
// the better.
Research.getResearchByName(string name)

// A recursive function. If this research has not been completed, it adds itself to the set, as well
// as any prerequisites (by calling this function on its prerequisites). The top-level, public
// version of this function would create the set, and then return it after performing this check. If
// the Set is empty, then the player can start the research.
Research.addUnresearched_Internal(Set<Research> unresearched)

我认为我的方法的主要问题是我只想使用Set而不是Tree; 但有一些变化,你可能比我做得更好。

答案 5 :(得分:1)

听起来你想做这样的事情:

public class Research
{
    private Collection<Research> prerequisites 
    public Collection<Research> Prerequisites 
    {
        get { return this.prerequisistes; }
    }

    public bool IsComplete { get; set; }

    public IEnumerable<Research> RequiredResearch()
    {
        if (this.IsComplete)
        {
            return new Research[0];
        } 
        else 
        {
            return new[] { this }.Concat(
                this.Prerequisites.SelectMany(r => r.RequiredResearch()));
        }
    }
}

如果你正在制作一个多人游戏,它可能必须是这样的:

public class Player
{
    private Collection<Research> completedResearch
    public Collection<Research> CompletedResearch
    {
        get { return this.completedResearch; }
    }
}

public class Research
{
    private Collection<Research> prerequisites 
    public Collection<Research> Prerequisites 
    {
        get { return this.prerequisistes; }
    }

    public IEnumerable<Research> RequiredResearch(Player player)
    {
        if (player.CompletedResearch.Contains(this))
        {
            return new Research[0];
        } 
        else 
        {
            return new[] { this }.Concat(
                this.Prerequisites.SelectMany(r => r.RequiredResearch(player)));
        }
    }
}

如果这些方法尚未完成(而不仅仅是它的先决条件),这两种方法都将返回当前的研究。您可以简单地对结果执行.Skip(1)以忽略当前结果。

答案 6 :(得分:1)

你可以在你的研究班中使用这样的函数,只需要一个可用的Research列表,用来检查玩家是否具有先决条件。这假设在Research类中有一个名为Prereqs的本地列表或其他可枚举Research个。

public bool CanHas(IEnumerable<Research> researches)
{
   return Prereqs.All((pr) => researches.Contains(pr) && pr.CanHas(researches));
}

该函数只是递归检查每个Prereq以查看它是否在传入的列表中,然后检查先决条件先决条件。 All是一个linq扩展方法,如果枚举中的所有元素都符合条件,则返回true。如果没有元素,则返回true(元素的所有零都满足条件),因此当你到达没有Prereqs的Research时,这将终止。

注意:这是深度优先搜索,对于同一研究是多于一个先决条件的子研究并不聪明。