标准寻路就足够了 -您的状态是您的当前位置+您的当前库存。“搬家”是指改变房间或改变存货。该答案未涵盖,但没有付出太多额外的努力,正在为A *编写一个很好的启发法-通过优先选择捡拾东西而不是移开它,宁愿解锁目标附近的门,它确实可以加快搜索速度搜寻很长的路要走等等
自从问世以来,这个答案已经得到了很多好评,并且有演示,但是对于更优化,更专业的解决方案,您还应该阅读“向后做起来更快”的答案 。/gamedev/ / a / 150155/2624
完整的Javascript概念验证如下。很抱歉将答案作为代码转储-在我确信这是一个不错的答案之前,我实际上已经实现了它,但是对我来说似乎很灵活。
要开始考虑寻路时,请记住,简单寻路算法的层次结构为:
- 广度优先搜索功能尽可能简单。
- Djikstra的算法类似于广度优先搜索,但是状态之间的“距离”不同
- A *是Djikstras,在这里您可以将“正确方向的一般感觉”用作启发式方法。
在我们的案例中,只需将“状态”编码为“位置+库存”,将“距离”编码为“运动或物品使用情况”,就可以使用Djikstra或A *解决问题。
这是一些实际的代码,说明您的示例级别。第一个代码段仅供比较-如果要查看最终的解决方案,请跳至第二部分。我们从Djikstra的实现开始,它找到正确的路径,但是我们忽略了所有的障碍和关键。(尝试一下,从房间0-> 2-> 3-> 4-> 6-> 5,您可以看到它只是完成工作的直线)
function Transition(cost, state) { this.cost = cost, this.state = state; }
// given a current room, return a room of next rooms we can go to. it costs
// 1 action to move to another room.
function next(n) {
var moves = []
// simulate moving to a room
var move = room => new Transition(1, room)
if (n == 0) moves.push(move(2))
else if ( n == 1) moves.push(move(2))
else if ( n == 2) moves.push(move(0), move(1), move(3))
else if ( n == 3) moves.push(move(2), move(4), move(6))
else if ( n == 4) moves.push(move(3))
else if ( n == 5) moves.push(move(6))
else if ( n == 6) moves.push(move(5), move(3))
return moves
}
// Standard Djikstra's algorithm. keep a list of visited and unvisited nodes
// and iteratively find the "cheapest" next node to visit.
function calc_Djikstra(cost, goal, history, nextStates, visited) {
if (!nextStates.length) return ['did not find goal', history]
var action = nextStates.pop()
cost += action.cost
var cur = action.state
if (cur == goal) return ['found!', history.concat([cur])]
if (history.length > 15) return ['we got lost', history]
var notVisited = (visit) => {
return visited.filter(v => JSON.stringify(v) == JSON.stringify(visit.state)).length === 0;
};
nextStates = nextStates.concat(next(cur).filter(notVisited))
nextStates.sort()
visited.push(cur)
return calc_Djikstra(cost, goal, history.concat([cur]), nextStates, visited)
}
console.log(calc_Djikstra(0, 5, [], [new Transition(0, 0)], []))
那么,我们如何在该代码中添加项目和键?简单!而不是每个“状态”都只以房间号开头,而是房间和我们的库存状态的元组:
// Now, each state is a [room, haskey, hasfeather, killedboss] tuple
function State(room, k, f, b) { this.room = room; this.k = k; this.f = f; this.b = b }
现在,转换已从(成本,房间)元组变为(成本,状态)元组,因此可以同时编码“移至另一个房间”和“拾取项目”
// move(3) keeps inventory but sets the room to 3
var move = room => new Transition(1, new State(room, cur.k, cur.f, cur.b))
// pickup("k") keeps room number but increments the key count
var pickup = (cost, item) => {
var n = Object.assign({}, cur)
n[item]++;
return new Transition(cost, new State(cur.room, n.k, n.f, n.b));
};
最后,我们对Djikstra函数进行一些与类型相关的较小更改(例如,它仍然只是匹配目标房间号而不是完整状态),我们得到了完整答案!请注意打印结果首先进入4号房间拿起钥匙,然后进入1号房间拿起羽毛,然后进入6号房间,杀死老板,然后进入5号房间)
// Now, each state is a [room, haskey, hasfeather, killedboss] tuple
function State(room, k, f, b) { this.room = room; this.k = k; this.f = f; this.b = b }
function Transition(cost, state, msg) { this.cost = cost, this.state = state; this.msg = msg; }
function next(cur) {
var moves = []
// simulate moving to a room
var n = cur.room
var move = room => new Transition(1, new State(room, cur.k, cur.f, cur.b), "move to " + room)
var pickup = (cost, item) => {
var n = Object.assign({}, cur)
n[item]++;
return new Transition(cost, new State(cur.room, n.k, n.f, n.b), {
"k": "pick up key",
"f": "pick up feather",
"b": "SLAY BOSS!!!!"}[item]);
};
if (n == 0) moves.push(move(2))
else if ( n == 1) { }
else if ( n == 2) moves.push(move(0), move(3))
else if ( n == 3) moves.push(move(2), move(4))
else if ( n == 4) moves.push(move(3))
else if ( n == 5) { }
else if ( n == 6) { }
// if we have a key, then we can move between rooms 1 and 2
if (cur.k && n == 1) moves.push(move(2));
if (cur.k && n == 2) moves.push(move(1));
// if we have a feather, then we can move between rooms 3 and 6
if (cur.f && n == 3) moves.push(move(6));
if (cur.f && n == 6) moves.push(move(3));
// if killed the boss, then we can move between rooms 5 and 6
if (cur.b && n == 5) moves.push(move(6));
if (cur.b && n == 6) moves.push(move(5));
if (n == 4 && !cur.k) moves.push(pickup(0, 'k'))
if (n == 1 && !cur.f) moves.push(pickup(0, 'f'))
if (n == 6 && !cur.b) moves.push(pickup(100, 'b'))
return moves
}
var notVisited = (visitedList) => (visit) => {
return visitedList.filter(v => JSON.stringify(v) == JSON.stringify(visit.state)).length === 0;
};
// Standard Djikstra's algorithm. keep a list of visited and unvisited nodes
// and iteratively find the "cheapest" next node to visit.
function calc_Djikstra(cost, goal, history, nextStates, visited) {
if (!nextStates.length) return ['No path exists', history]
var action = nextStates.pop()
cost += action.cost
var cur = action.state
if (cur.room == goal) return history.concat([action.msg])
if (history.length > 15) return ['we got lost', history]
nextStates = nextStates.concat(next(cur).filter(notVisited(visited)))
nextStates.sort()
visited.push(cur)
return calc_Djikstra(cost, goal, history.concat([action.msg]), nextStates, visited)
o}
console.log(calc_Djikstra(0, 5, [], [new Transition(0, new State(0, 0, 0, 0), 'start')], []))
从理论上讲,即使在BFS上也可以使用,并且我们不需要Djikstra的成本函数,但是有了成本,我们可以说“捡起钥匙很轻松,但是与老板打架真的很困难,我们宁愿回溯如果我们可以选择的话,则可以100步而不是与老板搏斗”:
if (n == 4 && !cur.k) moves.push(pickup(0, 'k'))
if (n == 1 && !cur.f) moves.push(pickup(0, 'f'))
if (n == 6 && !cur.b) moves.push(pickup(100, 'b'))