![]() |
Pathfinding mit A*
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe mich heute Nachmittag mal drangesetzt und habe versucht den obigen Algorithmus zum Finden eines Pfades zu implementieren. Also so halb funktionieren tut es schon mal. Ich habe mich an die Beschreibung von dieser Seite gehalten:
![]() Nur irgendwie bleibt mein Algorithmus auf halben Weg stecken, das heißt, er landet in einer Endlosschleife. Desweitern, wenn ich das Hindernis bis an die Oberkante des Spielfeldes mache, bleibt er auch stecken. Und ich finde den Fehler in meinem Algorithmus einfach nicht. Ich weiß, dass der Code bestimmt nicht sehr performant ist und auch nicht unbedingt den kürzesten Wg findet, aber ich will überhaupt erstmal einen Weg finden. :-? Ich glaube, hier Sourcecode zu posten ist etwas schlecht, da man es im Zusammenhang sehen muss. Deswegen hänge ich mal den Source an. Ich denke, der Source spricht für sich. |
Re: Pathfinding mit A*
Ich habe jetzt auch den horizontalen Abstand für die geschätzte Entfernung zum Ziel mit einbezogen:
Delphi-Quellcode:
Jetzt hängt er gleich beim ersten mal in einer Endlosschleife. :gruebel:
List.H := (Abs(Abs((Dest.X - i)) + (Abs(Dest.Y - j)))) * 10;
|
Re: Pathfinding mit A*
Ich hatte vor einiger Zeit mal
![]() |
Re: Pathfinding mit A*
@Luckie
Zwar nichts zum Problem, aber warum machst du ein Abs() um die Summe zweier anderer Abs()-gekapselten Rechnungen? Da diese 100% positiv sind und eine Summe zweier positiver Werte sicher nicht negativ ist, ist die äußerste abs() sinnlos ;) air |
Re: Pathfinding mit A*
Wenn ich den Beitrag richtig verstanden habe, habe ich vermutlich den fehler gefunden und evtl. eine lösung für dich(ich habes nicht gestet).
Meiner meinung nach berechnes du den H wert falsch.
Delphi-Quellcode:
Laut tutorial musst du nicht einfach die Horizontale länge bereichnen sondern auch die vertikal damit meine ich folgendes für die Richtung Rechts:
if Dest.X > Start.X then
List.H := (Dest.X - i) * 10 else List.H := (i - Dest.X) * 10;
Delphi-Quellcode:
das müste eigentlich so gehen.
if Dest.X > Start.x then begin
Liste.H:=(Dest.X - i)+Dest.Y-j * 10 end; damit meine ich folgendes: du berechnes vom Start zum Ziel Punkt und berechnes den abstand. Angenommen das Ziel ist unten Rechts und der Start Oben Linx. Dazwischen währen jetzt hindernisse. Jetzt musst du die Horizontale richtung berechnen und anschließnd die Vertiklae richtung und anschließnd zusammen addieren. So habe ich es verstanden. Bin mir aber nicht sicher aber das richtig ist. |
Re: Pathfinding mit A*
Zitat:
Delphi-Quellcode:
Aber er bleibt trotzdem wieder stecken. :evil:
if Dest.X > Start.x then
begin dx := Dest.X - i; end else if Dest.X < Start.X then begin dx := i - Dest.X; end else dx := Dest.X; if Dest.y > Start.y then begin dy := Dest.y - j; end else if Dest.Y < Start.Y then begin dy := j - Dest.y; end else dy := Dest.Y; |
Re: Pathfinding mit A*
Versuchs doch nochmal auf die kurze Weise:
Delphi-Quellcode:
air
List.H := (Abs(Dest.X - i) + Abs(Dest.Y - j)) * 10;
|
Re: Pathfinding mit A*
Habe ich auch schon versucht, das klappt auch nicht. Im obigen Code war ein Fehler. So stimmt es:
Delphi-Quellcode:
Jetzt kommt er bis zum Ziel, aber nur bis ein Kästchen vor das Ziel, so dass er aus der Schleife nicht rauskommt:
if Dest.X > i then
begin dx := Dest.X - i; end else if Dest.X < i then begin dx := i - Dest.X; end else dx := Dest.X; if Dest.y > j then begin dy := Dest.y - j; end else if Dest.Y < j then begin dy := j - Dest.y; end else dy := Dest.Y; List.H := (dx + dy) * 10;
Delphi-Quellcode:
while (Dest.X <> Start.X) or (Dest.Y <> Start.Y) do
begin Application.ProcessMessages; Start := FindPath(Maze, Start, Dest); SetLength(Path, length(Path) + 1); Path[length(Path) - 1] := Start; SetLength(OpenList, 0); Sleep(250); DrawSquare(2, 3, SPACING, clGreen); InvalidateRect(Handle, nil, True); end; |
Re: Pathfinding mit A*
Michael,
versuche mal folgendes in TForm1.Button1Click ersetze
Delphi-Quellcode:
durch
while (Dest.X - 1 <> Start.X) or (Dest.Y - 1 <> Start.Y) do
Delphi-Quellcode:
in FindPath
while (abs(dest.x-start.x)>1) or (abs(dest.y-start.y)>1) do
ersetze
Delphi-Quellcode:
durch
if Dest.X > Start.X then
List.H := (Dest.X - i) * 10 else List.H := (i - Dest.X) * 10; // Diagonale Felder if ((Start.X - 1 = i) and (Start.Y - 1 = j)) // oben links or ((Start.X - 1 = i) and (Start.Y + 1 = j)) // unten links or ((Start.X + 1 = i) and (Start.Y - 1 = j)) // oben rechts or ((Start.X + 1 = i) and (Start.Y + 1 = J)) then // unten rechts List.G := 14 else // alle anderen List.G := 10; List.F := List.G + List.H;
Delphi-Quellcode:
dx und dy mußt Du natürlich lokal deklarieren.
dx:=abs(dest.x-i);
dy:=abs(dest.y-j); list.f:=dx*dx+dy*dy; keine Garantie, aber bei mir funktioniert das. Gruß, Klaus |
Re: Pathfinding mit A*
Hm, danke. Für diese eine Situation scheint es zu klappen. Aber wenn ich das Ziel auf 6,4 versetze oder die Mauer oben zu mache, dann geht es schon nicht mehr. :gruebel:
Im ersten Posting jetzt noch mnal mein aktueller Code. |
Re: Pathfinding mit A*
Hallo Michael,
wenn die Mauer oben zu ist, dann wirst Du mit Deiner Methode die für den nächsten Schritt verfügbaren Felder zu ermitteln, nicht weiterkommen. Wenn die Mauer oben zu ist, und du von 2/3 startest, geht das Programm auf 3/3, dann auf 3/2, und dann pendelt es zwischen 3/2 und 3/3 hin und her. Warum ?: Weil das Programm immer das erste Feld mit der kürzesten Distanz zum Zielfeld wählt und nicht merkt, daß der Weg über 3/2 bereits erfolglos probiert wurde und daß es eine Alternative gibt. Die Methode, die Strecke von einem Feld zum Zielfeld zu berechnen, hatte ich Dir ja gegeben und die ist auch korrekt. Im Prinzip stelle Dir die Strecke von einem Feld zum Zielfeld als die Hypothenuse eines rechtwinkligen Dreiecks vor. Die Katheten sind dann die Strecken von i nach dest.x und von j nach dest.y dx:=abs(dest.x-i); dy:=abs(dest.y-j); hierbei kann natürlich eine der Strecken 0 sein (rein theoretisch auch beide, aber bei Deinem Programm wird diese Situation schon vorher abgefangen) Die Länge der Hypothenuse (also der Strecke von einem Feld zum Zielfeld) wäre dann Sqrt(dx^2 + dy^2), jedenfalls hat das Herr Pythagoras gesagt. Da Du aber nicht die Länge der Strecke brauchst, sondern nur die Information, bei welchem Feld die Strecke am kürzesten ist, kannst Du Sqrt() weglassen (andernfalls mußt Du List.F als Single, Double etc. deklarieren). Geh mal auf die Seite ![]() und schau Dir unter "Labyrinth" an wie solch ein Backtracking Verfahren funktioniert. Gruß, Klaus Übrigens : wenn Die Mauer offen ist, funktioniert das Programm auch, wenn Du die Position des Zielfeldes auf 6/4 oder 6/3 stellst. |
Re: Pathfinding mit A*
Zitat:
Delphi-Quellcode:
function FindPath(Maze: TMazeArray; Start, Dest: TPoint): TPoint;
var i, j : Integer; List : TOpenList; dx, dy : integer; begin for i := Start.X - 1 to Start.X + 1 do begin for j := Start.Y - 1 to Start.Y + 1 do begin if ((i = Start.X) and (j = Start.Y)) // Startfeld nicht in die Liste übernehmen or (Maze[i, j].Wall) // Hindernis nicht in die Liste übernehmen or (i = 0) // linker Spielfeldrand nicht in die Liste übernehmen or (j = 0) // oberer Speilfeldrand nicht in die Liste übernehmen or (i = COUNT) // rechter Spielfeldrand nicht in die Liste übernehmen or (j = COUNT) then // unterer Spielfeldrand nicht in die Liste übernehmen Continue; List.Square.X := i; List.Square.Y := j; dx := abs(dest.x - i); dy := abs(dest.y - j); List.H := (dx + dy) * 10; // Diagonale Felder if ((Start.X - 1 = i) and (Start.Y - 1 = j)) // oben links or ((Start.X - 1 = i) and (Start.Y + 1 = j)) // unten links or ((Start.X + 1 = i) and (Start.Y - 1 = j)) // oben rechts or ((Start.X + 1 = i) and (Start.Y + 1 = J)) then // unten rechts List.G := 14 else // alle anderen List.G := 10; List.F := List.G + List.H; OpenListAdd(List); end; end; for i := 0 to length(OpenList) - 1 do begin DrawSquare(OpenList[i].Square.X, OpenList[i].Square.Y, SPACING, clYellow); TextOutFValue(OpenList[i].Square.X, OpenList[i].Square.Y, SPACING, clYellow, OpenList[i].F); end; //DrawRect(OpenList[MinFInList(OpenList)].Square.X, OpenList[MinFInList(OpenList)].Square.Y, SPACING, clBlue); result.X := OpenList[MinFInList(OpenList)].Square.X; result.Y := OpenList[MinFInList(OpenList)].Square.Y; end; Zitat:
Hier noch mal die Beschreibung des Algorithmusses: Zitat:
|
Re: Pathfinding mit A*
Zitat:
Ja du musst das fehld von dem du kommst in die Geschlossende liste einfügen Zitat:
|
Re: Pathfinding mit A*
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Michael,
ich hab das mal etwas umgeschrieben - entspricht jetzt den Vorgaben und scheint zu funktionieren. Ist allerdings unter delphi 2005 geschrieben, aber das sollte kein größeres Problem sein. Du kannst jetzt Startfeld/Zielfeld und Mauern mit der Maus setzen. Startfeld setzen : Shift Taste drücken und Feld klicken Zielfeld setzen : Ctrl Taste drücken und Feld klicken Mauerfeld setzten/entfernen : Feld klicken Der Stop-Button stoppt die Pfadsuche (falls was nicht funktioniert und das Programm sich festfrißt). Gruß, Klaus |
Re: Pathfinding mit A*
Danke, das war mehr als ich erwartet habe :P , nur jetzt habe ich es ja wieder nicht selber geschrieben. Wo lag denn mein eigentlicher Denkfehler? Desweiteren wollte ich den Code für einen Artikel nehmen, da es für Delphi, da wohl so wa snoch nicht gibt. Und dazu müsste ich den Code natürlich verstehen, deine Kommentare sind aber etwas kurz geraten. ;)
Kennst du zufälligerweise auch noch einen einfachen Algorithmuss, um ein Labyrinth zu erzeugen? |
Re: Pathfinding mit A*
Tja, Michael, was soll ich dazu sagen ?:
Ich bin einfach mal auf die Seite gegangen, auf die der Link in Deinem Beitrag verweist und hab mir dort die Erklärungen durchgelesen. Besser als dort kann man den Ablauf eigentlich nicht beschreiben. Und Dein Fehler?: Zitat:
Zitat:
Zitat:
Gruß, Klaus |
Re: Pathfinding mit A*
Sprich, was mich ins Schleudern gebracht hat, war die Tatsaxhe, dass ich mich nicht genau an die Anleitung gehalten habe. :gruebel:
|
Re: Pathfinding mit A*
moin,
zum Thema Labyrinth erzeugen, bin ich grade auf ein interessantes Pdf gestoßen, vielleich hilft dir das weiter: ![]() |
Re: Pathfinding mit A*
@AmatuerProfi: Ich habe mal deinen Code kommentiert. Stimmt das so:
Delphi-Quellcode:
Danke xineohp, werde ich mir mal angucken.
////////////////////////////////////////////////////////////////////////////////
// FindPath // Comment: A* Algorithmus // Zusammenfassung der A*-Methode // // 1) Füge das Startquadrat der offenen Liste hinzu. // 2) Wiederhole das Folgende: // a) Suche in der offenen Liste nach dem Quadrat mit dem niedrigsten F-Wert. Wir bezeichnen dieses Quadrat im // Folgenden als das aktuelle Quadrat. // b) Verschiebe es in die geschlossene Liste. // c) Für jedes der 8 an das aktuelle Quadrat angrenzenden Quadrate: // Wenn es nicht begehbar ist oder sich bereits in der geschlossenen Liste befindet, ignoriere es; andernfalls // mach das Folgende: // Wenn es nicht in der offenen Liste ist, füge es der offenen Liste hinzu. Trage das aktuelle Quadrat als // Vorgängerquadrat dieses Quadrats ein. Trage zusätzlich die Werte für die F-, G- und H-Kosten dieses Quadrates // ein. // Falls es bereits in der offenen Liste ist, prüfe, ob der Pfad vom aktuellen Quadrat zu ihm - gemessen am // G-Wert -, besser ist, als der Pfad von seinem eingetragenen Vorgängerquadrat (ein geringerer G-Wert bedeutet // einen besseren Pfad). Falls dem so ist, ändere sein Vorgängerquadrat auf das aktuelle Quadrat und berechne // seine Werte für G und F neu. Sofern Du Deine offene Liste nach dem F-Wert sortiert hast, ist möglicherweise // eine Neusortierung dieser Liste erforderlich, um dieser Veränderung Rechnung zu tragen. // // d) Beende den Prozess, falls: // Du das Zielquadrat in die geschlossene Liste verschoben hast; in diesem Fall hast Du den Pfad ermittelt // kein Zielquadrat gefunden werden konnte und die offene Liste leer ist; in diesem Fall gibt es keinen Pfad. // // 3) Sichere den Pfad. Der Pfad erschließt sich, indem Du vom Zielquadrat aus Quadrat für Quadrat rückwärts // schreitend das Startquadrat erreichst function FindPath: boolean; var i, xx, yy : Integer; NewField, ActField: TOpenList; // Feld in die offene Liste aufnehmen procedure AddNewField; begin with newfield, square do begin predecessor := actfield.square; g := actfield.g; if (x = predecessor.x) or (y = predecessor.y) then inc(g, 10) else inc(g, 14); h := 10 * (abs(x - dest.x) + abs(y - dest.y)); f := h + g; end; ListAdd(OpenList, NewField); end; // Feldwerte (H, G und F) berechnen und Vorgängerfeld zu ordnen procedure AdjustFieldData(i: integer); var gg : integer; begin with openlist[i], actfield.square do begin gg := actfield.G; if (x = square.x) or (y = square.y) then inc(gg, 10) else inc(gg, 14); if gg < g then begin g := gg; f := g + h; predecessor := actfield.square; end; end; end; begin Abort := False; SetLength(OpenList, 0); SetLength(ClosedList, 0); FillChar(NewField, SizeOf(NewField), 0); NewField.Square := Start; // Startfeld in die offene Liste einfügen (1) ListAdd(OpenList, NewField); repeat // (2) Application.ProcessMessages; if Abort then break; // Feld mit den geringsten Wegkosten ermitteln i := ClosestField; // (2a) // selbiges Feld in die geschlossene Liste übernehmen MoveToClosedList(i, ActField); // (2b) with NewField, ActField.square do // einmal um das Feld drumrumgehen (2c) for xx := Max(X - 1, 1) to Min(X + 1, count) do for yy := Max(Y - 1, 1) to Min(Y + 1, count) do // if ((xx <> X) or (yy <> Y)) and not wall[xx, yy] then begin Square := Point(xx, yy); // wenn es nicht in der geschlossenen Liste existiert, ignorieren if not Exists(ClosedList, Square, i) then // aber in der geschlossenen, Feld bewerten if Exists(OpenList, Square, i) then AdjustFieldData(i) // ansonsten hinzufügen else AddNewField end; until (int64(ActField.Square) = int64(Dest)) or (length(OpenList) = 0); // (2d) result := (length(OpenList) > 0) and not abort; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:41 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz