![]() |
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 13:21 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-2025 by Thomas Breitkreuz