A* Nodes bestimmen bei Dynamischer Map

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu Seite 1, 2  Weiter

Neue Antwort erstellen

 

PhillipK

Betreff: A* Nodes bestimmen bei Dynamischer Map

BeitragDi, Okt 11, 2011 11:01
Antworten mit Zitat
Benutzer-Profile anzeigen
Heyho Community.

Dies ist mittlerweile version 3 des Posts, jeden Tag einen (den ich aber unabgeschickt verworfen hatte, weil mir beim schreiben immer eine weitere idee einfiel)

Leider fruchtet garnichts -.-
Mein Problem ist meine A* wegfindung. Sie funktioniert, sie tut so als würde sie was finden, alles super.
Nachteil:
Ich nutze direkt meinen "map" array (short[,]) als Nodes. Selbst bei 75x75 auflösung des Rasters und einer leicht komplexen map (beispielsweise 2 große bogen, ) ( so etwa, in der mitte der Map als wand) führt dazu, das meine TUnits nicht wirklich oft in bewegung sind.
Der grund ist eventuell die Heuristik die ich verwende und sicher auf die Auflösung der Map. Ich arbeite immer eine Wegfindung nach der anderen ab. Ich habe meinem Programm 10ms gegeben, soviele Nodes abzuarbeiten, wie es nur geht, aufgeteilt in 100 durchläufe pro aufruf. Grund: Weniger variablen initialisierungen etc (bring das bei Blitzmax überhaupt was? naja egal)
Ergebnis ist, das 100 einheiten Kurze pfade in einem Frame abarbeiten, allerdings wenn nur einer mit einem Längeren PFad dazwischen ist, können auch mal 5 sekunden vergehen, bevor sich überhaupt irgendwas wieder bewegt. Ich verzweifel Smile

Nun, beim stöbern bin ich auf dieser seite gelandet: KLICK - das klingt vielversprechend.
Tatsächlich, eine geringere auflösung würde enorm weniger Vergleiche nach sich ziehen - damit könnte ich den weg recht schnell finden.

Allerdings bin ich auch da nicht sicher - wie setze ich bei einer Block/Tilemap eine solche auflösung fest?
Bereits bei einer auflösung von 4x4 Kacheln pro Node könnte es diverse probleme geben:

Code: [AUSKLAPPEN]

X O O X
O X O O
O X O O
X O O X

Angenommen, X ist nicht passierbar, O ist passierbar.
Ausserdem ist die Dritte spalte auch die einzige möglichkeit, die Node darüber irgendwie zu erreichen.

Ich hatte schon etliche ideen der Speicherung, welche alle enorm viele Variablen mit sich bringen würde. Ich erachte das einfach als nicht zulässig Sad

Aber es muss doch irgendwie eine möglichkeit geben, bestimmte wegpunkte in der map zu verteilen. Einfach auf gut glück nach einem raster ausrichten kann auch nicht funktionieren - die gefahr ist zu groß, das die Wegpunkte die einen Schmalen pass am nächsten sind plötzlich in einer Mauer liegen. So wäre der weg - von den Wegpunkten her - unbrauchbar und es bräuchte wieder eine Hochauflösende A* methode, um den weg zu finden.

Die nächste idee die irgendwo auf der seite stand, war das unterteilen der map in "inseln". Das heißt, bestimmte bereiche die komplett umschlossen sind, makieren, und beim starten der A* Methode ersteinmal überprüfen, ob das Ziel innerhalb einer solchen 'Insel' liegt.

Die idee wollte ich ausbauen, prüfen ob der Weg der gefunden wurde, irgendwelche besonderheiten aufweist (zb erstmal weit weg vom ziel -> deutet auf eine kante hin) und diese Besonderen Punkte dann in eine Globale Wegpunktliste aufnehmen. Egal was ich tue, ich komm einfach auf keinen Zweig. Ich fürchte, hier muss ich an die hand genommen werden.


Vielleicht kennt jemand das Problem, vielleicht hat mal einer bei den ganzen aufkommenden Minecraftclones einen eigenen geschrieben und dort auch eine wegfindung eingebaut.
Ansonsten wäre ich auch über richtige Stichworte für die Googlesuche erfreut, hauptsache irgendwelches material, das ich aus meinem Nachdenk-loch rauskomme Sad

Gruß, Phillipk :3

mpmxyz

BeitragDi, Okt 11, 2011 15:19
Antworten mit Zitat
Benutzer-Profile anzeigen
Eine Frage: Nutzt du inzwischen Prioritätswarteschlangen auf Baumbasis für die offene Liste?
Wenn der Algorithmus nämlich nicht optimal ist, ist es nicht verwunderlich, dass es Geschwindigkeitsprobleme gibt.

Es sollte außerdem eine weitere große Optimierungsmöglichkeit geben:
Wandle das Raster in eine günstigere Form um! Das, was A-Star in diesem Fall so langsam macht, ist die Tatsache, dass es viel zu viele gleichwertige Möglichkeiten für den Weg gibt.
Du kannst das Problem vereinfachen, indem du nur folgende Knoten betrachtest:
1a. Randknoten zum begehbaren Bereich (Küste: Wasser<->Land, Klippen: Land<->Abgrund, Nachteil: viele Verbingungsdaten)
1b. Eckknoten am Rand des begehbaren Bereiches (Vorteil gegenüber 1a: Keine direkte Aneinanderreihung von Knoten und dadurch viel weniger Verbindungen)
2. Start- und Endknoten (nur während der Suche vorhanden)
Im Gegenzug müssen einmal am Anfang die möglichen Direktverbindungen zwischen den einzelnen Knoten bestimmt werden. (Was "direkt" heißt, bestimmst du! Die entsprechende Bewegung kann sich auch an das Raster halten.)
Dieser Ansatz verbraucht auch durch die extra gespeicherten Informationen mehr Speicherplatz - bei 100 Knoten maximal 5050 Verbindungen, bei 200 Knoten maximal 20100 Verbindungen, aber dafür wird er - ich habe es nicht getestet, aber die Anzahl an Knoten, die entfernt wird, spricht meiner Meinung nach für sich - viel schneller sein.
mfG
mpmxyz
Moin Moin!
Projekte: DBPC CodeCruncher Mandelbrot-Renderer
 

PhillipK

BeitragDi, Okt 11, 2011 15:57
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey mpmxyz (wasn name Surprised )

