Add a feature to build a cost_map in pathfind.xpp

In a cost_map each hex is mapped to total cost to reach this hex
(for one or multiple units).
Note that the runtime of find_reach() is not really affected
when the parameter full_cost_map is NULL (default).
This commit is contained in:
flix 2013-07-14 18:24:05 +02:00
parent b0aa3691e4
commit 9b87de58b4
3 changed files with 204 additions and 1 deletions

View file

@ -23,6 +23,7 @@ Version 1.11.5+dev:
effect or Follow Unit Actions is disabled in Advanced Preferences.
* [move_unit] accepts an optional force_scroll= attribute like
[move_unit_fake] above, defaults to using the [move_unit_fake] default.
* Added: Feature in pathfind.xpp to build a cost map.
Version 1.11.5:
* Add-ons client:

View file

@ -256,6 +256,10 @@ namespace {
* (No effect if teleporter and current_team are both NULL.)
* @param[in] jamming_map The relevant "jamming" of the costs being used
* (currently only used with vision costs).
* @param[out] full_cost_map If not NULL, build a cost_map instead of destinations.
* Destinations is ignored.
* full_cost_map is a vector of pairs. The first entry is the
* cost itself, the second how many units already visited this hex
*/
static void find_routes(
const map_location & origin, const movetype::terrain_costs & costs,
@ -263,7 +267,8 @@ static void find_routes(
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=NULL)
const std::map<map_location, int> * jamming_map=NULL,
std::vector<std::pair<int, int> > * full_cost_map=NULL)
{
const gamemap& map = *resources::game_map;
@ -293,6 +298,18 @@ static void find_routes(
findroute_comp node_comp(nodes);
findroute_indexer index(map.w(), map.h());
// Check if full_cost_map has the correct size.
// If not, ignore it. If yes, initialize the start position.
if ( full_cost_map ) {
if ( full_cost_map->size() != static_cast<unsigned>(map.w() * map.h()) )
full_cost_map = NULL;
else {
if ( (*full_cost_map)[index(origin)].second == 0 )
(*full_cost_map)[index(origin)].first = 0;
(*full_cost_map)[index(origin)].second += 1;
}
}
// Used to optimize the final collection of routes.
int xmin = origin.x, xmax = origin.x, ymin = origin.y, ymax = origin.y;
int nb_dest = 1;
@ -386,6 +403,15 @@ static void find_routes(
}
}
// Update full_cost_map
if ( full_cost_map ) {
if ( (*full_cost_map)[next_index].second == 0 )
(*full_cost_map)[next_index].first = 0;
int summed_cost = (turns_left - next.turns_left + 1) * max_moves - next.moves_left;
(*full_cost_map)[next_index].first += summed_cost;
(*full_cost_map)[next_index].second += 1;
}
// Mark next as being collected.
next.search_num = search_counter;
@ -406,6 +432,12 @@ static void find_routes(
}//for (i)
}//while (hexes_to_process)
// Currently the only caller who uses full_cost_map doesn't need the
// destinations. We can skip this part.
if ( full_cost_map ) {
return;
}
// Build the routes for every map_location that we reached.
// The ordering must be compatible with map_location::operator<.
destinations.reserve(nb_dest);
@ -799,5 +831,130 @@ double dummy_path_calculator::cost(const map_location&, const double) const
return 1.0;
}
/**
* Constructs a cost-map. For a unit each hex is mapped to the cost the
* unit will need to reach this hex. Considers movement-loss caused by
* turn changes.
* Can also used with multiple units to accumulate their costs efficiently.
* Will also count how many units could reach a hex for easy normalization.
* @param u the unit
* @param force_ignore_zoc Set to true to completely ignore zones of control.
* @param allow_teleport Set to true to consider teleportation abilities.
* @param viewing_team Usually the current team, except for "show enemy moves", etc.
* @param see_all Set to true to remove unit visibility from consideration.
* @param ignore_units Set to true if units should never obstruct paths (implies ignoring ZoC as well).
*/
full_cost_map::full_cost_map(const unit& u, bool force_ignore_zoc,
bool allow_teleport, const team &viewing_team,
bool see_all, bool ignore_units)
:force_ignore_zoc_(force_ignore_zoc), allow_teleport_(allow_teleport),
viewing_team_(viewing_team), see_all_(see_all), ignore_units_(ignore_units)
{
const gamemap& map = *resources::game_map;
cost_map = std::vector<std::pair<int, int> >(map.w() * map.h(), std::make_pair(-1, 0));
add_unit(u);
}
/**
* Same as other constructor but without unit. Use this when working
* with add_unit().
*/
full_cost_map::full_cost_map(bool force_ignore_zoc,
bool allow_teleport, const team &viewing_team,
bool see_all, bool ignore_units)
:force_ignore_zoc_(force_ignore_zoc), allow_teleport_(allow_teleport),
viewing_team_(viewing_team), see_all_(see_all), ignore_units_(ignore_units)
{
const gamemap& map = *resources::game_map;
cost_map = std::vector<std::pair<int, int> >(map.w() * map.h(), std::make_pair(-1, 0));
}
/**
* Adds a units cost map to cost_map (increments the elements in cost_map)
* @param u a real existing unit on the map
*/
void full_cost_map::add_unit(const unit& u)
{
std::vector<team> const &teams = *resources::teams;
if (u.side() < 1 || u.side() > int(teams.size())) {
return;
}
// We don't need the destinations, but find_routes() wants to have this parameter
paths::dest_vect dummy = paths::dest_vect();
find_routes(u.get_location(), u.movement_type().get_movement(),
u.get_state(unit::STATE_SLOWED), u.total_movement(),
u.total_movement(), 99, dummy, NULL,
allow_teleport_ ? &u : NULL,
ignore_units_ ? NULL : &teams[u.side()-1],
force_ignore_zoc_ ? NULL : &u,
see_all_ ? NULL : &viewing_team_,
NULL, &cost_map);
}
/**
* Adds a units cost map to cost_map (increments the elements in cost_map)
* This function can be used to generate a cost_map with a non existing unit.
* @param origin the location on the map from where the calculations shall start
* @param ut the unit type we are interested in
* @param side the side of the unit. Important for zocs.
*/
void full_cost_map::add_unit(const map_location& origin, const unit_type* const ut, int side)
{
if (!ut) {
return;
}
unit u(*ut, side, false);
u.set_location(origin);
add_unit(u);
}
/**
* Accessor for the cost/reach-amount pairs.
* Read comment in pathfind.hpp to cost_map.
* @param x
* @param y
* @return the entry of the cost_map at (x, y)
* or (-1, 0) if value is not set or (x, y) is invalid.
*/
std::pair<int, int> full_cost_map::get_pair_at(int x, int y) const
{
const gamemap& map = *resources::game_map;
assert(cost_map.size() == static_cast<unsigned>(map.w() * map.h()));
if (x < 0 || x >= map.w() || y < 0 || y >= map.h()) {
return std::make_pair(-1, 0); // invalid
}
return cost_map[x + (y * map.w())];
}
/**
* Accessor for the costs.
* @param x
* @param y
* @return the value of the cost_map at (x, y)
* or -1 if value is not set or (x, y) is invalid.
*/
int full_cost_map::get_cost_at(int x, int y) const
{
return get_pair_at(x, y).first;
}
/**
* Accessor for the costs.
* @param x
* @param y
* @return The average cost of all added units for this hex
* or -1 if no unit can reach the hex.
*/
double full_cost_map::get_average_cost_at(int x, int y) const
{
if (get_pair_at(x, y).second == 0) {
return -1;
} else {
return static_cast<double>(get_pair_at(x, y).first) / get_pair_at(x, y).second;
}
}
}//namespace pathfind

