Aktuelle Zeit: 27.04.2024, 12:53

Alle Zeiten sind UTC + 1 Stunde




Ein neues Thema erstellen Auf das Thema antworten  [ 22 Beiträge ]  Gehe zu Seite 1, 2  Nächste
Autor Nachricht
 Betreff des Beitrags: A* Pfadfinding
BeitragVerfasst: 20.02.2010, 23:48 
Offline
Benutzeravatar

Registriert: 24.07.2007, 23:11
Beiträge: 283
Hallo ich hab mich auch mal wieder her gefunden ;D,
hat wer von euch schon mal den A Stern in ne Programmiersprache umgesetzt?
Hab jetzt einige Tutorials durch hab vieles im Internet gelesen,
aber irgendwie bin ich nicht in der Lage den Pseudocode den ich überall finde in ne Programmiersprache umzusetzten...
Kennt also wer wo es in irgendeiner Programmiersprache geschrieben steht?
...
So als Info hatte mich mit PHP versucht da ichs in der Sprache brauche :S

MfG Bubble

_________________
Be Fluffy!
kemomi.de


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 21.02.2010, 00:40 
Offline
Benutzeravatar

Registriert: 17.04.2007, 08:42
Beiträge: 460
Wohnort: Willich, NRW
Aloha,

schön dich auch mal wieder hier zu sehen. Für dein Problem hab ich denke ich ne gute Erklärung. Hab auch noch streng geheimen source-code, den ich dir zukommen lassen könnte ;) Aber ich versuchs mal in C++. ZUm generellen Verfahren.

Man hat einen Startpunkt, einen Endpunkt und eine Menge von Punkten. Weiterhin gibt es Möglichkeiten von einen Punkt zu einem anderen zu kommen, wobei diese aber eine gewisse Distanz auseinander sind. Diese ist wichtig um abzuschätzen, welcher wohl der nächst beste Punkt ist, den man ansteuern könnte und wird mit einer Heuristik ermittelt. Weiterhin ist A* vollständig und korrekt. Ich versuch das mal alles am Beispeil zu erklären:

Erst definieren wir uns wie ein Punkt in unserer Welt aussieht:

Code:
struct Node
{
   Node*         parent;
   position2d<int>   pos;
   int            cost;
};


Wir halten uns ein paar Listen:

Code:
vector<Node*> queue, path, visited;


queue ist dabei unsere Liste von Punkten, die wir gerade bearbeiten, in path wird später uns weg sein, den wir abgehen werden und in visited speichern wir alle punkte, die wir bereits besucht haben, da sonst der Algorithmus unter umständen nicht terminieren würde.

Nun beginnen wir die suche:

Code:
void findPathWithAStar()
{
  //Wir initialisieren unsere Listen
  queue.clear();
  path.clear();
  visited.clear();

  //Nun legen wir uns eine lokale variable an, mit der wir arbeiten können. Diese wird der aktuelle Punkt sein, auf dem wir
  //uns befinden!
  Node* currentNode;

  //Node* getStartNode() gibt uns den Anfangspunkt zurück
  //das heißt: currentNode->parent = NULL;
  //currentNode->cost = 0;
  currentNode = getStartNode();

  //der anfangspunkt wird in die arbeitsliste hinzugefügt und als besucht markiert
  queue.push_back(currentNode);
  visited.push_back(currentNode);

  //solange wir noch arbeitsknoten haben
  while(!queue.empty())
  {
    //Node* getCheapestNode() gibt uns den Knoten mit den wenigsten Zugkosten zurück, also der jenige Knoten, bei
    //dem cost minimal ist.
    currentNode = getCheapestNode();

    //Uuups. Fehler ;(
    if(currentNode == NULL)
    {
      //fehlerausgabe ...
      return;
    }

    if(currentNode == getEndNode())
    {
      //Wir haben einen Weg gefunden, nun rekonstruieren wir einen Weg zurück
      restorePathFromNode(currentNode);
    }

    vector<Node*> allNeighborPointsFromHere = getAllNeighbors(currentNode);
    //diese Funktion ermittelt alle Nachbarknoten von currentNode. In einem rechtwinkligen Grid wäre das als
    //Beispiel wenn wir von dem Punkt (1,2) aus gehen die 4 Punkte { (0,2), (2,2), (1,1), (1,3) }

    //Nun fügen wir all die neuen Knoten in unsere Arbeitskette hin zu und markieren sie als besucht
    for(unsigned int i = 0; i < allNeighborPointsFromHere.size(); i ++)
    {
      queue.push_back(allNeighborPointsFromHere[i]);
      visited.push_back(allNeighborPointsFromHere[i]);
    }
  }
}