Nein, ich nutze noch keine Prioritätswarteschlange.
Ich habe HamZta's code eingebaut und getestet - das ergebnis war erschreckend.
An und für sich ist das ein super code, solange nicht zuviele unterschiedliche prioritäten darin vorkommen.
In seinem beispiel mit den 4 Prio's leuchtet mir das ganze noch wunderbar ein.
Das finden des günstigsten hat sich als performance armer rausgestellt, das das ständige neu linken des Data array's beim einfügen.

Sprich: Immernoch Tlist.

Allerdings hatte ich ein anderes Konstrukt im sinn, das ähnlich funktioniert aber beim einfügen in den meisten fällen schneller sein sollte:

BlitzMax: [AUSKLAPPEN]
Type THeap
Field _first:TNode
Field _last:Tnode

Field _nodeCnt:Int
Field _nodeAVGprio:Float
End Type

Type TNode
Field _data:Object
Field _priority:Object

Field _prevLink:TNode
Field _nextLink:TNode
End Type

Idee: _nodeAVGprio enthält die durchschnittliche priorität aller nodes und beim Adden wird geschaut, ob es mehr sinn machen würde, vorne oder hinten mit der suche anzufangen.
Das ungetüm ist allerdings ungeschrieben, ich plane es nur eventuell, wenn ich eine genauere auswertung der geschwindigkeiten aller Parts meiner A* funktion durchgeführt habe und die TList tatsächlich der größte speedverlust ist.


-------------

Zu 2:

"gleichwertige Möglichkeiten" - meinst du damit, das es (in einem Komplexen fall) viele Knoten gibt, die relativ ähnliche F_costs aufweisen?
Das ganze dick auszulagern und mit einer groben vorprüfung versehen ist grade das einzige, was mir noch einfällt. Ich verstehe zwar nicht genau, was du mir mit 1a) und 1b) vermitteln willst, allerdings klingt es recht ähnlich meinem vorhaben.

Ah, nun hab ichs, ich weiß was du mir sagen willst Very Happy

1a) Empfielt, jede Kachel die begehbar ist und an eine Wand endet, mit einer Node zu versehen. 1b) klingt nach formsuche: Jede begehbare Linie wird auch als solche aufgefasst und an deren enden wird ein Knoten gesetzt.

Ich weiß zwar noch nicht, wie ich die direktverbindungen bestimmen soll, aber es ist auf jedenfall ein anfang. Leider ist die Map ziemlich unsauber (hier mal ein Png render meiner Map [WARNUNG: 3.072px × 2.816px groß! https://www.blitzforum.de/upload/file.php?id=10964 - diese wird im endeffekt mit dem A* system bestückt!)
DH in beiden fällen werden auf jedenfall eine menge Datensätze anfallen.
Gut, das lässt sich noch eingrenzen, indem ich zb nur besuchte bereiche *verknote* - aber über kurz oder lang kommt da eine menge zusammen.

Dennoch, ich schreibs mir schonmal auf die Todo. Solang es erstmal funktioniert, will ich mich über das bisschen ram nicht beklagen *g* (hey das bringt mich drauf, ich könnte mal einen kleinen test machen - Wegpunkte sterben nach einer gewissen zeit aus, wenn sie nicht vom A* als "guter weg" erkannt werden - damit werden sich langsam aber sicher die wichtigsten punkte herauskristalisieren, oder? )

*grübel*
Heute ist leider nixmehr mit ausprobieren, aber morgen früh, punkt 06:30 werd ich mich gleich dran machen und deine vorschläge in erste Tests umschreiben.

(mir hat sich grade noch ein ungetüm der node platzieren ergeben. 1b) - dazu eine linienbildung von 2 oder 3 nodes, dann die bestimmung des normalvektors, vektor verlängern bis ich eine gedachte linie zwischen 2 weiteren Nodes schneide - länge halbieren, node platzieren. Damit dürfte ich die ungefähre mitte der flächen erhalten. Nun muss man nurnoch ein wenig basteln um die genauen ecken der flächen rauszufinden und die überschüssigen Knoten wieder entfernen - Das ganze im Kopf durchgespielt dürfte echt super platziere knoten geben - aber meine CPU würde mir einen husten, solch bescheuerte rechnungen machen zu müssen *g*)

Ich danke dir schoneinmal recht herzlich, mpmxyz Smile

Nachtrag:
Oh schreck!
Ich habe grade versucht, meine idee der Priority heap sache umzusetzen, Basierend auf verlinkungen untereinander.
Vorteil: Dynamischer, noch schnelleres auslesen der daten, höchste und niedrigste Priorität möglich.
Nachteil: Langsamer als die Methode von HamZta

und nun kommts: Mein Speedtest hat folgendes ergeben:
Code: [AUSKLAPPEN]
------------------ Priority_new system durchgearbeitet --------------

Einfügen von 10000 objekten: 1674

Auslesen von 10000 objekten: (sortiert) 3

------------------ TList durchgearbeitet --------------

Einfügen von 10000 objekten: 18

Auslesen von 10000 objekten: (sortiert) 23604

------------------ TPriorityHeap (by HamZta :) ) durchgearbeitet --------------

Einfügen von 10000 objekten: 119

Auslesen von 10000 objekten: (sortiert) 67


Das oberste ist meine version, das unterste die von Hamzta.
Die TList variante ist 1zu1 so, wie ich sie in meinem A* algo nutze.
Ich geh kaputt, ich habe HamZta's Type ganz umsonst verdächtigt :O
Der grund, warum es mit seinem Type langsamer war, ist definitv NICHT durch das Type selber, sondern ein - noch unidentifizierter fehler - meinerseits.

Der Test oben ist übrigends im debug. Im release braucht mein krams 200ms zum eintragen, 1ms zum auslesen, seins aber nur 64ms + 9ms.

Tja, das heißt alles neu schreiben und HamZta's Priority Heap verwenden :O *freu*

Midimaster

BeitragMi, Okt 12, 2011 12:42
Antworten mit Zitat
Benutzer-Profile anzeigen
sind deine Testergebnisse für das 75x75er Feld gewesen, oder für die große 3000x2800 Map?

Beschreib mal, was der Finde-Algo genau können muss. Vleiiecht hab ich das was für dich....
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe
 

PhillipK

