Freeciv21
Develop your civilization from humble roots to a global empire
path_finder.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2022 Louis Moureaux
2 // SPDX-License-Identifier: GPL-3.0-or-later
3 
4 #include "path_finder.h"
5 
6 #include "actions.h"
7 #include "game.h"
8 #include "map.h"
9 #include "movement.h"
10 #include "path.h"
11 #include "tile.h"
12 #include "unit.h"
13 #include "unit_utils.h"
14 #include "world_object.h"
15 
16 #include <map>
17 #include <queue>
18 #include <set>
19 
20 namespace freeciv {
21 
22 namespace detail {
23 
28 {
31  false, // Final
32  0, // Waypoints
33  0, // Turns
35  nullptr};
36 }
37 
44  tile *target)
45 {
46  auto ret = *this;
47  ret.parent = this;
49  ret.order.action = action;
50  ret.order.target = target->index;
51  ret.order.sub_target = NO_TARGET;
52  ret.order.dir = DIR8_ORIGIN;
53  ret.moves_left = probe.moves_left;
54  ret.location = target;
55 
56  const auto action_ptr = action_by_number(action);
57  if (utype_is_moved_to_tgt_by_action(action_ptr, probe.utype)) {
58  // We need a second probe because EFT_ACTION_SUCCESS_MOVE_COST is
59  // evaluated after the unit has moved
60  auto moved_probe = probe;
61  moved_probe.tile = target;
62  ret.moves_left -= unit_pays_mp_for_action(action_ptr, &moved_probe);
63  } else {
64  ret.moves_left -= unit_pays_mp_for_action(action_ptr, &probe);
65  }
66 
67  return ret;
68 }
69 
77 bool vertex::comparable(const vertex &other) const
78 {
79  // All "state" variables must be equal
81  != std::tie(other.location, other.loaded, other.moved,
82  other.paradropped, other.is_final, other.waypoints)) {
83  return false;
84  }
85 
86  // When the results of the expressions below are positive, this node does
87  // better than the other for this criteria. When it's negative, it's the
88  // opposite.
89  auto a = other.turns - turns;
90  auto b = moves_left - other.moves_left;
91  auto c = health - other.health;
92  auto d = fuel_left - other.fuel_left;
93  // For the comparison to be meaningful, all criteria must go in the same
94  // direction.
95  return (a <= 0 && b <= 0 && c <= 0 && d <= 0)
96  || (a >= 0 && b >= 0 && c >= 0 && d >= 0);
97 }
98 
103 void vertex::fill_probe(unit &probe) const
104 {
105  probe.activity = ACTIVITY_IDLE; // Else it doesn't want to move.
106  probe.tile = location;
107  probe.transporter = loaded;
108  probe.client.transported_by = loaded ? loaded->id : -1;
109  probe.moved = moved;
110  probe.paradropped = paradropped;
111  probe.moves_left = moves_left;
112  probe.hp = health;
113  probe.fuel = fuel_left;
114 }
115 
119 bool vertex::operator==(const vertex &other) const
120 {
121  return std::tie(location, loaded, moved, paradropped, is_final, waypoints,
123  == std::tie(other.location, other.loaded, other.moved,
124  other.paradropped, other.is_final, other.waypoints,
125  other.turns, other.moves_left, other.health,
126  other.fuel_left);
127 }
128 
132 bool vertex::operator>(const vertex &other) const
133 {
134  return std::tie(turns, other.moves_left, other.health, paradropped,
135  other.fuel_left)
136  > std::tie(other.turns, moves_left, health, other.paradropped,
137  fuel_left);
138 }
139 
140 } // namespace detail
141 
146  const ::unit *unit, const detail::vertex &init)
147  : unit(*unit), initial_vertex(init)
148 {
150 }
151 
156 {
157  maybe_insert_vertex(initial_vertex);
158 }
159 
165  const detail::vertex &v)
166 {
167  // Handle any constraint.
168  if (constraint && !constraint->is_allowed(v)) {
169  return;
170  }
171 
172  // Make a copy because it may change before insertion
173  auto insert = v;
174 
175  // Handle turn change
176  if (v.moves_left <= 0) {
177  // Make a probe
178  auto probe = unit;
179  v.fill_probe(probe);
180 
181  // FIXME The order could be important here: fuel before HP or HP before
182  // fuel?
183  insert.turns++;
184  insert.moves_left = unit_move_rate(&probe);
185 
186  // Fuel
187  if (utype_fuel(probe.utype)) {
188  if (is_unit_being_refueled(&probe)) {
189  // Refuel
190  probe.fuel = utype_fuel(probe.utype);
191  insert.fuel_left = probe.fuel;
192  } else if (probe.fuel <= 1) {
193  // The unit dies, don't generate a new vertex
194  return;
195  } else {
196  // Consume fuel
197  probe.fuel--;
198  insert.fuel_left--;
199  }
200  }
201 
202  // HP loss and recovery
203  // Userful for helis, killunhomed, slow damaged units with fuel. A path
204  // can require that the unit heals first. Of course, it will choose
205  // barracks if you have them, because healing is faster there.
206  unit_restore_hitpoints(&probe);
207  if (probe.hp <= 0) {
208  // Unit dies, don't let the user send it there.
209  return;
210  }
211  insert.health = probe.hp;
212 
213  // "Start of turn" part
214  insert.moved = false; // Didn't move yet
215  insert.paradropped = false; // Didn't paradrop yet
216  }
217 
218  // Did we just reach a waypoint?
219  if (insert.waypoints < waypoints.size()
220  && insert.location == waypoints[insert.waypoints]) {
221  insert.waypoints++;
222  }
223 
224  // The remaining logic checks whether we should insert the new vertex. This
225  // is complicated because the new candidate may be better than one or
226  // several of the previous paths to the same tile. Also do some bookkeeping
227  // so we only insert the new cost if it isn't already there.
228  const auto [begin, end] = best_vertices.equal_range(v.location);
229  bool do_insert = true;
230  for (auto it = begin; it != end; /* in loop body */) {
231  const bool comparable = it->second->comparable(insert);
232  if (comparable && *it->second > insert) {
233  // The new candidate is strictly better. Remove the old one
234  it = best_vertices.erase(it);
235  continue; // ++it is done inside erase()
236  } else if (comparable) {
237  // We already have it (or something equivalent, or even something
238  // better), no need to add it.
239  do_insert = false;
240  break;
241  }
242  ++it;
243  }
244 
245  // Insert the new cost if needed
246  if (do_insert) {
247  queue.push(insert);
248  best_vertices.emplace(v.location,
249  std::make_unique<detail::vertex>(insert));
250  }
251 }
252 
257  const destination &destination, const detail::vertex &v) const
258 {
259  // Check that we went through every waypoint
260  if (v.waypoints != waypoints.size()) {
261  return false;
262  }
263 
264  // We've just arrived.
265  if (destination.reached(v)) {
266  return true;
267  }
268 
269  return false;
270 }
271 
277 {
278  // Don't attempt to move loaded units
279  if (source.loaded) {
280  return;
281  }
282 
283  // Make a probe
284  auto probe = unit;
285  source.fill_probe(probe);
286 
287  // Try moving to adjacent tiles
288  adjc_dir_iterate(&(wld.map), source.location, target, dir)
289  {
290  bool can_move;
291  int move_cost;
292  if (tile_get_known(target, unit_owner(&probe)) == TILE_UNKNOWN) {
293  // Try to move into the unknown
294  can_move = true;
295  move_cost = probe.utype->unknown_move_cost;
296  } else {
297  can_move =
298  unit_can_move_to_tile(&wld.map, &probe, target, false, false);
299  move_cost = map_move_cost_unit(&wld.map, &probe, target);
300  }
301  if (can_move) {
302  move_cost = std::min(move_cost, probe.moves_left);
303 
304  // Construct the next vertex
305  auto next = source;
306  next.location = target;
307  next.moved = true;
308  next.moves_left -= move_cost;
309  next.parent = &source;
310  next.order.order = ORDER_MOVE;
311  next.order.dir = dir;
312  maybe_insert_vertex(next);
313  }
314  }
316 }
317 
324  detail::vertex &source)
325 {
326  auto next = source;
327  next.moves_left = 0; // Trigger end-of-turn logic
328  next.parent = &source;
329  next.order.order = ORDER_FULL_MP;
330  maybe_insert_vertex(next);
331 }
332 
338 {
339  // Make a probe
340  auto probe = unit;
341  source.fill_probe(probe);
342 
343  // Try to load into a transport -- even if we're already in a transport
344  // Same tile (maybe we can recover HP)
345  if (auto transport = transporter_for_unit(&probe);
346  transport != nullptr
347  && is_action_enabled_unit_on_unit(ACTION_TRANSPORT_BOARD, &probe,
348  transport)) {
349  auto next =
350  source.child_for_action(ACTION_TRANSPORT_BOARD, probe, probe.tile);
351  next.loaded = transport;
352  maybe_insert_vertex(next);
353  }
354 
355  // Nearby tiles
356  adjc_iterate(&(wld.map), probe.tile, target)
357  {
358  probe.tile = target;
359  auto transport = transporter_for_unit(&probe);
360  // Reset the probe -- needed now because is_action_enabled_unit_on_unit
361  // checks the range
362  probe.tile = source.location;
363  if (transport != nullptr
364  && is_action_enabled_unit_on_unit(ACTION_TRANSPORT_EMBARK, &probe,
365  transport)) {
366  auto next =
367  source.child_for_action(ACTION_TRANSPORT_EMBARK, probe, target);
368  // See unithand.cpp:do_unit_embark
369  next.moves_left -= map_move_cost_unit(&(wld.map), &probe, target);
370  next.moved = true;
371  next.loaded = transport;
372  maybe_insert_vertex(next);
373  }
374  }
376 }
377 
383 {
384  // Make a probe
385  auto probe = unit;
386  source.fill_probe(probe);
387 
388  // Try to unload from a transport -- but only if we're already loaded
389  if (probe.transporter != nullptr) {
390  // Same tile
391  if (is_action_enabled_unit_on_unit(ACTION_TRANSPORT_ALIGHT, &probe,
392  probe.transporter)) {
393  auto next = source.child_for_action(ACTION_TRANSPORT_ALIGHT, probe,
394  probe.tile);
395  next.loaded = nullptr;
396  maybe_insert_vertex(next);
397  }
398 
399  // Nearby tiles
400  adjc_iterate(&(wld.map), probe.tile, target)
401  {
402  // Thanks sveinung
403  for (auto action :
404  {ACTION_TRANSPORT_DISEMBARK1, ACTION_TRANSPORT_DISEMBARK2}) {
405  if (is_action_enabled_unit_on_tile(action, &probe, target,
406  nullptr)) {
407  auto next = source.child_for_action(action, probe, target);
408  next.moved = true;
409  // See unithand.cpp:do_disembark
410  next.moves_left -= map_move_cost_unit(&(wld.map), &probe, target);
411 
412  if (can_unit_survive_at_tile(&(wld.map), &probe, target)) {
413  next.loaded = nullptr;
414  } else {
415  // Unit need to load in a transport to survive
416  // FIXME Should consider some action enabler here... Server side
417  // code doesn't do it.
418  next.loaded = transporter_for_unit_at(&probe, target);
419  }
420 
421  maybe_insert_vertex(next);
422  }
423  }
424  }
426  }
427 }
428 
434  detail::vertex &source)
435 {
436  // Make a probe
437  auto probe = unit;
438  source.fill_probe(probe);
439 
440  // Try to paradrop -- if we can at all
441  if (!probe.paradropped
442  && utype_can_do_action(probe.utype, ACTION_PARADROP)) {
443  // Get action details
444  auto action = action_by_number(ACTION_PARADROP);
445 
446  // circle_dxyr_iterate will take the square root of this
447  auto sq_radius =
448  std::min(action->max_distance, probe.utype->paratroopers_range);
449  sq_radius *= sq_radius;
450 
451  // circle_dxyr_iterate does some magic with the name of the center tile
452  // variable, make sure it will works
453  auto tile = probe.tile;
454 
455  // Iterate over reachable tiles
456  circle_dxyr_iterate(&(wld.map), tile, sq_radius, target, _dx, _dy,
457  distance)
458  {
459  if (distance >= action->min_distance * action->min_distance) {
460  // Paradrop has many hard requirements, see unittools.cpp:do_paradrop
461  // The target tile must be seen or be a native tile
462  if (TILE_KNOWN_SEEN != tile_get_known(target, probe.owner)
463  && !is_native_tile(probe.utype, target)) {
464  continue;
465  }
466 
467  // Don't drown in the ocean (but allow dropping into ships)
468  if (!can_unit_exist_at_tile(&(wld.map), &probe, target)
469  && (!game.info.paradrop_to_transport
470  || !unit_could_load_at(&probe, target))) {
471  continue;
472  }
473 
474  // We must be allowed to go to the target tile (not at peace with
475  // potential owner)
476  if (target->owner
477  && pplayers_non_attack(probe.owner, target->owner)) {
478  continue;
479  }
480 
481  // Don't paradrop on top of other units or cities
482  if (is_non_allied_unit_tile(target, probe.owner)) {
483  continue;
484  }
485 
486  // Don't paradrop to non allied cities
487  if (tile_city(target) && !is_allied_city_tile(target, probe.owner)) {
488  continue;
489  }
490 
491  // Soft requirements
492  if (!is_action_enabled_unit_on_tile(ACTION_PARADROP, &probe, target,
493  nullptr)) {
494  continue;
495  }
496 
497  auto next = source.child_for_action(ACTION_PARADROP, probe, target);
498  next.location = target;
499  next.paradropped = true;
500  next.moved = true;
501  next.loaded = nullptr;
502  maybe_insert_vertex(next);
503  }
504  }
506  }
507 }
508 
515  detail::vertex &source)
516 {
517  // Make a probe
518  auto probe = unit;
519  source.fill_probe(probe);
520 
521  // Try action-moving to adjacent tiles
522  adjc_dir_iterate(&(wld.map), source.location, target, dir)
523  {
524  if (target->terrain == nullptr) {
525  // Can't see this tile
526  continue;
527  }
528 
529  // Only search for actions on tiles with cities or visible stacks. This
530  // matches the old client behavior and allows us to skip the (extremely
531  // slow) search for actions most of the times.
532  if (!tile_city(target) && unit_list_size(target->units) == 0) {
533  continue;
534  }
535 
536  // See unithand.cpp:unit_move_handling
537  const bool can_not_move =
538  !unit_can_move_to_tile(&(wld.map), &probe, target, false, false);
539 
540  const bool one_action_may_be_legal =
541  action_tgt_unit(&probe, target, can_not_move)
542  || action_tgt_city(&probe, target, can_not_move)
543  // A legal action with an extra sub target is a legal action
544  || action_tgt_tile_extra(&probe, target, can_not_move)
545  // Tile target actions with extra sub targets are handled above
546  // Includes actions vs unit stacks
547  || action_tgt_tile(&probe, target, nullptr, can_not_move);
548 
549  if (one_action_may_be_legal) {
550  // Construct the vertex
551  auto next = source;
552  next.location = target;
553  next.moved = true;
554  next.is_final = true;
555  next.parent = &source;
557  next.order.dir = dir;
558  maybe_insert_vertex(next);
559  }
560  }
562 }
563 
575  const destination &destination, bool full)
576 {
577  // Check if we've already found a path (but keep searching if the tip of
578  // the queue is cheaper: we haven't checked every possibility).
579  if (auto it = destination.find_best(best_vertices, waypoints.size());
580  it != best_vertices.end()
581  && !(!queue.empty() && *it->second > queue.top())) {
582  return true;
583  }
584 
585  // What follows is an implementation of Dijkstra's path finding algorithm.
586  while (!queue.empty()) {
587  // Get the "best" vertex
588  const auto v = queue.top();
589 
590  // Check if we just arrived
591  // Keep the node in the queue so adjacent nodes are generated if the
592  // search needs to be expanded later.
593  if (!full && is_reached(destination, v)) {
594  return true;
595  }
596 
597  queue.pop(); // Remove it from the queue
598 
599  // An equivalent (or better) vertex may already have been processed.
600  // Check that we have one of the "current best" vertices for that tile.
601  const auto [begin, end] = best_vertices.equal_range(v.location);
602  const auto it = std::find_if(
603  begin, end, [&v](const auto &pair) { return *pair.second == v; });
604  if (it == end) {
605  // Not found, we processed something else in the meantime. Since we
606  // processed it earlier, that path was at least as short.
607  continue;
608  }
609 
610  if (!v.is_final) {
611  // Fetch the pointer version of v for use as a parent
612  auto parent = it->second.get();
613 
614  // Generate vertices starting from this one
615  attempt_move(*parent);
616  attempt_full_mp(*parent);
617  attempt_load(*parent);
618  attempt_unload(*parent);
619  attempt_paradrop(*parent);
620  attempt_action_move(*parent);
621  }
622  }
623 
624  return false;
625 }
626 
632 {
633  best_vertices.clear();
634  while (!queue.empty()) {
635  queue.pop();
636  }
637  insert_initial_vertex();
638 }
639 
647  : path_finder(unit, detail::vertex::from_unit(*unit))
648 {
649 }
650 
659  : m_d(std::make_unique<path_finder_private>(
660  unit, detail::vertex{init, nullptr}))
661 {
662 }
663 
668 {
669  // Make sure that the destructor of path_finder_private is known.
670 }
671 
679  std::unique_ptr<step_constraint> &&constraint)
680 {
681  m_d->constraint = std::move(constraint);
682  m_d->reset();
683 }
684 
691 void path_finder::push_waypoint(const tile *location)
692 {
693  if (m_d->waypoints.empty() || location != m_d->waypoints.back()) {
694  m_d->waypoints.push_back(location);
695 
696  // We can try to be smarter later. For now, just invalidate everything.
697  m_d->reset();
698  }
699 }
700 
707 {
708  // Nothing to to.
709  if (m_d->waypoints.empty()) {
710  return false;
711  }
712 
713  m_d->waypoints.pop_back();
714 
715  // We can try to be smarter later. For now, just invalidate everything.
716  m_d->reset();
717 
718  return true;
719 }
724 void path_finder::unit_changed(const ::unit &unit)
725 {
726  Q_UNUSED(unit);
727 
728  // We can try to be smarter later. For now, just invalidate everything.
729  m_d->best_vertices.clear();
730  while (!m_d->queue.empty()) {
731  m_d->queue.pop();
732  }
733  m_d->insert_initial_vertex();
734 }
735 
745 {
746  // Unit frozen by scenario
747  if (m_d->unit.stay) {
748  return {};
749  }
750 
751  m_d->run_search(destination, true);
752 
753  // Collect results.
754  auto ret = std::vector<path>();
755  ret.reserve(m_d->best_vertices.size());
756 
757  for (const auto &[_, end] : m_d->best_vertices) {
758  // Only use vertices at the destination
759  if (!m_d->is_reached(destination, *end)) {
760  continue;
761  }
762 
763  // Build a path
764  auto steps = std::vector<path::step>();
765  for (auto vertex = end.get(); vertex->parent != nullptr;
766  vertex = vertex->parent) {
767  steps.push_back(*vertex);
768  }
769 
770  ret.emplace_back(std::vector<path::step>(steps.rbegin(), steps.rend()));
771  }
772 
773  return ret;
774 }
775 
779 std::optional<path> path_finder::find_path(const destination &destination)
780 {
781  // Unit frozen by scenario
782  if (m_d->unit.stay) {
783  return std::nullopt;
784  }
785 
786  // Check if we're already at the destination
787  if (m_d->waypoints.empty() && destination.reached(m_d->initial_vertex)) {
788  return path();
789  }
790 
791  if (m_d->run_search(destination)) {
792  // Find the best path. We may have several vertices, so select the one
793  // with the lowest cost.
794  const auto it =
795  destination.find_best(m_d->best_vertices, m_d->waypoints.size());
796 
797  // If run_search returned true, we should always have something. But
798  // better check anyway.
799  fc_assert_ret_val(it != m_d->best_vertices.end(), std::nullopt);
800 
801  // Build a path
802  auto steps = std::vector<path::step>();
803  for (auto vertex = it->second.get(); vertex->parent != nullptr;
804  vertex = vertex->parent) {
805  steps.push_back(*vertex);
806  }
807 
808  return path(std::vector<path::step>(steps.rbegin(), steps.rend()));
809  } else {
810  return std::nullopt;
811  }
812 }
813 
818 path_finder::storage_type::const_iterator
820  std::size_t num_waypoints) const
821 {
822  auto best = map.end();
823  for (auto it = map.begin(); it != map.end(); ++it) {
824  // Is this vertex a destination?
825  if (it->second->waypoints == num_waypoints && reached(*it->second)) {
826  // Is it better than the current `best'?
827  if (best == map.end() || *best->second > *it->second) {
828  best = it;
829  }
830  }
831  }
832  return best;
833 }
834 
838 bool tile_destination::reached(const detail::vertex &vertex) const
839 {
840  return vertex.location == m_destination;
841 }
842 
848 path_finder::storage_type::const_iterator
850  std::size_t num_waypoints) const
851 {
852  auto best = map.end();
853  const auto [begin, end] = map.equal_range(m_destination);
854  for (auto it = begin; it != end; ++it) {
855  // Is this vertex a destination?
856  if (it->second->waypoints == num_waypoints && reached(*it->second)) {
857  // Is it better than the current `best'?
858  if (best == map.end() || *best->second > *it->second) {
859  best = it;
860  }
861  }
862  }
863  return best;
864 }
865 
870 {
871  return is_allied_city_tile(vertex.location, m_player);
872 }
873 
878 {
879  // "final" vertices currently only include ORDER_ACTION_MOVE. Try to find
880  // why this was generated: reject refueling if it wasn't because of an
881  // allied city or unit.
882  if (vertex.is_final && !is_allied_unit_tile(vertex.location, m_unit.owner)
883  && !is_allied_city_tile(vertex.location, m_unit.owner)) {
884  return false;
885  }
886 
887  // Get a probe
888  auto probe = m_unit;
889  vertex.fill_probe(probe);
890 
891  return can_unit_survive_at_tile(&(wld.map), &probe, vertex.location);
892 }
893 
898 {
899  return tile_get_known(step.location, m_player) != TILE_UNKNOWN;
900 }
901 
902 } // namespace freeciv
bool is_action_enabled_unit_on_unit(const action_id wanted_action, const struct unit *actor_unit, const struct unit *target_unit)
Returns TRUE if actor_unit can do wanted_action to target_unit as far as action enablers are concerne...
Definition: actions.cpp:4007
struct extra_type * action_tgt_tile_extra(const struct unit *actor, const struct tile *target_tile, bool accept_all_actions)
Find an extra to target for an action at the specified tile.
Definition: actions.cpp:7191
struct unit * action_tgt_unit(struct unit *actor, struct tile *target_tile, bool accept_all_actions)
Find a unit to target for an action at the specified tile.
Definition: actions.cpp:7050
struct city * action_tgt_city(struct unit *actor, struct tile *target_tile, bool accept_all_actions)
Find a city to target for an action on the specified tile.
Definition: actions.cpp:6983
struct tile * action_tgt_tile(struct unit *actor, struct tile *target, const struct extra_type *target_extra, bool accept_all_actions)
Returns the tile iff it, from the point of view of the owner of the actor unit, looks like a target t...
Definition: actions.cpp:7078
bool is_action_enabled_unit_on_tile(const action_id wanted_action, const struct unit *actor_unit, const struct tile *target_tile, const struct extra_type *target_extra)
Returns TRUE if actor_unit can do wanted_action to the target_tile as far as action enablers are conc...
Definition: actions.cpp:4136
struct action * action_by_number(action_id act_id)
Return the action with the given id.
Definition: actions.cpp:1149
struct city * is_allied_city_tile(const struct tile *ptile, const struct player *pplayer)
Is there an friendly city on this tile?
Definition: city.cpp:1938
bool reached(const detail::vertex &vertex) const override
Checks if a vertex should be considered as being at the destination.
Abstraction for path finding destinations.
Definition: path_finder.h:148
virtual path_finder::storage_type::const_iterator find_best(const path_finder::storage_type &map, std::size_t num_waypoints) const
Returns an iterator to the best vertex that is a destination vertex.
virtual bool reached(const detail::vertex &vertex) const =0
Checks if a vertex should be considered as being at the destination.
void reset()
Resets the state of the path finder.
bool run_search(const destination &destination, bool full=false)
Runs the path finding seach until the stopping condition is met (the destination tile is reached).
void attempt_action_move(detail::vertex &source)
Opens vertices corresponding to attempts to do ORDER_ACTION_MOVE from the source vertex.
void insert_initial_vertex()
Inserts the initial vertex, from which the search will be started.
void attempt_load(detail::vertex &source)
Opens vertices corresponding to attempts to load into a transport from the source vertex.
void attempt_move(detail::vertex &source)
Opens vertices corresponding to attempts to do ORDER_MOVE from the source vertex.
void attempt_paradrop(detail::vertex &source)
Opens vertices corresponding to attempts to unload from a transport at the source vertex.
bool is_reached(const destination &destination, const detail::vertex &v) const
Checks if a vertex is at the destination, taking waypoints into account.
void attempt_unload(detail::vertex &source)
Opens vertices corresponding to attempts to unload from a transport at the source vertex.
path_finder_private(const unit *unit, const detail::vertex &init)
Constructor.
void maybe_insert_vertex(const detail::vertex &v)
Saves a new vertex for further processing if it is better than the current vertex at the same locatio...
void attempt_full_mp(detail::vertex &source)
Opens vertices corresponding to attempts to do ORDER_FULL_MP from the source vertex.
A path is a succession of moves and actions to go from one location to another.
Definition: path_finder.h:55
bool pop_waypoint()
Removes the last added waypoint.
void set_constraint(std::unique_ptr< step_constraint > &&constraint)
Sets a constraint to limit the number of steps considered when trying to find a path.
virtual ~path_finder()
Destructor.
std::vector< path > find_all(const destination &destination)
Runs the path finding algorithm, searching for all paths leading to the destination.
void unit_changed(const unit &unit)
Notifies the path finder that some unit died or changed state.
std::unique_ptr< path_finder_private > m_d
Definition: path_finder.h:129
std::multimap< const tile *, std::unique_ptr< detail::vertex > > storage_type
The type of the underlying storage, exposed through destination.
Definition: path_finder.h:63
void push_waypoint(const tile *location)
Adds a waypoint to the path finding.
path_finder(const unit *unit)
Constructs a path_finder for the given unit.
std::optional< path > find_path(const destination &destination)
Runs the path finding algorithm.
A path is a succession of moves and actions to go from one location to another.
Definition: path.h:18
bool reached(const detail::vertex &vertex) const override
Checks if a vertex should be considered as being at the destination.
const tile * m_destination
Definition: path_finder.h:196
bool reached(const detail::vertex &vertex) const override
Checks if a vertex should be considered as being at the destination.
path_finder::storage_type::const_iterator find_best(const path_finder::storage_type &map, std::size_t num_waypoints) const override
Returns an iterator to the best vertex that is a destination vertex.
bool is_allowed(const path::step &step) const override
Whether a step should be used in the search.
void unit_restore_hitpoints(struct unit *punit)
add hitpoints to the unit, hp_gain_coord returns the amount to add united nations will speed up the p...
Definition: unit_utils.cpp:45
#define NO_TARGET
Definition: fc_types.h:271
#define DIR8_ORIGIN
Definition: fc_types.h:372
int action_id
Definition: fc_types.h:306
#define _(String)
Definition: fcintl.h:50
struct civ_game game
Definition: game.cpp:47
struct world wld
Definition: game.cpp:48
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
#define adjc_dir_iterate(nmap, center_tile, itr_tile, dir_itr)
Definition: map.h:364
#define adjc_iterate_end
Definition: map.h:358
#define circle_dxyr_iterate(nmap, center_tile, sq_radius, _tile, dx, dy, dr)
Definition: map.h:329
#define adjc_iterate(nmap, center_tile, itr_tile)
Definition: map.h:351
#define circle_dxyr_iterate_end
Definition: map.h:342
static int map_move_cost_unit(const struct civ_map *nmap, struct unit *punit, const struct tile *ptile)
Definition: map.h:221
#define adjc_dir_iterate_end
Definition: map.h:368
bool init
Definition: mapimg.cpp:328
bool can_unit_exist_at_tile(const struct civ_map *nmap, const struct unit *punit, const struct tile *ptile)
Return TRUE iff the unit can "exist" at this location.
Definition: movement.cpp:267
bool is_native_tile(const struct unit_type *punittype, const struct tile *ptile)
This tile is native to unit.
Definition: movement.cpp:279
int unit_move_rate(const struct unit *punit)
This function calculates the move rate of the unit.
Definition: movement.cpp:78
bool unit_could_load_at(const struct unit *punit, const struct tile *ptile)
Return whether we could find a suitable transporter for given unit at 'ptile'.
Definition: movement.cpp:743
bool unit_can_move_to_tile(const struct civ_map *nmap, const struct unit *punit, const struct tile *dst_tile, bool igzoc, bool enter_enemy_city)
Returns whether the unit can move from its current tile to the destination tile.
Definition: movement.cpp:531
bool can_unit_survive_at_tile(const struct civ_map *nmap, const struct unit *punit, const struct tile *ptile)
Return TRUE iff the unit can "survive" at this location.
Definition: movement.cpp:452
bool is_unit_being_refueled(const struct unit *punit)
Is unit being refueled in its current position.
Definition: movement.cpp:759
Definition: path.cpp:10
bool pplayers_non_attack(const struct player *pplayer, const struct player *pplayer2)
Returns true iff players have peace, cease-fire, or armistice.
Definition: player.cpp:1388
int step
Definition: specpq.h:83
int max_distance
Definition: actions.h:320
int min_distance
Definition: actions.h:320
struct packet_game_info info
Definition: game.h:80
A vertex in the path-finding graph.
Definition: path_finder.h:31
vertex child_for_action(action_id action, const unit &probe, tile *target)
Creates a vertex starting from this one and executing an action.
Definition: path_finder.cpp:43
void fill_probe(unit &probe) const
Ensures that probe reflects the properties of this vertex.
bool operator>(const vertex &other) const
Defines an ordering for the priority queue.
bool comparable(const vertex &other) const
Checks whether two vertices are comparable, which is the case when one of them is unambiguously "bett...
Definition: path_finder.cpp:77
vertex * parent
The previous vertex, if any.
Definition: path_finder.h:34
static vertex from_unit(const unit &unit)
Creates a vertex representing the state of a unit.
Definition: path_finder.cpp:27
bool operator==(const vertex &other) const
Equality comparator.
unit * loaded
The unit we are loaded in.
Definition: path.h:23
int turns
How many turns it takes to get there.
Definition: path.h:30
int waypoints
How many waypoints we have visited so far.
Definition: path.h:27
int health
How many HP the unit has left.
Definition: path.h:32
unit_order order
The order to come here.
Definition: path.h:35
tile * location
Where we are.
Definition: path.h:22
bool moved
Whether we moved this turn (for HP recovery)
Definition: path.h:24
int fuel_left
How much fuel the unit has left.
Definition: path.h:33
bool paradropped
Whether we paradropped this turn.
Definition: path.h:25
int moves_left
How many move fragments the unit has left.
Definition: path.h:31
bool is_final
Whether this vertex can have children.
Definition: path.h:26
Definition: tile.h:42
int index
Definition: tile.h:43
enum unit_orders order
Definition: unit.h:80
Definition: unit.h:134
enum unit_activity activity
Definition: unit.h:154
int moves_left
Definition: unit.h:147
struct unit::@76::@78 client
int id
Definition: unit.h:141
bool moved
Definition: unit.h:170
int hp
Definition: unit.h:148
int fuel
Definition: unit.h:150
struct tile * tile
Definition: unit.h:136
bool paradropped
Definition: unit.h:171
struct unit * transporter
Definition: unit.h:180
const struct unit_type * utype
Definition: unit.h:135
struct player * owner
Definition: unit.h:139
struct civ_map map
Definition: world_object.h:21
enum known_type tile_get_known(const struct tile *ptile, const struct player *pplayer)
Return a known_type enumeration value for the tile.
Definition: tile.cpp:398
struct city * tile_city(const struct tile *ptile)
Return the city on this tile (or nullptr), checking for city center.
Definition: tile.cpp:72
@ TILE_UNKNOWN
Definition: tile.h:29
@ TILE_KNOWN_SEEN
Definition: tile.h:31
struct unit * transporter_for_unit(const struct unit *pcargo)
Find the best transporter at the given location for the unit.
Definition: unit.cpp:1779
int unit_pays_mp_for_action(const struct action *paction, const struct unit *punit)
Returns the amount of movement points successfully performing the specified action will consume in th...
Definition: unit.cpp:1973
struct unit * is_allied_unit_tile(const struct tile *ptile, const struct player *pplayer)
Returns true if the tile contains an allied unit and only allied units.
Definition: unit.cpp:1211
struct unit * is_non_allied_unit_tile(const struct tile *ptile, const struct player *pplayer)
Is there an non-allied unit on this tile?
Definition: unit.cpp:1252
struct unit * transporter_for_unit_at(const struct unit *pcargo, const struct tile *ptile)
Find the best transporter at the given location for the unit.
Definition: unit.cpp:1789
@ ORDER_ACTION_MOVE
Definition: unit.h:39
@ ORDER_FULL_MP
Definition: unit.h:37
@ ORDER_MOVE
Definition: unit.h:33
@ ORDER_PERFORM_ACTION
Definition: unit.h:41
#define unit_owner(_pu)
Definition: unit.h:370
bool utype_is_moved_to_tgt_by_action(const struct action *paction, const struct unit_type *utype)
Returns TRUE iff successfully performing the specified action always will move the actor unit of the ...
Definition: unittype.cpp:969
bool utype_can_do_action(const struct unit_type *putype, const action_id act_id)
Return TRUE iff units of the given type can do the specified generalized (ruleset defined) action ena...
Definition: unittype.cpp:386
#define utype_fuel(ptype)
Definition: unittype.h:772