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