我目前正在尝试从目标状态开始进行回归搜索,以找出一系列动作来实现我的GOAP规划器的目标状态。截至目前,我所做的伪代码如下所示:
closedList = list;s = stack;s.push(goal);while(s.size() > 0){ state = s.pop; if(!state exists in closedList) { closedList.add(state); for(action = firstAction; action != lastAction; action = nextAction) { if(action.getEffect() == state) { if(!action.ConditionsFulfilled()) { conditionList = action.getConditions; for(i = 0; i < conditionList.size(); i++) { s.push(conditionList[i]); } } } } }}
我听说GOAP与A*算法非常相似,只是节点是状态,边是动作。但由于在A*中,节点不需要满足任何条件,这让我在如何调整A*算法以适应前置条件上感到困惑。我难以理解的是如何存储动作并比较动作的成本,以便找到最有效的路径。如果我们假设动作类有一个getCost()函数来返回动作的成本,那么在考虑前置条件的情况下,我该如何处理这个问题呢?
回答:
节点确实是世界状态。而边是动作。但请注意,它们是有向边!
前置条件的作用在于:它们决定了哪些边(动作)从节点流出。只有前置条件满足的动作,才是有效的从该状态节点流出的边。
因此,要找到一个节点的邻居,你需要检查每个动作是否所有前置条件都满足。如果是,则应用后置条件以查看该动作将导致的节点。该动作就是这些状态(节点)之间的有效边。
请查看开源的GPGOAP(通用目标导向动作规划),这是GOAP与A*结合的实现。它的C语言代码简单明了,解释了所有步骤。我是GPGOAP的作者。
关于回归的思考
现在谈谈回归部分:我从未实现过从目标到当前世界状态的反向搜索。所以在这方面我能提供的帮助有限。
两个相邻的节点仍然基于单个动作连接。你现在不是基于动作的前置条件,而是基于动作的后置条件来启用/禁用边。如果后置条件与当前节点不匹配,那么该动作将无效。如果匹配,我预计你会通过强制动作的前置条件来添加邻居。
你为什么更倾向于使用反向搜索而不是正向搜索?