了解GoapPlanner脚本

时间:2017-05-11 14:00:03

标签: c#

我仍然是编程的新手并遇到了这个脚本,我对一些逻辑有疑问。我的问题与buildGraph()

有关
 private bool buildGraph(Node parent, List<Node> leaves, HashSet<GoapAction> usableActions, HashSet<KeyValuePair<string, object>> goal)
{

    bool foundOne = false;

    // go through each action available at this node and see if we can use it here
    foreach (GoapAction action in usableActions)
    {

        // if the parent state has the conditions for this action's preconditions, we can use it here
        if (inState(action.Preconditions, parent.state))
        {

            // apply the action's effects to the parent state
            HashSet<KeyValuePair<string, object>> currentState = populateState(parent.state, action.Effects);
            //Debug.Log(GoapAgent.prettyPrint(currentState));

            Node node = new Node(parent, parent.runningCost + action.cost, currentState, action);

            if (inState(goal, currentState))
            {
                // we found a solution! 
                leaves.Add(node);
                foundOne = true;
            }
            else
            {
                // not at a solution yet.
                HashSet<GoapAction> subset = actionSubset(usableActions, action);
                bool found = buildGraph(node, leaves, subset, goal);
                if (found)
                    foundOne = true;
            }
        }
    }

    return foundOne;
}

我还是编程新手,这个答案真的可以帮到我。

要问我的问题,我需要谈谈逻辑。有问题的代码旨在找到多个解决方案,然后计算哪个是最有效的。

enter image description here

每次状态条件满足前提条件

  

if(inState(action.Preconditions,parent.state))

代码将添加效果。

  

HashSet&gt; currentState = populateState   (parent.state,action.Effects);

然后我们创建一个节点并保存动作,成本和状态

  

Node node = new Node(parent,parent.runningCost + action.cost,   currentState,action);

如果父州符合我们的目标

  

if(inState(goal,currentState))

我们将节点添加到叶子列表作为解决方案。

  

leaves.Add(节点);

在叶子列表中,我们找到最便宜的节点,这将是最佳路径。如果有多个解决方案,则列表将包含多个节点。

现在我的问题

我不明白一旦找到一个解决方案,脚本如何能够继续寻找其他解决方案和路径。一旦遇到第一个解决方案,目标效果就会添加到当前状态。 foreach循环将继续从possibleActions HashSet中提取操作,但是,之后测试的每个操作都不会显示为解决方案,因为第一个解决方案已更改状态并添加了目标效果?我认为状态需要改回以避免这种情况,否则它不会进入递归步骤。脚本工作正常,所以显然我不太了解。

此代码取自GoapPlanner,这是规划师的完整代码。

 public class GoapPlanner
{

    /**
     * Plan what sequence of actions can fulfill the goal.
     * Returns null if a plan could not be found, or a list of the actions
     * that must be performed, in order, to fulfill the goal.
     */
    public Queue<GoapAction> plan(GameObject agent,
                                  HashSet<GoapAction> availableActions,
                                  HashSet<KeyValuePair<string, object>> worldState,
                                  HashSet<KeyValuePair<string, object>> goal)
    {
        // reset the actions 
        foreach (GoapAction a in availableActions)
        {
            a.doReset();
        }

        // check which actions can run using their checkProceduralPrecondition
        HashSet<GoapAction> usableActions = new HashSet<GoapAction>();
        foreach (GoapAction a in availableActions)
        {
            if (a.checkProceduralPrecondition(agent))
                usableActions.Add(a);
        }


        // build up the tree and record the leaf nodes that provide a solution to the goal. 
        List<Node> leaves = new List<Node>();

        // build graph
        Node start = new Node(null, 0, worldState, null); 
        bool success = buildGraph(start, leaves, usableActions, goal);

        if (!success)
        {
            // oh no, we didn't get a plan
            Debug.Log("NO PLAN");
            return null;
        }


        // gets the cheapest leaf. 
        Node cheapest = null;

        foreach (Node leaf in leaves)
        {

            if (cheapest == null)
                cheapest = leaf;
            else
            {

                if (leaf.runningCost < cheapest.runningCost)
                {
                    cheapest = leaf;
                }
            }

        }

        List<GoapAction> result = new List<GoapAction>();

        Node n = cheapest;
        while (n != null)
        {
            if (n.action != null)
            {

                result.Insert(0, n.action); // insert the action in the front.
            }
            n = n.parent;
        }

        // we now have actions in correct order in results list and we want to add it to the queue.
        Queue<GoapAction> queue = new Queue<GoapAction>();
        foreach (GoapAction a in result)
        {
            queue.Enqueue(a);

        }

        // hooray we have a plan!
        return queue;
    }

    /**
     * Returns true if at least one solution was found.
     * The possible paths are stored in the leaves list. Each leaf has a
     * 'runningCost' value where the lowest cost will be the best action
     * sequence.
     */
    private bool buildGraph(Node parent, List<Node> leaves, HashSet<GoapAction> usableActions, HashSet<KeyValuePair<string, object>> goal)
    {

        bool foundOne = false;

        // go through each action available at this node and see if we can use it here
        foreach (GoapAction action in usableActions)
        {

            // if the parent state has the conditions for this action's preconditions, we can use it here
            if (inState(action.Preconditions, parent.state))
            {

                // apply the action's effects to the parent state
                HashSet<KeyValuePair<string, object>> currentState = populateState(parent.state, action.Effects);
                //Debug.Log(GoapAgent.prettyPrint(currentState));

                Node node = new Node(parent, parent.runningCost + action.cost, currentState, action);

                if (inState(goal, currentState))
                {
                    // we found a solution! 
                    leaves.Add(node);
                    foundOne = true;
                }
                else
                {
                    // not at a solution yet.
                    HashSet<GoapAction> subset = actionSubset(usableActions, action);
                    bool found = buildGraph(node, leaves, subset, goal);
                    if (found)
                        foundOne = true;
                }
            }
        }

        return foundOne;
    }

    /**
     * Create a subset of the actions excluding the removeMe one. 
     */
    private HashSet<GoapAction> actionSubset(HashSet<GoapAction> actions, GoapAction removeMe)
    {
        HashSet<GoapAction> subset = new HashSet<GoapAction>();
        foreach (GoapAction a in actions)
        {
            if (!a.Equals(removeMe))
                subset.Add(a);
        }
        return subset;
    }

    /**
     * Check that all items in 'test' are in 'state'. If just one does not match or is not there
     * then this returns false.
     */
    private bool inState(HashSet<KeyValuePair<string, object>> test, HashSet<KeyValuePair<string, object>> state)
    {
        bool allMatch = true;
        foreach (KeyValuePair<string, object> t in test)
        {
            bool match = false;
            foreach (KeyValuePair<string, object> s in state)
            {
                if (s.Equals(t))
                {
                    match = true;
                    break;
                }
            }
            if (!match)
                allMatch = false;
        }
        return allMatch;
    }

    /**
     * Apply the stateChange to the currentState
     */
    private HashSet<KeyValuePair<string, object>> populateState(HashSet<KeyValuePair<string, object>> currentState, HashSet<KeyValuePair<string, object>> stateChange)
    {
        HashSet<KeyValuePair<string, object>> state = new HashSet<KeyValuePair<string, object>>();
        // copy the KVPs over as new objects
        foreach (KeyValuePair<string, object> s in currentState)
        {
            state.Add(new KeyValuePair<string, object>(s.Key, s.Value));
        }

        foreach (KeyValuePair<string, object> change in stateChange)
        {
            // if the key exists in the current state, update the Value
            bool exists = false;

            foreach (KeyValuePair<string, object> s in state)
            {
                if (s.Equals(change))
                {
                    exists = true;
                    break;
                }
            }

            if (exists)
            {
                state.RemoveWhere((KeyValuePair<string, object> kvp) => { return kvp.Key.Equals(change.Key); });
                KeyValuePair<string, object> updated = new KeyValuePair<string, object>(change.Key, change.Value);
                state.Add(updated);
            }
            // if it does not exist in the current state, add it
            else
            {
                state.Add(new KeyValuePair<string, object>(change.Key, change.Value));
            }
        }
        return state;
    }

    /**
     * Used for building up the graph and holding the running costs of actions.
     */
    private class Node
    {
        public Node parent;
        public float runningCost;
        public HashSet<KeyValuePair<string, object>> state;
        public GoapAction action;

        public Node(Node parent, float runningCost, HashSet<KeyValuePair<string, object>> state, GoapAction action) //state should be world state
        {
            this.parent = parent;
            this.runningCost = runningCost;
            this.state = state;
            this.action = action;
        }
    }
}

0 个答案:

没有答案