Main   Class Hierarchy   Classes   Compound List   Files   Compound Members   File Members   Pages  

astar.h

Go to the documentation of this file.
00001 //-----------------------------------------------------------------------------
00002 /** @file astar.h
00003     A* search algorithm.
00004 
00005     $Id: astar_8h-source.html,v 1.1.1.1 2003/08/07 19:41:38 emarkus Exp $
00006     $Source: /usr/cvsroot/www_pathfind/libpathfind/0.1.0/doc/astar_8h-source.html,v $
00007 */
00008 //-----------------------------------------------------------------------------
00009 
00010 #ifndef PATHFIND_ASTAR_H
00011 #define PATHFIND_ASTAR_H
00012 
00013 #include <list>
00014 #include <memory>
00015 #include <queue>
00016 #include <set>
00017 #include "marker.h"
00018 #include "search.h"
00019 
00020 //-----------------------------------------------------------------------------
00021 
00022 namespace PathFind
00023 {
00024     
00025     using namespace std;    
00026 
00027     /** Node used in implementation of AStar. */
00028     class AStarNode
00029     {
00030     public:
00031         /** Function object for comparing nodes in the open queue. */
00032         class Compare
00033         {
00034         public:
00035             virtual ~Compare();
00036             
00037             bool operator()(const AStarNode& node1,
00038                             const AStarNode& node2);
00039         };
00040         
00041         int m_nodeId;
00042         
00043         int m_parent;
00044         
00045         int m_g;
00046         
00047         int m_h;
00048         
00049         int m_f;
00050         
00051         AStarNode()
00052         {
00053         }
00054         
00055         AStarNode(int nodeId, int parent, int g, int h)
00056             : m_nodeId(nodeId),
00057               m_parent(parent),
00058               m_g(g),
00059               m_h(h),
00060               m_f(g + h)
00061         {
00062         }
00063         
00064         void print(ostream& ostrm) const;
00065     };
00066     
00067     /** Closed list implementation using a list. */
00068     class AStarClosed
00069     {
00070     public:
00071         void add(const AStarNode& node);
00072         
00073         vector<int> constructPath(int start, int target);
00074 
00075         void init(int maxNodeId);
00076         
00077         void print(ostream& ostrm) const;
00078         
00079         void remove(int nodeId);
00080         
00081         const AStarNode* search(int nodeId) const;
00082         
00083     private:
00084         list<AStarNode> m_list;            
00085     };
00086     
00087     /** Closed list implementation using lookup table. */
00088     template<class MARKER>
00089     class AStarClosedLookup
00090     {
00091     public:
00092         AStarClosedLookup();
00093         
00094         void add(const AStarNode& node);
00095         
00096         vector<int> constructPath(int start, int target);
00097 
00098         void init(int maxNodeId);
00099         
00100         void print(ostream& ostrm) const;
00101         
00102         void remove(int nodeId);
00103         
00104         const AStarNode* search(int nodeId) const;
00105         
00106     private:
00107         int m_maxNodeId;
00108         
00109         MARKER m_marker;
00110         
00111         vector<AStarNode> m_nodes;            
00112     };
00113     
00114     /** Open queue for AStar. */
00115     template<class MARKER>
00116     class AStarOpen
00117     {
00118     public:
00119         AStarOpen();
00120         
00121         void init(int numberNodes);
00122         
00123         void insert(const AStarNode& node);
00124         
00125         bool isEmpty() const
00126         {
00127             return (m_nodes.size() == 0);
00128         }
00129         
00130         AStarNode pop();
00131         
00132         void print(ostream& ostrm) const;
00133         
00134         bool  remove(int nodeId);
00135         
00136         const AStarNode* search(int nodeId) const;
00137         
00138     private:
00139         typedef multiset<AStarNode, AStarNode::Compare> NodeSet;
00140         
00141         typedef NodeSet::const_iterator NodeSetConstIterator;
00142         
00143         typedef NodeSet::iterator NodeSetIterator;
00144         
00145         int m_numberNodes;
00146         
00147         MARKER m_marker;
00148         
00149         /** Iterator in m_nodes by nodeId. */
00150         vector<NodeSetIterator> m_lookupTable;
00151         
00152         NodeSet m_nodes;
00153     };
00154 
00155     /** A* search engine.
00156 
00157         Pseudo algorithm for basic A* algorithm:
00158         @verbatim
00159         priorityqueue Open
00160         list Closed
00161 
00162         AStarSearch
00163             s.g = 0 // s is the start node
00164             s.h = GoalDistEstimate(s)
00165             s.f = s.g + s.h
00166             s.parent = null
00167             push s on Open
00168             while Open is not empty
00169                 pop node n from Open // n has the lowest f
00170                 if n is a goal node
00171                     construct path
00172                     return success
00173                 for each successor n' of n
00174                     newg = n.g + cost(n,n')
00175                     if n' is in Open or Closed
00176                        if n'.g <= newg skip
00177                        remove n' from Open or Closed
00178                     n'.parent = n
00179                     n'.g = newg
00180                     n'.h = GoalDistEstimate(n')
00181                     n'.f = n'.g + n'.h
00182                     push n' on Open
00183                 push n onto Closed
00184             return failure // if no path found
00185         @endverbatim
00186 
00187         This class is implemented as a template class taking
00188         the following template parameters:
00189         - MARKER Marker class to use (see @ref marker)
00190         - CLOSED Closed list class to use
00191 
00192         %Statistics (see Search::createStatistics and Search::getStatistics):
00193         - "cpu_time"
00194         - "path_cost"
00195         - "path_length"
00196         - "nodes_expanded"
00197         - "nodes_visited"
00198         - "branching_factor"
00199     */
00200     template<class MARKER = MarkerFastClear,
00201              class CLOSED = AStarClosedLookup<MARKER> >
00202     class AStar
00203         : public Search
00204     {
00205     public:
00206         AStar();
00207         
00208         StatisticsCollection createStatistics();
00209         
00210         bool findPath(const Environment& env, int start, int target);
00211 
00212         const vector<int>& getPath() const
00213         {
00214             return m_path;
00215         }
00216 
00217         const StatisticsCollection& getStatistics() const;
00218 
00219         /** Get a vector with 'x' char labels for each visited node. */
00220         const vector<char>& getVisitedNodes() const
00221         {
00222             return m_visitedNodes;
00223         }
00224 
00225         void setNodesLimit(long long int nodesLimit)
00226         {
00227             m_nodesLimit = nodesLimit;
00228         }
00229 
00230         int getPathCost() const
00231         {
00232             return m_pathCost;
00233         }
00234 
00235     private:
00236         int m_pathCost;
00237 
00238         int m_target;
00239 
00240         const Environment* m_env;
00241 
00242         long long int m_nodesExpanded;
00243 
00244         long long int m_nodesVisited;
00245 
00246         CLOSED m_closed;
00247 
00248         vector<char> m_visitedNodes;
00249 
00250         vector<int> m_path;
00251 
00252         AStarOpen<MARKER> m_open;
00253 
00254         StatisticsCollection m_statistics;
00255 
00256         Statistics& m_branchingFactor;
00257 
00258         /** Find a node in open or closed lists. */
00259         const AStarNode* findNode(int nodeId);
00260 
00261         void findPathAStar(int start);
00262 
00263         /** Construct path and set statistics after target node was found. */
00264         void finishSearch(int start, const AStarNode& node);
00265 
00266         AStarNode getBestNodeFromOpen();
00267     };
00268 
00269 } // namespace PathFind
00270 
00271 //-----------------------------------------------------------------------------
00272 
00273 namespace PathFind
00274 {
00275     
00276     using namespace std;
00277 
00278     template<class MARKER, class CLOSED>
00279     AStar<MARKER, CLOSED>::AStar()
00280         : m_statistics(createStatistics()),
00281           m_branchingFactor(m_statistics.get("branching_factor"))
00282     {
00283     }
00284 
00285     template<class MARKER, class CLOSED>
00286     StatisticsCollection AStar<MARKER, CLOSED>::createStatistics()
00287     {
00288         StatisticsCollection collection;
00289         collection.create("cpu_time");
00290         collection.create("path_cost");
00291         collection.create("path_length");
00292         collection.create("branching_factor");
00293         collection.create("nodes_expanded");
00294         collection.create("nodes_visited");
00295         return collection;
00296     }
00297     
00298     template<class MARKER, class CLOSED>
00299     const AStarNode* AStar<MARKER, CLOSED>::findNode(int nodeId)
00300     {
00301         const AStarNode* result = 0;
00302         result = m_open.search(nodeId);
00303         if (result != 0)
00304             return result;
00305         result = m_closed.search(nodeId);
00306         return result;
00307     }
00308 
00309     template<class MARKER, class CLOSED>
00310     bool AStar<MARKER, CLOSED>::findPath(const Environment& env, int start,
00311                                          int target)
00312     {
00313         assert(env.isValidNodeId(start));
00314         assert(env.isValidNodeId(target));
00315         clock_t startTime = clock();
00316         m_statistics.clear();
00317         m_nodesExpanded = 0;
00318         m_nodesVisited = 0;
00319         m_env = &env;
00320         m_target = target;
00321         m_path.clear();
00322         m_visitedNodes.resize(env.getNumberNodes());
00323         fill(m_visitedNodes.begin(), m_visitedNodes.end(), ' ');
00324         findPathAStar(start);
00325         double timeDiff =
00326             static_cast<double>(clock() - startTime) / CLOCKS_PER_SEC;
00327         m_statistics.get("cpu_time").add(timeDiff);
00328         m_statistics.get("nodes_expanded").add(m_nodesExpanded);
00329         m_statistics.get("nodes_visited").add(m_nodesVisited);
00330         m_statistics.get("path_length").add(m_path.size());
00331         return true;
00332     }
00333 
00334     template<class MARKER, class CLOSED>
00335     void AStar<MARKER, CLOSED>::findPathAStar(int start)
00336     {
00337         int numberNodes = m_env->getNumberNodes();
00338         m_closed.init(numberNodes);
00339         m_open.init(numberNodes);
00340         int heuristic = m_env->getHeuristic(start, m_target);
00341         m_pathCost = -1;
00342         AStarNode startNode(start, NO_NODE, 0, heuristic);
00343         m_open.insert(startNode);
00344         vector<Environment::Successor> successors;
00345         while (! m_open.isEmpty())
00346         {
00347             //m_open.print(cout);
00348             AStarNode node = getBestNodeFromOpen();
00349             //cout << '[';  node.print(cout); cout << ']' << endl;
00350             if (node.m_nodeId == m_target)
00351             {
00352                 finishSearch(start, node);
00353                 return;
00354             }
00355             ++m_nodesExpanded;
00356             m_env->getSuccessors(node.m_nodeId, NO_NODE, successors);
00357             m_branchingFactor.add(successors.size());
00358             for (vector<Environment::Successor>::const_iterator i
00359                      = successors.begin(); i != successors.end(); ++i)
00360             {
00361                 int newg = node.m_g + i->m_cost;
00362                 int target = i->m_target;
00363                 const AStarNode* targetAStarNode = findNode(target);
00364                 if (targetAStarNode != 0)
00365                 {
00366                     if (newg >= targetAStarNode->m_g)
00367                         continue;
00368                     if (! m_open.remove(target))
00369                         m_closed.remove(target);
00370                 }
00371                 int newHeuristic = m_env->getHeuristic(target, m_target);
00372                 AStarNode newAStarNode(target, node.m_nodeId, newg,
00373                                        newHeuristic);
00374                 m_open.insert(newAStarNode);
00375             }
00376             m_closed.add(node);
00377             //closed.print(cout);
00378         }
00379     }
00380 
00381     template<class MARKER, class CLOSED>
00382     void AStar<MARKER, CLOSED>::finishSearch(int start, const AStarNode& node)
00383     {
00384         m_closed.add(node);
00385         m_path = m_closed.constructPath(start, m_target);
00386         m_pathCost = node.m_f;
00387         m_statistics.get("path_cost").add(m_pathCost);
00388     }
00389     
00390     template<class MARKER, class CLOSED>
00391     AStarNode AStar<MARKER, CLOSED>::getBestNodeFromOpen()
00392     {
00393         assert(! m_open.isEmpty());
00394         AStarNode result = m_open.pop();
00395         int nodeId = result.m_nodeId;
00396         assert(nodeId >= 0);
00397         ++m_nodesVisited;
00398         m_visitedNodes[nodeId] = '+';
00399         return result;
00400     }
00401     
00402     template<class MARKER, class CLOSED>
00403     const StatisticsCollection& AStar<MARKER, CLOSED>::getStatistics() const
00404     {
00405         return m_statistics;
00406     }
00407     
00408     template<class MARKER>
00409     AStarClosedLookup<MARKER>::AStarClosedLookup()
00410     {
00411         m_maxNodeId = 0;
00412         init(0);
00413     }
00414     
00415     template<class MARKER>
00416     void AStarClosedLookup<MARKER>::add(const AStarNode& node)
00417     {
00418         int nodeId = node.m_nodeId;
00419         assert(nodeId >= 0);
00420         assert(nodeId < m_maxNodeId);
00421         assert(m_marker.get(nodeId) == false);
00422         m_marker.set(nodeId);
00423         m_nodes[nodeId] = node;
00424     }
00425     
00426     template<class MARKER>
00427     vector<int> AStarClosedLookup<MARKER>::constructPath(int start, int target)
00428     {
00429         vector<int> result;
00430         int nodeId = target;
00431         while (true)
00432         {
00433             result.push_back(nodeId);
00434             if (nodeId == start)
00435                 break;
00436             const AStarNode* node = search(nodeId);
00437             assert(node != 0);
00438             nodeId = node->m_parent;
00439         }
00440         assert(result.size() > 0);
00441         assert(*result.begin() == target);
00442         assert(*(result.end() - 1) == start);
00443         return result;
00444     }
00445 
00446     template<class MARKER>
00447     void AStarClosedLookup<MARKER>::init(int maxNodeId)
00448     {
00449         assert(maxNodeId >= 0);
00450         m_nodes.resize(maxNodeId);
00451         m_marker.init(maxNodeId);
00452         m_maxNodeId = maxNodeId;
00453     }
00454     
00455     template<class MARKER>
00456     void AStarClosedLookup<MARKER>::remove(int nodeId)
00457     {
00458         assert(nodeId >= 0);
00459         assert(nodeId < m_maxNodeId);
00460         assert(m_marker.get(nodeId) == true);
00461         m_marker.unset(nodeId);
00462     }
00463     
00464     template<class MARKER>
00465     const AStarNode* AStarClosedLookup<MARKER>::search(int nodeId) const
00466     {
00467         assert(nodeId >= 0);
00468         assert(nodeId < m_maxNodeId);
00469         if (! m_marker.get(nodeId))
00470             return 0;
00471         const AStarNode* result = &m_nodes[nodeId];
00472         return result;
00473     }
00474     
00475     template<class MARKER>
00476     AStarOpen<MARKER>::AStarOpen()
00477     {
00478         init(0);
00479     }
00480     
00481     template<class MARKER>
00482     void AStarOpen<MARKER>::init(int numberNodes)
00483     {
00484         m_marker.init(numberNodes);
00485         m_lookupTable.resize(numberNodes);
00486         m_numberNodes = numberNodes;
00487         m_nodes.clear();
00488     }
00489 
00490     template<class MARKER>
00491     void AStarOpen<MARKER>::insert(const AStarNode& node)
00492     {
00493         int nodeId = node.m_nodeId;
00494         assert(nodeId < m_numberNodes);
00495         assert(m_marker.get(nodeId) == false);
00496         m_marker.set(nodeId);
00497         NodeSetIterator pos = m_nodes.insert(node);
00498         m_lookupTable[nodeId] = pos;
00499     }
00500 
00501     template<class MARKER>
00502     AStarNode AStarOpen<MARKER>::pop()
00503     {
00504         assert(! isEmpty());
00505         AStarNode result = *(m_nodes.begin());
00506         m_nodes.erase(m_nodes.begin());
00507         int nodeId = result.m_nodeId;
00508         assert(nodeId < m_numberNodes);
00509         assert(m_marker.get(nodeId) == true);
00510         m_marker.unset(nodeId);
00511         return result;
00512     }
00513 
00514     template<class MARKER>
00515     void AStarOpen<MARKER>::print(ostream& ostrm) const
00516     {
00517         ostrm << "Open {\n"; 
00518         for (NodeSetConstIterator i = m_nodes.begin(); i != m_nodes.end(); ++i)
00519         {
00520             i->print(ostrm);
00521             ostrm << '\n';
00522         }   
00523         ostrm << "}\n";
00524     }
00525 
00526     template<class MARKER>
00527     bool AStarOpen<MARKER>::remove(int nodeId)
00528     {
00529         assert(nodeId >= 0);
00530         assert(nodeId < m_numberNodes);
00531         if (! m_marker.get(nodeId))
00532             return false;
00533         NodeSetIterator pos = m_lookupTable[nodeId];
00534         m_nodes.erase(pos);
00535         m_marker.unset(nodeId);
00536         return true;
00537     }
00538 
00539     template<class MARKER>
00540     const AStarNode* AStarOpen<MARKER>::search(int nodeId) const
00541     {
00542         assert(nodeId >= 0);
00543         assert(nodeId < m_numberNodes);
00544         if (m_marker.get(nodeId))
00545             return &(*m_lookupTable[nodeId]);
00546         return 0;
00547     }
00548 
00549 } // namespace PathFind
00550 
00551 //-----------------------------------------------------------------------------
00552 
00553 #endif


Generated on Thu Aug 7 13:04:49 2003 by Doxygen1.3.1