Standart yol bulma yeterlidir - eyaletleriniz mevcut konumunuz + mevcut envanterinizdir. "Hareketli" ya oda değiştirmek ya da envanter değiştirmek. Bu cevapta ele alınmayan, ancak fazladan fazla çaba göstermeyen, A * için iyi bir buluşsal yazı yazıyor - hedefin yakınında bir kapının kilidini açmayı tercih ederek, ondan uzaklaşırken bir şeyleri seçmeyi tercih ederek aramayı gerçekten hızlandırabilir etrafta uzun bir yol aramak vb.
Bu cevap ilk geldiği ve bir demosu olduğu için çok fazla yükseldi, ancak çok daha optimize edilmiş ve uzmanlaşmış bir çözüm için, "Geriye doğru yapmak çok daha hızlı" yanıtını da okumalısınız. Https://gamedev.stackexchange.com / a / 150155/2624
Tamamen operasyonel Javascript aşağıdaki kavram kanıtı. Bir kod dökümü olarak cevap için özür dilerim - bunu iyi bir cevap olduğuna ikna etmeden önce uygulamıştım, ancak bana oldukça esnek görünüyor.
Yol bulma hakkında düşünürken başlamak için, basit yol bulma algoritmalarının heirarşisinin:
- Genişlik İlk Arama alabildiğin kadar basit.
- Djikstra'nın Algoritması, Breadth First Search'e benziyor ancak eyaletler arasında değişken "mesafeler" var
- Y *, bir sezgisel olarak mevcut olan 'doğru yöne genel bir anlayışa sahip olduğunuz' Djikstralardır.
Bizim durumumuzda, sadece bir "durumu" bir "konum + envanter" olarak ve "mesafeleri" bir "hareket veya madde kullanımı" olarak kodlamak, sorunumuzu çözmek için Djikstra veya A * kullanmamıza izin verir.
Örnek seviyenizi gösteren bazı gerçek kodlar. İlk snippet sadece karşılaştırma için - son çözümü görmek istiyorsanız ikinci bölüme atlayın. Djikstra'nın doğru yolu bulan uygulamasıyla başlıyoruz, ancak tüm engelleri ve anahtarları görmezden geldik. (Deneyin, sadece finiş için beelines görebilirsiniz, oda 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)], []))
Peki bu koda nasıl eşya ve anahtar ekleriz? Basit! Her “devlet” yerine sadece oda numarası başlar, o şimdi odanın bir parçası ve envanter durumumuz:
// 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 }
Geçişler artık bir (maliyet, oda) dizisinden bir (maliyet, durum) dizisine değişmektedir, bu yüzden hem "başka bir odaya taşınmak" hem de "bir öğe almak" ı kodlayabilir
// 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));
};
Son olarak, Djikstra işlevinde bazı küçük tiplerle ilgili değişiklikler yapıyoruz (örneğin, hala tam bir durum yerine bir hedef oda numarasıyla eşleşiyor) ve tam cevabımızı alıyoruz! Basılı sonucun anahtarı almak için önce oda 4'e, ardından tüyü almak için oda 1'e, sonra oda 6'ya, patronu öldürür, sonra oda 5'e gittiğine dikkat edin.
// 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')], []))
Teorik olarak, bu BFS ile bile çalışıyor ve Djikstra'nın maliyet işlevine ihtiyacımız yoktu, ancak maliyete sahip olmak "anahtar teslim almak zahmetsiz, ancak bir patronla savaşmak gerçekten zor, ve geri dönmek istiyoruz" dememize izin veriyor Seçim şansımız olsa patronla savaşmak yerine 100 adım attı ":
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'))