View file

@ -23,6 +23,7 @@
class gamemap;
class team;
class unit;
class unit_type;
#include "map_location.hpp"
#include "movetype.hpp"
@ -256,6 +257,50 @@ struct dummy_path_calculator : cost_calculator
virtual double cost(const map_location& loc, const double so_far) const;
};
/**
* Structure which uses find_routes() to build a cost map
* This maps each hex to a the movements a unit will need to reach
* this hex.
* Can be used commutative by calling add_unit() multiple times.
*/
struct full_cost_map
{
// "normal" constructor
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);
// constructor for work with add_unit()
// same as "normal" constructor. Just without unit
full_cost_map(bool force_ignore_zoc,
bool allow_teleport, const team &viewing_team,
bool see_all=true, bool ignore_units=true);
void add_unit(const unit& u);
void add_unit(const map_location& origin, const unit_type* const unit_type, int side);
int get_cost_at(int x, int y) const;
std::pair<int, int> get_pair_at(int x, int y) const;
double get_average_cost_at(int x, int y) const;
virtual ~full_cost_map()
{
};
// This is a vector of pairs
// Every hex has an entry.
// The first int is the accumulated cost for one or multiple units
// It is -1 when no unit can reach this hex.
// The second int is how many units can reach this hex.
// (For some units some hexes or even a whole regions are unreachable)
// To calculate a *average cost map* it is recommended to divide first/second.
std::vector<std::pair<int, int> > cost_map;
private:
const bool force_ignore_zoc_;
const bool allow_teleport_;
const team &viewing_team_;
const bool see_all_;
const bool ignore_units_;
};
}
#endif