Das ist im Prinzip der ganze Zauber. Wir lassen die Schleife solange durchlaufen, bis wir die Liste komplett geleert haben, oder wir den Endpunkt gefunden haben, währen fleißig immer neue Knoten expandiert werden.

Nun müssen wir uns noch die diversen Helferfunktionen anschauen. Das wäre im wesentlichen die Methode die uns den billigsten Knoten zurückliefert und die die uns alle Nachbarknoten besorgt.

Code:
Node* getCheapestNode(void)
{
        //die liste ist leer
   if(queue.empty())
      return(NULL);
   
   unsigned int index = 0;
   int price = queue[0]->cost;

        //Wir durchsuchen die gesamte Liste
   for(unsigned int i = 0; i < queue.size(); i ++)
   {
                //Falls die Kosten des aktuellen Knoten kleiner sind als die des bisher besten, ist das nun unser bester!
      if(queue[i]->cost < price)
      {
         index = i;
         price = queue[i]->cost;
      }
   }

        //Wir geben unsern so gefunden günstigsten Knoten zurück und löschen diesen aus der Arbeitskette!
        //Falls man das vergisst, läuft man auch ne ganze Weile und findet auch nicht die optimale Lösung
   Node* temp = queue[index];
   queue.erase(queue.begin() + index);
   return(temp);
}

vector<Node*> getAllNeighbors(Node* currentNode)
{
  vector<Node*> temp;

  Node* next = new Node;
  next->parent = currentNode;
  next->pos = currentNode->pos + position2d<int>(0,1)
  // dann auch noch (0,-1), (1,0), (-1,0) das spare ich mir aber mal ;) dürfen aber nicht vergessen werden!
 
  //Jetzt kommen wir zur Besonderheit von A*: Die Kosten ergeben sich aus der Entfernung von dem derzeitigen Knoten
  //bis zum nächsten PLUS den bisherigen Wegkosten!
  //getDistance(position2d<int> a, position2d<int> b) gibt die Entfernung von a und b via dem pythagoras zurück
  next->cost = currentNode->cost + getDistance(currentNode->pos, next->pos);

  //Wenn der Knoten bereits besucht wurde dürfen wir ihn nicht mehr hinzufügen!
  if(nodeNotInVisited(next))
  {
    temp.push_back(next);
  }
 
  return(temp);
}


Nun haben wir in der while-schleife zwei Abbruchbedingungen. Einerseits wenn die Arbeitsschlange leer ist, dann haben wir keinen Weg (Knoten nicht erreichbar) oder wenn die Position des aktuellen Knotens gleich der des Endknotens ist, das heißt wir haben genau diesen Knoten gefunden und können abbrechen. So müssen wir aber noch den Wegrekonstruieren. Das geschiet folgendermaßen:

Code:
void restorePathFromNode(Node* currentNode)
{
   vector<Node*> reversePath;
   Node* temp = currentNode;

        //Wir rufen einfach vom Endknoten her immer alle Parents auf. Der erste Parent, der NULL ergibt war unser Startpunkt,
        //da deren Elternknoten als einziger mit NULL initialisiert wurde.
   while(true)
   {
      if(temp == NULL)
         break;

      reversePath.push_back(temp);
      temp = temp->parent;
   }

        //Nun haben wir die Reihenfolge, die wir gehen müssen rückwärts abgespeichert und müssen diese nur noch
        //umdrehen
   for(int i = (unsigned int) reversePath.size() - 1; i >= 0; i --)
   {
      path.push_back(reversePath[i]);
   }
}


