OpenTTD
aystar.cpp
Go to the documentation of this file.
1 /* $Id: aystar.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
18 /*
19  * Friendly reminder:
20  * Call (AyStar).free() when you are done with Aystar. It reserves a lot of memory
21  * And when not free'd, it can cause system-crashes.
22  * Also remember that when you stop an algorithm before it is finished, your
23  * should call clear() yourself!
24  */
25 
26 #include "../../stdafx.h"
27 #include "../../core/alloc_func.hpp"
28 #include "aystar.h"
29 
30 #include "../../safeguards.h"
31 
38 {
39  return (PathNode*)this->closedlist_hash.Get(node->tile, node->direction);
40 }
41 
48 {
49  /* Add a node to the ClosedList */
50  PathNode *new_node = MallocT<PathNode>(1);
51  *new_node = *node;
52  this->closedlist_hash.Set(node->node.tile, node->node.direction, new_node);
53 }
54 
61 {
62  return (OpenListNode*)this->openlist_hash.Get(node->tile, node->direction);
63 }
64 
71 {
72  /* Return the item the Queue returns.. the best next OpenList item. */
74  if (res != NULL) {
75  this->openlist_hash.DeleteValue(res->path.node.tile, res->path.node.direction);
76  }
77 
78  return res;
79 }
80 
85 void AyStar::OpenListAdd(PathNode *parent, const AyStarNode *node, int f, int g)
86 {
87  /* Add a new Node to the OpenList */
88  OpenListNode *new_node = MallocT<OpenListNode>(1);
89  new_node->g = g;
90  new_node->path.parent = parent;
91  new_node->path.node = *node;
92  this->openlist_hash.Set(node->tile, node->direction, new_node);
93 
94  /* Add it to the queue */
95  this->openlist_queue.Push(new_node, f);
96 }
97 
102 {
103  int new_f, new_g, new_h;
104  PathNode *closedlist_parent;
105  OpenListNode *check;
106 
107  /* Check the new node against the ClosedList */
108  if (this->ClosedListIsInList(current) != NULL) return;
109 
110  /* Calculate the G-value for this node */
111  new_g = this->CalculateG(this, current, parent);
112  /* If the value was INVALID_NODE, we don't do anything with this node */
113  if (new_g == AYSTAR_INVALID_NODE) return;
114 
115  /* There should not be given any other error-code.. */
116  assert(new_g >= 0);
117  /* Add the parent g-value to the new g-value */
118  new_g += parent->g;
119  if (this->max_path_cost != 0 && (uint)new_g > this->max_path_cost) return;
120 
121  /* Calculate the h-value */
122  new_h = this->CalculateH(this, current, parent);
123  /* There should not be given any error-code.. */
124  assert(new_h >= 0);
125 
126  /* The f-value if g + h */
127  new_f = new_g + new_h;
128 
129  /* Get the pointer to the parent in the ClosedList (the current one is to a copy of the one in the OpenList) */
130  closedlist_parent = this->ClosedListIsInList(&parent->path.node);
131 
132  /* Check if this item is already in the OpenList */
133  check = this->OpenListIsInList(current);
134  if (check != NULL) {
135  uint i;
136  /* Yes, check if this g value is lower.. */
137  if (new_g > check->g) return;
138  this->openlist_queue.Delete(check, 0);
139  /* It is lower, so change it to this item */
140  check->g = new_g;
141  check->path.parent = closedlist_parent;
142  /* Copy user data, will probably have changed */
143  for (i = 0; i < lengthof(current->user_data); i++) {
144  check->path.node.user_data[i] = current->user_data[i];
145  }
146  /* Re-add it in the openlist_queue. */
147  this->openlist_queue.Push(check, new_f);
148  } else {
149  /* A new node, add him to the OpenList */
150  this->OpenListAdd(closedlist_parent, current, new_f, new_g);
151  }
152 }
153 
164 {
165  int i;
166 
167  /* Get the best node from OpenList */
168  OpenListNode *current = this->OpenListPop();
169  /* If empty, drop an error */
170  if (current == NULL) return AYSTAR_EMPTY_OPENLIST;
171 
172  /* Check for end node and if found, return that code */
173  if (this->EndNodeCheck(this, current) == AYSTAR_FOUND_END_NODE) {
174  if (this->FoundEndNode != NULL) {
175  this->FoundEndNode(this, current);
176  }
177  free(current);
178  return AYSTAR_FOUND_END_NODE;
179  }
180 
181  /* Add the node to the ClosedList */
182  this->ClosedListAdd(&current->path);
183 
184  /* Load the neighbours */
185  this->GetNeighbours(this, current);
186 
187  /* Go through all neighbours */
188  for (i = 0; i < this->num_neighbours; i++) {
189  /* Check and add them to the OpenList if needed */
190  this->CheckTile(&this->neighbours[i], current);
191  }
192 
193  /* Free the node */
194  free(current);
195 
196  if (this->max_search_nodes != 0 && this->closedlist_hash.GetSize() >= this->max_search_nodes) {
197  /* We've expanded enough nodes */
198  return AYSTAR_LIMIT_REACHED;
199  } else {
200  /* Return that we are still busy */
201  return AYSTAR_STILL_BUSY;
202  }
203 }
204 
209 {
210  this->openlist_queue.Free(false);
211  /* 2nd argument above is false, below is true, to free the values only
212  * once */
213  this->openlist_hash.Delete(true);
214  this->closedlist_hash.Delete(true);
215 #ifdef AYSTAR_DEBUG
216  printf("[AyStar] Memory free'd\n");
217 #endif
218 }
219 
225 {
226  /* Clean the Queue, but not the elements within. That will be done by
227  * the hash. */
228  this->openlist_queue.Clear(false);
229  /* Clean the hashes */
230  this->openlist_hash.Clear(true);
231  this->closedlist_hash.Clear(true);
232 
233 #ifdef AYSTAR_DEBUG
234  printf("[AyStar] Cleared AyStar\n");
235 #endif
236 }
237 
248 {
249  int r, i = 0;
250  /* Loop through the OpenList
251  * Quit if result is no AYSTAR_STILL_BUSY or is more than loops_per_tick */
252  while ((r = this->Loop()) == AYSTAR_STILL_BUSY && (this->loops_per_tick == 0 || ++i < this->loops_per_tick)) { }
253 #ifdef AYSTAR_DEBUG
254  switch (r) {
255  case AYSTAR_FOUND_END_NODE: printf("[AyStar] Found path!\n"); break;
256  case AYSTAR_EMPTY_OPENLIST: printf("[AyStar] OpenList run dry, no path found\n"); break;
257  case AYSTAR_LIMIT_REACHED: printf("[AyStar] Exceeded search_nodes, no path found\n"); break;
258  default: break;
259  }
260 #endif
261  if (r != AYSTAR_STILL_BUSY) {
262  /* We're done, clean up */
263  this->Clear();
264  }
265 
266  switch (r) {
270  default: return AYSTAR_STILL_BUSY;
271  }
272 }
273 
282 void AyStar::AddStartNode(AyStarNode *start_node, uint g)
283 {
284 #ifdef AYSTAR_DEBUG
285  printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n",
286  TileX(start_node->tile), TileY(start_node->tile), start_node->direction);
287 #endif
288  this->OpenListAdd(NULL, start_node, 0, g);
289 }
290 
295 void AyStar::Init(Hash_HashProc hash, uint num_buckets)
296 {
297  /* Allocated the Hash for the OpenList and ClosedList */
298  this->openlist_hash.Init(hash, num_buckets);
299  this->closedlist_hash.Init(hash, num_buckets);
300 
301  /* Set up our sorting queue
302  * BinaryHeap allocates a block of 1024 nodes
303  * When that one gets full it reserves another one, till this number
304  * That is why it can stay this high */
305  this->openlist_queue.Init(102400);
306 }