stlastar.h
Go to the documentation of this file.
1 /*
2 A* Algorithm Implementation using STL is
3 Copyright (C)2001-2005 Justin Heyes-Jones
4 
5 Permission is given by the author to freely redistribute and
6 include this code in any program as long as this credit is
7 given where due.
8 
9  COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
10  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
11  INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE
12  IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
13  OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
14  PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED
15  CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL
16  DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
17  NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF
18  WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE
19  OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
20  THIS DISCLAIMER.
21 
22  Use at your own risk!
23 
24 */
25 
26 // used for text debugging
27 #include <iostream>
28 #include <stdio.h>
29 //#include <conio.h>
30 #include <assert.h>
31 
32 // some systems need this for uint8_t
33 #include <stdint.h>
34 
35 // stl includes
36 #include <algorithm>
37 #include <set>
38 #include <vector>
39 
40 using namespace std;
41 
42 // fast fixed size memory allocator, used for fast node memory management
43 #include "fsa.h"
44 
45 // Fixed size memory allocator can be disabled to compare performance
46 // Uses std new and delete instead if you turn it off
47 #define USE_FSA_MEMORY 0
48 
49 // disable warning that debugging information has lines that are truncated
50 // occurs in stl headers
51 //#pragma warning( disable : 4786 )
52 
53 // The AStar search class. UserState is the users state space type
54 template <class UserState> class AStarSearch {
55 public: // data
56  enum {
62  SEARCH_STATE_INVALID
63  };
64 
65  // A node represents a possible state in the search
66  // The user provided state type is included inside this type
67 
68 public:
69  class Node {
70  public:
71  Node *parent; // used during the search to record the parent of successor nodes
72  Node *child; // used after the search for the application to view the search in reverse
73 
74  float g; // cost of this node + it's predecessors
75  float h; // heuristic estimate of distance to goal
76  float f; // sum of cumulative cost of predecessors and self and heuristic
77 
78  Node() : parent(0), child(0), g(0.0f), h(0.0f), f(0.0f) {}
79  UserState m_UserState;
80  };
81 
82  // For sorting the heap the STL needs compare function that lets us compare
83  // the f value of two nodes
84 
85  class HeapCompare_f {
86  public:
87  bool operator()(const Node *x, const Node *y) const { return x->f > y->f; }
88  };
89 
90 public: // methods
91  // constructor just initialises private data
92  explicit AStarSearch()
93  : m_State(SEARCH_STATE_NOT_INITIALISED), m_Steps(0), m_Start(NULL), m_Goal(NULL),
94  m_CurrentSolutionNode(NULL),
96  m_FixedSizeAllocator(MaxNodes),
97 #endif
98  m_AllocateNodeCount(0), m_CancelRequest(false)
99  {
100  }
101 
102  // call at any time to cancel the search and free up all the memory
103  void CancelSearch() { m_CancelRequest = true; }
104  // Set Start and goal states
105  void SetStartAndGoalStates(UserState &Start, UserState &Goal)
106  {
107  m_CancelRequest = false;
108 
109  m_Start = AllocateNode();
110  m_Goal = AllocateNode();
111 
112  assert((m_Start != NULL && m_Goal != NULL));
113 
114  m_Start->m_UserState = Start;
115  m_Goal->m_UserState = Goal;
116 
117  m_State = SEARCH_STATE_SEARCHING;
118 
119  // Initialise the AStar specific parts of the Start Node
120  // The user only needs fill out the state information
121 
122  m_Start->g = 0;
123  m_Start->h = m_Start->m_UserState.GoalDistanceEstimate(m_Goal->m_UserState);
124  m_Start->f = m_Start->g + m_Start->h;
125  m_Start->parent = 0;
126 
127  // Push the start node on the Open list
128 
129  m_OpenList.push_back(m_Start); // heap now unsorted
130 
131  // Sort back element into heap
132  push_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f());
133 
134  // Initialise counter for search steps
135  m_Steps = 0;
136  }
137 
138  // Advances search one step
139  unsigned int SearchStep()
140  {
141  // Firstly break if the user has not initialised the search
142  assert((m_State > SEARCH_STATE_NOT_INITIALISED) && (m_State < SEARCH_STATE_INVALID));
143 
144  // Next I want it to be safe to do a searchstep once the search has succeeded...
145  if ((m_State == SEARCH_STATE_SUCCEEDED) || (m_State == SEARCH_STATE_FAILED)) {
146  return m_State;
147  }
148 
149  // Failure is defined as emptying the open list as there is nothing left to
150  // search...
151  // New: Allow user abort
152  if (m_OpenList.empty() || m_CancelRequest) {
153  FreeAllNodes();
154  m_State = SEARCH_STATE_FAILED;
155  return m_State;
156  }
157 
158  // Incremement step count
159  m_Steps++;
160 
161  // Pop the best node (the one with the lowest f)
162  Node *n = m_OpenList.front(); // get pointer to the node
163  pop_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f());
164  m_OpenList.pop_back();
165 
166  // Check for the goal, once we pop that we're done
167  if (n->m_UserState.IsGoal(m_Goal->m_UserState)) {
168  // The user is going to use the Goal Node he passed in
169  // so copy the parent pointer of n
170  m_Goal->parent = n->parent;
171 
172  // A special case is that the goal was passed in as the start state
173  // so handle that here
174  if (false == n->m_UserState.IsSameState(m_Start->m_UserState)) {
175  FreeNode(n);
176 
177  // set the child pointers in each node (except Goal which has no child)
178  Node *nodeChild = m_Goal;
179  Node *nodeParent = m_Goal->parent;
180 
181  do {
182  nodeParent->child = nodeChild;
183 
184  nodeChild = nodeParent;
185  nodeParent = nodeParent->parent;
186 
187  } while (nodeChild != m_Start); // Start is always the first node by definition
188  }
189 
190  // delete nodes that aren't needed for the solution
191  FreeUnusedNodes();
192 
193  m_State = SEARCH_STATE_SUCCEEDED;
194 
195  return m_State;
196  } else // not goal
197  {
198  // We now need to generate the successors of this node
199  // The user helps us to do this, and we keep the new nodes in
200  // m_Successors ...
201 
202  m_Successors.clear(); // empty vector of successor nodes to n
203 
204  // User provides this functions and uses AddSuccessor to add each successor of
205  // node 'n' to m_Successors
206  bool ret = n->m_UserState.GetSuccessors(this, n->parent ? &n->parent->m_UserState : NULL);
207 
208  if (!ret) {
209  typename vector<Node *>::iterator successor;
210 
211  // free the nodes that may previously have been added
212  for (successor = m_Successors.begin(); successor != m_Successors.end(); ++successor) {
213  FreeNode((*successor));
214  }
215 
216  m_Successors.clear(); // empty vector of successor nodes to n
217 
218  // free up everything else we allocated
219  FreeAllNodes();
220 
221  m_State = SEARCH_STATE_OUT_OF_MEMORY;
222  return m_State;
223  }
224 
225  // Now handle each successor to the current node ...
226  for (typename vector<Node *>::iterator successor = m_Successors.begin();
227  successor != m_Successors.end(); ++successor) {
228  // The g value for this successor ...
229  float newg = n->g + n->m_UserState.GetCost((*successor)->m_UserState);
230 
231  // Now we need to find whether the node is on the open or closed lists
232  // If it is but the node that is already on them is better (lower g)
233  // then we can forget about this successor
234 
235  // First linear search of open list to find node
236 
237  typename vector<Node *>::iterator openlist_result;
238 
239  for (openlist_result = m_OpenList.begin(); openlist_result != m_OpenList.end();
240  ++openlist_result) {
241  if ((*openlist_result)->m_UserState.IsSameState((*successor)->m_UserState)) {
242  break;
243  }
244  }
245 
246  if (openlist_result != m_OpenList.end()) {
247  // we found this state on open
248 
249  if ((*openlist_result)->g <= newg) {
250  FreeNode((*successor));
251 
252  // the one on Open is cheaper than this one
253  continue;
254  }
255  }
256 
257  typename vector<Node *>::iterator closedlist_result;
258 
259  for (closedlist_result = m_ClosedList.begin(); closedlist_result != m_ClosedList.end();
260  ++closedlist_result) {
261  if ((*closedlist_result)->m_UserState.IsSameState((*successor)->m_UserState)) {
262  break;
263  }
264  }
265 
266  if (closedlist_result != m_ClosedList.end()) {
267  // we found this state on closed
268 
269  if ((*closedlist_result)->g <= newg) {
270  // the one on Closed is cheaper than this one
271  FreeNode((*successor));
272 
273  continue;
274  }
275  }
276 
277  // This node is the best node so far with this particular state
278  // so lets keep it and set up its AStar specific data ...
279 
280  (*successor)->parent = n;
281  (*successor)->g = newg;
282  (*successor)->h = (*successor)->m_UserState.GoalDistanceEstimate(m_Goal->m_UserState);
283  (*successor)->f = (*successor)->g + (*successor)->h;
284 
285  // Remove successor from closed if it was on it
286 
287  if (closedlist_result != m_ClosedList.end()) {
288  // remove it from Closed
289  FreeNode((*closedlist_result));
290  m_ClosedList.erase(closedlist_result);
291 
292  // Fix thanks to ...
293  // Greg Douglas <gregdouglasmail@gmail.com>
294  // who noticed that this code path was incorrect
295  // Here we have found a new state which is already CLOSED
296  // anus
297  }
298 
299  // Update old version of this node
300  if (openlist_result != m_OpenList.end()) {
301  FreeNode((*openlist_result));
302  m_OpenList.erase(openlist_result);
303 
304  // re-make the heap
305  // make_heap rather than sort_heap is an essential bug fix
306  // thanks to Mike Ryynanen for pointing this out and then explaining
307  // it in detail. sort_heap called on an invalid heap does not work
308  make_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f());
309  }
310 
311  // heap now unsorted
312  m_OpenList.push_back((*successor));
313 
314  // sort back element into heap
315  push_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f());
316  }
317 
318  // push n onto Closed, as we have expanded it now
319 
320  m_ClosedList.push_back(n);
321 
322  } // end else (not goal so expand)
323 
324  return m_State; // Succeeded bool is false at this point.
325  }
326 
327  // User calls this to add a successor to a list of successors
328  // when expanding the search frontier
329  bool AddSuccessor(UserState &State)
330  {
331  Node *node = AllocateNode();
332 
333  if (node) {
334  node->m_UserState = State;
335 
336  m_Successors.push_back(node);
337 
338  return true;
339  }
340 
341  return false;
342  }
343 
344  // Free the solution nodes
345  // This is done to clean up all used Node memory when you are done with the
346  // search
348  {
349  Node *n = m_Start;
350 
351  if (m_Start->child) {
352  do {
353  Node *del = n;
354  n = n->child;
355  FreeNode(del);
356 
357  del = NULL;
358 
359  } while (n != m_Goal);
360 
361  FreeNode(n); // Delete the goal
362 
363  } else {
364  // if the start node is the solution we need to just delete the start and goal
365  // nodes
366  FreeNode(m_Start);
367  FreeNode(m_Goal);
368  }
369  }
370 
371  // Functions for traversing the solution
372 
373  // Get start node
374  UserState *GetSolutionStart()
375  {
376  m_CurrentSolutionNode = m_Start;
377  if (m_Start) {
378  return &m_Start->m_UserState;
379  } else {
380  return NULL;
381  }
382  }
383 
384  // Get next node
385  UserState *GetSolutionNext()
386  {
387  if (m_CurrentSolutionNode) {
388  if (m_CurrentSolutionNode->child) {
389  Node *child = m_CurrentSolutionNode->child;
390 
391  m_CurrentSolutionNode = m_CurrentSolutionNode->child;
392 
393  return &child->m_UserState;
394  }
395  }
396 
397  return NULL;
398  }
399 
400  // Get end node
401  UserState *GetSolutionEnd()
402  {
403  m_CurrentSolutionNode = m_Goal;
404  if (m_Goal) {
405  return &m_Goal->m_UserState;
406  } else {
407  return NULL;
408  }
409  }
410 
411  // Step solution iterator backwards
412  UserState *GetSolutionPrev()
413  {
414  if (m_CurrentSolutionNode) {
415  if (m_CurrentSolutionNode->parent) {
416  Node *parent = m_CurrentSolutionNode->parent;
417 
418  m_CurrentSolutionNode = m_CurrentSolutionNode->parent;
419 
420  return &parent->m_UserState;
421  }
422  }
423 
424  return NULL;
425  }
426 
427  // For educational use and debugging it is useful to be able to view
428  // the open and closed list at each step, here are two functions to allow that.
429 
430  UserState *GetOpenListStart()
431  {
432  float f, g, h;
433  return GetOpenListStart(f, g, h);
434  }
435 
436  UserState *GetOpenListStart(float &f, float &g, float &h)
437  {
438  iterDbgOpen = m_OpenList.begin();
439  if (iterDbgOpen != m_OpenList.end()) {
440  f = (*iterDbgOpen)->f;
441  g = (*iterDbgOpen)->g;
442  h = (*iterDbgOpen)->h;
443  return &(*iterDbgOpen)->m_UserState;
444  }
445 
446  return NULL;
447  }
448 
449  UserState *GetOpenListNext()
450  {
451  float f, g, h;
452  return GetOpenListNext(f, g, h);
453  }
454 
455  UserState *GetOpenListNext(float &f, float &g, float &h)
456  {
457  ++iterDbgOpen;
458  if (iterDbgOpen != m_OpenList.end()) {
459  f = (*iterDbgOpen)->f;
460  g = (*iterDbgOpen)->g;
461  h = (*iterDbgOpen)->h;
462  return &(*iterDbgOpen)->m_UserState;
463  }
464 
465  return NULL;
466  }
467 
468  UserState *GetClosedListStart()
469  {
470  float f, g, h;
471  return GetClosedListStart(f, g, h);
472  }
473 
474  UserState *GetClosedListStart(float &f, float &g, float &h)
475  {
476  iterDbgClosed = m_ClosedList.begin();
477  if (iterDbgClosed != m_ClosedList.end()) {
478  f = (*iterDbgClosed)->f;
479  g = (*iterDbgClosed)->g;
480  h = (*iterDbgClosed)->h;
481 
482  return &(*iterDbgClosed)->m_UserState;
483  }
484 
485  return NULL;
486  }
487 
488  UserState *GetClosedListNext()
489  {
490  float f, g, h;
491  return GetClosedListNext(f, g, h);
492  }
493 
494  UserState *GetClosedListNext(float &f, float &g, float &h)
495  {
496  ++iterDbgClosed;
497  if (iterDbgClosed != m_ClosedList.end()) {
498  f = (*iterDbgClosed)->f;
499  g = (*iterDbgClosed)->g;
500  h = (*iterDbgClosed)->h;
501 
502  return &(*iterDbgClosed)->m_UserState;
503  }
504 
505  return NULL;
506  }
507 
508  // Get the number of steps
509 
510  int GetStepCount() { return m_Steps; }
512  {
513 #if USE_FSA_MEMORY
514  assert(m_AllocateNodeCount == 0);
515 #endif
516  }
517 
518 private: // methods
519  // This is called when a search fails or is cancelled to free all used
520  // memory
522  {
523  // iterate open list and delete all nodes
524  typename vector<Node *>::iterator iterOpen = m_OpenList.begin();
525 
526  while (iterOpen != m_OpenList.end()) {
527  Node *n = (*iterOpen);
528  FreeNode(n);
529 
530  ++iterOpen;
531  }
532 
533  m_OpenList.clear();
534 
535  // iterate closed list and delete unused nodes
536  typename vector<Node *>::iterator iterClosed;
537 
538  for (iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); ++iterClosed) {
539  Node *n = (*iterClosed);
540  FreeNode(n);
541  }
542 
543  m_ClosedList.clear();
544 
545  // delete the goal
546 
547  FreeNode(m_Goal);
548  }
549 
550  // This call is made by the search class when the search ends. A lot of nodes may be
551  // created that are still present when the search ends. They will be deleted by this
552  // routine once the search ends
554  {
555  // iterate open list and delete unused nodes
556  typename vector<Node *>::iterator iterOpen = m_OpenList.begin();
557 
558  while (iterOpen != m_OpenList.end()) {
559  Node *n = (*iterOpen);
560 
561  if (!n->child) {
562  FreeNode(n);
563 
564  n = NULL;
565  }
566 
567  ++iterOpen;
568  }
569 
570  m_OpenList.clear();
571 
572  // iterate closed list and delete unused nodes
573  typename vector<Node *>::iterator iterClosed;
574 
575  for (iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); ++iterClosed) {
576  Node *n = (*iterClosed);
577 
578  if (!n->child) {
579  FreeNode(n);
580  n = NULL;
581  }
582  }
583 
584  m_ClosedList.clear();
585  }
586 
587  // Node memory management
589  {
590 #if !USE_FSA_MEMORY
591  Node *p = new Node;
592  return p;
593 #else
594  Node *address = m_FixedSizeAllocator.alloc();
595 
596  if (!address) {
597  return NULL;
598  }
599  m_AllocateNodeCount++;
600  Node *p = new (address) Node;
601  return p;
602 #endif
603  }
604 
605  void FreeNode(Node *node)
606  {
607  m_AllocateNodeCount--;
608 
609 #if !USE_FSA_MEMORY
610  delete node;
611 #else
612  m_FixedSizeAllocator.free(node);
613 #endif
614  }
615 
616 private: // data
617  // Heap (simple vector but used as a heap, cf. Steve Rabin's game gems article)
618  vector<Node *> m_OpenList;
619 
620  // Closed list is a vector.
621  vector<Node *> m_ClosedList;
622 
623  // Successors is a vector filled out by the user each type successors to a node
624  // are generated
625  vector<Node *> m_Successors;
626 
627  // State
628  unsigned int m_State;
629 
630  // Counts steps
631  int m_Steps;
632 
633  // Start and goal state pointers
636 
638 
639 #if USE_FSA_MEMORY
640  // Memory
641  FixedSizeAllocator<Node> m_FixedSizeAllocator;
642 #endif
643 
644  // Debug : need to keep these two iterators around
645  // for the user Dbg functions
646  typename vector<Node *>::iterator iterDbgOpen;
647  typename vector<Node *>::iterator iterDbgClosed;
648 
649  // debugging : count memory allocation and free's
651 
653 };
AStarSearch()
Definition: stlastar.h:92
UserState * GetOpenListStart()
Definition: stlastar.h:430
UserState * GetOpenListNext()
Definition: stlastar.h:449
UserState * GetSolutionPrev()
Definition: stlastar.h:412
int GetStepCount()
Definition: stlastar.h:510
bool operator()(const Node *x, const Node *y) const
Definition: stlastar.h:87
vector< Node * > m_ClosedList
Definition: stlastar.h:621
void CancelSearch()
Definition: stlastar.h:103
void FreeUnusedNodes()
Definition: stlastar.h:553
int m_AllocateNodeCount
Definition: stlastar.h:650
UserState * GetClosedListStart()
Definition: stlastar.h:468
UserState * GetSolutionEnd()
Definition: stlastar.h:401
UserState * GetOpenListNext(float &f, float &g, float &h)
Definition: stlastar.h:455
#define USE_FSA_MEMORY
Definition: stlastar.h:47
UserState * GetClosedListNext(float &f, float &g, float &h)
Definition: stlastar.h:494
UserState * GetOpenListStart(float &f, float &g, float &h)
Definition: stlastar.h:436
vector< Node * > m_OpenList
Definition: stlastar.h:618
vector< Node * > m_Successors
Definition: stlastar.h:625
Node * m_Goal
Definition: stlastar.h:635
void EnsureMemoryFreed()
Definition: stlastar.h:511
UserState * GetClosedListStart(float &f, float &g, float &h)
Definition: stlastar.h:474
void FreeAllNodes()
Definition: stlastar.h:521
unsigned int m_State
Definition: stlastar.h:628
UserState * GetClosedListNext()
Definition: stlastar.h:488
void SetStartAndGoalStates(UserState &Start, UserState &Goal)
Definition: stlastar.h:105
void FreeSolutionNodes()
Definition: stlastar.h:347
int m_Steps
Definition: stlastar.h:631
Node * m_Start
Definition: stlastar.h:634
void FreeNode(Node *node)
Definition: stlastar.h:605
vector< Node * >::iterator iterDbgOpen
Definition: stlastar.h:646
Node * m_CurrentSolutionNode
Definition: stlastar.h:637
unsigned int SearchStep()
Definition: stlastar.h:139
Node * AllocateNode()
Definition: stlastar.h:588
bool AddSuccessor(UserState &State)
Definition: stlastar.h:329
UserState * GetSolutionStart()
Definition: stlastar.h:374
UserState m_UserState
Definition: stlastar.h:79
bool m_CancelRequest
Definition: stlastar.h:652
UserState * GetSolutionNext()
Definition: stlastar.h:385
vector< Node * >::iterator iterDbgClosed
Definition: stlastar.h:647


stage
Author(s): Richard Vaughan , Brian Gerkey , Reed Hedges , Andrew Howard , Toby Collett , Pooya Karimian , Jeremy Asher , Alex Couture-Beil , Geoff Biggs , Rich Mattes , Abbas Sadat
autogenerated on Mon Feb 28 2022 23:48:56