Also nochmal in kurz: A* hält sich alle Knoten in einer Liste. WIr beginnen zunächst mit einer Liste, die nur den Startknoten enthält. Wir überprüfen in jedem Schleifendurchlauf, ob der derzeitige Knoten, der jenige in unserer Arbeitsschlange queue mit den geringsten Wegkosten, unser gesuchte ist. Falls ja haben wir gewonnen. Falls nicht untersuchen wir alle Nachfolgerknoten. Falls diese nicht untersucht wurden. Berechnen wir die neuen Wegkosten in den Nachfolgerknoten und fügen sie sowohl unserer Arbeitskette queue hinzu als auch der schlange visited, damit wir wissen, dass wir diese nicht mehr untersuchen müssen. Und dann beginnen wir wieder von vorne. Die beiden Abbruchbedingungen können nur sein, dass die Liste leer ist, dann gibt es keinen Weg, oder dass der derzeitig billigste Knoten, der Endknoten ist. Et voila haben wir den Weg, den wir gesucht haben.

Ich hoffe man konnte helfen und falls es noch fragen gibt immer raus damit. Der source ist zum copy und paste übrigens nicht geeignet, da ich einige micro Funktionen nicht aufgeführt habe, da deren Funktionalität hoffentlich vom Namen her ableitbar waren ;)

mfg heck

_________________
Bild

Irrlicht - From Noob To Pro A Guideline

--

Sonstige Projekte, Blog : http://www.rpdev.net


Zuletzt geändert von das heck am 21.02.2010, 10:49, insgesamt 2-mal geändert.

Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 21.02.2010, 01:19 
Offline
Benutzeravatar

Registriert: 24.07.2007, 23:11
Beiträge: 283
Jo thx...
habs mir durchgelesen und ich denke anhand diesem Codes,
sollt ich's hoffentlich hin bekommen :S...
Wenn nicht kann ich dich ja fragen :D...

Werd's morgen mal testen ob ich's hin bekomme.
Hab den A Stern erst mal weiter hinten in die Planung gesteckt.

Nur eins versteh ich irgendwie net:

Zitat:
...
next->pos = currentNode->pos + position2d<int>(0,1)
...


und nen paar Zeilen drunter:

Zitat:
...
next->pos = currentNode->cost + getDistance(currentNode->pos, next->pos);
...


überschreibt das dann nicht den Wert von zuvor?

MfG Bubble

_________________
Be Fluffy!
kemomi.de


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 21.02.2010, 10:46 
Offline
Benutzeravatar

Registriert: 17.04.2007, 08:42
Beiträge: 460
Wohnort: Willich, NRW
Das dürfte auch so nicht funktionieren. Hab mich verschrieben =D Muss heißen:

Code:
next->cost = currentNode->cost + getDistance(currentNode->pos, next->pos);


Wir setzten ja den neuen Kostenwert an der Stelle und nicht die Position. Werds auch oben nochmal ändern. Danke für den Tipp :>

_________________
Bild

Irrlicht - From Noob To Pro A Guideline

--

Sonstige Projekte, Blog : http://www.rpdev.net


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 22.02.2010, 14:31 
Offline
Benutzeravatar

Registriert: 24.07.2007, 23:11
Beiträge: 283
Die Kosten geben an ob ein Feld betretbar ist oder nicht?
wenn die Kosten 9999 sind wird er wahrscheinlich nicht mehr darauf gehen?

_________________
Be Fluffy!
kemomi.de


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 22.02.2010, 14:51 
Offline
Benutzeravatar

Registriert: 17.04.2007, 08:42
Beiträge: 460
Wohnort: Willich, NRW
Kommt auf die Dimensionierung deiner Karte an. Wenn der Weg recht weit ist, sagen wir mal der Weg bis zu einem Feld summiert sich so auf die 50000 an (gar nicht so unrealistisch bei großen Karten oder wenn du Wegpunkte einrechnest --> Krieger geht durch Schlamm kostet nicht nur ein Bewegungspunkt sondern 5 Bewegungspunkte, etc.), dann wird er auf jeden Fall den Weg der auf ein nichtbetretbares Feld führt, und somit nur schlappe 10k Punkte mehr kostet, mit betrachten, da es ja dann günstiger ist. Es wird zwar mit hoher Wahrscheinlichkeit wieder rausfliegen, aber darauf verlassen solltest du dich nicht. Ich würde vorschlagen dass in der Funktion vector<Node*> getAllNeighbors(Node* currentNode) anzupassen, etwa so (Änderungen sind mit [NEU] eingerahmt):