BeitragMi, Okt 12, 2011 13:00
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich glaube, das von HamZta ist schon mit eines der oberen Optimums die ich dafür bekommen kann.

Sinn von AStern ist, das es mögliche nachbar felder der knoten auswertet und ihre ungefähren wegkosten bestimmt. Die niedrigsten Wegkosten kriegen gleichzeitig die höchste priorität zugeschrieben, das sie wahrscheinlicher zum Ziel führen.

Das heißt, es muss eine Sortierte liste her, welche nach möglichkeit direktzugriff auf den 'Wichtigsten Knoten' bietet und auch beim einfügen nicht sehr lange braucht. Das scheint - nach den testergebnissen auf 100.000 durchläufe - mit HamZta's code der fall zu sein.

Der Test bezog sich auf einen direkten vergleich mit identischen umständen.
Hierzu habe ich einen Array der Länge 100.000 mit ebensovielen Random-integern gefüllt und diese in die Listen einsortiert. Die randomzahl der array einträge war gleichermaßen ein String mit den Zahlen als auch die Priorität. Identische bedingungen also Smile

Midimaster

BeitragMi, Okt 12, 2011 13:44
Antworten mit Zitat
Benutzer-Profile anzeigen
ich wollte das eher ganz praktisch auf das Feld bezogen wissen...

Wie "schnell" findet der akt. Algo denn nun den Weg? Wie groß war das Array? 100.000 bedeutet 320x320?
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe
 

PhillipK

BeitragMi, Okt 12, 2011 13:50
Antworten mit Zitat
Benutzer-Profile anzeigen
Das kann ich grade garnicht so genau sagen.

Ich nehme grade einen kompletten Rewrite vor , da das letzte system beständig um ideen erweitert wurde. Hier haben sich wohl ein paar fehler eingeschlichen.

Vorher war es unterschiedlich - je Komplexer die wegfindung, desto länger, logischerweise. Bei den alten tests habe ich pro frame etwa 1000 Knoten überprüfen können, bevor es unter 60fps gerutscht ist.
Das ganze war so aufgebaut, das ich meiner funktion ein paar MS zeit gegeben habe, bestimmt durch den vorangegangenen Mainloop-code. In der Zeit konnte er etwa 10 durchläufe starten, mit je 100 knoten zu prüfen.

Ich brauche noch zirka 20minuten, dann habe ich den Testcode soweit wieder Flott. Diesmal besser sortiert, vollends durchdacht von anfang an und mit HamZta's heapnode system. Danach werde ich auch explizite Zeitmessungen durchführen, um festzustellen, welche programmteile die meiste "zeit" benötigen.

Zu den 100.000: Das ganze war ein Testcode, der nichts weiter tut, als die 3 sortiermöglichkeiten exakt gegeneinander zu testen. Das war keineswegs auf die Testmap bezogen, diese hatte ein 75x75 Raster (5600 knoten odersowas Smile ). Die 100.00 ist möglichst hoch gewählt, allerdings so, das keine Kaffepause drin war *g*

Midimaster

BeitragMi, Okt 12, 2011 15:22
Antworten mit Zitat
Benutzer-Profile anzeigen
ich hab da mal was für dich:

#EDIT Version 1.03:#

https://www.blitzforum.de/upload/file.php?id=11018

ist eine demo meines Wegfindungs-Algos. lege mit der linken Maus einen Punkt fest, von dem der Weg zum Ziel (ca. Bildmitte) gefunden werden soll. Du kannst es beliebig wiederholen.

Lege mit der rechten Maus einen neuen Zielpunkt fest. Die Grün-Tönung verrät Dir schon viel über den Algo. Je heller ein Feld, desto näher ist es beim Ziel.

mit "R" kannst du ein neues Random-Feld erzeugen. Danach muss man durch Eingabe von <1> bis <5> bestimmen, wieviele Mauern prozenztual zu sehen sein sollen.

mit <M> kommst du ins Malen. Dann mit der Maus malen, dann mit <ESC> wieder zum Wegtesten zurück..


das Problem mit dem Aufhängen habe ich auch gelöst. Nun kann man auch hoffnungslose Positionen anklicken und das Programm merkt, dass es keine Verbindung gibt.

Aber Vorsicht: das Test Programm ist ohne Fehlerabfragen: das bedeutet: halte dich von den Rändern fern. (wenn doch geklickt: beendest du es mit STRG-ALT-ENTF)
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe
  • Zuletzt bearbeitet von Midimaster am Mi, Okt 12, 2011 17:59, insgesamt 2-mal bearbeitet
 

PhillipK

BeitragMi, Okt 12, 2011 15:26
Antworten mit Zitat
Benutzer-Profile anzeigen
Schaut super aus.
Soweit ich das sehen konnte, ist rot ne mauer.
Wenn ich mit der Rechten maustaste klicke, sehe ich immer ein gewissen bereich, was so betrehtbar ist. Was ist das genau?

Meine A* methode ist leider noch am scheitern, ich hab ne abfrage vermurxt, sodass er nur den startknoten überprüft :-/

Midimaster

BeitragMi, Okt 12, 2011 15:33
Antworten mit Zitat
Benutzer-Profile anzeigen
Der Effekt zeigt beim ersten Start, von welche Feldern überhaupt nicht das Ziel erreicht werden kann.


Der Effekt zeigt nach einem RECHTE MAUS, dass nur ein Bereich des Feldes gescannt werden musste um die Verbindung zu finden. Betretbar ist nun aber auch der graue Bereich ( solange du in dieser Testversion keinen abgeschlossenen Raum anklickst).

Witzig dabei sind die Zeiten, die fast immer unter 2msec liegen. Wen mal was längeres angezeigt wird, dann nur deshalb, weil das Zeichnen mitdazukam!
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe
 

PhillipK

BeitragMi, Okt 12, 2011 15:47
Antworten mit Zitat
Benutzer-Profile anzeigen
Kannst du eine kleine Wand-dazumalen funktion einbauen?

Mein größtes problem bei den wegfindungen waren bis jetzt immer mauerteile, die ähnlich einem C aussahen.

Code: [AUSKLAPPEN]

---------------------------------------------
|                 _________________         |
|               /                                         |
|             /                                          |
|           /                                            |
| ZIEL  |                       START             |
|           \                                            |
|             \                                          |
|               \__________________       |
|                                                        |
---------------------------------------------

