# aystar.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    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 current one 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 }