stlastar.h
Go to the documentation of this file.
00001 /*
00002 A* Algorithm Implementation using STL is
00003 Copyright (C)2001-2005 Justin Heyes-Jones
00004 
00005 Permission is given by the author to freely redistribute and 
00006 include this code in any program as long as this credit is 
00007 given where due.
00008  
00009   COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 
00010   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 
00011   INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE 
00012   IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
00013   OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND 
00014   PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED 
00015   CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL 
00016   DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY 
00017   NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF 
00018   WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE 
00019   OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
00020   THIS DISCLAIMER.
00021  
00022   Use at your own risk!
00023 
00024 */
00025 
00026 // used for text debugging
00027 #include <iostream>
00028 #include <stdio.h>
00029 //#include <conio.h>
00030 #include <assert.h>
00031 
00032 // some systems need this for uint8_t 
00033 #include <stdint.h>
00034 
00035 // stl includes
00036 #include <algorithm>
00037 #include <set>
00038 #include <vector>
00039 
00040 using namespace std;
00041 
00042 // fast fixed size memory allocator, used for fast node memory management
00043 #include "fsa.h"
00044 
00045 // Fixed size memory allocator can be disabled to compare performance
00046 // Uses std new and delete instead if you turn it off
00047 #define USE_FSA_MEMORY 0
00048 
00049 // disable warning that debugging information has lines that are truncated
00050 // occurs in stl headers
00051 //#pragma warning( disable : 4786 )
00052 
00053 // The AStar search class. UserState is the users state space type
00054 template <class UserState> class AStarSearch
00055 {
00056 
00057 public: // data
00058 
00059         enum
00060         {
00061                 SEARCH_STATE_NOT_INITIALISED,
00062                 SEARCH_STATE_SEARCHING,
00063                 SEARCH_STATE_SUCCEEDED,
00064                 SEARCH_STATE_FAILED,
00065                 SEARCH_STATE_OUT_OF_MEMORY,
00066                 SEARCH_STATE_INVALID
00067         };
00068 
00069 
00070         // A node represents a possible state in the search
00071         // The user provided state type is included inside this type
00072 
00073         public:
00074 
00075         class Node
00076         {
00077                 public:
00078 
00079                         Node *parent; // used during the search to record the parent of successor nodes
00080                         Node *child; // used after the search for the application to view the search in reverse
00081                         
00082                         float g; // cost of this node + it's predecessors
00083                         float h; // heuristic estimate of distance to goal
00084                         float f; // sum of cumulative cost of predecessors and self and heuristic
00085 
00086                         Node() :
00087                                 parent( 0 ),
00088                                 child( 0 ),
00089                                 g( 0.0f ),
00090                                 h( 0.0f ),
00091                                 f( 0.0f )
00092                         {                       
00093                         }
00094 
00095                         UserState m_UserState;
00096         };
00097 
00098 
00099         // For sorting the heap the STL needs compare function that lets us compare
00100         // the f value of two nodes
00101 
00102         class HeapCompare_f 
00103         {
00104                 public:
00105 
00106                         bool operator() ( const Node *x, const Node *y ) const
00107                         {
00108                                 return x->f > y->f;
00109                         }
00110         };
00111 
00112 
00113 public: // methods
00114 
00115 
00116         // constructor just initialises private data
00117  AStarSearch( int MaxNodes = 2000 ) :
00118 #if USE_FSA_MEMORY
00119         m_FixedSizeAllocator( MaxNodes ),
00120 #endif
00121                 m_State( SEARCH_STATE_NOT_INITIALISED ),
00122                 m_CurrentSolutionNode( NULL ),
00123                 m_AllocateNodeCount(0),
00124                 m_CancelRequest( false )
00125                         {
00126                         }
00127         
00128         // call at any time to cancel the search and free up all the memory
00129         void CancelSearch()
00130         {
00131                 m_CancelRequest = true;
00132         }
00133 
00134         // Set Start and goal states
00135         void SetStartAndGoalStates( UserState &Start, UserState &Goal )
00136         {
00137                 m_CancelRequest = false;
00138 
00139                 m_Start = AllocateNode();
00140                 m_Goal = AllocateNode();
00141 
00142                 assert((m_Start != NULL && m_Goal != NULL));
00143                 
00144                 m_Start->m_UserState = Start;
00145                 m_Goal->m_UserState = Goal;
00146 
00147                 m_State = SEARCH_STATE_SEARCHING;
00148                 
00149                 // Initialise the AStar specific parts of the Start Node
00150                 // The user only needs fill out the state information
00151 
00152                 m_Start->g = 0; 
00153                 m_Start->h = m_Start->m_UserState.GoalDistanceEstimate( m_Goal->m_UserState );
00154                 m_Start->f = m_Start->g + m_Start->h;
00155                 m_Start->parent = 0;
00156 
00157                 // Push the start node on the Open list
00158 
00159                 m_OpenList.push_back( m_Start ); // heap now unsorted
00160 
00161                 // Sort back element into heap
00162                 push_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() );
00163 
00164                 // Initialise counter for search steps
00165                 m_Steps = 0;
00166         }
00167 
00168         // Advances search one step 
00169         unsigned int SearchStep()
00170         {
00171                 // Firstly break if the user has not initialised the search
00172                 assert( (m_State > SEARCH_STATE_NOT_INITIALISED) &&
00173                                 (m_State < SEARCH_STATE_INVALID) );
00174 
00175                 // Next I want it to be safe to do a searchstep once the search has succeeded...
00176                 if( (m_State == SEARCH_STATE_SUCCEEDED) ||
00177                         (m_State == SEARCH_STATE_FAILED) 
00178                   )
00179                 {
00180                         return m_State; 
00181                 }
00182 
00183                 // Failure is defined as emptying the open list as there is nothing left to 
00184                 // search...
00185                 // New: Allow user abort
00186                 if( m_OpenList.empty() || m_CancelRequest )
00187                 {
00188                         FreeAllNodes();
00189                         m_State = SEARCH_STATE_FAILED;
00190                         return m_State;
00191                 }
00192                 
00193                 // Incremement step count
00194                 m_Steps ++;
00195 
00196                 // Pop the best node (the one with the lowest f) 
00197                 Node *n = m_OpenList.front(); // get pointer to the node
00198                 pop_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() );
00199                 m_OpenList.pop_back();
00200 
00201                 // Check for the goal, once we pop that we're done
00202                 if( n->m_UserState.IsGoal( m_Goal->m_UserState ) )
00203                 {
00204                         // The user is going to use the Goal Node he passed in 
00205                         // so copy the parent pointer of n 
00206                         m_Goal->parent = n->parent;
00207 
00208                         // A special case is that the goal was passed in as the start state
00209                         // so handle that here
00210                         if( false == n->m_UserState.IsSameState( m_Start->m_UserState ) )
00211                         {
00212                                 FreeNode( n );
00213 
00214                                 // set the child pointers in each node (except Goal which has no child)
00215                                 Node *nodeChild = m_Goal;
00216                                 Node *nodeParent = m_Goal->parent;
00217 
00218                                 do 
00219                                 {
00220                                         nodeParent->child = nodeChild;
00221 
00222                                         nodeChild = nodeParent;
00223                                         nodeParent = nodeParent->parent;
00224                                 
00225                                 } 
00226                                 while( nodeChild != m_Start ); // Start is always the first node by definition
00227 
00228                         }
00229 
00230                         // delete nodes that aren't needed for the solution
00231                         FreeUnusedNodes();
00232 
00233                         m_State = SEARCH_STATE_SUCCEEDED;
00234 
00235                         return m_State;
00236                 }
00237                 else // not goal
00238                 {
00239 
00240                         // We now need to generate the successors of this node
00241                         // The user helps us to do this, and we keep the new nodes in
00242                         // m_Successors ...
00243 
00244                         m_Successors.clear(); // empty vector of successor nodes to n
00245 
00246                         // User provides this functions and uses AddSuccessor to add each successor of
00247                         // node 'n' to m_Successors
00248                         bool ret = n->m_UserState.GetSuccessors( this, n->parent ? &n->parent->m_UserState : NULL ); 
00249 
00250                         if( !ret )
00251                         {
00252 
00253                             typename vector< Node * >::iterator successor;
00254 
00255                                 // free the nodes that may previously have been added 
00256                                 for( successor = m_Successors.begin(); successor != m_Successors.end(); successor ++ )
00257                                 {
00258                                         FreeNode( (*successor) );
00259                                 }
00260 
00261                                 m_Successors.clear(); // empty vector of successor nodes to n
00262 
00263                                 // free up everything else we allocated
00264                                 FreeAllNodes();
00265 
00266                                 m_State = SEARCH_STATE_OUT_OF_MEMORY;
00267                                 return m_State;
00268                         }
00269                         
00270                         // Now handle each successor to the current node ...
00271                         for( typename vector< Node * >::iterator successor = m_Successors.begin(); successor != m_Successors.end(); successor ++ )
00272                         {
00273 
00274                                 //      The g value for this successor ...
00275                                 float newg = n->g + n->m_UserState.GetCost( (*successor)->m_UserState );
00276 
00277                                 // Now we need to find whether the node is on the open or closed lists
00278                                 // If it is but the node that is already on them is better (lower g)
00279                                 // then we can forget about this successor
00280 
00281                                 // First linear search of open list to find node
00282 
00283                                 typename vector< Node * >::iterator openlist_result;
00284 
00285                                 for( openlist_result = m_OpenList.begin(); openlist_result != m_OpenList.end(); openlist_result ++ )
00286                                 {
00287                                         if( (*openlist_result)->m_UserState.IsSameState( (*successor)->m_UserState ) )
00288                                         {
00289                                                 break;                                  
00290                                         }
00291                                 }
00292 
00293                                 if( openlist_result != m_OpenList.end() )
00294                                 {
00295 
00296                                         // we found this state on open
00297 
00298                                         if( (*openlist_result)->g <= newg )
00299                                         {
00300                                                 FreeNode( (*successor) );
00301 
00302                                                 // the one on Open is cheaper than this one
00303                                                 continue;
00304                                         }
00305                                 }
00306 
00307                                 typename vector< Node * >::iterator closedlist_result;
00308 
00309                                 for( closedlist_result = m_ClosedList.begin(); closedlist_result != m_ClosedList.end(); closedlist_result ++ )
00310                                 {
00311                                         if( (*closedlist_result)->m_UserState.IsSameState( (*successor)->m_UserState ) )
00312                                         {
00313                                                 break;                                  
00314                                         }
00315                                 }
00316 
00317                                 if( closedlist_result != m_ClosedList.end() )
00318                                 {
00319 
00320                                         // we found this state on closed
00321 
00322                                         if( (*closedlist_result)->g <= newg )
00323                                         {
00324                                                 // the one on Closed is cheaper than this one
00325                                                 FreeNode( (*successor) );
00326 
00327                                                 continue;
00328                                         }
00329                                 }
00330 
00331                                 // This node is the best node so far with this particular state
00332                                 // so lets keep it and set up its AStar specific data ...
00333 
00334                                 (*successor)->parent = n;
00335                                 (*successor)->g = newg;
00336                                 (*successor)->h = (*successor)->m_UserState.GoalDistanceEstimate( m_Goal->m_UserState );
00337                                 (*successor)->f = (*successor)->g + (*successor)->h;
00338 
00339                                 // Remove successor from closed if it was on it
00340 
00341                                 if( closedlist_result != m_ClosedList.end() )
00342                                 {
00343                                         // remove it from Closed
00344                                         FreeNode(  (*closedlist_result) ); 
00345                                         m_ClosedList.erase( closedlist_result );
00346 
00347                                         // Fix thanks to ...
00348                                         // Greg Douglas <gregdouglasmail@gmail.com>
00349                                         // who noticed that this code path was incorrect
00350                                         // Here we have found a new state which is already CLOSED
00351                                         // anus
00352                                         
00353                                 }
00354 
00355                                 // Update old version of this node
00356                                 if( openlist_result != m_OpenList.end() )
00357                                 {          
00358 
00359                                         FreeNode( (*openlist_result) ); 
00360                                         m_OpenList.erase( openlist_result );
00361 
00362                                         // re-make the heap 
00363                                         // make_heap rather than sort_heap is an essential bug fix
00364                                         // thanks to Mike Ryynanen for pointing this out and then explaining
00365                                         // it in detail. sort_heap called on an invalid heap does not work
00366                                         make_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() );
00367                         
00368                                 }
00369 
00370                                 // heap now unsorted
00371                                 m_OpenList.push_back( (*successor) );
00372 
00373                                 // sort back element into heap
00374                                 push_heap( m_OpenList.begin(), m_OpenList.end(), HeapCompare_f() );
00375 
00376                         }
00377 
00378                         // push n onto Closed, as we have expanded it now
00379 
00380                         m_ClosedList.push_back( n );
00381 
00382                 } // end else (not goal so expand)
00383 
00384                 return m_State; // Succeeded bool is false at this point. 
00385 
00386         }
00387 
00388         // User calls this to add a successor to a list of successors
00389         // when expanding the search frontier
00390         bool AddSuccessor( UserState &State )
00391         {
00392                 Node *node = AllocateNode();
00393 
00394                 if( node )
00395                 {
00396                         node->m_UserState = State;
00397 
00398                         m_Successors.push_back( node );
00399 
00400                         return true;
00401                 }
00402 
00403                 return false;
00404         }
00405 
00406         // Free the solution nodes
00407         // This is done to clean up all used Node memory when you are done with the
00408         // search
00409         void FreeSolutionNodes()
00410         {
00411                 Node *n = m_Start;
00412 
00413                 if( m_Start->child )
00414                 {
00415                         do
00416                         {
00417                                 Node *del = n;
00418                                 n = n->child;
00419                                 FreeNode( del );
00420 
00421                                 del = NULL;
00422 
00423                         } while( n != m_Goal );
00424 
00425                         FreeNode( n ); // Delete the goal
00426 
00427                 }
00428                 else
00429                 {
00430                         // if the start node is the solution we need to just delete the start and goal
00431                         // nodes
00432                         FreeNode( m_Start );
00433                         FreeNode( m_Goal );
00434                 }
00435 
00436         }
00437 
00438         // Functions for traversing the solution
00439 
00440         // Get start node
00441         UserState *GetSolutionStart()
00442         {
00443                 m_CurrentSolutionNode = m_Start;
00444                 if( m_Start )
00445                 {
00446                         return &m_Start->m_UserState;
00447                 }
00448                 else
00449                 {
00450                         return NULL;
00451                 }
00452         }
00453         
00454         // Get next node
00455         UserState *GetSolutionNext()
00456         {
00457                 if( m_CurrentSolutionNode )
00458                 {
00459                         if( m_CurrentSolutionNode->child )
00460                         {
00461 
00462                                 Node *child = m_CurrentSolutionNode->child;
00463 
00464                                 m_CurrentSolutionNode = m_CurrentSolutionNode->child;
00465 
00466                                 return &child->m_UserState;
00467                         }
00468                 }
00469 
00470                 return NULL;
00471         }
00472         
00473         // Get end node
00474         UserState *GetSolutionEnd()
00475         {
00476                 m_CurrentSolutionNode = m_Goal;
00477                 if( m_Goal )
00478                 {
00479                         return &m_Goal->m_UserState;
00480                 }
00481                 else
00482                 {
00483                         return NULL;
00484                 }
00485         }
00486         
00487         // Step solution iterator backwards
00488         UserState *GetSolutionPrev()
00489         {
00490                 if( m_CurrentSolutionNode )
00491                 {
00492                         if( m_CurrentSolutionNode->parent )
00493                         {
00494 
00495                                 Node *parent = m_CurrentSolutionNode->parent;
00496 
00497                                 m_CurrentSolutionNode = m_CurrentSolutionNode->parent;
00498 
00499                                 return &parent->m_UserState;
00500                         }
00501                 }
00502 
00503                 return NULL;
00504         }
00505 
00506         // For educational use and debugging it is useful to be able to view
00507         // the open and closed list at each step, here are two functions to allow that.
00508 
00509         UserState *GetOpenListStart()
00510         {
00511                 float f,g,h;
00512                 return GetOpenListStart( f,g,h );
00513         }
00514 
00515         UserState *GetOpenListStart( float &f, float &g, float &h )
00516         {
00517                 iterDbgOpen = m_OpenList.begin();
00518                 if( iterDbgOpen != m_OpenList.end() )
00519                 {
00520                         f = (*iterDbgOpen)->f;
00521                         g = (*iterDbgOpen)->g;
00522                         h = (*iterDbgOpen)->h;
00523                         return &(*iterDbgOpen)->m_UserState;
00524                 }
00525 
00526                 return NULL;
00527         }
00528 
00529         UserState *GetOpenListNext()
00530         {
00531                 float f,g,h;
00532                 return GetOpenListNext( f,g,h );
00533         }
00534 
00535         UserState *GetOpenListNext( float &f, float &g, float &h )
00536         {
00537                 iterDbgOpen++;
00538                 if( iterDbgOpen != m_OpenList.end() )
00539                 {
00540                         f = (*iterDbgOpen)->f;
00541                         g = (*iterDbgOpen)->g;
00542                         h = (*iterDbgOpen)->h;
00543                         return &(*iterDbgOpen)->m_UserState;
00544                 }
00545 
00546                 return NULL;
00547         }
00548 
00549         UserState *GetClosedListStart()
00550         {
00551                 float f,g,h;
00552                 return GetClosedListStart( f,g,h );
00553         }
00554 
00555         UserState *GetClosedListStart( float &f, float &g, float &h )
00556         {
00557                 iterDbgClosed = m_ClosedList.begin();
00558                 if( iterDbgClosed != m_ClosedList.end() )
00559                 {
00560                         f = (*iterDbgClosed)->f;
00561                         g = (*iterDbgClosed)->g;
00562                         h = (*iterDbgClosed)->h;
00563 
00564                         return &(*iterDbgClosed)->m_UserState;
00565                 }
00566 
00567                 return NULL;
00568         }
00569 
00570         UserState *GetClosedListNext()
00571         {
00572                 float f,g,h;
00573                 return GetClosedListNext( f,g,h );
00574         }
00575 
00576         UserState *GetClosedListNext( float &f, float &g, float &h )
00577         {
00578                 iterDbgClosed++;
00579                 if( iterDbgClosed != m_ClosedList.end() )
00580                 {
00581                         f = (*iterDbgClosed)->f;
00582                         g = (*iterDbgClosed)->g;
00583                         h = (*iterDbgClosed)->h;
00584 
00585                         return &(*iterDbgClosed)->m_UserState;
00586                 }
00587 
00588                 return NULL;
00589         }
00590 
00591         // Get the number of steps
00592 
00593         int GetStepCount() { return m_Steps; }
00594 
00595         void EnsureMemoryFreed()
00596         {
00597 #if USE_FSA_MEMORY
00598                 assert(m_AllocateNodeCount == 0);
00599 #endif
00600 
00601         }
00602 
00603 private: // methods
00604 
00605         // This is called when a search fails or is cancelled to free all used
00606         // memory 
00607         void FreeAllNodes()
00608         {
00609                 // iterate open list and delete all nodes
00610                 typename vector< Node * >::iterator iterOpen = m_OpenList.begin();
00611 
00612                 while( iterOpen != m_OpenList.end() )
00613                 {
00614                         Node *n = (*iterOpen);
00615                         FreeNode( n );
00616 
00617                         iterOpen ++;
00618                 }
00619 
00620                 m_OpenList.clear();
00621 
00622                 // iterate closed list and delete unused nodes
00623                 typename vector< Node * >::iterator iterClosed;
00624 
00625                 for( iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed ++ )
00626                 {
00627                         Node *n = (*iterClosed);
00628                         FreeNode( n );
00629                 }
00630 
00631                 m_ClosedList.clear();
00632 
00633                 // delete the goal
00634 
00635                 FreeNode(m_Goal);
00636         }
00637 
00638 
00639         // This call is made by the search class when the search ends. A lot of nodes may be
00640         // created that are still present when the search ends. They will be deleted by this 
00641         // routine once the search ends
00642         void FreeUnusedNodes()
00643         {
00644                 // iterate open list and delete unused nodes
00645                 typename vector< Node * >::iterator iterOpen = m_OpenList.begin();
00646 
00647                 while( iterOpen != m_OpenList.end() )
00648                 {
00649                         Node *n = (*iterOpen);
00650 
00651                         if( !n->child )
00652                         {
00653                                 FreeNode( n );
00654 
00655                                 n = NULL;
00656                         }
00657 
00658                         iterOpen ++;
00659                 }
00660 
00661                 m_OpenList.clear();
00662 
00663                 // iterate closed list and delete unused nodes
00664                 typename vector< Node * >::iterator iterClosed;
00665 
00666                 for( iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed ++ )
00667                 {
00668                         Node *n = (*iterClosed);
00669 
00670                         if( !n->child )
00671                         {
00672                                 FreeNode( n );
00673                                 n = NULL;
00674 
00675                         }
00676                 }
00677 
00678                 m_ClosedList.clear();
00679 
00680         }
00681 
00682         // Node memory management
00683         Node *AllocateNode()
00684         {
00685 
00686 #if !USE_FSA_MEMORY
00687                 Node *p = new Node;
00688                 return p;
00689 #else
00690                 Node *address = m_FixedSizeAllocator.alloc();
00691 
00692                 if( !address )
00693                 {
00694                         return NULL;
00695                 }
00696                 m_AllocateNodeCount ++;
00697                 Node *p = new (address) Node;
00698                 return p;
00699 #endif
00700         }
00701 
00702         void FreeNode( Node *node )
00703         {
00704 
00705                 m_AllocateNodeCount --;
00706 
00707 #if !USE_FSA_MEMORY
00708                 delete node;
00709 #else
00710                 m_FixedSizeAllocator.free( node );
00711 #endif
00712         }
00713 
00714 private: // data
00715 
00716         // Heap (simple vector but used as a heap, cf. Steve Rabin's game gems article)
00717         vector< Node *> m_OpenList;
00718 
00719         // Closed list is a vector.
00720         vector< Node * > m_ClosedList; 
00721 
00722         // Successors is a vector filled out by the user each type successors to a node
00723         // are generated
00724         vector< Node * > m_Successors;
00725 
00726         // State
00727         unsigned int m_State;
00728 
00729         // Counts steps
00730         int m_Steps;
00731 
00732         // Start and goal state pointers
00733         Node *m_Start;
00734         Node *m_Goal;
00735 
00736         Node *m_CurrentSolutionNode;
00737 
00738 #if USE_FSA_MEMORY
00739         // Memory
00740         FixedSizeAllocator<Node> m_FixedSizeAllocator;
00741 #endif
00742         
00743         //Debug : need to keep these two iterators around
00744         // for the user Dbg functions
00745         typename vector< Node * >::iterator iterDbgOpen;
00746         typename vector< Node * >::iterator iterDbgClosed;
00747 
00748         // debugging : count memory allocation and free's
00749         int m_AllocateNodeCount;
00750         
00751         bool m_CancelRequest;
00752 
00753 };
00754 
00755 
00756 
00757    


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 Thu Aug 27 2015 15:20:57