(mal eine kleine asciiskizze dazu)

In diesem Gebilde durchsucht ein A* fast immer den kompletten kasten, da dies der einfachste weg zu sein scheint (heuristik!).
Dh man unmengen abfragen, obwohl der weg ganz einfach zu sein scheint.

Genau wegen solchen (zwar ziemlich unmöglichen, aber dennoch auftretetenden gebilden) versuche ich grade, eine grobe Vorprüfung zu bauen.
Beim Testen habe ich solche Mauern auch als "worst case" ausgemacht und versuche nun, eine solche Wegfindung zu beschleunigen, sodass mindestens 50 einheiten relativ schnell (dh weniger als 5sek standzeit pro Einheit) umher wandern kann.

Meine testmap mit dem simplen AStern algo läuft nun wieder. Ich werde mich gleich mal ans einbauen von Debug-DrawText's machen, damit ich auch eine dauer-anzeige habe Smile

Midimaster

BeitragMi, Okt 12, 2011 16:12
Antworten mit Zitat
Benutzer-Profile anzeigen
ich habe es geupdatet:

https://www.blitzforum.de/upload/file.php?id=11018

mit "R" kannst du ein neues Random-Feld erzeugen.

mit <M> kommst du ins Malen. Dann mit der Maus malen, dann mit <ESC> wieder zum Wegtesten zurück

das Problem mit dem Aufhängen habe ich auch gelöst. Nun kann man auch hoffnungslose Positionen anklicken und er merkt, dass es keine Verbindung gibt.
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe
  • Zuletzt bearbeitet von Midimaster am Mi, Okt 12, 2011 17:59, insgesamt 2-mal bearbeitet
 

PhillipK

BeitragMi, Okt 12, 2011 16:18
Antworten mit Zitat
Benutzer-Profile anzeigen
schnüff^^

Ich habe meine Funktion auch mit einer Dauer-ausgabe versehen.
Nun habe ich in beiden programmein ein ähnliches feld gebastelt - e voila - deins braucht 4ms, meins läuft immernoch.

Bei Komplexen wegen gibts bei mir grade derbe Performance einbrüche, ich denke da habe ich irgendwo eine abfrage falsch.
Das ganze muss ich nocheinmal genauer durchschauen, denn es kann weder die Knoten-findung sein, noch die Abfragen oO

Zur sicherheit poste ich hier mal den Code:

BlitzMax: [AUSKLAPPEN]

Type TAStern2D
Global list:TList = CreateList()

Field _offen:TPriorityHeap = New TPriorityHeap
Field _geschlossen:Byte[,]
Field __offenArr:TKnoten[,]

Field zielX:Int
Field zielY:Int

Field map:Short[][]
Field sizeX:Int
Field sizeY:Int

Field impossible:Byte = 0

Field dauer:Int = 0

Function Create:TAStern2D(startX:Int, startY:Int, zielX:Int, zielY:Int, mapArray:Short[][], attach_to_list:Byte = False)
Local as:TAStern2D = New TAStern2D

If mapArray[zielX][zielY] > 0 Then Return Null

as.zielX = zielX
as.zielY = zielY

as.map = mapArray

as.sizeX = Len(mapArray)
For Local i:Int = 0 Until as.sizeX
'findet einen brauchbaren punkt - geht von einem "quadratischen" array aus!
If mapArray[i] Then
as.sizeY = Len(mapArray[i])
Exit
End If
Next

as._geschlossen = New Byte[as.sizeX, as.sizeY]
as.__offenArr = New TKnoten[as.sizeX, as.sizeY]
Local knoten:Tknoten = TKnoten.Create(startx, starty, zielx, ziely, Null, 0)

as._offen.add(knoten.f_costs, knoten)
' Print "Startnode eingefügt"
If attach_to_list Then TAStern2D.list.AddLast(as)

as.dauer = MilliSecs()

Return as

End Function

Method Update:Int[][] (times:Int)
Local knoten:TKnoten
Local x:Int, y:Int
Local step_x:Int, step_y:Int
Local newNode:TKnoten

'Geht TIMES x die knoten ab. Dh es werden TIMES knoten bearbeitet.
For Local i:Int = 0 Until times
'holt den Besten knoten.

If Self._offen.isEmpty() Then
TAStern2D.list.Remove(Self)
Self.impossible = 1
Return Null
End If
knoten = TKnoten(Self._offen.removeHighest()._data)

_geschlossen[knoten.posx, knoten.posy] = 1 'in GESCHLOSSEN eintragen.
__offenArr[knoten.posx, knoten.posy] = Null 'aus OFFEN entfernen.

'position setzen
x = knoten.posx
y = knoten.posy

If x = zielX And y = zielY Then
Return Self.BuildWaypoint(knoten)
End If

'umliegende punkte überprüfen.
For step_x = x - 1 To x + 1 Step 1
For step_y = y - 1 To y + 1 Step 1
'Print "bla"
'out of bounds: Continue.
If step_x < 0 Or step_y < 0 Or step_x >= sizeX Or step_y >= sizeY Then
' Print "X: " + step_x + " Y: " + step_y + " - Out of bounds"
Continue
EndIf
'Wallcheck
If Self.map[step_x][step_y] > 0 Then
' Print "X: " + step_x + " Y: " + step_y + " - Wand"
Continue
EndIf 'wand.

'geschlossen: Continue.
If _geschlossen[step_x, step_y] Then
' Print "X: " + step_x + " Y: " + step_y + " - geschlossen"
Continue
EndIf

Local g:Int = 14 - ((x = step_x Or y = step_y) * 4) '10 für Graden schritt, 14 für einen Schrägen schritt. Die abfrage ergibt 0, wenn diagonal.

'Check, obs ein diagonaler schritt an einer wand lang wäre!
If g = 14 Then
Local dirX:Int = Sgn(x - step_x)
Local dirY:Int = Sgn(y - step_y)

If dirX = 1 And step_x > 0 And map[step_x - 1][step_y] > 0 Then
Continue
ElseIf dirX = -1 And step_x < sizeX - 1 And map[step_x + 1][step_y] > 0 Then
Continue
ElseIf dirY = 1 And step_y > 0 And map[step_x][step_y - 1] > 0 Then
Continue
ElseIf dirY = -1 And step_y < sizeY - 1 And map[step_x][step_y + 1] > 0 Then
Continue
EndIf
End If

