The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
pathfind.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2005 - 2025
3 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
4 Copyright (C) 2003 by David White <dave@whitevine.net>
5 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY.
13
14 See the COPYING file for more details.
15*/
16
17/**
18 * @file
19 * Various pathfinding functions and utilities.
20 */
21
22#include "pathfind/pathfind.hpp"
23#include "pathfind/teleport.hpp"
24
25#include "display.hpp"
26#include "game_board.hpp"
27#include "gettext.hpp"
28#include "log.hpp"
29#include "map/map.hpp"
30#include "resources.hpp"
31#include "team.hpp"
32#include "units/unit.hpp"
33#include "units/map.hpp"
34#include "wml_exception.hpp"
35
36#include <vector>
37#include <algorithm>
38
39static lg::log_domain log_engine("engine");
40#define ERR_PF LOG_STREAM(err, log_engine)
41
42namespace pathfind {
43
44
45/**
46 * Function that will find a location on the board that is as near
47 * to @a loc as possible, but which is unoccupied by any units.
48 * If no valid location can be found, it will return a null location.
49 * If @a pass_check is provided, the found location must have a terrain
50 * that this unit can enter.
51 * If @a shroud_check is provided, only locations not covered by this
52 * team's shroud will be considered.
53 */
55 const unit* pass_check, const team* shroud_check, const game_board* board)
56{
57 if (!board) {
59 assert(board);
60 }
61 const gamemap & map = board->map();
62 const unit_map & units = board->units();
63
64 if (!map.on_board(loc)) return map_location();
65
66 const bool do_shroud = shroud_check && shroud_check->uses_shroud();
67 std::set<map_location> pending_tiles_to_check, tiles_checked;
68 pending_tiles_to_check.insert(loc);
69 // Iterate out 50 hexes from loc
70 for (int distance = 0; distance < 50; ++distance) {
71 if (pending_tiles_to_check.empty())
72 return map_location();
73 //Copy over the hexes to check and clear the old set
74 std::set<map_location> tiles_checking;
75 tiles_checking.swap(pending_tiles_to_check);
76 //Iterate over all the hexes we need to check
77 for (const map_location &l : tiles_checking)
78 {
79 // Skip shrouded locations.
80 if ( do_shroud && shroud_check->shrouded(l) )
81 continue;
82 //If this area is not a castle but should, skip it.
83 if ( vacancy == VACANT_CASTLE && !map.is_castle(l) ) continue;
84 const bool pass_check_and_unreachable = pass_check
85 && pass_check->movement_cost(map[l]) == movetype::UNREACHABLE;
86 //If the unit can't reach the tile and we have searched
87 //an area of at least radius 10 (arbitrary), skip the tile.
88 //Necessary for cases such as an unreachable
89 //starting hex surrounded by 6 other unreachable hexes, in which case
90 //the algorithm would not even search distance==1
91 //even if there's a reachable hex for distance==2.
92 if (pass_check_and_unreachable && distance > 10) continue;
93 //If the hex is empty and we do either no pass check or the hex is reachable, return it.
94 if (units.find(l) == units.end() && !pass_check_and_unreachable) return l;
95
96 for(const map_location& l2 : get_adjacent_tiles(l)) {
97 if (!map.on_board(l2)) continue;
98 // Add the tile to be checked if it hasn't already been and
99 // isn't being checked.
100 if (tiles_checked.find(l2) == tiles_checked.end() &&
101 tiles_checking.find(l2) == tiles_checking.end())
102 {
103 pending_tiles_to_check.insert(l2);
104 }
105 }
106 }
107 tiles_checked.swap(tiles_checking);
108 }
109 return map_location();
110}
111
112/**
113 * Wrapper for find_vacant_tile() when looking for a vacant castle tile
114 * near a leader.
115 * If no valid location can be found, it will return a null location.
116 */
118{
120 nullptr, &resources::gameboard->get_team(leader.side()));
121}
122
123
124/**
125 * Determines if a given location is in an enemy zone of control.
126 *
127 * @param current_team The moving team (only ZoC of enemies of this team are considered).
128 * @param loc The location to check.
129 * @param viewing_team Only units visible to this team are considered.
130 * @param see_all If true, all units are considered (and viewing_team is ignored).
131 *
132 * @return true iff a visible enemy exerts zone of control over loc.
133 */
134bool enemy_zoc(const team& current_team, const map_location& loc,
135 const team& viewing_team, bool see_all)
136{
137 // Check the adjacent tiles.
138 for(const map_location& adj : get_adjacent_tiles(loc)) {
139 const unit *u = resources::gameboard->get_visible_unit(adj, viewing_team, see_all);
140 if ( u && current_team.is_enemy(u->side()) && u->emits_zoc() )
141 return true;
142 }
143
144 // No adjacent tiles had an enemy exerting ZoC over loc.
145 return false;
146}
147
148
149namespace {
150 /**
151 * Nodes used by find_routes().
152 * These store the information necessary for extending the path
153 * and for tracing the route back to the source.
154 */
155 struct findroute_node {
158 // search_num is used to detect which nodes have been collected
159 // in the current search. (More than just a boolean value so
160 // that nodes can be stored between searches.)
161 unsigned search_num;
162
163 // Constructors.
164 findroute_node(int moves, int turns, const map_location &prev_loc, unsigned search_count)
165 : moves_left(moves)
166 , turns_left(turns)
167 , prev(prev_loc)
168 , search_num(search_count)
169 { }
170 findroute_node()
171 : moves_left(0)
172 , turns_left(0)
173 , prev()
174 , search_num(0)
175 { }
176
177 // Compare these nodes based on movement consumed.
178 bool operator<(const findroute_node& o) const
179 {
180 return std::tie(turns_left, moves_left) > std::tie(o.turns_left, o.moves_left);
181 }
182 };
183
184 /**
185 * Converts map locations to and from integer indices.
186 */
187 struct findroute_indexer {
188 int w, h; // Width and height of the map.
189
190 // Constructor:
191 findroute_indexer(int a, int b) : w(a), h(b) { }
192 // Convert to an index: (throws on out of bounds)
193 unsigned operator()(int x, int y) const {
194 VALIDATE(this->on_board(x,y),
195 "Pathfind: Location not on board");
196 return x + static_cast<unsigned>(y)*w;
197 }
198 unsigned operator()(const map_location& loc) const {
199 return (*this)(loc.x, loc.y);
200 }
201 // Convert from an index:
202 map_location operator()(unsigned index) const {
203 return map_location(
204 static_cast<int>(index%w),
205 static_cast<int>(index/w));
206 }
207 // Check if location is on board
208 inline bool on_board(const map_location& loc) const {
209 return this->on_board(loc.x, loc.y);
210 }
211 inline bool on_board(int x, int y) const {
212 return (x >= 0) && (x < w) && (y >= 0) && (y < h);
213 }
214 };
215
216 /**
217 * A function object for comparing indices.
218 */
219 struct findroute_comp {
220 const std::vector<findroute_node>& nodes;
221
222 // Constructor:
223 findroute_comp(const std::vector<findroute_node>& n) : nodes(n) { }
224 // Binary predicate evaluating the order of its arguments:
225 bool operator()(int l, int r) const {
226 return nodes[r] < nodes[l];
227 }
228 };
229}
230
231/**
232 * Creates a list of routes that a unit can traverse from the provided location.
233 * (This is called when creating pathfind::paths and descendant classes.)
234 *
235 * @param[in] origin The location at which to begin the routes.
236 * @param[in] costs The costs to use for route finding.
237 * @param[in] slowed Whether or not to use the slowed costs.
238 * @param[in] moves_left The number of movement points left for the current turn.
239 * @param[in] max_moves The number of movement points in each future turn.
240 * @param[in] turns_left The number of future turns of movement to calculate.
241 * @param[out] destinations The traversable routes.
242 * @param[out] edges The hexes (possibly off-map) adjacent to those in
243 * destinations. (It is permissible for this to contain
244 * some hexes that are also in destinations.)
245 *
246 * @param[in] teleporter If not nullptr, teleportation will be considered, using
247 * this unit's abilities.
248 * @param[in] current_team If not nullptr, enemies of this team can obstruct routes
249 * both by occupying hexes and by exerting zones of control.
250 * In addition, the presence of units can affect
251 * teleportation options.
252 * @param[in] skirmisher If not nullptr, use this to determine where ZoC can and
253 * cannot be ignored (due to this unit having or not
254 * having the skirmisher ability).
255 * If nullptr, then ignore all zones of control.
256 * (No effect if current_team is nullptr).
257 * @param[in] viewing_team If not nullptr, use this team's vision when detecting
258 * enemy units and teleport destinations.
259 * If nullptr, then "see all".
260 * (No effect if teleporter and current_team are both nullptr.)
261 * @param[in] jamming_map The relevant "jamming" of the costs being used
262 * (currently only used with vision costs).
263 * @param[out] full_cost_map If not nullptr, build a cost_map instead of destinations.
264 * Destinations is ignored.
265 * full_cost_map is a vector of pairs. The first entry is the
266 * cost itself, the second how many units already visited this hex
267 * @param[in] check_vision If true, use vision check for teleports, that is, ignore
268 * units potentially blocking the teleport exit
269 */
270static void find_routes(
271 const map_location & origin, const movetype::terrain_costs & costs,
272 bool slowed, int moves_left, int max_moves, int turns_left,
273 paths::dest_vect & destinations, std::set<map_location> * edges,
274 const unit * teleporter, const team * current_team,
275 const unit * skirmisher, const team * viewing_team,
276 const std::map<map_location, int> * jamming_map=nullptr,
277 std::vector<std::pair<int, int>> * full_cost_map=nullptr, bool check_vision=false)
278{
279 const gamemap& map = resources::gameboard->map();
280
281 const bool see_all = viewing_team == nullptr;
282 // When see_all is true, the viewing team never matters, but we still
283 // need to supply one to some functions.
284 if ( viewing_team == nullptr )
285 viewing_team = &resources::gameboard->teams().front();
286
287 // Build a teleport map, if needed.
288 const teleport_map teleports = teleporter ?
289 get_teleport_locations(*teleporter, *viewing_team, see_all, current_team == nullptr, check_vision) :
290 teleport_map();
291
292 // Since this is called so often, keep memory reserved for the node list.
293 static std::vector<findroute_node> nodes;
294 static unsigned search_counter = 0;
295 // Incrementing search_counter means we ignore results from earlier searches.
296 ++search_counter;
297 // Whenever the counter cycles, trash the contents of nodes and restart at 1.
298 if ( search_counter == 0 ) {
299 nodes.resize(0);
300 search_counter = 1;
301 }
302 // Initialize the nodes for this search.
303 nodes.resize(static_cast<std::size_t>(map.w()) * map.h());
304 findroute_comp node_comp(nodes);
305 findroute_indexer index(map.w(), map.h());
306
307 assert(index.on_board(origin));
308
309 // Check if full_cost_map has the correct size.
310 // If not, ignore it. If yes, initialize the start position.
311 if ( full_cost_map ) {
312 if ( full_cost_map->size() != static_cast<unsigned>(map.w() * map.h()) )
313 full_cost_map = nullptr;
314 else {
315 if ( (*full_cost_map)[index(origin)].second == 0 )
316 (*full_cost_map)[index(origin)].first = 0;
317 (*full_cost_map)[index(origin)].second += 1;
318 }
319 }
320
321 // Used to optimize the final collection of routes.
322 int xmin = origin.x, xmax = origin.x, ymin = origin.y, ymax = origin.y;
323 int nb_dest = 1;
324
325 // Record the starting location.
326 nodes[index(origin)] = findroute_node(moves_left, turns_left,
328 search_counter);
329 // Begin the search at the starting location.
330 std::vector<unsigned> hexes_to_process(1, index(origin)); // Will be maintained as a heap.
331
332 while ( !hexes_to_process.empty() ) {
333 // Process the hex closest to the origin.
334 const unsigned cur_index = hexes_to_process.front();
335 const map_location cur_hex = index(cur_index);
336 const findroute_node& current = nodes[cur_index];
337 // Remove from the heap.
338 std::pop_heap(hexes_to_process.begin(), hexes_to_process.end(), node_comp);
339 hexes_to_process.pop_back();
340
341 // Get the locations adjacent to current.
342 std::vector<map_location> adj_locs(6);
343 get_adjacent_tiles(cur_hex, utils::span{adj_locs}.first<6>());
344
345 // Sort adjacents by on-boardness
346 auto off_board_it = std::partition(adj_locs.begin(), adj_locs.end(), [&index](map_location loc){
347 return index.on_board(loc);
348 });
349 // Store off-board edges if needed
350 if(edges != nullptr){
351 edges->insert(off_board_it, adj_locs.end());
352 }
353 // Remove off-board map locations
354 adj_locs.erase(off_board_it, adj_locs.end());
355
356 if ( teleporter ) {
357 auto allowed_teleports = teleports.get_adjacents(cur_hex);
358 adj_locs.insert(adj_locs.end(), allowed_teleports.begin(), allowed_teleports.end());
359 }
360 for ( int i = adj_locs.size()-1; i >= 0; --i ) {
361 // Get the node associated with this location.
362 const map_location & next_hex = adj_locs[i];
363 const unsigned next_index = index(next_hex);
364 findroute_node & next = nodes[next_index];
365
366 // Skip nodes we have already collected.
367 // (Since no previously checked routes were longer than
368 // the current one, the current route cannot be shorter.)
369 // (Significant difference from classic Dijkstra: we have
370 // vertex weights, not edge weights.)
371 if ( next.search_num == search_counter )
372 continue;
373
374 // If we go to next, it will be from current.
375 next.prev = cur_hex;
376
377 // Calculate the cost of entering next_hex.
378 int cost = costs.cost(map[next_hex], slowed);
379 if ( jamming_map ) {
380 const std::map<map_location, int>::const_iterator jam_it =
381 jamming_map->find(next_hex);
382 if ( jam_it != jamming_map->end() )
383 cost += jam_it->second;
384 }
385
386 // Calculate movement remaining after entering next_hex.
387 next.moves_left = current.moves_left - cost;
388 next.turns_left = current.turns_left;
389 if ( next.moves_left < 0 ) {
390 // Have to delay until the next turn.
391 next.turns_left--;
392 next.moves_left = max_moves - cost;
393 }
394 if ( next.moves_left < 0 || next.turns_left < 0 ) {
395 // Either can never enter this hex or out of turns.
396 if ( edges != nullptr )
397 edges->insert(next_hex);
398 continue;
399 }
400
401 if ( current_team ) {
402 // Account for enemy units.
403 const unit *v = resources::gameboard->get_visible_unit(next_hex, *viewing_team, see_all);
404 if ( v && current_team->is_enemy(v->side()) ) {
405 // Cannot enter enemy hexes.
406 if ( edges != nullptr )
407 edges->insert(next_hex);
408 continue;
409 }
410
411 if ( skirmisher && next.moves_left > 0 &&
412 enemy_zoc(*current_team, next_hex, *viewing_team, see_all) &&
413 !skirmisher->get_ability_bool("skirmisher", next_hex) ) {
414 next.moves_left = 0;
415 }
416 }
417
418 if ( !see_all && viewing_team && current_team && viewing_team != current_team && viewing_team->shrouded(next_hex) ) {
419 // bug #2199: in "Show Enemy Moves", don't pathfind enemy units through the player's shroud
420 continue;
421 }
422
423 // Update full_cost_map
424 if ( full_cost_map ) {
425 if ( (*full_cost_map)[next_index].second == 0 )
426 (*full_cost_map)[next_index].first = 0;
427 int summed_cost = (turns_left - next.turns_left + 1) * max_moves - next.moves_left;
428 (*full_cost_map)[next_index].first += summed_cost;
429 (*full_cost_map)[next_index].second += 1;
430 }
431
432 // Mark next as being collected.
433 next.search_num = search_counter;
434
435 // Add this node to the heap.
436 hexes_to_process.push_back(next_index);
437 std::push_heap(hexes_to_process.begin(), hexes_to_process.end(), node_comp);
438
439 // Bookkeeping (for later).
440 ++nb_dest;
441 if ( next_hex.x < xmin )
442 xmin = next_hex.x;
443 else if ( xmax < next_hex.x )
444 xmax = next_hex.x;
445 if ( next_hex.y < ymin )
446 ymin = next_hex.y;
447 else if ( ymax < next_hex.y )
448 ymax = next_hex.y;
449 }//for (i)
450 }//while (hexes_to_process)
451
452 // Currently the only caller who uses full_cost_map doesn't need the
453 // destinations. We can skip this part.
454 if ( full_cost_map ) {
455 return;
456 }
457
458 // Build the routes for every map_location that we reached.
459 // The ordering must be compatible with map_location::operator<.
460 destinations.reserve(nb_dest);
461 for (int x = xmin; x <= xmax; ++x) {
462 for (int y = ymin; y <= ymax; ++y)
463 {
464 const findroute_node &n = nodes[index(x,y)];
465 if ( n.search_num == search_counter ) {
466 paths::step s =
467 { map_location(x,y), n.prev, n.moves_left + n.turns_left*max_moves };
468 destinations.push_back(s);
469 }
470 }
471 }
472}
473
474static bool step_compare(const paths::step& a, const map_location& b) {
475 return a.curr < b;
476}
477
478paths::dest_vect::const_iterator paths::dest_vect::find(const map_location &loc) const
479{
480 const_iterator i = std::lower_bound(begin(), end(), loc, step_compare);
481 if (i != end() && i->curr != loc) return end();
482 return i;
483}
484
486{
487 iterator i = std::lower_bound(begin(), end(), loc, step_compare);
488 if (i != end() && i->curr == loc) return;
489 paths::step s { loc, map_location(), 0 };
490 std::vector<step>::insert(i, s);
491}
492
493/**
494 * Returns the path going from the source point (included) to the
495 * destination point @a j (excluded).
496 */
497std::vector<map_location> paths::dest_vect::get_path(const const_iterator &j) const
498{
499 std::vector<map_location> path;
500 if (!j->prev.valid()) {
501 path.push_back(j->curr);
502 } else {
503 const_iterator i = j;
504 do {
505 i = find(i->prev);
506 assert(i != end());
507 path.push_back(i->curr);
508 } while (i->prev.valid());
509 }
510 std::reverse(path.begin(), path.end());
511 return path;
512}
513
515{
516 return find(loc) != end();
517}
518
519/**
520 * Construct a list of paths for the specified unit.
521 *
522 * This function is used for several purposes, including showing a unit's
523 * potential moves and generating currently possible paths.
524 * @param u The unit whose moves and movement type will be used.
525 * @param force_ignore_zoc Set to true to completely ignore zones of control.
526 * @param allow_teleport Set to true to consider teleportation abilities.
527 * @param viewing_team Usually the current team, except for "show enemy moves", etc.
528 * @param additional_turns The number of turns to account for, in addition to the current.
529 * @param see_all Set to true to remove unit visibility from consideration.
530 * @param ignore_units Set to true if units should never obstruct paths (implies ignoring ZoC as well).
531 */
532paths::paths(const unit& u, bool force_ignore_zoc,
533 bool allow_teleport, const team &viewing_team,
534 int additional_turns, bool see_all, bool ignore_units)
535 : destinations()
536{
537 try {
539 u.get_location(),
542 u.movement_left(),
543 u.total_movement(),
544 additional_turns,
546 nullptr,
547 allow_teleport ? &u : nullptr,
548 ignore_units ? nullptr : &resources::gameboard->get_team(u.side()),
549 force_ignore_zoc ? nullptr : &u,
550 see_all ? nullptr : &viewing_team
551 );
552 } catch(const std::out_of_range&) {
553 // Invalid unit side.
554 }
555}
556
557/**
558 * Virtual destructor to support child classes.
559 */
561{
562}
563
564/**
565 * Constructs a list of vision paths for a unit.
566 *
567 * This is used to construct a list of hexes that the indicated unit can see.
568 * It differs from pathfinding in that it will only ever go out one turn,
569 * and that it will also collect a set of border hexes (the "one hex beyond"
570 * movement to which vision extends).
571 * @param viewer The unit doing the viewing.
572 * @param loc The location from which the viewing occurs
573 * (does not have to be the unit's location).
574 * @param jamming_map The relevant "jamming" of the costs being used.
575 */
577 const std::map<map_location, int>& jamming_map)
578 : paths(), edges()
579{
580 const int sight_range = viewer.vision();
581
582 // The three nullptr parameters indicate (in order):
583 // ignore units, ignore ZoC (no effect), and don't build a cost_map.
584 // The viewing team needs to be the unit's team here.
585 const team& viewing_team = resources::gameboard->get_team(viewer.side());
587 viewer.get_state(unit::STATE_SLOWED), sight_range, sight_range,
588 0, destinations, &edges, &viewer, nullptr, nullptr, &viewing_team, &jamming_map, nullptr, true);
589}
590
591/**
592 * Constructs a list of vision paths for a unit.
593 *
594 * This constructor is provided so that only the relevant portion of a unit's
595 * data is required to construct the vision paths.
596 * @param view_costs The vision costs of the unit doing the viewing.
597 * @param slowed Whether or not the unit is slowed.
598 * @param sight_range The vision() of the unit.
599 * @param loc The location from which the viewing occurs
600 * (does not have to be the unit's location).
601 * @param jamming_map The relevant "jamming" of the costs being used.
602 */
603vision_path::vision_path(const movetype::terrain_costs & view_costs, bool slowed,
604 int sight_range, const map_location & loc,
605 const std::map<map_location, int>& jamming_map)
606 : paths(), edges()
607{
608 // The three nullptr parameters indicate (in order):
609 // ignore units, ignore ZoC (no effect), and don't build a cost_map.
611
612 if(u.valid())
613 {
614 // The viewing team needs to be the unit's team here.
615 const team& viewing_team = resources::gameboard->get_team(u->side());
616 find_routes(loc, view_costs, slowed, sight_range, sight_range, 0,
617 destinations, &edges, &*u, nullptr, nullptr, &viewing_team, &jamming_map, nullptr, true);
618 }
619 else
620 {
621 find_routes(loc, view_costs, slowed, sight_range, sight_range, 0,
622 destinations, &edges, nullptr, nullptr, nullptr, nullptr, &jamming_map, nullptr, true);
623 }
624}
625
626/** Default destructor */
630
631
632/**
633 * Constructs a list of jamming paths for a unit.
634 *
635 * This is used to construct a list of hexes that the indicated unit can jam.
636 * It differs from pathfinding in that it will only ever go out one turn.
637 * @param jammer The unit doing the jamming.
638 * @param loc The location from which the jamming occurs
639 * (does not have to be the unit's location).
640 */
642 : paths()
643{
644 const int jamming_range = jammer.jamming();
645
646 // The five nullptr parameters indicate (in order): no edges, no teleports,
647 // ignore units, ignore ZoC (no effect), and see all (no effect).
649 jammer.get_state(unit::STATE_SLOWED), jamming_range, jamming_range,
650 0, destinations, nullptr, nullptr, nullptr, nullptr, nullptr);
651}
652
653/** Default destructor */
657
658marked_route mark_route(const plain_route &rt, bool update_move_cost)
659{
660 marked_route res;
661
662 if (rt.steps.empty()) return marked_route();
663 res.route = rt;
664
666 if (it == resources::gameboard->units().end()) return marked_route();
667 const unit& u = *it;
668
669 int turns = 0;
670 int total_costs = 0;
671 int movement = u.movement_left();
672 const team& unit_team = resources::gameboard->get_team(u.side());
673 bool zoc = false;
674
675 std::vector<map_location>::const_iterator i = rt.steps.begin();
676
677 for (; i !=rt.steps.end(); ++i) {
678 bool last_step = (i+1 == rt.steps.end());
679
680 // move_cost of the next step is irrelevant for the last step
681 assert(last_step || resources::gameboard->map().on_board(*(i+1)));
682 const int move_cost = last_step ? 0 : u.movement_cost(static_cast<const game_board*>(resources::gameboard)->map()[*(i+1)]);
683
684 const team& viewing_team = display::get_singleton()->viewing_team();
685
686 if (last_step || zoc || move_cost > movement) {
687 // check if we stop an a village and so maybe capture it
688 // if it's an enemy unit and a fogged village, we assume a capture
689 // (if he already owns it, we can't know that)
690 // if it's not an enemy, we can always know if he owns the village
691 bool capture = resources::gameboard->map().is_village(*i) && ( !unit_team.owns_village(*i)
692 || (viewing_team.is_enemy(u.side()) && viewing_team.fogged(*i)) );
693
694 ++turns;
695
696 bool invisible = u.invisible(*i, false);
697
698 res.marks[*i] = marked_route::mark(turns, zoc, capture, invisible);
699
700 if(last_step) {
701 if(capture) {
702 total_costs += movement;
703 }
704 break; // finished and we used dummy move_cost
705 }
706
707 total_costs += movement;
708 movement = u.total_movement();
709 if(move_cost > movement) {
710 return res; //we can't reach destination
711 }
712 }
713
714 zoc = enemy_zoc(unit_team, *(i + 1), viewing_team)
715 && !u.get_ability_bool("skirmisher", *(i+1));
716
717 if (zoc) {
718 total_costs += movement;
719 movement = 0;
720 } else {
721 movement -= move_cost;
722 total_costs += move_cost;
723 }
724 }
725
726 if(update_move_cost) {
727 res.move_cost = total_costs;
728 }
729 return res;
730}
731
733 const std::vector<team>& teams, const gamemap& map,
734 bool ignore_unit, bool ignore_defense, bool see_all)
735 : unit_(u), viewing_team_(t), teams_(teams), map_(map),
736 movement_left_(unit_.movement_left()),
737 total_movement_(unit_.total_movement()),
738 ignore_unit_(ignore_unit), ignore_defense_(ignore_defense),
739 see_all_(see_all)
740{}
741
742double shortest_path_calculator::cost(const map_location& loc, const double so_far) const
743{
744 assert(map_.on_board(loc));
745
746 // loc is shrouded, consider it impassable
747 // NOTE: This is why AI must avoid to use shroud
749 return getNoPathValue();
750
751 const t_translation::terrain_code terrain = map_[loc];
752 const int terrain_cost = unit_.movement_cost(terrain);
753 // Pathfinding heuristic: the cost must be at least 1
754 VALIDATE(terrain_cost >= 1, _("Terrain with a movement cost less than 1 encountered."));
755
756 // Compute how many movement points are left in the game turn
757 // needed to reach the previous hex.
758 // total_movement_ is not zero, thanks to the pathfinding heuristic
759 int remaining_movement = movement_left_ - static_cast<int>(so_far);
760 if (remaining_movement < 0) {
761 remaining_movement = total_movement_ - (-remaining_movement) % total_movement_;
762 }
763
764 if (terrain_cost >= movetype::UNREACHABLE || (total_movement_ < terrain_cost && remaining_movement < terrain_cost)) {
765 return getNoPathValue();
766 }
767
768 int other_unit_subcost = 0;
769 if (!ignore_unit_) {
770 const unit *other_unit =
772
773 // We can't traverse visible enemy and we also prefer empty hexes
774 // (less blocking in multi-turn moves and better when exploring fog,
775 // because we can't stop on a friend)
776
777 if (other_unit)
778 {
779 if (teams_[unit_.side() - 1].is_enemy(other_unit->side()))
780 return getNoPathValue();
781 else
782 // This value will be used with the defense_subcost (see below)
783 // The 1 here means: consider occupied hex as a -1% defense
784 // (less important than 10% defense because friends may move)
785 other_unit_subcost = 1;
786 }
787 }
788
789 // this will sum all different costs of this move
790 int move_cost = 0;
791
792 // Suppose that we have only 2 remaining MP and want to move onto a hex
793 // costing 3 MP. We don't have enough MP now, so we must end our turn here,
794 // thus spend our remaining MP by waiting (next turn, with full MP, we will
795 // be able to move on that hex)
796 if (remaining_movement < terrain_cost) {
797 move_cost += remaining_movement;
798 remaining_movement = total_movement_; // we consider having full MP now
799 }
800
801 // check ZoC
802 if (!ignore_unit_ && remaining_movement != terrain_cost
804 && !unit_.get_ability_bool("skirmisher", loc)) {
805 // entering ZoC cost all remaining MP
806 move_cost += remaining_movement;
807 } else {
808 // empty hex, pay only the terrain cost
809 move_cost += terrain_cost;
810 }
811
812 // We will add a tiny cost based on terrain defense, so the pathfinding
813 // will prefer good terrains between 2 with the same MP cost
814 // Keep in mind that defense_modifier is inverted (= 100 - defense%)
815 const int defense_subcost = ignore_defense_ ? 0 : unit_.defense_modifier(terrain);
816
817 // We divide subcosts by 100 * 100, because defense is 100-based and
818 // we don't want any impact on move cost for less then 100-steps path
819 // (even ~200 since mean defense is around ~50%)
820 return move_cost + (defense_subcost + other_unit_subcost) / 10000.0;
821}
822
823move_type_path_calculator::move_type_path_calculator(const movetype& mt, int movement_left, int total_movement, const team& t, const gamemap& map)
824 : movement_type_(mt), movement_left_(movement_left),
825 total_movement_(total_movement), viewing_team_(t), map_(map)
826{}
827
828// This is an simplified version of shortest_path_calculator (see above for explanation)
829double move_type_path_calculator::cost(const map_location& loc, const double so_far) const
830{
831 assert(map_.on_board(loc));
833 return getNoPathValue();
834
835 const t_translation::terrain_code terrain = map_[loc];
836 const int terrain_cost = movement_type_.movement_cost(terrain);
837
838 if (total_movement_ < terrain_cost)
839 return getNoPathValue();
840
841 int remaining_movement = movement_left_ - static_cast<int>(so_far);
842 if (remaining_movement < 0)
843 remaining_movement = total_movement_ - (-remaining_movement) % total_movement_;
844
845 int move_cost = 0;
846
847 if (remaining_movement < terrain_cost) {
848 move_cost += remaining_movement;
849 }
850
851 move_cost += terrain_cost;
852
853 return move_cost;
854}
855
856
858 : unit_(u), map_(map)
859{}
860
861double emergency_path_calculator::cost(const map_location& loc, const double) const
862{
863 assert(map_.on_board(loc));
864
865 return unit_.movement_cost(map_[loc]);
866}
867
870
871double dummy_path_calculator::cost(const map_location&, const double) const
872{
873 return 1.0;
874}
875
876/**
877 * Constructs a cost-map. For a unit each hex is mapped to the cost the
878 * unit will need to reach this hex. Considers movement-loss caused by
879 * turn changes.
880 * Can also used with multiple units to accumulate their costs efficiently.
881 * Will also count how many units could reach a hex for easy normalization.
882 * @param u the unit
883 * @param force_ignore_zoc Set to true to completely ignore zones of control.
884 * @param allow_teleport Set to true to consider teleportation abilities.
885 * @param viewing_team Usually the current team, except for "show enemy moves", etc.
886 * @param see_all Set to true to remove unit visibility from consideration.
887 * @param ignore_units Set to true if units should never obstruct paths (implies ignoring ZoC as well).
888 */
889full_cost_map::full_cost_map(const unit& u, bool force_ignore_zoc,
890 bool allow_teleport, const team &viewing_team,
891 bool see_all, bool ignore_units)
892 :force_ignore_zoc_(force_ignore_zoc), allow_teleport_(allow_teleport),
893 viewing_team_(viewing_team), see_all_(see_all), ignore_units_(ignore_units)
894{
895 const gamemap& map = resources::gameboard->map();
896 cost_map = std::vector<std::pair<int, int>>(static_cast<std::size_t>(map.w()) * map.h(), std::pair(-1, 0));
897 add_unit(u);
898}
899
900/**
901 * Same as other constructor but without unit. Use this when working
902 * with add_unit().
903 */
904full_cost_map::full_cost_map(bool force_ignore_zoc,
905 bool allow_teleport, const team &viewing_team,
906 bool see_all, bool ignore_units)
907 :force_ignore_zoc_(force_ignore_zoc), allow_teleport_(allow_teleport),
908 viewing_team_(viewing_team), see_all_(see_all), ignore_units_(ignore_units)
909{
910 const gamemap& map = resources::gameboard->map();
911 cost_map = std::vector<std::pair<int, int>>(static_cast<std::size_t>(map.w()) * map.h(), std::pair(-1, 0));
912}
913
914/**
915 * Adds a units cost map to cost_map (increments the elements in cost_map)
916 * @param u a real existing unit on the map
917 * @param use_max_moves whether to use the unit's max movement or the unit's remaining movement
918 */
919void full_cost_map::add_unit(const unit& u, bool use_max_moves)
920{
921 try {
922 // We don't need the destinations, but find_routes() wants to have this parameter
924
926 u.get_location(),
929 (use_max_moves) ? u.total_movement() : u.movement_left(),
930 u.total_movement(),
931 99,
932 dummy,
933 nullptr,
934 allow_teleport_ ? &u : nullptr,
936 force_ignore_zoc_ ? nullptr : &u,
937 see_all_ ? nullptr : &viewing_team_,
938 nullptr,
939 &cost_map
940 );
941 } catch(const std::out_of_range&) {
942 // Invalid unit side.
943 }
944}
945
946/**
947 * Adds a units cost map to cost_map (increments the elements in cost_map)
948 * This function can be used to generate a cost_map with a non existing unit.
949 * @param origin the location on the map from where the calculations shall start
950 * @param ut the unit type we are interested in
951 * @param side the side of the unit. Important for zocs.
952 */
953void full_cost_map::add_unit(const map_location& origin, const unit_type* const ut, int side)
954{
955 if (!ut) {
956 return;
957 }
958 unit_ptr u = unit::create(*ut, side, false);
959 u->set_location(origin);
960 add_unit(*u);
961}
962
963/**
964 * Accessor for the cost/reach-amount pairs.
965 * Read comment in pathfind.hpp to cost_map.
966 *
967 * @return the entry of the cost_map at (x, y)
968 * or (-1, 0) if value is not set or (x, y) is invalid.
969 */
971{
972 const gamemap& map = resources::gameboard->map();
973 assert(cost_map.size() == static_cast<unsigned>(map.w() * map.h()));
974
975 if (!map.on_board(loc)) {
976 return std::pair(-1, 0); // invalid
977 }
978
979 return cost_map[loc.x + (loc.y * map.w())];
980}
981
982/**
983 * Accessor for the costs.
984 *
985 * @return the value of the cost_map at (x, y)
986 * or -1 if value is not set or (x, y) is invalid.
987 */
989{
990 return get_pair_at(loc).first;
991}
992
993/**
994 * Accessor for the costs.
995 *
996 * @return The average cost of all added units for this hex
997 * or -1 if no unit can reach the hex.
998 */
1000{
1001 auto p = get_pair_at(loc);
1002 if (p.second == 0) {
1003 return -1;
1004 } else {
1005 return static_cast<double>(p.first) /p.second;
1006 }
1007}
1008}//namespace pathfind
map_location loc
Definition move.cpp:172
static auto & dummy
double t
map_location prev
constexpr span< T, C > first() const
Definition span.hpp:315
const unit * get_visible_unit(const map_location &loc, const team &current_team, bool see_all=false) const
const team & viewing_team() const
Definition display.cpp:337
static display * get_singleton()
Returns the display object if a display object exists.
Definition display.hpp:102
Game board class.
virtual const gamemap & map() const override
virtual const std::vector< team > & teams() const override
team & get_team(int i)
virtual const unit_map & units() const override
int w() const
Effective map width.
Definition map.hpp:50
int h() const
Effective map height.
Definition map.hpp:53
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition map.cpp:347
Encapsulates the map of the game.
Definition map.hpp:176
bool is_village(const map_location &loc) const
Definition map.cpp:60
bool is_castle(const map_location &loc) const
Definition map.cpp:64
A const-only interface for how many (movement, vision, or "jamming") points a unit needs for each hex...
Definition movetype.hpp:53
int cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost associated with the given terrain.
Definition movetype.hpp:68
The basic "size" of the unit - flying, small land, large land, etc.
Definition movetype.hpp:44
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition movetype.hpp:174
const terrain_costs & get_vision() const
Definition movetype.hpp:267
const terrain_costs & get_jamming() const
Definition movetype.hpp:268
const terrain_costs & get_movement() const
Definition movetype.hpp:266
int movement_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
Definition movetype.hpp:278
const std::unordered_set< map_location > & get_adjacents(map_location loc) const
Definition teleport.cpp:231
This class stores all the data for a single 'side' (in game nomenclature).
Definition team.hpp:74
bool uses_shroud() const
Definition team.hpp:344
bool is_enemy(int n) const
Definition team.hpp:270
bool owns_village(const map_location &loc) const
Definition team.hpp:179
bool shrouded(const map_location &loc) const
Definition team.cpp:636
bool fogged(const map_location &loc) const
Definition team.cpp:645
Container associating units to locations.
Definition map.hpp:98
unit_iterator end()
Definition map.hpp:428
unit_iterator find(std::size_t id)
Definition map.cpp:302
A single unit type that the player may recruit.
Definition types.hpp:43
This class represents a single unit of a specific type.
Definition unit.hpp:39
static unit_ptr create(const config &cfg, bool use_traits=false, const vconfig *vcfg=nullptr)
Initializes a unit from a config.
Definition unit.hpp:107
map_display and display: classes which take care of displaying the map and game-data on the screen.
static bool operator<(const placing_info &a, const placing_info &b)
static std::string _(const char *str)
Definition gettext.hpp:100
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses or is affected by a given ability.
bool invisible(const map_location &loc, bool see_all=true) const
Definition unit.cpp:2640
bool get_state(const std::string &state) const
Check if the unit is affected by a status effect.
Definition unit.cpp:1446
int side() const
The side this unit belongs to.
Definition unit.hpp:249
@ STATE_SLOWED
Definition unit.hpp:775
int defense_modifier(const t_translation::terrain_code &terrain, const map_location &loc) const
The unit's defense on a given terrain.
Definition unit.cpp:1777
bool emits_zoc() const
Tests whether the unit has a zone-of-control, considering incapacitated.
Definition unit.hpp:1308
const map_location & get_location() const
The current map location this unit is at.
Definition unit.hpp:1327
int jamming() const
Gets the unit's jamming points.
Definition unit.hpp:1376
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
Definition unit.hpp:1410
const movetype & movement_type() const
Get the unit's movement type.
Definition unit.hpp:1400
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition unit.hpp:1252
int total_movement() const
The maximum moves this unit has.
Definition unit.hpp:1236
int vision() const
Gets the unit's vision points.
Definition unit.hpp:1370
void get_adjacent_tiles(const map_location &a, utils::span< map_location, 6 > res)
Function which, given a location, will place all adjacent locations in res.
Definition location.cpp:513
Standard logging facilities (interface).
map_location find_vacant_castle(const unit &leader)
Wrapper for find_vacant_tile() when looking for a vacant castle tile near a leader.
Definition pathfind.cpp:117
static bool step_compare(const paths::step &a, const map_location &b)
Definition pathfind.cpp:474
@ VACANT_CASTLE
Definition pathfind.hpp:39
bool enemy_zoc(const team &current_team, const map_location &loc, const team &viewing_team, bool see_all)
Determines if a given location is in an enemy zone of control.
Definition pathfind.cpp:134
marked_route mark_route(const plain_route &rt, bool update_move_cost)
Add marks on a route rt assuming that the unit located at the first hex of rt travels along it.
Definition pathfind.cpp:658
const teleport_map get_teleport_locations(const unit &u, const team &viewing_team, bool see_all, bool ignore_units, bool check_vision)
Definition teleport.cpp:251
static void find_routes(const map_location &origin, const movetype::terrain_costs &costs, bool slowed, int moves_left, int max_moves, int turns_left, paths::dest_vect &destinations, std::set< map_location > *edges, const unit *teleporter, const team *current_team, const unit *skirmisher, const team *viewing_team, const std::map< map_location, int > *jamming_map=nullptr, std::vector< std::pair< int, int > > *full_cost_map=nullptr, bool check_vision=false)
Creates a list of routes that a unit can traverse from the provided location.
Definition pathfind.cpp:270
map_location find_vacant_tile(const map_location &loc, VACANT_TILE_TYPE vacancy, const unit *pass_check, const team *shroud_check, const game_board *board)
Function that will find a location on the board that is as near to loc as possible,...
Definition pathfind.cpp:54
game_board * gameboard
Definition resources.cpp:20
static lg::log_domain log_engine("engine")
int turns_left
Definition pathfind.cpp:156
int moves_left
Definition pathfind.cpp:156
const std::vector< findroute_node > & nodes
Definition pathfind.cpp:220
unsigned search_num
Definition pathfind.cpp:161
int w
Definition pathfind.cpp:188
This module contains various pathfinding functions and utilities.
std::shared_ptr< unit > unit_ptr
Definition ptr.hpp:26
Encapsulates the map of the game.
Definition location.hpp:46
static const map_location & null_location()
Definition location.hpp:103
static double getNoPathValue()
Definition pathfind.hpp:65
virtual double cost(const map_location &loc, const double so_far) const
Definition pathfind.cpp:871
dummy_path_calculator(const unit &u, const gamemap &map)
Definition pathfind.cpp:868
emergency_path_calculator(const unit &u, const gamemap &map)
Definition pathfind.cpp:857
virtual double cost(const map_location &loc, const double so_far) const
Definition pathfind.cpp:861
Structure which uses find_routes() to build a cost map This maps each hex to a the movements a unit w...
Definition pathfind.hpp:268
const bool force_ignore_zoc_
Definition pathfind.hpp:299
full_cost_map(const unit &u, bool force_ignore_zoc, bool allow_teleport, const team &viewing_team, bool see_all=true, bool ignore_units=true)
Constructs a cost-map.
Definition pathfind.cpp:889
double get_average_cost_at(map_location loc) const
Accessor for the costs.
Definition pathfind.cpp:999
void add_unit(const unit &u, bool use_max_moves=true)
Adds a units cost map to cost_map (increments the elements in cost_map)
Definition pathfind.cpp:919
const team & viewing_team_
Definition pathfind.hpp:301
int get_cost_at(map_location loc) const
Accessor for the costs.
Definition pathfind.cpp:988
std::pair< int, int > get_pair_at(map_location loc) const
Accessor for the cost/reach-amount pairs.
Definition pathfind.cpp:970
std::vector< std::pair< int, int > > cost_map
Definition pathfind.hpp:296
jamming_path(const unit &jammer, const map_location &loc)
Construct a list of jammed hexes for a unit.
Definition pathfind.cpp:641
virtual ~jamming_path()
Default destructor.
Definition pathfind.cpp:654
Structure which holds a single route and marks for special events.
Definition pathfind.hpp:142
virtual double cost(const map_location &loc, const double so_far) const
Definition pathfind.cpp:829
move_type_path_calculator(const movetype &mt, int movement_left, int total_movement, const team &t, const gamemap &map)
Definition pathfind.cpp:823
Ordered vector of possible destinations.
Definition pathfind.hpp:95
std::vector< map_location > get_path(const const_iterator &) const
Returns the path going from the source point (included) to the destination point j (excluded).
Definition pathfind.cpp:497
void insert(const map_location &)
Definition pathfind.cpp:485
bool contains(const map_location &) const
Definition pathfind.cpp:514
const_iterator find(const map_location &) const
Definition pathfind.cpp:478
map_location curr
Definition pathfind.hpp:89
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition pathfind.hpp:73
virtual ~paths()
Virtual destructor (default processing).
Definition pathfind.cpp:560
dest_vect destinations
Definition pathfind.hpp:101
Structure which holds a single route between one location and another.
Definition pathfind.hpp:133
std::vector< map_location > steps
Definition pathfind.hpp:135
shortest_path_calculator(const unit &u, const team &t, const std::vector< team > &teams, const gamemap &map, bool ignore_unit=false, bool ignore_defense_=false, bool see_all=false)
Definition pathfind.cpp:732
const std::vector< team > & teams_
Definition pathfind.hpp:215
virtual double cost(const map_location &loc, const double so_far) const
Definition pathfind.cpp:742
std::set< map_location > edges
The edges are the non-destination hexes bordering the destinations.
Definition pathfind.hpp:117
virtual ~vision_path()
Default destructor.
Definition pathfind.cpp:627
vision_path(const unit &viewer, const map_location &loc, const std::map< map_location, int > &jamming_map)
Constructs a list of vision paths for a unit.
Definition pathfind.cpp:576
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
bool valid() const
Definition map.hpp:273
mock_party p
static map_location::direction n
static map_location::direction s
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
#define i
#define h
#define b