pathfind/astarsearch.cpp

Go to the documentation of this file.
00001 /* $Id: astarsearch.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 by David White <dave@whitevine.net>
00004                  2005 - 2012 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY.
00013 
00014    See the COPYING file for more details.
00015 */
00016 
00017 #include "global.hpp"
00018 
00019 #include "log.hpp"
00020 #include "map.hpp"
00021 #include "pathfind/pathfind.hpp"
00022 #include "pathfind/teleport.hpp"
00023 #include "foreach.hpp"
00024 
00025 #include <queue>
00026 #include <map>
00027 
00028 static lg::log_domain log_engine("engine");
00029 #define LOG_PF LOG_STREAM(info, log_engine)
00030 #define DBG_PF LOG_STREAM(debug, log_engine)
00031 #define ERR_PF LOG_STREAM(err, log_engine)
00032 
00033 namespace {
00034 double heuristic(const map_location& src, const map_location& dst)
00035 {
00036     // We will mainly use the distances in hexes
00037     // but we subtract a tiny bonus for shorter Euclidean distance
00038     // based on how the path looks on the screen.
00039 
00040     // 0.75 comes from the horizontal hex imbrication
00041     double xdiff = (src.x - dst.x) * 0.75;
00042     // we must add 0.5 to the y coordinate when x is odd
00043     double ydiff = (src.y - dst.y) + ((src.x & 1) - (dst.x & 1)) * 0.5;
00044 
00045     // we assume a map with a maximum diagonal of 300 (bigger than a 200x200)
00046     // and we divide by 90000 * 10000 to avoid interfering with the defense subcost
00047     // (see shortest_path_calculator::cost)
00048     // NOTE: In theory, such heuristic is barely 'admissible' for A*,
00049     // But not a problem for our current A* (we use heuristic only for speed)
00050     // Plus, the euclidean fraction stay below the 1MP minimum and is also
00051     // a good heuristic, so we still find the shortest path efficiently.
00052     return distance_between(src, dst) + (xdiff*xdiff + ydiff*ydiff) / 900000000.0;
00053 
00054     // TODO: move the heuristic function into the cost_calculator
00055     // so we can use case-specific heuristic
00056     // and clean the definition of these numbers
00057 }
00058 
00059 // values 0 and 1 mean uninitialized
00060 const unsigned bad_search_counter = 0;
00061 // The number of nodes already processed.
00062 static unsigned search_counter = bad_search_counter;
00063 
00064 struct node {
00065     double g, h, t;
00066     map_location curr, prev;
00067     /**
00068      * If equal to search_counter, the node is off the list.
00069      * If equal to search_counter + 1, the node is on the list.
00070      * Otherwise it is outdated.
00071      */
00072     unsigned in;
00073 
00074     node()
00075         : g(1e25)
00076         , h(1e25)
00077         , t(1e25)
00078         , curr()
00079         , prev()
00080         , in(bad_search_counter)
00081     {
00082     }
00083     node(double s, const map_location &c, const map_location &p, const map_location &dst, bool i, const pathfind::teleport_map* teleports):
00084         g(s), h(heuristic(c, dst)), t(g + h), curr(c), prev(p), in(search_counter + i)
00085     {
00086         if (teleports && !teleports->empty()) {
00087 
00088             double new_srch = 1.0;
00089             std::set<map_location> sources;
00090             teleports->get_sources(sources);
00091 
00092             std::set<map_location>::const_iterator it = sources.begin();
00093             for(; it != sources.end(); ++it) {
00094                 const double tmp_srch = heuristic(c, *it);
00095                 if (tmp_srch < new_srch) { new_srch = tmp_srch; }
00096             }
00097 
00098 
00099             double new_dsth = 1.0;
00100             std::set<map_location> targets;
00101             teleports->get_targets(targets);
00102 
00103             for(it = targets.begin(); it != targets.end(); ++it) {
00104                 const double tmp_dsth = heuristic(*it, dst);
00105                 if (tmp_dsth < new_dsth) { new_dsth = tmp_dsth; }
00106             }
00107 
00108             double new_h = new_srch + new_dsth + 1.0;
00109             if (new_h < h) {
00110                 h = new_h;
00111                 t = g + h;
00112             }
00113         }
00114     }
00115 
00116     bool operator<(const node& o) const {
00117         return t < o.t;
00118     }
00119 };
00120 
00121 class comp {
00122     const std::vector<node>& nodes_;
00123 
00124 public:
00125     comp(const std::vector<node>& n) : nodes_(n) { }
00126     bool operator()(int a, int b) {
00127         return nodes_[b] < nodes_[a];
00128     }
00129 };
00130 
00131 class indexer {
00132     size_t h_, w_;
00133 
00134 public:
00135     indexer(size_t a, size_t b) : h_(a), w_(b) { }
00136     size_t operator()(const map_location& loc) {
00137         return loc.y * h_ + loc.x;
00138     }
00139 };
00140 }
00141 
00142 
00143 pathfind::plain_route pathfind::a_star_search(const map_location& src, const map_location& dst,
00144                             double stop_at, const cost_calculator *calc, const size_t width,
00145                             const size_t height,
00146                             const teleport_map *teleports) {
00147     //----------------- PRE_CONDITIONS ------------------
00148     assert(src.valid(width, height));
00149     assert(dst.valid(width, height));
00150     assert(calc != NULL);
00151     assert(stop_at <= calc->getNoPathValue());
00152     //---------------------------------------------------
00153 
00154     DBG_PF << "A* search: " << src << " -> " << dst << '\n';
00155 
00156     if (calc->cost(dst, 0) >= stop_at) {
00157         LOG_PF << "aborted A* search because Start or Dest is invalid\n";
00158         pathfind::plain_route locRoute;
00159         locRoute.move_cost = int(calc->getNoPathValue());
00160         return locRoute;
00161     }
00162 
00163     // increment search_counter but skip the range equivalent to uninitialized
00164     search_counter += 2;
00165     if (search_counter - bad_search_counter <= 1u)
00166         search_counter += 2;
00167 
00168     static std::vector<node> nodes;
00169     nodes.resize(width * height);  // this create uninitalized nodes
00170 
00171     indexer index(width, height);
00172     comp node_comp(nodes);
00173 
00174     nodes[index(dst)].g = stop_at + 1;
00175     nodes[index(src)] = node(0, src, map_location::null_location, dst, true, teleports);
00176 
00177     std::vector<int> pq;
00178     pq.push_back(index(src));
00179 
00180     while (!pq.empty()) {
00181         node& n = nodes[pq.front()];
00182 
00183         n.in = search_counter;
00184 
00185         std::pop_heap(pq.begin(), pq.end(), node_comp);
00186         pq.pop_back();
00187 
00188         if (n.t >= nodes[index(dst)].g) break;
00189 
00190         std::vector<map_location> locs;
00191 
00192         int i;
00193         if (teleports && !teleports->empty()) {
00194 
00195             std::set<map_location> allowed_teleports;
00196             teleports->get_adjacents(allowed_teleports, n.curr);
00197 
00198             i = allowed_teleports.size() +6;
00199             locs = std::vector<map_location>(i);
00200 
00201             std::copy(allowed_teleports.begin(), allowed_teleports.end(), locs.begin() + 6);
00202         } else
00203         { locs = std::vector<map_location>(6); i = 6;}
00204 
00205         get_adjacent_tiles(n.curr, &locs[0]);
00206 
00207         for (; i-- > 0;) {
00208             if (!locs[i].valid(width, height)) continue;
00209             if (locs[i] == n.curr) continue;
00210             node& next = nodes[index(locs[i])];
00211 
00212             double thresh = (next.in - search_counter <= 1u) ? next.g : stop_at + 1;
00213             // cost() is always >= 1  (assumed and needed by the heuristic)
00214             if (n.g + 1 >= thresh) continue;
00215             double cost = n.g + calc->cost(locs[i], n.g);
00216             if (cost >= thresh) continue;
00217 
00218             bool in_list = next.in == search_counter + 1;
00219 
00220             next = node(cost, locs[i], n.curr, dst, true, teleports);
00221 
00222             if (in_list) {
00223                 std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), static_cast<int>(index(locs[i]))) + 1, node_comp);
00224             } else {
00225                 pq.push_back(index(locs[i]));
00226                 std::push_heap(pq.begin(), pq.end(), node_comp);
00227             }
00228         }
00229     }
00230 
00231     pathfind::plain_route route;
00232     if (nodes[index(dst)].g <= stop_at) {
00233         DBG_PF << "found solution; calculating it...\n";
00234         route.move_cost = static_cast<int>(nodes[index(dst)].g);
00235         for (node curr = nodes[index(dst)]; curr.prev != map_location::null_location; curr = nodes[index(curr.prev)]) {
00236             route.steps.push_back(curr.curr);
00237         }
00238         route.steps.push_back(src);
00239         std::reverse(route.steps.begin(), route.steps.end());
00240     } else {
00241         LOG_PF << "aborted a* search  " << "\n";
00242         route.move_cost = static_cast<int>(calc->getNoPathValue());
00243     }
00244 
00245     return route;
00246 }
00247 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Wed May 23 2012 01:02:52 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs