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 {
56 
57 public: // data
58 
59  enum
60  {
66  SEARCH_STATE_INVALID
67  };
68 
69 
70  // A node represents a possible state in the search
71  // The user provided state type is included inside this type
72 
73  public:
74 
75  class Node
76  {
77  public:
78 
79  Node *parent; // used during the search to record the parent of successor nodes
80  Node *child; // used after the search for the application to view the search in reverse
81 
82  float g; // cost of this node + it's predecessors
83  float h; // heuristic estimate of distance to goal
84  float f; // sum of cumulative cost of predecessors and self and heuristic
85 
86  Node() :
87  parent( 0 ),
88  child( 0 ),
89  g( 0.0f ),
90  h( 0.0f ),
91  f( 0.0f )
92  {
93  }
94 
95  UserState m_UserState;
96  };
97 
98 
99  // For sorting the heap the STL needs compare function that lets us compare
100  // the f value of two nodes
101 
103  {
104  public:
105 
106  bool operator() ( const Node *x, const Node *y ) const
107  {
108  return x->f > y->f;
109  }
110  };
111 
112 
113 public: // methods
114 
115 
116  // constructor just initialises private data
117  AStarSearch( int MaxNodes = 2000 ) :
118 #if USE_FSA_MEMORY
119  m_FixedSizeAllocator( MaxNodes ),
120 #endif
121  m_State( SEARCH_STATE_NOT_INITIALISED ),
122  m_CurrentSolutionNode( NULL ),
123  m_AllocateNodeCount(0),
124  m_CancelRequest( false )
125  {
126  }
127 
128  // call at any time to cancel the search and free up all the memory
130  {
131  m_CancelRequest = true;
132  }
133 
134  // Set Start and goal states
135  void SetStartAndGoalStates( UserState &Start, UserState &Goal )
136  {
137  m_CancelRequest = false;
138 
139  m_Start = AllocateNode();
140  m_Goal = AllocateNode();
141 
142  assert((m_Start != NULL && m_Goal != NULL));
143 
144  m_Start->m_UserState = Start;
145  m_Goal->m_UserState = Goal;
146 
147  m_State = SEARCH_STATE_SEARCHING;
148 
149  // Initialise the AStar specific parts of the Start Node
150  // The user only needs fill out the state information
151 
152  m_Start->g = 0;
153  m_Start->h = m_Start->m_UserState.GoalDistanceEstimate( m_Goal->m_UserState );
154  m_Start->f = m_Start->g + m_Start->h;
155  m_Start->parent = 0;
156 
157  // Push the start node on the Open list
158 
159  m_OpenList.push_back( m_Start ); // heap now unsorted
160 
161  // Sort back element into heap
162  push_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() );
163 
164  // Initialise counter for search steps
165  m_Steps = 0;
166  }
167 
168  // Advances search one step
169  unsigned int SearchStep()
170  {
171  // Firstly break if the user has not initialised the search
172  assert( (m_State > SEARCH_STATE_NOT_INITIALISED) &&
173  (m_State < SEARCH_STATE_INVALID) );
174 
175  // Next I want it to be safe to do a searchstep once the search has succeeded...
176  if( (m_State == SEARCH_STATE_SUCCEEDED) ||
177  (m_State == SEARCH_STATE_FAILED)
178  )
179  {
180  return m_State;
181  }
182 
183  // Failure is defined as emptying the open list as there is nothing left to
184  // search...
185  // New: Allow user abort
186  if( m_OpenList.empty() || m_CancelRequest )
187  {
188  FreeAllNodes();
189  m_State = SEARCH_STATE_FAILED;
190  return m_State;
191  }
192 
193  // Incremement step count
194  m_Steps ++;
195 
196  // Pop the best node (the one with the lowest f)
197  Node *n = m_OpenList.front(); // get pointer to the node
198  pop_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() );
199  m_OpenList.pop_back();
200 
201  // Check for the goal, once we pop that we're done
202  if( n->m_UserState.IsGoal( m_Goal->m_UserState ) )
203  {
204  // The user is going to use the Goal Node he passed in
205  // so copy the parent pointer of n
206  m_Goal->parent = n->parent;
207 
208  // A special case is that the goal was passed in as the start state
209  // so handle that here
210  if( false == n->m_UserState.IsSameState( m_Start->m_UserState ) )
211  {
212  FreeNode( n );
213 
214  // set the child pointers in each node (except Goal which has no child)
215  Node *nodeChild = m_Goal;
216  Node *nodeParent = m_Goal->parent;
217 
218  do
219  {
220  nodeParent->child = nodeChild;
221 
222  nodeChild = nodeParent;
223  nodeParent = nodeParent->parent;
224 
225  }
226  while( nodeChild != m_Start ); // Start is always the first node by definition
227 
228  }
229 
230  // delete nodes that aren't needed for the solution
231  FreeUnusedNodes();
232 
233  m_State = SEARCH_STATE_SUCCEEDED;
234 
235  return m_State;
236  }
237  else // not goal
238  {
239 
240  // We now need to generate the successors of this node
241  // The user helps us to do this, and we keep the new nodes in
242  // m_Successors ...
243 
244  m_Successors.clear(); // empty vector of successor nodes to n
245 
246  // User provides this functions and uses AddSuccessor to add each successor of
247  // node 'n' to m_Successors
248  bool ret = n->m_UserState.GetSuccessors( this, n->parent ? &n->parent->m_UserState : NULL );
249 
250  if( !ret )
251  {
252 
253  typename vector< Node * >::iterator successor;
254 
255  // free the nodes that may previously have been added
256  for( successor = m_Successors.begin(); successor != m_Successors.end(); successor ++ )
257  {
258  FreeNode( (*successor) );
259  }
260 
261  m_Successors.clear(); // empty vector of successor nodes to n
262 
263  // free up everything else we allocated
264  FreeAllNodes();
265 
266  m_State = SEARCH_STATE_OUT_OF_MEMORY;
267  return m_State;
268  }
269 
270  // Now handle each successor to the current node ...
271  for( typename vector< Node * >::iterator successor = m_Successors.begin(); successor != m_Successors.end(); successor ++ )
272  {
273 
274  // The g value for this successor ...
275  float newg = n->g + n->m_UserState.GetCost( (*successor)->m_UserState );
276 
277  // Now we need to find whether the node is on the open or closed lists
278  // If it is but the node that is already on them is better (lower g)
279  // then we can forget about this successor
280 
281  // First linear search of open list to find node
282 
283  typename vector< Node * >::iterator openlist_result;
284 
285  for( openlist_result = m_OpenList.begin(); openlist_result != m_OpenList.end(); openlist_result ++ )
286  {
287  if( (*openlist_result)->m_UserState.IsSameState( (*successor)->m_UserState ) )
288  {
289  break;
290  }
291  }
292 
293  if( openlist_result != m_OpenList.end() )
294  {
295 
296  // we found this state on open
297 
298  if( (*openlist_result)->g <= newg )
299  {
300  FreeNode( (*successor) );
301 
302  // the one on Open is cheaper than this one
303  continue;
304  }
305  }
306 
307  typename vector< Node * >::iterator closedlist_result;
308 
309  for( closedlist_result = m_ClosedList.begin(); closedlist_result != m_ClosedList.end(); closedlist_result ++ )
310  {
311  if( (*closedlist_result)->m_UserState.IsSameState( (*successor)->m_UserState ) )
312  {
313  break;
314  }
315  }
316 
317  if( closedlist_result != m_ClosedList.end() )
318  {
319 
320  // we found this state on closed
321 
322  if( (*closedlist_result)->g <= newg )
323  {
324  // the one on Closed is cheaper than this one
325  FreeNode( (*successor) );
326 
327  continue;
328  }
329  }
330 
331  // This node is the best node so far with this particular state
332  // so lets keep it and set up its AStar specific data ...
333 
334  (*successor)->parent = n;
335  (*successor)->g = newg;
336  (*successor)->h = (*successor)->m_UserState.GoalDistanceEstimate( m_Goal->m_UserState );
337  (*successor)->f = (*successor)->g + (*successor)->h;
338 
339  // Remove successor from closed if it was on it
340 
341  if( closedlist_result != m_ClosedList.end() )
342  {
343  // remove it from Closed
344  FreeNode( (*closedlist_result) );
345  m_ClosedList.erase( closedlist_result );
346 
347  // Fix thanks to ...
348  // Greg Douglas <gregdouglasmail@gmail.com>
349  // who noticed that this code path was incorrect
350  // Here we have found a new state which is already CLOSED
351  // anus
352 
353  }
354 
355  // Update old version of this node
356  if( openlist_result != m_OpenList.end() )
357  {
358 
359  FreeNode( (*openlist_result) );
360  m_OpenList.erase( openlist_result );
361 
362  // re-make the heap
363  // make_heap rather than sort_heap is an essential bug fix
364  // thanks to Mike Ryynanen for pointing this out and then explaining
365  // it in detail. sort_heap called on an invalid heap does not work
366  make_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() );
367 
368  }
369 
370  // heap now unsorted
371  m_OpenList.push_back( (*successor) );
372 
373  // sort back element into heap
374  push_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() );
375 
376  }
377 
378  // push n onto Closed, as we have expanded it now
379 
380  m_ClosedList.push_back( n );
381 
382  } // end else (not goal so expand)
383 
384  return m_State; // Succeeded bool is false at this point.
385 
386  }
387 
388  // User calls this to add a successor to a list of successors
389  // when expanding the search frontier
390  bool AddSuccessor( UserState &State )
391  {
392  Node *node = AllocateNode();
393 
394  if( node )
395  {
396  node->m_UserState = State;
397 
398  m_Successors.push_back( node );
399 
400  return true;
401  }
402 
403  return false;
404  }
405 
406  // Free the solution nodes
407  // This is done to clean up all used Node memory when you are done with the
408  // search
410  {
411  Node *n = m_Start;
412 
413  if( m_Start->child )
414  {
415  do
416  {
417  Node *del = n;
418  n = n->child;
419  FreeNode( del );
420 
421  del = NULL;
422 
423  } while( n != m_Goal );
424 
425  FreeNode( n ); // Delete the goal
426 
427  }
428  else
429  {
430  // if the start node is the solution we need to just delete the start and goal
431  // nodes
432  FreeNode( m_Start );
433  FreeNode( m_Goal );
434  }
435 
436  }
437 
438  // Functions for traversing the solution
439 
440  // Get start node
441  UserState *GetSolutionStart()
442  {
443  m_CurrentSolutionNode = m_Start;
444  if( m_Start )
445  {
446  return &m_Start->m_UserState;
447  }
448  else
449  {
450  return NULL;
451  }
452  }
453 
454  // Get next node
455  UserState *GetSolutionNext()
456  {
457  if( m_CurrentSolutionNode )
458  {
459  if( m_CurrentSolutionNode->child )
460  {
461 
462  Node *child = m_CurrentSolutionNode->child;
463 
464  m_CurrentSolutionNode = m_CurrentSolutionNode->child;
465 
466  return &child->m_UserState;
467  }
468  }
469 
470  return NULL;
471  }
472 
473  // Get end node
474  UserState *GetSolutionEnd()
475  {
476  m_CurrentSolutionNode = m_Goal;
477  if( m_Goal )
478  {
479  return &m_Goal->m_UserState;
480  }
481  else
482  {
483  return NULL;
484  }
485  }
486 
487  // Step solution iterator backwards
488  UserState *GetSolutionPrev()
489  {
490  if( m_CurrentSolutionNode )
491  {
492  if( m_CurrentSolutionNode->parent )
493  {
494 
495  Node *parent = m_CurrentSolutionNode->parent;
496 
497  m_CurrentSolutionNode = m_CurrentSolutionNode->parent;
498 
499  return &parent->m_UserState;
500  }
501  }
502 
503  return NULL;
504  }
505 
506  // For educational use and debugging it is useful to be able to view
507  // the open and closed list at each step, here are two functions to allow that.
508 
509  UserState *GetOpenListStart()
510  {
511  float f,g,h;
512  return GetOpenListStart( f,g,h );
513  }
514 
515  UserState *GetOpenListStart( float &f, float &g, float &h )
516  {
517  iterDbgOpen = m_OpenList.begin();
518  if( iterDbgOpen != m_OpenList.end() )
519  {
520  f = (*iterDbgOpen)->f;
521  g = (*iterDbgOpen)->g;
522  h = (*iterDbgOpen)->h;
523  return &(*iterDbgOpen)->m_UserState;
524  }
525 
526  return NULL;
527  }
528 
529  UserState *GetOpenListNext()
530  {
531  float f,g,h;
532  return GetOpenListNext( f,g,h );
533  }
534 
535  UserState *GetOpenListNext( float &f, float &g, float &h )
536  {
537  iterDbgOpen++;
538  if( iterDbgOpen != m_OpenList.end() )
539  {
540  f = (*iterDbgOpen)->f;
541  g = (*iterDbgOpen)->g;
542  h = (*iterDbgOpen)->h;
543  return &(*iterDbgOpen)->m_UserState;
544  }
545 
546  return NULL;
547  }
548 
549  UserState *GetClosedListStart()
550  {
551  float f,g,h;
552  return GetClosedListStart( f,g,h );
553  }
554 
555  UserState *GetClosedListStart( float &f, float &g, float &h )
556  {
557  iterDbgClosed = m_ClosedList.begin();
558  if( iterDbgClosed != m_ClosedList.end() )
559  {
560  f = (*iterDbgClosed)->f;
561  g = (*iterDbgClosed)->g;
562  h = (*iterDbgClosed)->h;
563 
564  return &(*iterDbgClosed)->m_UserState;
565  }
566 
567  return NULL;
568  }
569 
570  UserState *GetClosedListNext()
571  {
572  float f,g,h;
573  return GetClosedListNext( f,g,h );
574  }
575 
576  UserState *GetClosedListNext( float &f, float &g, float &h )
577  {
578  iterDbgClosed++;
579  if( iterDbgClosed != m_ClosedList.end() )
580  {
581  f = (*iterDbgClosed)->f;
582  g = (*iterDbgClosed)->g;
583  h = (*iterDbgClosed)->h;
584 
585  return &(*iterDbgClosed)->m_UserState;
586  }
587 
588  return NULL;
589  }
590 
591  // Get the number of steps
592 
593  int GetStepCount() { return m_Steps; }
594 
596  {
597 #if USE_FSA_MEMORY
598  assert(m_AllocateNodeCount == 0);
599 #endif
600 
601  }
602 
603 private: // methods
604 
605  // This is called when a search fails or is cancelled to free all used
606  // memory
608  {
609  // iterate open list and delete all nodes
610  typename vector< Node * >::iterator iterOpen = m_OpenList.begin();
611 
612  while( iterOpen != m_OpenList.end() )
613  {
614  Node *n = (*iterOpen);
615  FreeNode( n );
616 
617  iterOpen ++;
618  }
619 
620  m_OpenList.clear();
621 
622  // iterate closed list and delete unused nodes
623  typename vector< Node * >::iterator iterClosed;
624 
625  for( iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed ++ )
626  {
627  Node *n = (*iterClosed);
628  FreeNode( n );
629  }
630 
631  m_ClosedList.clear();
632 
633  // delete the goal
634 
635  FreeNode(m_Goal);
636  }
637 
638 
639  // This call is made by the search class when the search ends. A lot of nodes may be
640  // created that are still present when the search ends. They will be deleted by this
641  // routine once the search ends
643  {
644  // iterate open list and delete unused nodes
645  typename vector< Node * >::iterator iterOpen = m_OpenList.begin();
646 
647  while( iterOpen != m_OpenList.end() )
648  {
649  Node *n = (*iterOpen);
650 
651  if( !n->child )
652  {
653  FreeNode( n );
654 
655  n = NULL;
656  }
657 
658  iterOpen ++;
659  }
660 
661  m_OpenList.clear();
662 
663  // iterate closed list and delete unused nodes
664  typename vector< Node * >::iterator iterClosed;
665 
666  for( iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed ++ )
667  {
668  Node *n = (*iterClosed);
669 
670  if( !n->child )
671  {
672  FreeNode( n );
673  n = NULL;
674 
675  }
676  }
677 
678  m_ClosedList.clear();
679 
680  }
681 
682  // Node memory management
684  {
685 
686 #if !USE_FSA_MEMORY
687  Node *p = new Node;
688  return p;
689 #else
690  Node *address = m_FixedSizeAllocator.alloc();
691 
692  if( !address )
693  {
694  return NULL;
695  }
696  m_AllocateNodeCount ++;
697  Node *p = new (address) Node;
698  return p;
699 #endif
700  }
701 
702  void FreeNode( Node *node )
703  {
704 
705  m_AllocateNodeCount --;
706 
707 #if !USE_FSA_MEMORY
708  delete node;
709 #else
710  m_FixedSizeAllocator.free( node );
711 #endif
712  }
713 
714 private: // data
715 
716  // Heap (simple vector but used as a heap, cf. Steve Rabin's game gems article)
717  vector< Node *> m_OpenList;
718 
719  // Closed list is a vector.
720  vector< Node * > m_ClosedList;
721 
722  // Successors is a vector filled out by the user each type successors to a node
723  // are generated
724  vector< Node * > m_Successors;
725 
726  // State
727  unsigned int m_State;
728 
729  // Counts steps
730  int m_Steps;
731 
732  // Start and goal state pointers
735 
737 
738 #if USE_FSA_MEMORY
739  // Memory
740  FixedSizeAllocator<Node> m_FixedSizeAllocator;
741 #endif
742 
743  //Debug : need to keep these two iterators around
744  // for the user Dbg functions
745  typename vector< Node * >::iterator iterDbgOpen;
746  typename vector< Node * >::iterator iterDbgClosed;
747 
748  // debugging : count memory allocation and free's
750 
752 
753 };
754 
755 
756 
757 
UserState * GetOpenListStart()
Definition: stlastar.h:509
UserState * GetOpenListNext()
Definition: stlastar.h:529
vector< Node * > m_OpenList
Definition: stlastar.h:717
UserState * GetSolutionPrev()
Definition: stlastar.h:488
int GetStepCount()
Definition: stlastar.h:593
void CancelSearch()
Definition: stlastar.h:129
void FreeUnusedNodes()
Definition: stlastar.h:642
int m_AllocateNodeCount
Definition: stlastar.h:749
AStarSearch(int MaxNodes=2000)
Definition: stlastar.h:117
vector< Node * > m_Successors
Definition: stlastar.h:724
UserState * GetClosedListStart()
Definition: stlastar.h:549
UserState * GetSolutionEnd()
Definition: stlastar.h:474
UserState * GetOpenListNext(float &f, float &g, float &h)
Definition: stlastar.h:535
vector< Node * >::iterator iterDbgClosed
Definition: stlastar.h:746
#define USE_FSA_MEMORY
Definition: stlastar.h:47
UserState * GetClosedListNext(float &f, float &g, float &h)
Definition: stlastar.h:576
UserState * GetOpenListStart(float &f, float &g, float &h)
Definition: stlastar.h:515
Node * m_Goal
Definition: stlastar.h:734
void EnsureMemoryFreed()
Definition: stlastar.h:595
UserState * GetClosedListStart(float &f, float &g, float &h)
Definition: stlastar.h:555
void FreeAllNodes()
Definition: stlastar.h:607
unsigned int m_State
Definition: stlastar.h:727
UserState * GetClosedListNext()
Definition: stlastar.h:570
void SetStartAndGoalStates(UserState &Start, UserState &Goal)
Definition: stlastar.h:135
void FreeSolutionNodes()
Definition: stlastar.h:409
int m_Steps
Definition: stlastar.h:730
Node * m_Start
Definition: stlastar.h:733
void FreeNode(Node *node)
Definition: stlastar.h:702
vector< Node * >::iterator iterDbgOpen
Definition: stlastar.h:745
Node * m_CurrentSolutionNode
Definition: stlastar.h:736
unsigned int SearchStep()
Definition: stlastar.h:169
Node * AllocateNode()
Definition: stlastar.h:683
bool AddSuccessor(UserState &State)
Definition: stlastar.h:390
vector< Node * > m_ClosedList
Definition: stlastar.h:720
UserState * GetSolutionStart()
Definition: stlastar.h:441
UserState m_UserState
Definition: stlastar.h:95
bool m_CancelRequest
Definition: stlastar.h:751
UserState * GetSolutionNext()
Definition: stlastar.h:455


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 Jun 10 2019 15:06:10