The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
pathutils.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Various pathfinding functions and utilities.
18  */
19 
20 #include "global.hpp"
21 
22 #include "pathutils.hpp"
23 
24 #include "map/map.hpp"
25 
26 
27 /**
28  * Function that will add to @a result all locations exactly @a radius tiles
29  * from @a center (or nothing if @a radius is not positive). @a result must be
30  * a std::vector of locations.
31  */
32 void get_tile_ring(const map_location& center, const int radius,
33  std::vector<map_location>& result)
34 {
35  if ( radius <= 0 ) {
36  return;
37  }
38 
40 
41  for(int n = 0; n != 6; ++n) {
42  const map_location::DIRECTION dir = static_cast<map_location::DIRECTION>(n);
43  for(int i = 0; i != radius; ++i) {
44  result.push_back(loc);
45  loc = loc.get_direction(dir, 1);
46  }
47  }
48 }
49 
50 
51 /**
52  * Function that will add to @a result all locations within @a radius tiles
53  * of @a center (excluding @a center itself). @a result must be a std::vector
54  * of locations.
55  */
56 void get_tiles_in_radius(const map_location& center, const int radius,
57  std::vector<map_location>& result)
58 {
59  for(int n = 1; n <= radius; ++n) {
60  get_tile_ring(center, n, result);
61  }
62 }
63 
64 
65 /**
66  * Function that will add to @a result all locations within @a radius tiles
67  * of @a center (including @a center itself). @a result must be a std::set
68  * of locations.
69  */
70 void get_tiles_radius(const map_location& center, size_t radius,
71  std::set<map_location>& result)
72 {
73  // Re-use some logic.
74  std::vector<map_location> internal_result(1, center);
75  get_tiles_in_radius(center, static_cast<int>(radius), internal_result);
76 
77  // Convert to a set.
78  result.insert(internal_result.begin(), internal_result.end());
79 }
80 
81 
82 namespace { // Helpers for get_tiles_radius() without a radius filter.
83 
84  // Ranges of rows are stored as pairs of a row number and a number of rows.
85  typedef std::pair<int, size_t> row_range;
86  // This is a map from column numbers to sets of ranges of rows.
87  typedef std::map<int, std::set<row_range> > column_ranges;
88 
89 
90  /**
91  * Function that will collect all locations within @a radius tiles of an
92  * element of @a locs, subject to the restriction col_begin <= x < col_end.
93  */
94  // Complexity: O(nr lg(nr)), where n = locs.size() and r = radius.
95  // In this formula, r is bound by col_end-col_begin (but that is
96  // probably a rare event).
97  void get_column_ranges(column_ranges & collected_tiles,
98  const std::vector<map_location>& locs,
99  const size_t radius,
100  const int col_begin, const int col_end)
101  {
102  // Shorter names for the directions we'll use.
106 
107  // Perform this conversion once.
108  const int radius_i = static_cast<int>(radius);
109 
110  for (const map_location &loc : locs)
111  if ( loc != map_location::null_location() )
112  {
113  // Calculate the circle of hexes around this one.
114  size_t height = radius;
115  map_location top = loc.get_direction(NORTH_WEST, radius_i);
116  // Don't start off the map edge.
117  if ( top.x < col_begin ) {
118  const int col_shift = std::min(col_begin, loc.x) - top.x;
119  top = top.get_direction(NORTH_EAST, col_shift);
120  height += col_shift;
121  }
122  // The left side.
123  const int end_l = std::min(loc.x, col_end);
124  for ( ; top.x < end_l; top = top.get_direction(NORTH_EAST, 1) )
125  collected_tiles[top.x].insert(row_range(top.y, ++height));
126  // Extra increment so the middle column is tall enough.
127  height += 2;
128  // Don't start off the map edge (we allow loc to be off-board).
129  if ( top.x < col_begin ) {
130  const int col_shift = col_begin - top.x;
131  top = top.get_direction(SOUTH_EAST, col_shift);
132  height -= col_shift;
133  }
134  // The middle column and right side.
135  const int end_r = std::min(loc.x + radius_i + 1, col_end);
136  for ( ; top.x < end_r; top = top.get_direction(SOUTH_EAST, 1) )
137  collected_tiles[top.x].insert(row_range(top.y, --height));
138  }
139  }
140 
141  /**
142  * Function that interprets @a collected_tiles and adds to @a result those
143  * whose y-coordinate satisifies row_begin <= y < row_end.
144  * When passed to this function, @a result must not be empty. (This allows
145  * a code simplification and is currently always the case anyway.)
146  */
147  // Complexity: O(number of distinct hexes collected), assuming that the
148  // insertion hint makes insertions O(1). Furthermore, hexes outside the
149  // interval [row_begin, row_end) are skipped and do not count towards the
150  // complexity.
151  void ranges_to_tiles(std::set<map_location> & result,
152  const column_ranges & collected_tiles,
153  int row_begin, int row_end)
154  {
155  // This should help optimize the insertions (since we will be
156  // processing hexes in their lexicographical order).
157  std::set<map_location>::const_iterator insert_hint = result.begin();
158  // Note: This hint will get incremented later, which is the only
159  // reason we require result to be initially non-empty.
160 
161  for (const column_ranges::value_type & column : collected_tiles)
162  {
163  // For this loop, the order within the set is crucial; we need
164  // rows.first to be non-decreasing with each iteration.
165  // Loop invariant: within this column, all rows before next_row
166  // have been processed and either added to result or skipped.
167  // There is no going back (nor a need to).
168  int next_row = row_begin;
169  for (const row_range &rows : column.second)
170  {
171  // Skipping some rows?
172  if ( next_row < rows.first )
173  next_row = rows.first;
174 
175  // Add this range of hexes.
176  const int end = std::min(rows.first + static_cast<int>(rows.second),
177  row_end);
178  for ( ; next_row < end; ++next_row )
179  insert_hint = result.insert(++insert_hint,
180  map_location(column.first, next_row));
181 
182  // Have we reached the end of the board?
183  if ( next_row >= row_end )
184  break;
185  }
186  }
187  }
188 
189 } // namespage for get_tiles_radius() helpers.
190 
191 
192 /**
193  * Function that will add to @a result all elements of @a locs, plus all
194  * on-board locations that are within @a radius tiles of an element of locs.
195  * @a result must be a std::set of locations.
196  */
197 // Complexity: O(nr lg(nr) + nr^2), where n = locs.size(), r = radius.
198 // The nr^2 term is bounded by the size of the board.
199 void get_tiles_radius(const gamemap& map, const std::vector<map_location>& locs,
200  size_t radius, std::set<map_location>& result,
201  bool with_border)
202 {
203  // Make sure the provided locations are included.
204  // This would be needed in case some of the provided locations are off-map.
205  // It also allows simpler processing if the radius is zero.
206  // For efficiency, do this first since locs is potentially unsorted.
207  result.insert(locs.begin(), locs.end());
208 
209  if ( radius != 0 && !locs.empty() )
210  {
211  const int border = with_border ? map.border_size() : 0;
212  column_ranges collected_tiles;
213 
214  // Collect the hexes within the desired disks into collected_tiles.
215  // This maps each x-value to a set of ranges of y-values that
216  // are covered by the disks around each element of locs.
217  // (So the data size at this point is proportional to the number
218  // of x-values involved, which is O(nr). The lg(nr) factor comes
219  // from the data being sorted.)
220  get_column_ranges(collected_tiles, locs, radius, -border, map.w() + border);
221 
222  // Now that all the tiles have been collected, add them to result.
223  // (There are O(nr^2) hexes to add.) By collecting before adding, each
224  // hex will be processed only once, even when disks overlap. This is
225  // how we can get good performance if there is significant overlap, and
226  // how the work required can be bound by the size of the board.
227  ranges_to_tiles(result, collected_tiles, -border, map.h() + border);
228  }
229 }
230 
231 
232 /**
233  * Function that will add to @a result all elements of @a locs, plus all
234  * on-board locations matching @a pred that are connected to elements of
235  * locs by a chain of at most @a radius tiles, each of which matches @a pred.
236  * @a result must be a std::set of locations.
237  */
238 void get_tiles_radius(gamemap const &map, std::vector<map_location> const &locs,
239  size_t radius, std::set<map_location> &result,
240  bool with_border, xy_pred const &pred)
241 {
242  typedef std::set<map_location> location_set;
243 
244  location_set must_visit, filtered_out;
245  location_set not_visited(locs.begin(), locs.end());
246 
247  for ( ; radius != 0 && !not_visited.empty(); --radius )
248  {
249  location_set::const_iterator it = not_visited.begin();
250  location_set::const_iterator it_end = not_visited.end();
251 
252  result.insert(it, it_end);
253  for(; it != it_end; ++it) {
254  map_location adj[6];
255  get_adjacent_tiles(*it, adj);
256  for(size_t i = 0; i != 6; ++i) {
257  map_location const &loc = adj[i];
258  if ( with_border ? map.on_board_with_border(loc) :
259  map.on_board(loc) ) {
260  if ( !result.count(loc) && !filtered_out.count(loc) ) {
261  if ( pred(loc) )
262  must_visit.insert(loc);
263  else
264  filtered_out.insert(loc);
265  }
266  }
267  }
268  }
269 
270  not_visited.swap(must_visit);
271  must_visit.clear();
272  }
273 
274  result.insert(not_visited.begin(), not_visited.end());
275 }
276 
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:472
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
void get_tiles_in_radius(const map_location &center, const int radius, std::vector< map_location > &result)
Function that will add to result all locations within radius tiles of center (excluding center itself...
Definition: pathutils.cpp:56
void get_tile_ring(const map_location &center, const int radius, std::vector< map_location > &result)
Function that will add to result all locations exactly radius tiles from center (or nothing if radius...
Definition: pathutils.cpp:32
GLuint GLuint end
Definition: glew.h:1221
GLuint64EXT * result
Definition: glew.h:10727
int w() const
Effective map width.
Definition: map.hpp:105
Encapsulates the map of the game.
Definition: map.hpp:37
int border_size() const
Size of the map border.
Definition: map.hpp:111
static const map_location & null_location()
Definition: location.hpp:195
Encapsulates the map of the game.
Definition: location.hpp:38
GLenum GLenum GLvoid GLvoid * column
Definition: glew.h:3805
int h() const
Effective map height.
Definition: map.hpp:108
size_t i
Definition: function.cpp:1057
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
void get_tiles_radius(const map_location &center, size_t radius, std::set< map_location > &result)
Function that will add to result all locations within radius tiles of center (including center itself...
Definition: pathutils.cpp:70
GLint GLint GLint GLint GLint GLint GLsizei GLsizei height
Definition: glew.h:1220
GLint GLint GLsizei GLsizei GLsizei GLint border
Definition: glew.h:1222
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:467
GLclampd n
Definition: glew.h:5903
map_location get_direction(DIRECTION d, unsigned int n=1u) const
Definition: location.hpp:232