การค้นหาเส้นทางแบบมาตรฐานดีพอ - สถานะของคุณคือที่ตั้งปัจจุบันของคุณ + สินค้าคงคลังปัจจุบันของคุณ "ย้าย" เป็นห้องเปลี่ยนเสื้อผ้าหรือเปลี่ยนสินค้าคงคลัง ไม่ครอบคลุมในคำตอบนี้ แต่ไม่ใช่ความพยายามเพิ่มเติมมากเกินไปคือการเขียนฮิวริสติกที่ดีสำหรับ A * - สามารถเพิ่มความเร็วในการค้นหาได้โดยเลือกที่จะหยิบสิ่งต่าง ๆ ออกไปแทนที่จะเลือกประตูที่อยู่ใกล้เป้าหมาย การค้นหาทางไกล ฯลฯ
คำตอบนี้ได้รับ upvotes มากมายตั้งแต่ครั้งแรกและมีการสาธิต แต่สำหรับโซลูชันที่ได้รับการปรับปรุงและพิเศษกว่านั้นคุณควรอ่านคำตอบ "การทำแบบย้อนหลังนั้นเร็วกว่ามาก" /gamedev/ / a / 150155/2624
การใช้งาน Javascript อย่างสมบูรณ์ในการพิสูจน์แนวคิดด้านล่าง ขออภัยสำหรับคำตอบในรูปแบบการถ่ายโอนข้อมูล - ฉันใช้งานจริงก่อนที่จะเชื่อมั่นว่าเป็นคำตอบที่ดี แต่ดูเหมือนยืดหยุ่นสำหรับฉัน
ในการเริ่มต้นเมื่อคิดเกี่ยวกับการหาเส้นทางโปรดจำไว้ว่าลำดับขั้นตอนวิธีการหาเส้นทางแบบง่ายคือ:
- การค้นหาครั้งแรกของ Breadth นั้นเกี่ยวกับเรียบง่ายอย่างที่คุณจะได้รับ
- อัลกอริทึมของ 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 }
การเปลี่ยนตอนนี้เปลี่ยนจากการเป็น tuple (ราคาห้อง) เป็น tuple (ราคา, สถานะ) ดังนั้นจึงสามารถเข้ารหัสได้ทั้ง "ย้ายไปที่ห้องอื่น" และ "เก็บรายการ"
// 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's แต่การมีค่าใช้จ่ายทำให้เราสามารถพูดได้ว่า "การหยิบกุญแจมีความสะดวก 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'))