Ich möchte auf einer fundamentalen Ebene verstehen, wie A * -Pfadfindung funktioniert. Alle Code- oder Pseudo-Code-Implementierungen sowie Visualisierungen wären hilfreich.
Ich möchte auf einer fundamentalen Ebene verstehen, wie A * -Pfadfindung funktioniert. Alle Code- oder Pseudo-Code-Implementierungen sowie Visualisierungen wären hilfreich.
Antworten:
Es gibt Unmengen von Codebeispielen und Erklärungen zu A * im Internet. Diese Frage hat auch viele gute Antworten mit vielen nützlichen Links erhalten. In meiner Antwort werde ich versuchen, ein illustriertes Beispiel für den Algorithmus zu liefern, das möglicherweise leichter zu verstehen ist als Code oder Beschreibungen.
Um A * zu verstehen, schlage ich vor, dass Sie sich zuerst den Dijkstra-Algorithmus ansehen . Lassen Sie sich von mir durch die Schritte führen, die der Dijkstra-Algorithmus für eine Suche ausführt.
Unser Startknoten ist A
und wir wollen den kürzesten Weg finden F
. Mit jeder Kante des Graphen sind Bewegungskosten verbunden (bezeichnet als schwarze Ziffern neben den Kanten). Unser Ziel ist es, die minimalen Reisekosten für jeden Scheitelpunkt (oder Knoten) des Diagramms zu bewerten, bis wir unseren Zielknoten erreichen.
Dies ist unser Ausgangspunkt. Wir müssen einen Listenknoten untersuchen. Diese Liste ist derzeit:
{ A(0) }
A
hat die Kosten von 0
, alle anderen Knoten sind auf unendlich gesetzt (in einer typischen Implementierung wäre dies so ähnlich int.MAX_VALUE
oder ähnlich).
Wir nehmen den Knoten mit den niedrigsten Kosten aus unserer Knotenliste (da unsere Liste nur enthält A
, ist dies unser Kandidat) und besuchen alle seine Nachbarn. Wir setzen die Kosten für jeden Nachbarn auf:
Cost_of_Edge + Cost_of_previous_Node
und verfolgen Sie den vorherigen Knoten (angezeigt als kleiner rosa Buchstabe unter dem Knoten). A
kann jetzt als gelöst (rot) markiert werden, damit wir es nicht wieder besuchen. Unsere Kandidatenliste sieht jetzt so aus:
{ B(2), D(3), C(4) }
Wieder nehmen wir den Knoten mit den niedrigsten Kosten aus unserer Liste ( B
) und bewerten seine Nachbarn. Der Pfad zu D
ist teurer als die aktuellen Kosten von D
, daher kann dieser Pfad verworfen werden. E
wird unserer Kandidatenliste hinzugefügt, die jetzt so aussieht:
{ D(3), C(4), E(4) }
Der nächste zu untersuchende Knoten ist jetzt D
. Die Verbindung zu C
kann verworfen werden, da der Pfad nicht kürzer als die vorhandenen Kosten ist. Wir haben einen kürzeren Weg gefunden E
, daher werden die Kosten für E
und den vorherigen Knoten aktualisiert. Unsere Liste sieht jetzt so aus:
{ E(3), C(4) }
Wie zuvor untersuchen wir nun den Knoten mit den niedrigsten Kosten aus unserer Liste E
. E
hat nur einen ungelösten Nachbarn, der auch der Zielknoten ist. Die Kosten zum Erreichen des Zielknotens werden auf 10
und seines vorherigen Knotens auf festgelegt E
. Unsere Kandidatenliste sieht jetzt so aus:
{ C(4), F(10) }
Als nächstes untersuchen wir C
. Wir können die Kosten und den vorherigen Knoten für aktualisieren F
. Da unsere Liste jetzt den F
Knoten mit den niedrigsten Kosten hat, sind wir fertig. Unser Weg kann konstruiert werden, indem die vorherigen kürzesten Knoten zurückverfolgt werden.
Sie fragen sich vielleicht, warum ich Ihnen Dijkstra anstelle des A * -Algorithmus erklärt habe ? Der einzige Unterschied besteht darin, wie Sie Ihre Kandidaten wiegen (oder sortieren). Mit Dijkstra ist es:
Cost_of_Edge + Cost_of_previous_Node
Mit A * ist es:
Cost_of_Edge + Cost_of_previous_Node + Estimated_Cost_to_reach_Target_from(Node)
Wo Estimated_Cost_to_reach_Target_from
wird allgemein eine heuristische Funktion genannt. Dies ist eine Funktion, die versucht, die Kosten zum Erreichen des Zielknotens abzuschätzen. Mit einer guten heuristischen Funktion wird erreicht, dass weniger Knoten besucht werden müssen, um das Ziel zu finden. Während sich der Dijkstra-Algorithmus nach allen Seiten ausdehnt, sucht A * (dank der Heuristik) in Richtung des Ziels.
Amits Seite über Heuristiken bietet einen guten Überblick über gängige Heuristiken.
Eine * -Pfadfindung ist eine Suche nach dem besten zuerst, die eine zusätzliche Heuristik verwendet.
Als erstes müssen Sie Ihren Suchbereich aufteilen. Für diese Erklärung ist die Karte ein quadratisches Raster aus Kacheln, da die meisten 2D-Spiele ein Raster aus Kacheln verwenden und dies einfach zu visualisieren ist. Beachten Sie jedoch, dass der Suchbereich beliebig aufgeteilt werden kann: Vielleicht ein Hex-Raster oder sogar beliebige Formen wie Risk. Die verschiedenen Kartenpositionen werden als "Knoten" bezeichnet. Dieser Algorithmus funktioniert immer dann, wenn Sie eine Reihe von Knoten durchlaufen und Verbindungen zwischen den Knoten definiert haben.
Wie auch immer, beginnend mit einem bestimmten Startplättchen:
Die 8 Kacheln um die Startkachel werden "gewertet", basierend auf a) den Kosten für den Wechsel von der aktuellen Kachel zur nächsten Kachel (im Allgemeinen 1 für horizontale oder vertikale Bewegungen, sqrt (2) für diagonale Bewegungen).
Jedem Plättchen wird dann eine zusätzliche "heuristische" Punktzahl zugewiesen - eine Annäherung an den relativen Wert des Bewegens zu jedem Plättchen. Es werden verschiedene Heuristiken verwendet, wobei die einfachste der geradlinige Abstand zwischen den Mittelpunkten der gegebenen Kachel und der Endkachel ist.
Das aktuelle Plättchen wird dann "geschlossen" und der Agent bewegt sich zu dem benachbarten Plättchen, das offen ist, die niedrigste Bewegungsbewertung und die niedrigste heuristische Bewertung hat.
Dieser Vorgang wird wiederholt, bis der Zielknoten erreicht ist oder keine offenen Knoten mehr vorhanden sind (dh der Agent ist blockiert).
Diagramme, die diese Schritte veranschaulichen, finden Sie in diesem guten Tutorial für Anfänger .
Es gibt einige Verbesserungen, die hauptsächlich bei der Verbesserung der Heuristik vorgenommen werden können:
Berücksichtigung von Geländeunterschieden, Rauheit, Steilheit usw.
Manchmal ist es auch nützlich, einen "Sweep" über das Raster auszuführen, um Bereiche der Karte auszublenden, die keine effizienten Pfade sind: z. B. eine U-Form, die dem Agenten zugewandt ist. Ohne einen Wobbeltest würde der Agent zuerst das U betreten, sich umdrehen, dann das U verlassen und um den Rand des U herumgehen. Ein "echter" intelligenter Agent würde die U-förmige Falle bemerken und sie einfach vermeiden. Das Kehren kann dabei helfen, dies zu simulieren.
Es ist alles andere als das Beste, aber dies ist eine Implementierung von A * in C ++, die ich vor einigen Jahren gemacht habe.
Es ist wahrscheinlich besser, dass ich Sie auf Ressourcen hinweise, als zu versuchen, den gesamten Algorithmus zu erklären. Spielen Sie beim Lesen des Wiki-Artikels mit der Demo und sehen Sie, ob Sie sich vorstellen können, wie sie funktioniert. Hinterlassen Sie einen Kommentar, wenn Sie eine bestimmte Frage haben.
Sie könnten ActiveTut Artikel über finden Path Finding nützlich. Es geht sowohl über den A * - als auch über den Dijkstra-Algorithmus und die Unterschiede zwischen ihnen. Es richtet sich an Flash-Entwickler, sollte jedoch einen guten Einblick in die Theorie bieten, auch wenn Sie Flash nicht verwenden.
Wenn Sie sich mit A * und dem Dijkstra-Algorithmus beschäftigen, ist es wichtig zu visualisieren, dass A * gerichtet ist. es versucht, den kürzesten Weg zu einem bestimmten Punkt zu finden, indem es "errät", in welche Richtung es schauen soll. Der Dijkstra-Algorithmus findet den kürzesten Weg zu / jedem / Punkt.
A * ist also genau wie eine erste Aussage ein Graph-Explorations-Algorithmus. Normalerweise verwenden wir in Spielen entweder Kacheln oder eine andere Weltgeometrie als Grafik, aber Sie können A * für andere Dinge verwenden. Die beiden Ur-Algorithmen für das Durchlaufen von Graphen sind Tiefensuche und Breitensuche. In DFS durchsuchen Sie Ihren aktuellen Zweig immer vollständig, bevor Sie sich die Geschwister des aktuellen Knotens ansehen, und in BFS betrachten Sie immer zuerst die Geschwister und dann die Kinder. A * versucht, einen Mittelweg zwischen diesen zu finden, auf dem Sie einen Zweig erkunden (eher wie DFS), wenn Sie sich dem gewünschten Ziel nähern, aber manchmal anhalten und ein Geschwister versuchen, wenn es in seinem Zweig bessere Ergebnisse erzielt. Die eigentliche Mathematik ist, dass Sie eine Liste möglicher Knoten führen, um als nächstes zu erkunden, wo jeder eine "Güte" hat. Punktzahl, die angibt, wie nahe (in gewisser Weise abstrakt) das Ziel liegt, wobei niedrigere Punktzahlen besser sind (0 würde bedeuten, dass Sie das Ziel gefunden haben). Sie wählen aus, welche Option als Nächstes verwendet werden soll, indem Sie das Minimum der Punktzahl plus die Anzahl der vom Stamm entfernten Knoten ermitteln (wobei es sich im Allgemeinen um die aktuelle Konfiguration oder die aktuelle Position bei der Pfadfindung handelt). Jedes Mal, wenn Sie einen Knoten durchsuchen, fügen Sie dieser Liste alle untergeordneten Knoten hinzu und wählen dann den neuen besten aus.
Auf einer abstrakten Ebene funktioniert A * wie folgt: