我仍然是编程的新手并遇到了这个脚本,我对一些逻辑有疑问。我的问题与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;
}
我还是编程新手,这个答案真的可以帮到我。
要问我的问题,我需要谈谈逻辑。有问题的代码旨在找到多个解决方案,然后计算哪个是最有效的。
每次状态条件满足前提条件
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;
}
}
}