A* Funktioniert eher suboptimal?

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Neue Antwort erstellen

M0rgenstern

Betreff: A* Funktioniert eher suboptimal?

BeitragSo, Feb 20, 2011 19:41
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo Leute.

Ich habe mich jetzt mal auf gegebenem Anlass mit Wegfindung auseinander gesetzt und versucht den A*-Algorithmus umzusetzen.
Ich hab ihn jedoch (auch aus gegebenem Anlass) so gebaut, dass diagonale Schritte gar nicht erst möglich sind.
Man kann damit also nur waagerecht und senkrecht laufen.
Das funktioniert soweit auch gut.
Jetzt hab ich auch eingebaut, dass man die "Map" bearbeiten kann, also verschiedene Felder teurer zu machen etc. Das klappt soweit auch ganz gut.
Aber: Manchmal kommt es vor, dass er sich an einer Stelle verennt und somit keinen Weg mehr finden kann (es ist verboten auf bereits benutzte Knoten nochmal zu nutzen).
Ich hab hier mal ein Bild als Beispiel:

user posted image

Grün ist der Startknoten, Rot das Ziel und braun die teuren Bereiche.

Die Wegfindung besteht aus folgenden Methoden:
BlitzMax: [AUSKLAPPEN]

'*Searches the way automatically*
Method SearchWay:TList() 'Returns the Closed List
HasFoundWay = False 'Can't have found a way now
ClearLists() 'Has to clear the lists

'*Loop which fills the Closed List*
Repeat
SearchPossibleNewNodes() 'Fills the Open List
If Not (OpenList.IsEmpty()) Then
GetNextClosedNode() 'Adds the cheapest Node to the Closed List
EndIf

'*If the End Node is reached*'
If (HasFoundWay) Then
Return ClosedList 'Return the Closed List
End If

Until OpenList.IsEmpty() 'End the loop if there are no possible Nodes

Return Null 'Return Null
End Method

'*Fills the Open List*
Method SearchPossibleNewNodes()
OpenList.Clear() 'First Clear the List
Local Node:TPathNode = TPathNode(ClosedList.Last()) 'Get the last added Node
Local xPos:Int = Node.iaPos[0]
Local yPos:Int = Node.iaPos[1]

For Local i:Int = -1 To 1 'Only horizontal
If (((xPos + i) >= 0) And ((xPos + i) <= (TGameAttributesProvider.Map.iaMapSize[0] - 1)))
Local PosAr:Int[] = [xpos + i, ypos] 'Same y Position
Local NewNode:TPathNode = TPathNode.CreateAuto(PosAr)
OpenList.AddLast(NewNode) 'Add a new Node to the Open List
EndIf
If(((ypos + i) >= 0) And ((ypos + i) <= (TGameAttributesProvider.Map.iaMapSize[1] - 1)))
Local PosAr:Int[] = [xpos, ypos + i] 'Same x Position
Local NewNode:TPathNode = TPathNode.CreateAuto(PosAr)
OpenList.AddLast(NewNode) 'Add a new Node to the Open List
EndIf
Next

'*Make sure that no Node in the Open List is Also a Node in the Open List*
For Local OpenNode:TPathNode = EachIn OpenList
For Local ClosedNode:TPathNode = EachIn ClosedList
If (OpenNode.iaPos[0] = ClosedNode.iaPos[0]) Then
If (OpenNode.iaPos[1] = ClosedNode.iaPos[1]) Then
OpenList.Remove(OpenNode)
End If
End If
Next
Next
End Method

'*Add the next Node to the Closed List*
Method GetNextClosedNode()
Local ChosenNode:TPathNode = Null

For Local NodeNew:TPathNode = EachIn OpenList
If (ChosenNode = Null) Then ChosenNode = NodeNew

If (NodeNew.iCosts < ChosenNode.iCosts) Then
ChosenNode = NodeNew 'Cheapest Node should be the chosen one
End If
Next

'*Found the way if Position is equal to the Position of the Endnode*
If (ChosenNode.iaPos[0] = EndNode.iaPos[0]) Then
If (ChosenNode.iaPos[1] = EndNode.iaPos[1]) Then
HasFoundWay = True
End If
End If

ClosedList.AddLast(ChosenNode) 'Add the Node to the List
End Method


In dem Spiel, in dem der Algorithmus genutzt werden soll, werden solche Extremfälle gar nicht auftreten (können), aber trotzdem möchte ich das anständig machen.
Kann mir vielleicht jemand weiterhelfen um das Programm zu verbessen? Ich wäre auch schon über Anregungen sehr froh.
Wenn noch Fragen zum Code bestehen, dann fragt einfach, werde es dann erklären.

Danke schonmal,
Lg, M0rgenstern

