aystar.cpp

Go to the documentation of this file.
00001 /* $Id: aystar.cpp 20890 2010-10-02 19:55:13Z alberth $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00018 /*
00019  * Friendly reminder:
00020  *  Call (AyStar).free() when you are done with Aystar. It reserves a lot of memory
00021  *  And when not free'd, it can cause system-crashes.
00022  * Also remember that when you stop an algorithm before it is finished, your
00023  * should call clear() yourself!
00024  */
00025 
00026 #include "../../stdafx.h"
00027 #include "../../core/alloc_func.hpp"
00028 #include "aystar.h"
00029 
00035 PathNode *AyStar::ClosedListIsInList(const AyStarNode *node)
00036 {
00037   return (PathNode*)this->closedlist_hash.Get(node->tile, node->direction);
00038 }
00039 
00045 void AyStar::ClosedListAdd(const PathNode *node)
00046 {
00047   /* Add a node to the ClosedList */
00048   PathNode *new_node = MallocT<PathNode>(1);
00049   *new_node = *node;
00050   this->closedlist_hash.Set(node->node.tile, node->node.direction, new_node);
00051 }
00052 
00058 OpenListNode *AyStar::OpenListIsInList(const AyStarNode *node)
00059 {
00060   return (OpenListNode*)this->openlist_hash.Get(node->tile, node->direction);
00061 }
00062 
00068 OpenListNode *AyStar::OpenListPop()
00069 {
00070   /* Return the item the Queue returns.. the best next OpenList item. */
00071   OpenListNode *res = (OpenListNode*)this->openlist_queue.Pop();
00072   if (res != NULL) {
00073     this->openlist_hash.DeleteValue(res->path.node.tile, res->path.node.direction);
00074   }
00075 
00076   return res;
00077 }
00078 
00083 void AyStar::OpenListAdd(PathNode *parent, const AyStarNode *node, int f, int g)
00084 {
00085   /* Add a new Node to the OpenList */
00086   OpenListNode *new_node = MallocT<OpenListNode>(1);
00087   new_node->g = g;
00088   new_node->path.parent = parent;
00089   new_node->path.node = *node;
00090   this->openlist_hash.Set(node->tile, node->direction, new_node);
00091 
00092   /* Add it to the queue */
00093   this->openlist_queue.Push(new_node, f);
00094 }
00095 
00099 void AyStar::CheckTile(AyStarNode *current, OpenListNode *parent)
00100 {
00101   int new_f, new_g, new_h;
00102   PathNode *closedlist_parent;
00103   OpenListNode *check;
00104 
00105   /* Check the new node against the ClosedList */
00106   if (this->ClosedListIsInList(current) != NULL) return;
00107 
00108   /* Calculate the G-value for this node */
00109   new_g = this->CalculateG(this, current, parent);
00110   /* If the value was INVALID_NODE, we don't do anything with this node */
00111   if (new_g == AYSTAR_INVALID_NODE) return;
00112 
00113   /* There should not be given any other error-code.. */
00114   assert(new_g >= 0);
00115   /* Add the parent g-value to the new g-value */
00116   new_g += parent->g;
00117   if (this->max_path_cost != 0 && (uint)new_g > this->max_path_cost) return;
00118 
00119   /* Calculate the h-value */
00120   new_h = this->CalculateH(this, current, parent);
00121   /* There should not be given any error-code.. */
00122   assert(new_h >= 0);
00123 
00124   /* The f-value if g + h */
00125   new_f = new_g + new_h;
00126 
00127   /* Get the pointer to the parent in the ClosedList (the currentone is to a copy of the one in the OpenList) */
00128   closedlist_parent = this->ClosedListIsInList(&parent->path.node);
00129 
00130   /* Check if this item is already in the OpenList */
00131   check = this->OpenListIsInList(current);
00132   if (check != NULL) {
00133     uint i;
00134     /* Yes, check if this g value is lower.. */
00135     if (new_g > check->g) return;
00136     this->openlist_queue.Delete(check, 0);
00137     /* It is lower, so change it to this item */
00138     check->g = new_g;
00139     check->path.parent = closedlist_parent;
00140     /* Copy user data, will probably have changed */
00141     for (i = 0; i < lengthof(current->user_data); i++) {
00142       check->path.node.user_data[i] = current->user_data[i];
00143     }
00144     /* Re-add it in the openlist_queue. */
00145     this->openlist_queue.Push(check, new_f);
00146   } else {
00147     /* A new node, add him to the OpenList */
00148     this->OpenListAdd(closedlist_parent, current, new_f, new_g);
00149   }
00150 }
00151 
00161 int AyStar::Loop()
00162 {
00163   int i;
00164 
00165   /* Get the best node from OpenList */
00166   OpenListNode *current = this->OpenListPop();
00167   /* If empty, drop an error */
00168   if (current == NULL) return AYSTAR_EMPTY_OPENLIST;
00169 
00170   /* Check for end node and if found, return that code */
00171   if (this->EndNodeCheck(this, current) == AYSTAR_FOUND_END_NODE) {
00172     if (this->FoundEndNode != NULL) {
00173       this->FoundEndNode(this, current);
00174     }
00175     free(current);
00176     return AYSTAR_FOUND_END_NODE;
00177   }
00178 
00179   /* Add the node to the ClosedList */
00180   this->ClosedListAdd(&current->path);
00181 
00182   /* Load the neighbours */
00183   this->GetNeighbours(this, current);
00184 
00185   /* Go through all neighbours */
00186   for (i = 0; i < this->num_neighbours; i++) {
00187     /* Check and add them to the OpenList if needed */
00188     this->CheckTile(&this->neighbours[i], current);
00189   }
00190 
00191   /* Free the node */
00192   free(current);
00193 
00194   if (this->max_search_nodes != 0 && this->closedlist_hash.GetSize() >= this->max_search_nodes) {
00195     /* We've expanded enough nodes */
00196     return AYSTAR_LIMIT_REACHED;
00197   } else {
00198     /* Return that we are still busy */
00199     return AYSTAR_STILL_BUSY;
00200   }
00201 }
00202 
00206 void AyStar::Free()
00207 {
00208   this->openlist_queue.Free(false);
00209   /* 2nd argument above is false, below is true, to free the values only
00210    * once */
00211   this->openlist_hash.Delete(true);
00212   this->closedlist_hash.Delete(true);
00213 #ifdef AYSTAR_DEBUG
00214   printf("[AyStar] Memory free'd\n");
00215 #endif
00216 }
00217 
00222 void AyStar::Clear()
00223 {
00224   /* Clean the Queue, but not the elements within. That will be done by
00225    * the hash. */
00226   this->openlist_queue.Clear(false);
00227   /* Clean the hashes */
00228   this->openlist_hash.Clear(true);
00229   this->closedlist_hash.Clear(true);
00230 
00231 #ifdef AYSTAR_DEBUG
00232   printf("[AyStar] Cleared AyStar\n");
00233 #endif
00234 }
00235 
00245 int AyStar::Main()
00246 {
00247   int r, i = 0;
00248   /* Loop through the OpenList
00249    *  Quit if result is no AYSTAR_STILL_BUSY or is more than loops_per_tick */
00250   while ((r = this->Loop()) == AYSTAR_STILL_BUSY && (this->loops_per_tick == 0 || ++i < this->loops_per_tick)) { }
00251 #ifdef AYSTAR_DEBUG
00252   switch (r) {
00253     case AYSTAR_FOUND_END_NODE: printf("[AyStar] Found path!\n"); break;
00254     case AYSTAR_EMPTY_OPENLIST: printf("[AyStar] OpenList run dry, no path found\n"); break;
00255     case AYSTAR_LIMIT_REACHED:  printf("[AyStar] Exceeded search_nodes, no path found\n"); break;
00256     default: break;
00257   }
00258 #endif
00259   if (r != AYSTAR_STILL_BUSY) {
00260     /* We're done, clean up */
00261     this->Clear();
00262   }
00263 
00264   switch (r) {
00265     case AYSTAR_FOUND_END_NODE: return AYSTAR_FOUND_END_NODE;
00266     case AYSTAR_EMPTY_OPENLIST:
00267     case AYSTAR_LIMIT_REACHED:  return AYSTAR_NO_PATH;
00268     default:                    return AYSTAR_STILL_BUSY;
00269   }
00270 }
00271 
00280 void AyStar::AddStartNode(AyStarNode *start_node, uint g)
00281 {
00282 #ifdef AYSTAR_DEBUG
00283   printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n",
00284     TileX(start_node->tile), TileY(start_node->tile), start_node->direction);
00285 #endif
00286   this->OpenListAdd(NULL, start_node, 0, g);
00287 }
00288 
00293 void AyStar::Init(Hash_HashProc hash, uint num_buckets)
00294 {
00295   /* Allocated the Hash for the OpenList and ClosedList */
00296   this->openlist_hash.Init(hash, num_buckets);
00297   this->closedlist_hash.Init(hash, num_buckets);
00298 
00299   /* Set up our sorting queue
00300    *  BinaryHeap allocates a block of 1024 nodes
00301    *  When that one gets full it reserves another one, till this number
00302    *  That is why it can stay this high */
00303   this->openlist_queue.Init(102400);
00304 }