'Check: Ist in offen?
If __offenArr[step_x, step_y] Then
'knoten ist offen. G vergleich.
If __offenArr[step_x, step_y].g_costs > knoten.g_costs + g Then
'umschreiben.
__offenArr[step_x, step_y].ReLink(knoten, g)
Continue
End If
End If

'nicht geschlossen, nicht offen, nicht out of bounds: einfügen.
newNode = TKnoten.Create(step_x, step_y, zielX, zielY, knoten, g + knoten.g_costs)
__offenArr[step_x, step_y] = newNode
_offen.add(newNode.f_costs, newNode)
' Print "node eingefügt."
Next
Next
Next
End Method

Method BuildWaypoint:Int[][] (knoten:TKnoten)
Local ret:Int[][]

ret = ret[..Int(knoten.g_costs / 10) + 1]

Local i:Int = Len(ret) - 1
Local a:Int = 0
While knoten
ret[i] = New Int[2]

ret[i][0] = knoten.posx
ret[i][1] = knoten.posy

i:-1
a:+1

knoten = knoten._prev
Wend
Print "len vorher: " + Len(ret)
Print "A: " + a + " i: " + i + " len nach slicen: " + Len(ret[i..])
Return ret[i..]
End Method
End Type

Type TKnoten
Field posx:Int
Field posy:Int

Field f_costs:Int
Field h_costs:Int
Field g_costs:Int

Field _prev:TKnoten

Function Create:TKnoten(posx:Int, posy:Int, zielX:Int, zielY:Int, previous_node:TKnoten, g_costs:Int)
Local knoten:TKnoten = New TKnoten

knoten.posx = posx
knoten.posy = posy

Local h:Int = 14 * Max(Abs(posx - zielX), Abs(posy - zielY))

knoten.h_costs = h
knoten.g_costs = g_costs
knoten.f_costs = g_costs + knoten.h_costs

knoten._prev = previous_node

Return knoten
End Function

Method Relink(knoten:TKnoten, g_costs:Int)
Self._prev = knoten
Self.g_costs = g_costs + knoten.g_costs
Self.f_costs = Self.h_costs + Self.g_costs
End Method
End Type


Das ganze nutzt folgende Funktion (von hamZta!):
BlitzMax: [AUSKLAPPEN]
Const BLOCK_SIZE:Int = 16

Type TPriorityHeap

Field _data:THeapNode[]
Field _heapPtr:Int

Method New()
_data = New THeapNode[BLOCK_SIZE]
End Method

Rem
Adds a new element to the heap
End Rem

Method add(priority:Int, obj:Object)
_reallocateHeap()

' add the element at the end of the heap
_data[_heapPtr] = THeapNode.Create(priority, obj)
_heapPtr:+1

_siftUp(_heapPtr-1)
End Method

Rem
Adds a new element to the heap
End Rem

Method addNode(node:THeapNode)
_reallocateHeap()

' add the element at the end of the heap

_data[_heapPtr] = node
_heapPtr:+1

_siftUp(_heapPtr-1)
End Method

Rem
Returns true if the heap is empty
End Rem

Method isEmpty:Byte()
Return (_heapPtr = 0)
End Method

Rem
Returns the element with highest priority from the heap
End Rem

Method removeHighest:THeapNode()

' no nodes left
If _heapPtr = 0 Return Null

Local tmpNode:THeapNode = _data[0]
' swap lowest with last heap item
_data[0] = _data[_heapPtr-1]
_heapPtr:-1
' restore heap condition
_heapify(0)
Return tmpNode
End Method

Rem
allocates new space block wise for extra speed
End Rem

Method _reallocateHeap()
If _heapPtr => _data.Length-1
_data = _data[.._data.length+BLOCK_SIZE]
EndIf
End Method

Rem
Restores the heap condition after inserting a new item
End Rem

Method _siftUp(i:Int)
Local father:Int = Floor(i/2)
While i > 0 And _data[i].priority() < _data[father].priority()
Local tmpNode:THeapNode = _data[i]
_data[i] = _data[father]
_data[father] = tmpNode
i = father
father = Floor(i/2)
Wend
End Method

Rem
Heap items with lower priority go down the heap until the heap condition is met
End Rem

Method _heapify(i:Int)
Local leftId:Int = 2*i
Local rightId:Int = 2*i+1
Local smallest:Int = i

If (leftId < _heapPtr) And (_data[leftId].priority() < _data[i].priority())
smallest = leftId
End If

If (rightId < _heapPtr) And (_data[rightId].priority() < _data[smallest].priority())
smallest = rightId
End If

If i <> smallest
Local tmpNode:THeapNode = _data[i]
_data[i] = _data[smallest]
_data[smallest] = tmpNode
_heapify(smallest)
End If
End Method

Rem
DEBUG prints out the heap
End Rem

Method prnt()
For Local i:Int = 0 Until _heapPtr
Print _data[i].priority()
Next
End Method

Rem
Eine Contains - funktion.
end rem

Method Contains:Byte(Value:Object)
For Local i:Int = 0 Until _heapPtr
If _data[i]._data = Value Then Return 1
Next
Return 0
End Method
End Type

Rem
The heap node stores the priority and the associated object
End Rem

Type THeapNode

Field _priority:Int
Field _data:Object

Rem
Creates a new heap node and returns it
End Rem

Function Create:THeapNode(priority:Int, data:Object)
Local tmpNode:THeapNode = New THeapNode
tmpNode._priority = priority
tmpNode._data = data
Return tmpNode
End Function

Method priority:Int()
Return _priority
End Method

Method data:Object()
Return _data
End Method

End Type


Und hier das Testprogramm dazu:

BlitzMax: [AUSKLAPPEN]
SuperStrict

Include "AStern Type.bmx"
Include "TPriorityHeap.bmx"

Graphics 800, 600

Const rasterSize:Int = 100

Local land:TLandscape = New TLandscape
Local timer:TTimer = CreateTimer(62)
Local unit:TUnit = Tunit.Create(1, 1, land.map)

Global aStern_dauer:Int = 0

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
Cls

Local oriX:Float = 0
Local oriY:Float = 0

GetOrigin(oriX, oriY)
oriX:+KeyDown(KEY_A) * 3.5 - KeyDown(KEY_D) * 3.5
oriY:+KeyDown(KEY_W) * 3.5 - KeyDown(KEY_S) * 3.5
SetOrigin(oriX, oriY)

Local mx:Int = MouseX() - oriX
Local my:Int = MouseY() - oriY

If MouseHit(1) Then
unit.AStern = TAStern2D.Create(unit.posx, unit.posy, (mx) / 10, (my) / 10, land.map, 1)
EndIf
If MouseDown(2) Then
land.setBlock(mx / 10, my / 10, 1)
End If


TUnit.Update()

land.draw()
SetColor(255, 255, 255)
DrawText("Dauer: " + aStern_dauer, -oriX + 50, -oriY + 50)

Flip 1

WaitTimer(timer)

Wend


Type TLandscape
Field map:Short[][]

Method New()
Self.map = Self.map[..rasterSize]
For Local i:Int = 0 Until rasterSize
Self.map[i] = New Short[rasterSize]
Self.map[i][0] = 1
Self.map[i][rasterSize - 1] = 1
Next
For Local a:Int = 0 Until rasterSize
Self.map[0][a] = 1
Self.map[rasterSize - 1][a] = 1
Next
End Method
Method draw()
Local x:Int, y:Int
Local sizeY:Int
SetColor(200, 200, 200)
For x = 0 Until Len(map)
sizeY = Len(map[x])
For y = 0 Until sizeY

If map[x][y] = 1 Then
DrawRect(x * 10 - 5, y * 10 - 5, 10, 10)
End If

Next
Next
End Method

Method SetBlock:Int(x:Int, y:Int, id:Int)
If x < 0 Or y < 0 Then Return - 1

If x >= Len(map) Or y >= Len(map[x]) Then Return - 1

map[x][y] = id
End Method
End Type

Type TUnit
Global list:TList = CreateList()

Field posx:Int
Field posy:Int

Field _X:Float
Field _Y:Float

Field aStern:TAStern2d

Field waypoints:Int[][]

Function Create:TUnit(posx:Int, posy:Int, mapArr:Short[][])
If posx < 0 Or posy < 0 Or posx >= Len(mapArr) Or posy >= Len(mapArr[0]) Then Return Null

If mapArr[posx][posy] = 1 Then Return Null

Local unit:TUnit = New TUnit

unit.posx = posx
unit.posy = posy

TUnit.list.AddLast(unit)

Return unit
End Function
Function Update:Int()
SetColor(255, 0, 0)
For Local unit:TUnit = EachIn TUnit.list
unit._update()
unit._draw()
Next
End Function

Method _Update:Int()
If AStern Then
Local waypoints:Int[][] = AStern.Update(100)
If Not waypoints Then
If AStern.impossible Then
' Print "Astern:: Impossible"
Self.AStern = Null
End If
Else
' Print "Astern:: Erfolgreich - eingetragen!"
aStern_dauer = MilliSecs() - Self.AStern.dauer
Self.waypoints = waypoints
Self.AStern = Null
End If
EndIf

'wegpunkte abgehen.
Local i:Int
Local wp:Int[]
For i = 0 Until Len(Self.waypoints)
If Self.waypoints[i] Then
wp = Self.waypoints[i]
Exit
End If
Next

If wp Then
Local dirX:Int = Sgn(Self.posx - wp[0])
Local dirY:Int = Sgn(Self.posy - wp[1])

Self._X:-dirX / 1.0
Self._Y:-dirY / 1.0

If Self._X <= - 10 Then
Self.posx:-1
Self._X = 0
ElseIf Self._X >= 10 Then
Self.posx:+1
Self._X = 0
EndIf
If Self._Y <= - 10 Then
Self.posy:-1
Self._Y = 0
ElseIf Self._Y >= 10 Then
Self.posy:+1
Self._Y = 0
EndIf

If Self.posx = wp[0] And Self.posy = wp[1] Then
Self.waypoints[i] = Null
End If
End If
End Method

Method _draw()
DrawOval(posx * 10 - 5 + _X, posy * 10 - 5 + _Y, 10, 10)
End Method
End Type


Sollte eigentlich alles mit dem Identisch sein, was ich in den ersten versionen auch habe. Tatsächlich kommt es mir so vor, als würde mit jedem Compilen meine funktion Länger brauchen o.o

Ps: In AStern ist eine Abfrage für Schrägbewegungen an einer mauer entlang enthalten. Diese ist zu missachten. Ist noch verdreht :>

Midimaster

BeitragMi, Okt 12, 2011 16:24
Antworten mit Zitat
Benutzer-Profile anzeigen
ich mach das gar nicht mit a* sondern mit was eigenem Smile
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe
 

PhillipK

BeitragMi, Okt 12, 2011 16:29
Antworten mit Zitat
Benutzer-Profile anzeigen
Gut, raus mit der Sprache ^^

Ausser es ist geheim :3

Ich wollte die tage mit einem Wegpunktesystem anfangen, worauf mich eine bekannte gebracht hat.
Idee : Alle X felder die eine einheit zurückgelegt hat, wird geprüft ob ein neuer, globaler Wegpunkt geschaffen wird, welcher seine nachbarn kennt.
Das könnte zum erschaffen ein wenig Berechnung brauchen, hat aber den sinn, das 2 Wegpunkte dann miteinander verbunden sind, wenn sie weniger als 50 Knoten berechnung als A* brauchen.

Das ist im allgemeinen Ziemlich schnell, da meist auch eine direkt (oder indirekte) sichtverbindung herscht - man kann die einheiten bequehm von punkt zu punkt tingeln lassen und die wege sogar noch wärend des Laufens in ihrerer vollen form berechnen lassen.

Zu meinem code:

Kannst du grade - rein zufällig - erkennen, ob ich irgendwo einen Leak in die Astern-update methode eingebaut habe?
Je weiter die Suchtiefe vorran schreitet, desto länger brauchen die einzelnen update aufrufe.
Hab das grade nachgeprüft, indem ich bei unit.astern.update() (hauptprogramm) eine ms-differenz bestimmt habe und diese ausgegeben habe.
Die ausgabe war eindeutig: Gute 20-30% länger als der vorherige aufruf, ab und an eine zurücksetzung Sad

Midimaster

BeitragMi, Okt 12, 2011 17:02
Antworten mit Zitat
Benutzer-Profile anzeigen
also hier:

der Code ist eher symbolisch zu verstehen. Ich hab ihn gleich mal umfangreich kommentiert. Die Vorgehensweise ist folgende:

Angefangen wird beim Ziel. Zuerst scanne ich alle Nachbarfelder des Ziels. Von hier aus wäre 1 Kosten nötig, um zum Ziel zu kommen. Felder, die Mauern sind, werden gleich erst gar nicht aufgenommen. Diese bis zu 4 Nachbarfelder kommen nun in die Liste.

Die Liste wird von vorne bearbeitet und von allen wieder die Nachbarn gescannt. Wieder verwerfe ich alle Mauern, aber auch gleich alle Nachbarn, die bereits früher besser (mit weniger Kosten) gescannt waren.

Um frische Felder von bereits gescannten zu unterscheiden haben alle "frischen" Felder beim Start eine 999 als Kosten bekommen.

Langsam entstehen sowas wie ISO-Kreise um das Ziel. Und irgendwann ist plötzlich das Startfeld dabei.

Der eigentliche Weg wird nun rückwärts (also von Start zum Ziel) gefunden. Ich muss nur noch den Kosten in den Nachbarfelder Checked[x,y] so folgen, dass ich immer dem kleinsten Wert folge, bis ich am Ziel bin.



BlitzMax: [AUSKLAPPEN]

Global Feld%[75,75] 'ist das eigentliche Spielfeld

Global Checked%[75,75] ' ist für die Messergebnisse

Type TStelle
Field x%,y%
End Type

' Trick I: es wird so getan als wären es zu jeden Punkt des Spielfeldes anstrengende 999 Schritte:
For Local i%=0 To 74
For Local j%=0 To 74
checked[i,j]=999
Next
Next




' erstes Objekt in der Liste ist die ZielPos:
Local Stelle:TStelle=New TStelle
Stelle.X=ZielX
Stelle.Y=ZielY
ListAddLast Liste , Stelle

Repeat

Local i%=0
For Local loc:TStelle = EachIn Liste
i=i+1
' das ist der Trick II, dank i% werden immer erst alle alten TList-Einträge abgearbeitet:
' (Anmerkung: geht auch ohne dies, solange die Kosten überallhin gleich sind)
If i>Anzahl Then Exit
Scanne loc
Next
'Die Liste wird mittendrin abgebrochen, die Elemente gezählt und dann wieder von vorne begonnen
' bevor es von vorne losgeht, könnte man hier sortieren
Anzahl=CountList(Liste)

' Abbruch, wenn alle Felder gescannt sind:
If Anzahl=0 Then Exit

' oder Abbruch, wenn das Ziel entdeckt wurde:
Until Checked[SuchX,SuchY]<999

ZeigeWeg StartX, StartY

End




Function Scanne()
Local Preis%
X=loc.X
Y=loc.Y
Preis=Checked[x,y]
' zunächst wird die aktuelle Stelle aus der Liste gelöscht
ListRemove liste, loc

' dann werden ihre 4 Nachbar möglicherweise aufgenommen:
Aufnehmen (X+1,y,Preis+1)
Aufnehmen (X-1,y,Preis+1)
Aufnehmen (X,y+1,Preis+1)
Aufnehmen (X,y-1,Preis+1)
End Function




Function Aufnehmen(X%,Y%,Preis%)
' nur ein Test gegen die Spielfeldränder
If x<0 Or x>74 Or y<0 Or y>74
Return
' ein Test auf Mauer an dieser Stelle
ElseIf Feld[x,y]=1
' Checked=0 bedeutet unerreichbar
Checked[x,y]=0
Return
EndIf

' jetzt der Check auf bereits früher gecheckt:
If Checked[x,y]>Preis
' es war noch nie so leicht hierher zu kommen, also Aufnahme:
Checked[x,y]=Preis
Local Stelle:TStelle=New TStelle
Stelle.X=X
Stelle.Y=Y
ListAddLast Liste , Stelle
EndIf
End Function




Function ZeigeWeg(X%,y%)
Repeat
' was kostet es hier?
Wert=Checked[x,y]
' und wohin (4 Himmelsrichtungen)wäre es billiger?
If Checked[x-1,y]= wert-1
x=x-1
ElseIf Checked[x+1,y]= wert-1
x=x+1
ElseIf Checked[x,y-1]= wert-1
y=y-1
ElseIf Checked[x,y+1]= wert-1
y=y+1
EndIf

Print "gehe nach " + x + " " + y
'dort, wo die kosten 0 sind, haben wir Ziel erreicht:
Until Wert=0
End Function
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe
 

PhillipK

BeitragMi, Okt 12, 2011 19:32
Antworten mit Zitat
Benutzer-Profile anzeigen
An sich liest sich das wie eine allgemeinere Suchroutine. Das kreisförmige ausbreiten habe ich die tage schonmal in einem Java applet gesehen.
In den meisten fällen ist A* leider schneller :<
Ich werde dennoch deine idee einmal in meine Wegfinderoutine aufnehmen um einen direktvergleich zu haben.

Ziemlich stark auffallen tut schonmal, das deine Funktion komplett ohne eine extra Klasse (zb TKnoten ) auskommt, das dürfte einiges an Speed sparen.
In anbetracht der tatsache, das meine TKnoten klasse eine kleinere Berechnung durchführt und wiederholt einige wichtige felder abgefragt werden.
Deine Version kommt mit einem Simplen integer und einem Map gleichdimensionierten Array aus -intressant Smile

Am ende tuts vielleicht gar eine Mischung aus beiden.

Selbst wenn ich das ganze nicht in dieser Form nutzen sollte, schwebt mir dennoch schonmal eine anwendung dafür vor: In der nähebefindliche Wegpunkte finden! Diese sollen auf direktem wege verbunden sein, allerdings ohne große Mauern dazwischen - mit einer gewissen Suchtiefe (dh kreisförmiges durchgehen) kann ich so ziemlich schnell ausfindig machen, ob um einen Punkt herum eine Wegmakierung ist.

Hmpf, soviele ideen und doch so wenig zeit ^^

Ich danke dir für diese vorführung und erklärung, ich melde mich, sobald ich das ganze einmal durchprobiert habe Smile

Midimaster