mpmxyz

BeitragSo, Feb 20, 2011 20:41
Antworten mit Zitat
Benutzer-Profile anzeigen
BlitzMax: [AUSKLAPPEN]
OpenList.Clear() 'First Clear the List

Warum löschst du andauernd alle Einträge dieser Liste?
Wenn der vorerst eingeschlagene Weg in eine Sackgasse führt, sorgt diese Zeile dafür, dass die Alternativen vergessen werden. Das merkst du auch daran, dass die "offene Liste" maximal 8 Einträge besitzt.
Wenn diese Zeile entfernt wurde, kann man feststellen, dass Einträge mehrfach eingefügt werden können.
Da aber beim Abgleich mit der "geschlossenen Liste" immer nur ein Element auf einmal entfernt wird, kann es dann neue Probleme geben.

Nun noch eine Sache:
Du hast dort eine Schleife in einer Schleife in einer Schleife, wobei im Extremfall jede der Schleifen durch einen großen Anteil aller Knoten laufen kann.
->Das macht ein Laufzeitverhalten von O(n^3). (normal bei A-Star: O(log(n)*n))
Wundere dich also nicht, wenn es bei großen Karten zu lange dauert! Wink
mfG
mpmxyz
Moin Moin!
Projekte: DBPC CodeCruncher Mandelbrot-Renderer

M0rgenstern

BeitragSo, Feb 20, 2011 21:05
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey.
Die Liste lösche ich, weil er sonst Felder, die schon in der ClosedList sind als mögliche Felder genommen hatte und dann nicht vom Fleck kam.
Er ist dann ein Feld weitergelaufne und dann kam nix mehr.

Ich hab den Algorithmus so verstanden, dass:
- In der OpenList alle Felder sind, die als nächstes betreten werden können (in meinem Fall übrigens keine 8 sondern höchstens 4 weil Diagonal laufen ausgeschlossen ist).
- In der ClosedList dann das billigste Feld aus der OpenList eingefügt wird und das nächste Feld von diesem aus gesucht wird.

Mein Problem seh ich ja: Wenn ich mich einmal verannt habe, dann kommt er nicht mehr raus. Aber ich weiß im Moment nicht wie ich das anders machen sollte/könnte, damit Fehlschritte bemerkt werden.

Wegen der Laufzeit: Die Repeat - Until Schleife brauch ich ja aufjeden fall.
Ob ich die beiden For Eachin Schleifen umgehen kann muss ich noch sehen, aber momentan eher nicht. Damit wird sicher gestellt, dass sich ein Knoten nicht in beiden Listen befindet.
Im übrigen werden die Karten wahrscheinlich nicht größer als 24*24 Felder, aber die Laufzeit sollte trotzdem nicht so hoch sein, da hast du schon Recht.

Lg, M0rgenstern

mpmxyz

BeitragSo, Feb 20, 2011 21:54
Antworten mit Zitat
Benutzer-Profile anzeigen
Jedes Feld wird aber nur einmal in der OpenList vorkommen, wenn alles richtig läuft.
Sobald es abgearbeitet wurde, wird es dort entfernt und in die ClosedList gesteckt, damit es nicht mehr in die OpenList kommen kann.
Der Fehler, den die nicht korrekte Zeile verhindert, entsteht, weil ein Objekt mehrfach in der OpenList stehen kann. (->Packe das Problem an der Wurzel und versuche nicht, die Symptome bloß zu überdecken!)
Sobald diese Zeile aktiv ist, werden die noch nicht besuchten Felder, an denen das Programm "vorbei gegangen" ist, nicht mehr beachtet, bis es über einen anderen Weg wieder dort ist.
Mache mal einen graphischen Debug-Durchlauf ohne diese Zeile und lasse dir alle Daten zu den Feldern anzeigen. (ähnlich, wie das A-Star-Beispiel vom BlitzMax)
So etwas kann beim Debuggen von Suchalgorithmen ziemlich gut helfen.
mfG
mpmxyz
PS: Fehlschritte werden bei A-Star gar nicht bemerkt. Es geht einfach immer wieder an der günstigsten Stelle weiter. In der Sackgasse gibt es dann irgendwann keine. Daher wird sie dann nicht mehr beachtet.
Moin Moin!
Projekte: DBPC CodeCruncher Mandelbrot-Renderer
  • Zuletzt bearbeitet von mpmxyz am So, Feb 20, 2011 21:57, insgesamt 2-mal bearbeitet

BladeRunner

Moderator

BeitragSo, Feb 20, 2011 21:56
Antworten mit Zitat
Benutzer-Profile anzeigen
Über die Forensuche kannst Du einen von mir implementierten A* für BMax finden, der auch recht ausführlich dokumentiert ist. Vielleicht hilft der dir ein wenig weiter.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