Code:
vector<Node*> getAllNeighbors(Node* currentNode)
{
  vector<Node*> temp;

  Node* next = new Node;
  next->parent = currentNode;
  next->pos = currentNode->pos + position2d<int>(0,1)
  // dann auch noch (0,-1), (1,0), (-1,0) das spare ich mir aber mal ;) dürfen aber nicht vergessen werden!

  //Jetzt kommen wir zur Besonderheit von A*: Die Kosten ergeben sich aus der Entfernung von dem derzeitigen Knoten
  //bis zum nächsten PLUS den bisherigen Wegkosten!
  //getDistance(position2d<int> a, position2d<int> b) gibt die Entfernung von a und b via dem pythagoras zurück
  next->cost = currentNode->cost + getDistance(currentNode->pos, next->pos);

  //Wenn der Knoten bereits besucht wurde dürfen wir ihn nicht mehr hinzufügen!
  if(nodeNotInVisited(next))
  {
    //[NEU]
    //Hier werden wir nun abfragen, ob dieses Tile ein reguläres Feld ist, das heißt, ob es
    //überhaupt auf der Karte liegt, oder ob es betretbar ist. Unsere Karte ist vom Typ GameMap
    //und hat eine Funktion bool GameMap::isEnterable(position2d<int> position), sowie eine Funtkion
    //bool GameMap::isTileInMap(position2d<int> position), die zurück gibt, ob position überhaupt auf der Karte
    //liegt. (-3,-9) liegt zum Beispiel nicht auf einer Karte der Größe[0-10][0-10]
    if(myMap->isTileInMap(next->pos) && myMap->isEnterable(next->pos))
    {
      temp.push_back(next);
    }
    //[/NEU]   
  }

  return(temp);
}


Ein wenig mehr extra Aufwand, aber das sollte deine Karte an und für sich ja sowieso mitbringen (Kartengrße abfragen usw.). Aber so ist es für jeden Fall sicher, und du kannst die Wegpunkte beliebig dimensionieren.

mfg heck

_________________
Bild

Irrlicht - From Noob To Pro A Guideline

--

Sonstige Projekte, Blog : http://www.rpdev.net


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 22.02.2010, 15:18 
Offline
Benutzeravatar

Registriert: 24.07.2007, 23:11
Beiträge: 283
Die Map wird nicht ganz so groß, 40 auf 40 Felder ist denke ich mal Maximum. Es wird nur ein Browsergame und will es so Kompatibel machen wie möglich das es auch auf nen Nintendo DS oder IPod läuft und in Zunkunftsplänen soll der Server im Hintergrund neue Grafiken / Maps mit Irrlicht berechnen. So wie ichs damals schon mit dem Deathnote Game vorhatte.

_________________
Be Fluffy!
kemomi.de


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 22.02.2010, 15:55 
Offline
Benutzeravatar

Registriert: 17.04.2007, 08:42
Beiträge: 460
Wohnort: Willich, NRW
Wie gesagt: Wenn du abfragst, ob die entsprechende Position begehbar ist, dann bist du immer auf der sicheren Seite. Aber bei einem 40*40 Feld solltest du auch mit der 10k Bewertung hinkommen, vorrausgesetzt du bleibst bei der naiven Heuristik, dass man einen Wegpunkt pro Feld braucht. D.h. die weiteste Entfernung wäre 40*40 = 1600 (von links oben nach rechts unten). So gesehen ist die Variante schon echt dirty, da du evtl wirklich da nochmal ändern musst, falls sich die Bewertungen schlagartig mal ändern ;)

EDIT: Mir fällt gerade etwas ein. Die Variante mit der schlechten Bewertung für ein nicht begehbares Feld wird definitiv fehlschlagen. Und zwar genau dann wenn das Ziel nicht erreichbar sein sollte von deinem jetzigen Standpunkt aus. Simples Beispiel:

Code:
O -> nicht betretbar
X -> Spielerposition
Z -> Ziel

OOOOO
OXO ZO
OOOOO


Dein Ansatz würde einen Weg finden, da alle gleich schlecht bewertet werden, die nicht begehbaren Tiles aber trotzdem in der Arbeitsschlange landen also auch abgearbeitet werden. D.h. du gehst durch die Wand.

