The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
astarsearch.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 by David White <[email protected]>
3  2005 - 2015 by Guillaume Melquiond <[email protected]>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "global.hpp"
17 
18 #include "log.hpp"
19 #include "map/map.hpp"
20 #include "pathfind/pathfind.hpp"
21 #include "pathfind/teleport.hpp"
22 
23 #include <queue>
24 #include <map>
25 
26 static lg::log_domain log_engine("engine");
27 #define LOG_PF LOG_STREAM(info, log_engine)
28 #define DBG_PF LOG_STREAM(debug, log_engine)
29 #define ERR_PF LOG_STREAM(err, log_engine)
30 
31 namespace pathfind {
32 
33 
34 namespace {
35 double heuristic(const map_location& src, const map_location& dst)
36 {
37  // We will mainly use the distances in hexes
38  // but we subtract a tiny bonus for shorter Euclidean distance
39  // based on how the path looks on the screen.
40 
41  // 0.75 comes from the horizontal hex imbrication
42  double xdiff = (src.x - dst.x) * 0.75;
43  // we must add 0.5 to the y coordinate when x is odd
44  double ydiff = (src.y - dst.y) + ((src.x & 1) - (dst.x & 1)) * 0.5;
45 
46  // we assume a map with a maximum diagonal of 300 (bigger than a 200x200)
47  // and we divide by 90000 * 10000 to avoid interfering with the defense subcost
48  // (see shortest_path_calculator::cost)
49  // NOTE: In theory, such heuristic is barely 'admissible' for A*,
50  // But not a problem for our current A* (we use heuristic only for speed)
51  // Plus, the euclidean fraction stay below the 1MP minimum and is also
52  // a good heuristic, so we still find the shortest path efficiently.
53  return distance_between(src, dst) + (xdiff*xdiff + ydiff*ydiff) / 900000000.0;
54 
55  // TODO: move the heuristic function into the cost_calculator
56  // so we can use case-specific heuristic
57  // and clean the definition of these numbers
58 }
59 
60 // values 0 and 1 mean uninitialized
61 const unsigned bad_search_counter = 0;
62 // The number of nodes already processed.
63 static unsigned search_counter = bad_search_counter;
64 
65 struct node {
66  double g, h, t;
68  /**
69  * If equal to search_counter, the node is off the list.
70  * If equal to search_counter + 1, the node is on the list.
71  * Otherwise it is outdated.
72  */
73  unsigned in;
74 
75  node()
76  : g(1e25)
77  , h(1e25)
78  , t(1e25)
79  , curr()
80  , prev()
81  , in(bad_search_counter)
82  {
83  }
84  node(double s, const map_location &c, const map_location &p, const map_location &dst, bool i, const teleport_map* teleports):
85  g(s), h(heuristic(c, dst)), t(g + h), curr(c), prev(p), in(search_counter + i)
86  {
87  if (teleports && !teleports->empty()) {
88 
89  double new_srch = 1.0;
90  std::set<map_location> sources;
91  teleports->get_sources(sources);
92 
93  std::set<map_location>::const_iterator it = sources.begin();
94  for(; it != sources.end(); ++it) {
95  const double tmp_srch = heuristic(c, *it);
96  if (tmp_srch < new_srch) { new_srch = tmp_srch; }
97  }
98 
99 
100  double new_dsth = 1.0;
101  std::set<map_location> targets;
102  teleports->get_targets(targets);
103 
104  for(it = targets.begin(); it != targets.end(); ++it) {
105  const double tmp_dsth = heuristic(*it, dst);
106  if (tmp_dsth < new_dsth) { new_dsth = tmp_dsth; }
107  }
108 
109  double new_h = new_srch + new_dsth + 1.0;
110  if (new_h < h) {
111  h = new_h;
112  t = g + h;
113  }
114  }
115  }
116 
117  bool operator<(const node& o) const {
118  return t < o.t;
119  }
120 };
121 
122 class comp {
123  const std::vector<node>& nodes_;
124 
125 public:
126  comp(const std::vector<node>& n) : nodes_(n) { }
127  bool operator()(int a, int b) {
128  return nodes_[b] < nodes_[a];
129  }
130 };
131 
132 class indexer {
133  size_t w_;
134 
135 public:
136  indexer(size_t w) : w_(w) { }
137  size_t operator()(const map_location& loc) {
138  return loc.y * w_ + loc.x;
139  }
140 };
141 }//anonymous namespace
142 
143 
145  double stop_at, const cost_calculator *calc,
146  const size_t width, const size_t height,
147  const teleport_map *teleports, bool border) {
148  //----------------- PRE_CONDITIONS ------------------
149  assert(src.valid(width, height, border));
150  assert(dst.valid(width, height, border));
151  assert(calc != nullptr);
152  assert(stop_at <= calc->getNoPathValue());
153  //---------------------------------------------------
154 
155  DBG_PF << "A* search: " << src << " -> " << dst << '\n';
156 
157  if (calc->cost(dst, 0) >= stop_at) {
158  LOG_PF << "aborted A* search because Start or Dest is invalid\n";
159  plain_route locRoute;
160  locRoute.move_cost = int(calc->getNoPathValue());
161  return locRoute;
162  }
163 
164  // increment search_counter but skip the range equivalent to uninitialized
165  search_counter += 2;
166  if (search_counter - bad_search_counter <= 1u)
167  search_counter += 2;
168 
169  static std::vector<node> nodes;
170  nodes.resize(width * height); // this create uninitialized nodes
171 
172  indexer index(width);
173  comp node_comp(nodes);
174 
175  nodes[index(dst)].g = stop_at + 1;
176  nodes[index(src)] = node(0, src, map_location::null_location(), dst, true, teleports);
177 
178  std::vector<int> pq;
179  pq.push_back(index(src));
180 
181  while (!pq.empty()) {
182  node& n = nodes[pq.front()];
183 
184  n.in = search_counter;
185 
186  std::pop_heap(pq.begin(), pq.end(), node_comp);
187  pq.pop_back();
188 
189  if (n.t >= nodes[index(dst)].g) break;
190 
191  std::vector<map_location> locs(6);
192 
193  if (teleports && !teleports->empty()) {
194 
195  std::set<map_location> allowed_teleports;
196  teleports->get_adjacents(allowed_teleports, n.curr);
197  locs.insert(locs.end(), allowed_teleports.begin(), allowed_teleports.end());
198  }
199 
200  int i = locs.size();
201 
202  get_adjacent_tiles(n.curr, &locs[0]);
203 
204  for (; i-- > 0;) {
205  if (!locs[i].valid(width, height, border)) continue;
206  if (locs[i] == n.curr) continue;
207  node& next = nodes[index(locs[i])];
208 
209  double thresh = (next.in - search_counter <= 1u) ? next.g : stop_at + 1;
210  // cost() is always >= 1 (assumed and needed by the heuristic)
211  if (n.g + 1 >= thresh) continue;
212  double cost = n.g + calc->cost(locs[i], n.g);
213  if (cost >= thresh) continue;
214 
215  bool in_list = next.in == search_counter + 1;
216 
217  next = node(cost, locs[i], n.curr, dst, true, teleports);
218 
219  if (in_list) {
220  std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), static_cast<int>(index(locs[i]))) + 1, node_comp);
221  } else {
222  pq.push_back(index(locs[i]));
223  std::push_heap(pq.begin(), pq.end(), node_comp);
224  }
225  }
226  }
227 
228  plain_route route;
229  if (nodes[index(dst)].g <= stop_at) {
230  DBG_PF << "found solution; calculating it...\n";
231  route.move_cost = static_cast<int>(nodes[index(dst)].g);
232  for (node curr = nodes[index(dst)]; curr.prev != map_location::null_location(); curr = nodes[index(curr.prev)]) {
233  route.steps.push_back(curr.curr);
234  }
235  route.steps.push_back(src);
236  std::reverse(route.steps.begin(), route.steps.end());
237  } else {
238  LOG_PF << "aborted a* search " << "\n";
239  route.move_cost = static_cast<int>(calc->getNoPathValue());
240  }
241 
242  return route;
243 }
244 
245 
246 }//namespace pathfind
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator *calc, const size_t width, const size_t height, const teleport_map *teleports, bool border)
const std::vector< node > & nodes
const GLfloat * c
Definition: glew.h:12741
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.hpp:274
bool empty() const
Definition: teleport.hpp:123
GLenum src
Definition: glew.h:2392
static double getNoPathValue()
Definition: pathfind.hpp:64
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
std::vector< map_location > steps
Definition: pathfind.hpp:135
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:132
bool valid() const
Definition: location.hpp:69
size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.hpp:357
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
GLenum GLenum dst
Definition: glew.h:2392
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
const std::vector< node > & nodes_
static const map_location & null_location()
Definition: location.hpp:195
GLfloat GLfloat p
Definition: glew.h:12766
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
#define LOG_PF
Definition: astarsearch.cpp:27
int move_cost
Movement cost for reaching the end of the route.
Definition: pathfind.hpp:137
Encapsulates the map of the game.
Definition: location.hpp:38
size_t w_
GLuint index
Definition: glew.h:1782
double g
Definition: astarsearch.cpp:66
static bool operator<(const placing_info &a, const placing_info &b)
Definition: game_state.cpp:127
map_location curr
Definition: astarsearch.cpp:67
size_t i
Definition: function.cpp:1057
GLint GLint GLint GLint GLint GLint GLsizei GLsizei height
Definition: glew.h:1220
static lg::log_domain log_engine("engine")
GLint GLint GLsizei GLsizei GLsizei GLint border
Definition: glew.h:1222
double h
Definition: astarsearch.cpp:66
#define next(ls)
Definition: llex.cpp:27
GLclampd n
Definition: glew.h:5903
double t
Definition: astarsearch.cpp:66
virtual double cost(const map_location &loc, const double so_far) const =0
bool find(E event, F functor)
Tests whether an event handler is available.
map_location prev
Definition: astarsearch.cpp:67
CALLABLE_WRAPPER_INPUT_END if(key=="terrain")
Standard logging facilities (interface).
GLint GLint GLint GLint GLint GLint GLsizei width
Definition: glew.h:1220
GLsizei GLenum * sources
Definition: glew.h:3155
#define DBG_PF
Definition: astarsearch.cpp:28
GLdouble s
Definition: glew.h:1358
unsigned in
If equal to search_counter, the node is off the list.
Definition: astarsearch.cpp:73
This module contains various pathfinding functions and utilities.
void get_adjacents(std::set< map_location > &adjacents, map_location loc) const
Definition: teleport.cpp:202
const std::string valid
Little parts of regex templates used to parse Wml annoations.