path.cc

Go to the documentation of this file.
00001 /*
00002  *  path.cc - Pathfinding algorithms.
00003  *
00004  *  Copyright (C) 2000-2001  The Exult Team
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #  include <config.h>
00023 #endif
00024 
00025 #include <iostream>
00026 
00027 #include "hash_utils.h"
00028 #include "PathFinder.h"
00029 #include "exult_constants.h"
00030 #include "vec.h"
00031 
00032 using std::cout;
00033 using std::endl;
00034 using std::size_t;
00035 
00036 Tile_coord *Find_path
00037   (
00038   Tile_coord start,   // Where to start from.
00039   Tile_coord goal,    // Where to end up.
00040   Pathfinder_client *client,  // Provides costs.
00041   int& pathlen      // Length of path returned.
00042   );
00043 
00044 
00045 /*
00046  *  Iterate through neighbors of a tile (in 2 dimensions).
00047  */
00048 class Neighbor_iterator
00049   {
00050   Tile_coord tile;    // Original tile.
00051   static int coords[16];    // Coords to go through ((x,y) pairs)
00052   int index;      // 0-7.
00053 public:
00054   Neighbor_iterator(Tile_coord t) : tile(t), index(0)
00055     {  }
00056           // Get next neighbor.
00057   int operator()(Tile_coord& newt)
00058     {
00059     if (index < 8)
00060       {
00061       newt = Tile_coord(tile.tx + coords[2*index],
00062         tile.ty + coords[2*index + 1], tile.tz);
00063       index++;
00064           // Handle world-wrapping.
00065       newt.tx = (newt.tx + c_num_tiles)%c_num_tiles;
00066       newt.ty = (newt.ty + c_num_tiles)%c_num_tiles;
00067       return (1);
00068       }
00069     return (0);
00070     }
00071   };
00072 
00073 /*
00074  *  Statics:
00075  */
00076 int Neighbor_iterator::coords[16] = {
00077   -1, -1, 0, -1, 1, -1,
00078   -1,  0,        1,  0,
00079   -1,  1, 0,  1, 1,  1
00080   };
00081 
00082 /*
00083  *  A node for our search:
00084  */
00085 class Search_node
00086   {
00087   Tile_coord tile;    // The coords (x, y, z) in tiles.
00088   short start_cost;   // Actual cost from start.
00089   short goal_cost;    // Estimated cost to goal.
00090   short total_cost;   // Sum of the two above.
00091   Search_node *parent;    // Prev. in path.
00092   Search_node *priority_next; // ->next with same total_cost, or
00093           //   NULL if not in 'open' set.
00094 public:
00095   Search_node(Tile_coord& t, short scost, short gcost, Search_node *p)
00096     : tile(t), start_cost(scost), goal_cost(gcost),
00097       parent(p), priority_next(0)
00098     {
00099     total_cost = gcost + scost;
00100     }
00101           // For creating a key to search for.
00102   Search_node(Tile_coord& t) : tile(t)
00103     {  }
00104   Tile_coord get_tile() const
00105     { return tile; }
00106   int get_start_cost()
00107     { return start_cost; }
00108   int get_goal_cost()
00109     { return goal_cost; }
00110   int get_total_cost()
00111     { return total_cost; }
00112   int is_open()     // In 'open' priority queue?
00113     { return priority_next != 0; }
00114   void update(short scost, short gcost, Search_node *p)
00115     {
00116     start_cost = scost;
00117     goal_cost = gcost;
00118     total_cost = gcost + scost;
00119     parent = p;
00120     }
00121           // Create path back to start.
00122   Tile_coord *create_path(int& pathlen)
00123     {
00124     int cnt = 1;    // This.
00125           // Count back to start.
00126     Search_node *each = this;
00127     while ((each = each->parent) != 0)
00128       cnt++;
00129     pathlen = cnt - 1;  // Don't want starting tile.
00130     Tile_coord *result = new Tile_coord[pathlen];
00131     each = this;
00132     for (int i = pathlen - 1; i >= 0; i--)
00133       {
00134       result[i] = each->tile;
00135       each = each->parent;
00136       }
00137     return result;
00138     }
00139 #if VERIFYCHAIN
00140           // Returns 0 if bad chain.
00141   int verify_chain(Search_node *last, int removed = 0)
00142     {
00143     if (!last)
00144       return (1);
00145     int found = 0;
00146     Search_node *prev = last;
00147     int cnt = 0;
00148     do
00149       {
00150       Search_node *next = prev->priority_next;
00151       if (next == this)
00152         found = 1;
00153       prev = next;
00154       if (cnt > 10000)
00155         break;
00156       }
00157     while (prev != last);
00158     if (!found && !removed)
00159       return (0);
00160     if (cnt == 10000)
00161       return (0);
00162     return (1);
00163     }
00164 #endif
00165           // Add to chain of same priorities.
00166   void add_to_chain(Search_node *&last)
00167     {
00168     if (last)
00169       {
00170       priority_next = last->priority_next;
00171       last->priority_next = this;
00172       }
00173     else
00174       {
00175       last = this;
00176       priority_next = this;
00177       }
00178 #if VERIFYCHAIN
00179     if (!verify_chain(last))
00180       cout << "Bad chain after adding." << endl;
00181 #endif
00182     }
00183           // Remove this from its chain.
00184   void remove_from_chain(Search_node *&last)
00185     {
00186 #if VERIFYCHAIN
00187     if (!verify_chain(last))
00188       cout << "Bad chain before removing." << endl;
00189 #endif
00190     if (priority_next == this)
00191           // Only one in chain?
00192       last = 0;
00193     else
00194       {   // Got to find prev. to this.
00195       Search_node *prev = last;
00196       do
00197         {
00198         Search_node *next = prev->priority_next;
00199         if (next == this)
00200           break;
00201         prev = next;
00202         }
00203       while (prev != last);
00204       if (prev)
00205         {
00206         prev->priority_next = priority_next;
00207         if (last == this)
00208           last = priority_next;
00209         }
00210       }
00211     priority_next = 0;  // No longer in 'open'.
00212 #if VERIFYCHAIN
00213     if (!verify_chain(last, 1))
00214       cout << "Bad chain after removing." << endl;
00215 #endif
00216     }
00217           // Remove 1st from a priority chain.
00218   static Search_node *remove_first_from_chain(Search_node *&last)
00219     {
00220     Search_node *first = last->priority_next;
00221     if (first == last)  // Last entry?
00222       last = 0;
00223     else
00224       last->priority_next = first->priority_next;
00225     first->priority_next = 0;
00226     return first;
00227     }
00228   };
00229 
00230 #ifndef DONT_HAVE_HASH_SET
00231 
00232 /*
00233  *  Hash function for nodes:
00234  */
00235 class Hash_node
00236   {
00237 public:
00238   size_t operator() (const Search_node *a) const
00239     {
00240     const Tile_coord t = a->get_tile();
00241     return ((t.tz << 24) + (t.ty << 12) + t.tx);
00242     }
00243   };
00244 
00245 /*
00246  *  For testing if two nodes match.
00247  */
00248 class Equal_nodes
00249   {
00250 public:
00251       bool operator() (const Search_node *a, const Search_node *b) const
00252         {
00253     Tile_coord ta = a->get_tile(), tb = b->get_tile();
00254     return ta == tb;
00255     }
00256   };
00257 
00258 #else
00259 
00260 /*
00261  *  "Less than" relation for nodes
00262  */
00263 class Less_nodes
00264   {
00265 public:
00266       bool operator() (const Search_node *a, const Search_node *b) const
00267         {
00268       Tile_coord ta = a->get_tile(), tb = b->get_tile();
00269       uint32 apos = ta.tx << 16, bpos = tb.tx << 16;
00270       apos |= ta.ty << 4;
00271       bpos |= tb.ty << 4;
00272       apos |= ta.tz;
00273       bpos |= tb.tz;
00274       /* Because #(short x short) is <= #int, we can define an injective projection,
00275       ** which is all we need. */
00276       return apos < bpos;
00277     }
00278   };
00279 
00280 #endif
00281 
00282 
00283 /*
00284  *  The priority queue for the A* algorithm:
00285  */
00286 class A_star_queue
00287   {
00288   Exult_vector<Search_node*> open;// Nodes to be done, by priority. Each
00289           //   is a ->last node in chain.
00290   int best;     // Index of 1st non-null ent. in open.
00291           // For finding each tile's node:
00292 #ifndef DONT_HAVE_HASH_SET
00293   typedef hash_set<Search_node *, Hash_node, Equal_nodes> Lookup_set;
00294 #else
00295   typedef std::set<Search_node *, Less_nodes> Lookup_set;
00296 #endif
00297   Lookup_set lookup;
00298 public:
00299 #ifndef DONT_HAVE_HASH_SET
00300   A_star_queue() : open(256), lookup(1000)
00301 #else
00302   A_star_queue() : open(256), lookup()
00303 #endif
00304     {
00305     open.insert(open.begin(), 256, (Search_node *) 0);
00306     best = open.size(); // Best is past end.
00307     }
00308   ~A_star_queue()
00309     {
00310 #if 1 
00311   /* 
00312   This _should_ work, but might hang some hash_set implementations.
00313   The problem is that on deleting the Search_node, the hash_set can
00314   no longer properly evaluate the hash value (since the hash function
00315   dereferences the Search_node* stored). This might cause an endless
00316   loop.
00317   */
00318     for(Lookup_set::iterator X = lookup.begin(); X != lookup.end();) {
00319       Search_node *sn = *X;
00320       X++;
00321       delete sn; // only delete this _after_ iterating
00322 }
00323 #endif
00324     lookup.clear();   // Remove all nodes.
00325     }
00326   void add_back(Search_node *nd)  // Add an existing node back to 'open'.
00327     {
00328     int total_cost = nd->get_total_cost();
00329     Search_node *last = total_cost < open.size() ?
00330             open[total_cost] : 0;
00331     nd->add_to_chain(last); // Add node to this chain.
00332     open.put(total_cost, last);
00333     if (total_cost < best)
00334       best = total_cost;
00335     }
00336   void add(Search_node *nd) // Add new node to 'open' set.
00337     {
00338     lookup.insert(nd);
00339     add_back(nd);
00340     }
00341           // Remove node from 'open' set.
00342   void remove_from_open(Search_node *nd)
00343     {
00344     if (!nd->is_open())
00345       return;   // Nothing to do.
00346     int total_cost = nd->get_total_cost();
00347     Search_node *last = total_cost < open.size() ?
00348             open[total_cost] : 0;
00349     nd->remove_from_chain(last);
00350           // Store updated 'last'.
00351     open.put(total_cost, last);
00352     if (!last)    // Last in chain?
00353       {
00354       if (total_cost == best)
00355         {
00356         int cnt = open.size();
00357         for (best++; best < cnt; best++)
00358           if (open[best] != 0)
00359             break;
00360         }
00361       }
00362     }
00363   Search_node *pop()    // Pop best from priority queue.
00364     {
00365     Search_node *last = best < open.size() ? open[best] : 0;
00366     if (!last)
00367       return (0);
00368           // Return 1st in list.
00369     Search_node *node = Search_node::remove_first_from_chain(last);
00370           // Store updated 'last'.
00371     open.put(best, last);
00372     if (!last)    // List now empty?
00373       {
00374       int cnt = open.size();
00375       for (best++; best < cnt; best++)
00376         if (open[best] != 0)
00377           break;
00378       }
00379     return node;
00380     }
00381           // Find node for given tile.
00382   Search_node *find(Tile_coord tile)
00383     {
00384     Search_node key(tile);
00385 #ifndef DONT_HAVE_HASH_SET
00386     hash_set<Search_node *, Hash_node, Equal_nodes>::iterator it =
00387               lookup.find(&key);
00388 #else
00389     std::set<Search_node *, Less_nodes>::iterator it =
00390       lookup.find(&key);
00391 #endif
00392     if (it != lookup.end())
00393       return *it;
00394     else
00395       return 0;
00396     }
00397   };
00398 
00399 static int tracing = 0;
00400 
00401 /*
00402  *  First cut at using the A* pathfinding algorithm.
00403  *
00404  *  Output: ->(allocated) array of Tile_coords to follow, or 0 if failed.
00405  */
00406 
00407 Tile_coord *Find_path
00408   (
00409   Tile_coord start,   // Where to start from.
00410   Tile_coord goal,    // Where to end up.
00411   Pathfinder_client *client,  // Provides costs.
00412   int& pathlen      // Length of path returned.
00413   )
00414   {
00415   A_star_queue nodes;   // The priority queue & hash table.
00416   int max_cost = client->estimate_cost(start, goal);
00417           // Create start node.
00418   nodes.add(new Search_node(start, 0, max_cost, 0));
00419           // Figure when to give up.
00420   max_cost = client->get_max_cost(max_cost);
00421   Search_node *node;    // Try 'best' node each iteration.
00422   while ((node = nodes.pop()) != 0)
00423     {
00424     if (tracing)
00425       cout << "Goal: (" << goal.tx << ", " << goal.ty <<
00426       "), Node: (" << node->get_tile().tx << ", " <<
00427       node->get_tile().ty << ")" << endl;
00428     Tile_coord curtile = node->get_tile();
00429     if (client->at_goal(curtile, goal))
00430           // Success.
00431       return node->create_path(pathlen);
00432           // Go through surrounding tiles.
00433     Neighbor_iterator get_next(curtile);
00434     Tile_coord ntile(0, 0, 0);
00435     while (get_next(ntile))
00436       {   // Get cost to next tile.
00437       int step_cost = client->get_step_cost(curtile, ntile);
00438           // Blocked?
00439       if (step_cost == -1)
00440         continue;
00441           // Get cost from start to ntile.
00442       int new_cost = node->get_start_cost() + step_cost;
00443           // See if next tile already seen.
00444       Search_node *next = nodes.find(ntile);
00445           // Already there, and cheaper?
00446       if (next && next->get_start_cost() <= new_cost)
00447         continue;
00448       int new_goal_cost = client->estimate_cost(ntile, goal);
00449           // Skip nodes too far away.
00450       if (new_cost + new_goal_cost >= max_cost)
00451         continue;
00452       if (!next)  // Create if necessary.
00453         {
00454         next = new Search_node(ntile, new_cost,
00455             new_goal_cost, node);
00456         nodes.add(next);
00457         }
00458       else
00459         { // It's going to move.
00460         nodes.remove_from_open(next);
00461         next->update(new_cost, new_goal_cost, node);
00462         nodes.add_back(next);
00463         }
00464       }
00465     }
00466   pathlen = 0;      // Failed if here.
00467   return 0; 
00468   }

Generated on Mon Jul 9 14:42:49 2007 for ExultEngine by  doxygen 1.5.1