Wenn du vorher abfragst ob ich da durchgehen darf, landet nichts in der Arbeitsschlange und wir haben eine leere Schlange was gemäß unseres Algos zum Abbruch führt und "leeren Weg" zurück liefert, auf den du dann reagieren musst (Karte neusetzen).

Das heißt also deine Variante geht nur, wenn du keine Einschlüsse in der Karte hast, was wieder gegen die Variante spricht, da sie nur unter Sonderbedingungen funktioniert ;)

mfg heck

_________________
Bild

Irrlicht - From Noob To Pro A Guideline

--

Sonstige Projekte, Blog : http://www.rpdev.net


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 22.02.2010, 18:53 
Offline
Benutzeravatar

Registriert: 24.07.2007, 23:11
Beiträge: 283
Mhh momentan ist der A* erstmal weiter hinten angesetzt. Da ich die Wahrscheinlich nur für die KI brauchen werde.
Werden wir dann sehen werde sie wenn ich zeit hab trotzdem mal testen.

_________________
Be Fluffy!
kemomi.de


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 22.02.2010, 20:02 
Offline
Benutzeravatar

Registriert: 17.04.2007, 08:42
Beiträge: 460
Wohnort: Willich, NRW
Du kannst auch einen Breitensuchalgorithmus implementieren. Muss ja nicht immer A* sein^^ Bei Breitensuche hast du den Vorteil, dass du definitv den kürzesten Weg findest und bei der heutigen Rechnergeneration fällt auch der "extrem lange" Rechenweg nicht ins Gewicht. Ich hab mit meiner KI ein Rectangle der Größe 35*20 (mit nicht begehbaren Feldern) mit der Breitensuche in ganz wenig Zeit (0.01 secs, geschätzt) berechnet. Immernoch schnell genug, damit die KI Echtzeitberechnungen ausführen kann.

Ansonsten gibts ja noch den Dijkstra, wobei ich den noch nicht implementiert habe und auhc nicht vor hab :P

Hier ist noch n Link zu einer Seite, die wunderbar die beiden Suchalgorithmen erklärt (Breiten- und Tiefensuche):
http://user.cs.tu-berlin.de/~magus/labyrinth/
Sogar mit Anwendungsbeispiel ;)

_________________
Bild

Irrlicht - From Noob To Pro A Guideline

--

Sonstige Projekte, Blog : http://www.rpdev.net


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 22.02.2010, 22:31 
Offline
Benutzeravatar

Registriert: 24.07.2007, 23:11
Beiträge: 283
Breitensuche hab ich drinne, aber der dauert zu lange PHP ist nicht die schnellste Sprache ;D und die Zeit dies braucht hab ich nicht. Ich muss optimieren wos geht da ich schon nen langsames Framework auf ne langsame Programmiersprache setzte.

_________________
Be Fluffy!
kemomi.de


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: A* Pfadfinding
BeitragVerfasst: 23.02.2010, 10:40 
Offline
Benutzeravatar

Registriert: 17.04.2007, 08:42
Beiträge: 460
Wohnort: Willich, NRW
Ok, da ist was dran. Könntest aber auch mit A* an ein paar Grenzen kommen (Hatte mal ein 4*4 Schiebepuzzle in Java mit A* gerechnet ~4 Stunden). Also ich hoffe nicht das es ein Echtzeitspiel wird, werd aber gleich mal schauen, was du vor hast ;)

Ansonsten es schnellste was du hinbekommen könntest wäre glaube ich dann die Tiefensuche, wobei dann aber die Lösung teils nicht optimal ist. Komplexität O =( |V| + |E| ), also Anzahl der Knoten + die Anzahl aller Kanten, in deinem Fall weils ja quadratisch ist für V = 40: O = ( |40| + |2*(V²-V) = 3120|)= 3160. So viel Instanzen musst du durchrechnen können. Ich hab mit PHP jez nicht so viel Erfahrungen, aber ich denke du kannst dann in etwa einschätzen wie kostenspielig das ganze in der Laufzeit ist.

_________________
Bild

Irrlicht - From Noob To Pro A Guideline

--

Sonstige Projekte, Blog : http://www.rpdev.net


Nach oben
 Profil  
 
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 22 Beiträge ]  Gehe zu Seite 1, 2  Nächste

Alle Zeiten sind UTC + 1 Stunde


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 3 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de