00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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,
00039 Tile_coord goal,
00040 Pathfinder_client *client,
00041 int& pathlen
00042 );
00043
00044
00045
00046
00047
00048 class Neighbor_iterator
00049 {
00050 Tile_coord tile;
00051 static int coords[16];
00052 int index;
00053 public:
00054 Neighbor_iterator(Tile_coord t) : tile(t), index(0)
00055 { }
00056
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
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
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
00084
00085 class Search_node
00086 {
00087 Tile_coord tile;
00088 short start_cost;
00089 short goal_cost;
00090 short total_cost;
00091 Search_node *parent;
00092 Search_node *priority_next;
00093
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
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()
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
00122 Tile_coord *create_path(int& pathlen)
00123 {
00124 int cnt = 1;
00125
00126 Search_node *each = this;
00127 while ((each = each->parent) != 0)
00128 cnt++;
00129 pathlen = cnt - 1;
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
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
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
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
00192 last = 0;
00193 else
00194 {
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;
00212 #if VERIFYCHAIN
00213 if (!verify_chain(last, 1))
00214 cout << "Bad chain after removing." << endl;
00215 #endif
00216 }
00217
00218 static Search_node *remove_first_from_chain(Search_node *&last)
00219 {
00220 Search_node *first = last->priority_next;
00221 if (first == last)
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
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
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
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
00275
00276 return apos < bpos;
00277 }
00278 };
00279
00280 #endif
00281
00282
00283
00284
00285
00286 class A_star_queue
00287 {
00288 Exult_vector<Search_node*> open;
00289
00290 int best;
00291
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();
00307 }
00308 ~A_star_queue()
00309 {
00310 #if 1
00311
00312
00313
00314
00315
00316
00317
00318 for(Lookup_set::iterator X = lookup.begin(); X != lookup.end();) {
00319 Search_node *sn = *X;
00320 X++;
00321 delete sn;
00322 }
00323 #endif
00324 lookup.clear();
00325 }
00326 void add_back(Search_node *nd)
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);
00332 open.put(total_cost, last);
00333 if (total_cost < best)
00334 best = total_cost;
00335 }
00336 void add(Search_node *nd)
00337 {
00338 lookup.insert(nd);
00339 add_back(nd);
00340 }
00341
00342 void remove_from_open(Search_node *nd)
00343 {
00344 if (!nd->is_open())
00345 return;
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
00351 open.put(total_cost, last);
00352 if (!last)
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()
00364 {
00365 Search_node *last = best < open.size() ? open[best] : 0;
00366 if (!last)
00367 return (0);
00368
00369 Search_node *node = Search_node::remove_first_from_chain(last);
00370
00371 open.put(best, last);
00372 if (!last)
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
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
00403
00404
00405
00406
00407 Tile_coord *Find_path
00408 (
00409 Tile_coord start,
00410 Tile_coord goal,
00411 Pathfinder_client *client,
00412 int& pathlen
00413 )
00414 {
00415 A_star_queue nodes;
00416 int max_cost = client->estimate_cost(start, goal);
00417
00418 nodes.add(new Search_node(start, 0, max_cost, 0));
00419
00420 max_cost = client->get_max_cost(max_cost);
00421 Search_node *node;
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
00431 return node->create_path(pathlen);
00432
00433 Neighbor_iterator get_next(curtile);
00434 Tile_coord ntile(0, 0, 0);
00435 while (get_next(ntile))
00436 {
00437 int step_cost = client->get_step_cost(curtile, ntile);
00438
00439 if (step_cost == -1)
00440 continue;
00441
00442 int new_cost = node->get_start_cost() + step_cost;
00443
00444 Search_node *next = nodes.find(ntile);
00445
00446 if (next && next->get_start_cost() <= new_cost)
00447 continue;
00448 int new_goal_cost = client->estimate_cost(ntile, goal);
00449
00450 if (new_cost + new_goal_cost >= max_cost)
00451 continue;
00452 if (!next)
00453 {
00454 next = new Search_node(ntile, new_cost,
00455 new_goal_cost, node);
00456 nodes.add(next);
00457 }
00458 else
00459 {
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;
00467 return 0;
00468 }