표준 길 찾기는 충분 합니다.주는 현재 위치 + 현재 인벤토리입니다. "이동"은 방을 변경하거나 재고를 변경하는 것입니다. 이 답변에서 다루지 않았지만 너무 많은 노력을 기울이지 않으면 서 A *에 대해 좋은 휴리스틱을 작성하고 있습니다. 멀리 떨어져서 물건을 집어 올리는 것을 선호하고 대상 근처의 문을 여는 것을 선호하여 검색 속도를 높일 수 있습니다 먼 길을 찾는 등
이 답변은 처음 나온 이후 많은 찬사를 받았으며 데모가 있지만 훨씬 최적화되고 전문화 된 솔루션의 경우 "거꾸로하는 것이 훨씬 빠릅니다"라는 답변도 읽어보십시오. /gamedev/ / a / 150155 / 2624
아래에서 완전히 작동하는 Javascript 개념 증명. 코드 덤프로서의 답변에 대해 죄송합니다. 실제로 좋은 답변이라고 확신하기 전에 실제로 구현했지만 실제로는 유연 해 보입니다.
경로 찾기에 대해 생각할 때 시작하려면 간단한 경로 찾기 알고리즘의 계층 구조는 다음과 같습니다.
- 너비 우선 검색은 최대한 간단합니다.
- Djikstra의 알고리즘은 너비 우선 탐색과 비슷하지만 주마다 "거리"가 다양합니다.
- A *는 지크 스트라 (Jikstras)로, 휴리스틱으로 사용할 수있는 '올바른 방향의 일반적인 감각'이 있습니다.
우리의 경우, "상태"를 "위치 + 재고"로 "거리"를 "이동 또는 아이템 사용"으로 인코딩하면 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'))