M0rgenstern

BeitragMo, Feb 21, 2011 14:50
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey, ich lass mir den Weg Schrittweise ausgeben.
Wenn ich die Zeile "OpenList.Clear()" weglasse, dann erhalte ich folgendes Bild:
user posted image

Die gelben Felder sind die Felder, die sich in der OpenList befinden. Das blaue Feld ist das letzte Feld in der Closedlist.
In der Openlist stehen also Knoten, die gar nicht mehr beachtet werden dürften.
Hab ich schon früher was falsch implementiert oder was ist da los?
Ich hab mir die Implementation nämlich mal auf Wikipedia angeguckt und glaube es danach verstanden zu haben.
Vielleicht seht ihr ja meinen Denkfehler, weil richtig ist es offensichtlich nicht.
Wäre super, wenn mir jemand auf die Sprünge helfen könnte.

@Bladerunner: Danke, aber ich steig bei deinem Code leider nicht ganz durch.

Lg, M0rgenstern

BladeRunner

Moderator

BeitragMo, Feb 21, 2011 15:05
Antworten mit Zitat
Benutzer-Profile anzeigen
Nein, es ist ganz normal das in der Open list auch zurückliegende Felder sind.
Es wird ja nur dann eines der "alten" Felder abgearbeitet wenn das nächstgelegene keinen Erfolg bot. Abschliessen darfst Du alte Felder erst wenn sie bearbeitet wurden.
Die Open List ist also deine Fahrkarte um garantiert einen gültigen Weg zu finden.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

M0rgenstern

BeitragMo, Feb 21, 2011 15:27
Antworten mit Zitat
Benutzer-Profile anzeigen
Also brauch ich im Prinzip 3 Listen:
- Eine in der alle möglichen Knoten liegen (OpenList)
- Eine in der schon alle (egal wann) überprüften Knoten liegen (ClosedList)
- Eine die die Knoten beinhaltet, die dann den Weg bilden
Oder?
Denn dann kann man x-Schritte züruckgehen wenn man sich verrannt hat.
Also, ist mein Fehler eigentlich, dass alle Knoten in der ClosedList auch direkt meinen Weg bilden?
Ich müsste in der ClosedList ja die Knoten so abspeichern, dass sie auch immer auf den Vorgängerknoten, von dem aus dieser Knoten gefunden wurde, zeigen und dann aus der ClosedList einen Weg in einer Liste speichern.
Stimmt das so?

Lg, M0rgenstern
 

n-Halbleiter

BeitragMo, Feb 21, 2011 15:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Du brauchst zwei Listen:
-Die OpenList, die alle Knoten beinhaltet, die noch nicht erweitert wurden
-Die ClosedList, in der alle schon erweiterten/besuchten Knoten drin sind

Jeder Knoten verweist zusätzlich noch auf den Knoten, von dem aus er erweitert wurde (bzw. der, von dem aus er die niedrigsten F-Kosten hätte). Der Weg wird nur über diese Information rekonstruiert.
mfg, Calvin
Maschine: Intel Core2 Duo E6750, 4GB DDR2-Ram, ATI Radeon HD4850, Win 7 x64 und Ubuntu 12.04 64-Bit
Ploing!
Blog

"Die Seele einer jeden Ordnung ist ein großer Papierkorb." - Kurt Tucholsky (09.01.1890 - 21.12.1935)

M0rgenstern

BeitragMo, Feb 21, 2011 17:14
Antworten mit Zitat
Benutzer-Profile anzeigen
Das ist ja im Prinzip was ich sage:
In der Openlist stehen alle Knoten, die noch nicht näher betrachtet wurden.
Ind der Closedlist stehen alle Knoten, die schon überprüft wurden.
Wenn man dann am Endknoten angekommen ist, dann hat man einen Weg gefunden und kann ihn rückwärts, also vom Endknoten aus, laufen, jeweils über die billigsten Knoten.

Mir war vorher nicht klar, dass der A*-Algorithmus eine Breitensuche betreibt.
Jetzt weiß ich auch, warum ich die Open-List nicht löschen sollte.

Werde das gleich entsprechend umbauen.
Vielen Dank,
Lg, M0rgenstern

EDIT:
Wollte nur kurz Bescheid geben.
Es funktioniert jetzt alles wunderbar.
Sobald mir klar war, dass das Teil als Breitensuche angelegt ist, wusste ich was ich falsch gemacht habe.

Vielen Dank für eure Hilfe und eure Denkanstöße.
Ich denke, ich werde es noch ein wenig modularer bauen und dann ins Codearchiv setzen.

Lg, M0rgenstern

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group