BeitragDo, Okt 13, 2011 10:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Genau, aber das ist ja A* auch (Quelle: Wikipedia:
Zitat:
Der A*-Algorithmus... gehört zur Klasse der informierten Suchalgorithmen.


Und so sehr weit ist mein Algo von dem A* nicht weg. Wenn Du Dir die Definition von A* mit den Eigenschaften meines Codes vergleichst, so hält sich mein Algo an alle dort vorgegebenen Schritte. Vergleiche mal den Text auf Wikipedia!

Einzig die "Vorahnung" einer Richtung habe ich ersetzt durch "in alle Richtungen suchen". Was sich in vielen Fällen als nutzvoll heraussstellt. Es ist halt im Spiel doch ganz oft so, dass die Luftlinie nicht zum Ziel führt. So gesehen ist mein Algo eher ein Dijkstra-Algorithmus

Heuristik fällt auch weg bei meinem Algo, denn das Ziel wird ja so unglaublich schnell gefunden, also warum schätzen? Solange die Findezeiten unter 1 msec liegen, muss man nicht über weitere Maßnahmen nachdenken.

"Entscheidend ist, was hinten rauskommt"

Es wäre erst noch zu beweisen, dass der A* hier mehr leisten kann. Wenn der Aufbau eines Spielfeldes (Anz. Dimensionen, Array() ja/nein, Bewegungsmuster) bekannt ist, dann ist ein auf diesen Situation angepasster Algo im Vorteil.

Und genau das macht mein Algo. Er kennt die Struktur des Spielfeldes und ahmt sie in seinem Checked[] nach. Dadurch spar er viel Zeit, sich über die Knoten zu informieren. So kommt er "billig" an die Nachbarknoten. Die Knoten werden nicht alle in einer Liste gehalten, sondern immer nur die neuesten. Fertig untersuche Informationen kommen in das Array.

Das "Zeiger-Modell" der Knoten ersetze ich durch die späte Rückwärtssuche im Array. Das Array zeigt ebenso den Weg zum Ausgangspunkt, wie es die Zeiger tun würden. Denn je näher ich dem Startpunkt komme, desto niedriger sind die auf den Array-Feldern errechneten Kosten.

Das Array ist auch gleichzeitig die Closed-List. Der Algo erfährt hier eindeutig, ob ein Knoten bereits zuende untersucht wurde und nimmt ihn nicht wieder in die OpenList auf. Das bringt den entscheidenden Vorteil des A*.

Hier würde ich auch in Deinem Algo den Fehler vermuten. Eine Lösung ist immer eine schrittweise Darstellung. Stoppe nach jedem Knoten. Lass dir die Koordinaten ausgeben, die als neue Knoten aufgenommen werden und vergleiche ob sie nicht manchmal zweimal in der Liste stehen. Ein typischer Fehler ist nämlich, dass die Liste aus mehr Knoten besteht als Felder im Spielfeld betroffen sind.

Ich habe übrigens die Visualisierung der Vorgehensweise in meinem Code überarbeitet. Den Download fiindest Du an der alten Stelle.
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe

BladeRunner

Moderator

BeitragDo, Okt 13, 2011 12:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Zitat:
Einzig die "Vorahnung" einer Richtung habe ich ersetzt durch "in alle Richtungen suchen". Was sich in vielen Fällen als nutzvoll heraussstellt. Es ist halt im Spiel doch ganz oft so, dass die Luftlinie nicht zum Ziel führt. So gesehen ist mein Algo eher ein Dijkstra-Algorithmus

das stimmt, und dieser ist eben nicht der optimale Weg, zumindest nicht Wenn Start- und Zielpunkt bekannt sind.
Zitat:
Es wäre erst noch zu beweisen, dass der A* hier mehr leisten kann. Wenn der Aufbau eines Spielfeldes (Anz. Dimensionen, Array() ja/nein, Bewegungsmuster) bekannt ist, dann ist ein auf diesen Situation angepasster Algo im Vorteil.

Hier darf ich auf den von dir angeführten Wikipedia-Artikel von Dir verweisen, in dem auch der mathematische Beweis dafür ausgeführt ist dass der A-Star die optimale Lösung findet, insofern muss ich also sagen dass Du dich mit deiner Behauptung recht weit aus dem Fenster lehnst.

Der Dijkstra ist ja mathematisch betrachtet nur eine Sonderform des Astar, allerdings nicht die automatisch Günstigste. Auch hier verweise ich auf den von Dir angeführten Wikipedia-Artikel.
Natürlich kann man mit solch einer Breitensuche viel erreichen, aber der optimale Weg ist es idR. nicht.
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

Midimaster

BeitragDo, Okt 13, 2011 13:53
Antworten mit Zitat
Benutzer-Profile anzeigen
@BladeRunner

..naja... der A* wird ja oft mit dem Beispiel "Straßennetz" erklärt. Hierzu passt er auch optimal. Der Unterschied zum Labyrith" besteht ja darin, dass man oft davon ausgehen kann, das ein Straße die wenigstens schon mal in die Himmelrichtung zum Ziel zeigt auch wirklich dort hinführt. Straßen haben auch den Vorteil, dass es "irgendwie immer" weitergeht.

Im freien Feld mit plötzlich auftretenden Total-Hindernissen sieht die Sache schon anders aus. Oder im Labyrith, wo durch die Himmelsrichtung leider gar nichts gewonnen ist. Auch hierzu findet sich Kritik im Wikipedia-Beitrag.

In solchen Fällen muss der A* viel zu oft und immer wieder bereits betrachtete Knoten neu aufnehmen und kann sich so gut wie nie sicher sein, dass er jetzt für diesen Knoten das Optimum gefunden hat.

Und irgendeinen Grund muss es ja haben, dass so viele andere Algos nebenher existieren Wink Wir werden sehen, was bei PhillipK am Ende rauskommt. Ich hatte sein Bild von dem Test-Labyrith (früherer Thread) vor Augen und wußte, das A* hier nicht die Lösung sein wird.

Und letzemdlich ist es ja Unsinn einen "noch besseren" Algo zu suchen, wenn für das akt. Problem eine Lösung von unter 1msec gefunden ist.

PhillipK könnte ja mal eine typische zu erwartenden Testumgebung erstellen, wo man dann den Algo drauf loslässt.

Nur so ins Blaue einen Algo zu basten, ohne zu wissen was er genau lösen soll, ist wie "Koffer packen für einen unbekannte Urlaubsregion".
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe

Gehe zu Seite 1, 2  Weiter

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group