Reduced memory consumption of Dijkstra's solution...

...from quadratic to linear with respect to movement points.
This commit is contained in:
Guillaume Melquiond 2009-05-02 12:22:28 +00:00
parent 89842480b5
commit 2632d77ea6
9 changed files with 144 additions and 130 deletions

View File

@ -19,6 +19,7 @@
#include "ai/testing.hpp"
#include "attack_prediction.hpp"
#include "foreach.hpp"
#include "game_display.hpp"
#include "game_end_exceptions.hpp"
#include "game_events.hpp"
@ -2102,9 +2103,8 @@ namespace {
}
paths p(map,units,loc,teams,true,false,teams[team],0,false,true);
for(paths::routes_map::const_iterator i = p.routes.begin();
i != p.routes.end(); ++i) {
clear_shroud_loc(map,teams[team],i->first,&cleared_locations);
foreach (const paths::step &dest, p.destinations) {
clear_shroud_loc(map, teams[team], dest.curr, &cleared_locations);
}
// clear_shroud_loc is supposed not introduce repetition in cleared_locations

View File

@ -336,23 +336,22 @@ bool ai::multistep_move_possible(const location& from,
LOG_AI << "found leader moves..\n";
int move_left = 0;
// See if the unit can make it to 'via', and if it can,
// how much movement it will have left when it gets there.
const paths::routes_map::const_iterator itor = moves->second.routes.find(via);
if(itor != moves->second.routes.end()) {
move_left = itor->second.move_left;
LOG_AI << "can make it to keep with " << move_left << " movement left\n";
paths::dest_vect::const_iterator itor =
moves->second.destinations.find(via);
if (itor != moves->second.destinations.end())
{
LOG_AI << "Can make it to keep with " << itor->move_left << " movement left.\n";
unit temp_unit(i->second);
temp_unit.set_movement(move_left);
temp_unit.set_movement(itor->move_left);
const temporary_unit_placer unit_placer(units_,via,temp_unit);
const paths unit_paths(map_,units_,via,teams_,false,false,current_team());
LOG_AI << "found " << unit_paths.routes.size() << " moves for temp leader\n";
LOG_AI << "Found " << unit_paths.destinations.size() << " moves for temp leader.\n";
// See if this leader could make it back to the keep.
if(unit_paths.routes.count(to) != 0) {
if (unit_paths.destinations.contains(to)) {
LOG_AI << "can make it back to the keep\n";
return true;
}
@ -590,8 +589,6 @@ void ai::do_move()
raise_user_interact();
typedef paths::route route;
typedef std::map<location,paths> moves_map;
moves_map possible_moves, enemy_possible_moves;
@ -1584,12 +1581,12 @@ void ai::move_leader_to_goals( const move_map& enemy_dstsrc)
possible_moves.insert(std::pair<map_location,paths>(leader->first,leader_paths));
map_location loc;
for(std::vector<map_location>::const_iterator itor = route.steps.begin();
itor != route.steps.end(); ++itor) {
if(leader_paths.routes.count(*itor) == 1 &&
power_projection(*itor,enemy_dstsrc) < double(leader->second.hitpoints()/2)) {
loc = *itor;
foreach (const map_location &l, route.steps)
{
if (leader_paths.destinations.contains(l) &&
power_projection(l, enemy_dstsrc) < double(leader->second.hitpoints() / 2))
{
loc = l;
}
}
@ -1629,13 +1626,14 @@ void ai::move_leader_after_recruit(const move_map& /*srcdst*/,
int current_distance = distance_between(i->first,leader->first);
location current_loc;
for(paths::routes_map::const_iterator j = leader_paths.routes.begin();
j != leader_paths.routes.end(); ++j) {
const int distance = distance_between(i->first,j->first);
if(distance < current_distance && is_accessible(j->first,enemy_dstsrc) == false) {
foreach (const paths::step &dest, leader_paths.destinations)
{
const int distance = distance_between(i->first, dest.curr);
if (distance < current_distance &&
!is_accessible(dest.curr, enemy_dstsrc))
{
current_distance = distance;
current_loc = j->first;
current_loc = dest.curr;
}
}
@ -1649,7 +1647,8 @@ void ai::move_leader_after_recruit(const move_map& /*srcdst*/,
const paths p(map_, temp_units, current_loc, teams_, false,
false, current_team());
if(p.routes.count(i->first)) {
if (p.destinations.contains(i->first))
{
move_unit(leader->first,current_loc,possible_moves);
// FIXME: suokko's r29531 included this line
// leader->second.set_movement(0);
@ -1692,10 +1691,10 @@ void ai::move_leader_after_recruit(const move_map& /*srcdst*/,
for(size_t n = 0; n != 6; ++n) {
// Vacate to the first location found that is on the board,
// our leader can move to, and no enemies can reach.
if(map_.on_board(adj[n]) &&
leader_paths.routes.count(adj[n]) != 0 &&
is_accessible(adj[n],enemy_dstsrc) == false) {
if (map_.on_board(adj[n]) &&
leader_paths.destinations.contains(adj[n]) &&
!is_accessible(adj[n], enemy_dstsrc))
{
if (move_unit(keep,adj[n],possible_moves)!=keep) {
return;
}
@ -1738,7 +1737,7 @@ bool ai::leader_can_reach_keep()
const paths leader_paths(map_,units_,leader->first,teams_,false,false,current_team());
return leader_paths.routes.count(start_pos) > 0;
return leader_paths.destinations.contains(start_pos);
}
int ai::rate_terrain(const unit& u, const map_location& loc)

View File

@ -21,6 +21,7 @@
#include "../global.hpp"
#include "ai.hpp"
#include "foreach.hpp"
#include "../gettext.hpp"
#include "../log.hpp"
#include "../map.hpp"
@ -876,11 +877,12 @@ const map_location& ai::suitable_keep(const map_location& leader_location, const
map_location const* best_occupied_keep = &map_location::null_location;
double cost_to_best_occupied_keep = 0.0;
for(std::map<location,paths::route>::const_iterator rt = leader_paths.routes.begin(); rt != leader_paths.routes.end(); ++rt) {
const map_location& loc = rt->first;
foreach (const paths::step &dest, leader_paths.destinations)
{
const map_location &loc = dest.curr;
if (keeps().find(loc)!=keeps().end()){
//@todo 1.7 move_left for 1-turn-moves is really "cost_to_get_there", it is just not renamed there yet. see r34430 for more detais.
const int cost_to_loc = rt->second.move_left;
const int cost_to_loc = dest.move_left;
if (units_.count(loc) == 0) {
if ((*best_free_keep==map_location::null_location)||(cost_to_loc<cost_to_best_free_keep)){
best_free_keep = &loc;
@ -923,8 +925,7 @@ void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
// If the leader is not on keep, move him there.
if(leader->first != keep) {
const paths::routes_map::const_iterator itor = leader_paths.routes.find(keep);
if(itor != leader_paths.routes.end() && units_.count(keep) == 0) {
if (leader_paths.destinations.contains(keep) && units_.count(keep) == 0) {
move_unit(leader->first,keep,possible_moves);
} else {
// Make a map of the possible locations the leader can move to,
@ -934,12 +935,11 @@ void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
// The leader can't move to his keep, try to move to the closest location
// to the keep where there are no enemies in range.
const int current_distance = distance_between(leader->first,keep);
for(paths::routes_map::const_iterator i = leader_paths.routes.begin();
i != leader_paths.routes.end(); ++i) {
const int new_distance = distance_between(i->first,keep);
foreach (const paths::step &dest, leader_paths.destinations)
{
const int new_distance = distance_between(dest.curr,keep);
if(new_distance < current_distance) {
moves_toward_keep.insert(std::pair<int,map_location>(new_distance,i->first));
moves_toward_keep.insert(std::make_pair(new_distance, dest.curr));
}
}

View File

@ -249,31 +249,25 @@ map_location ai_readwrite_context::move_unit_partial(map_location from, map_loca
if(p_it != possible_moves.end()) {
paths& p = p_it->second;
std::map<map_location,paths::route>::iterator rt = p.routes.begin();
for(; rt != p.routes.end(); ++rt) {
if(rt->first == to) {
break;
}
}
paths::dest_vect::const_iterator rt = p.destinations.find(to);
if (rt != p.destinations.end())
{
u_it->second.set_movement(rt->move_left);
if(rt != p.routes.end()) {
if (static_cast<size_t>(u_it->second.movement_left()) >= rt->second.steps.size()) {
LOG_AI<<"Trying to move unit without enough move points left\n";
}
u_it->second.set_movement(rt->second.move_left);
steps = rt->second.steps;
while(steps.empty() == false && get_info().units.find(to) != get_info().units.end() && from != to){
while (rt != p.destinations.end() &&
get_info().units.find(to) != get_info().units.end() && from != to)
{
LOG_AI << "AI attempting illegal move. Attempting to move onto existing unit\n";
LOG_AI << "\t" << get_info().units.find(to)->second.underlying_id() <<" already on " << to << "\n";
LOG_AI <<"\tremoving "<<*(steps.end()-1)<<"\n";
to = *(steps.end()-1);
steps.pop_back();
LOG_AI <<"\tremoving last step\n";
to = rt->prev;
rt = p.destinations.find(to);
LOG_AI << "\tresetting to " << from << " -> " << to << '\n';
}
if(steps.size()) { // First step is starting hex
if (rt != p.destinations.end()) // First step is starting hex
{
steps = p.destinations.get_path(rt);
unit_map::const_iterator utest=get_info().units.find(*(steps.begin()));
if(utest != get_info().units.end() && current_team().is_enemy(utest->second.side())){
ERR_AI << "AI tried to move onto existing enemy unit at" << *steps.begin() << '\n';
@ -441,10 +435,10 @@ void ai_readonly_context::calculate_moves(const unit_map& units, std::map<map_lo
}
for(std::map<map_location,paths>::iterator m = res.begin(); m != res.end(); ++m) {
for(paths::routes_map::iterator rtit =
m->second.routes.begin(); rtit != m->second.routes.end(); ++rtit) {
foreach (const paths::step &dest, m->second.destinations)
{
const map_location& src = m->first;
const map_location& dst = rtit->first;
const map_location& dst = dest.curr;
if(remove_destinations != NULL && remove_destinations->count(dst) != 0) {
continue;

View File

@ -1994,8 +1994,8 @@ map_location formula_ai::path_calculator(const map_location& src, const map_loca
map_location destination = dst;
//check if destination is within unit's reach, if not, calculate where to move
if( path->second.routes.count(dst) == 0) {
if (!path->second.destinations.contains(dst))
{
std::set<map_location> allowed_teleports = get_allowed_teleports(unit_it);
//destination is too far, check where unit can go
@ -2120,7 +2120,7 @@ bool formula_ai::execute_variant(const variant& var, bool commandline)
std::map<map_location,paths>::iterator path = possible_moves_.find(attack->move_from());
if( path->second.routes.count(attack->src()) == 0) {
if (!path->second.destinations.contains(attack->src())) {
ERR_AI << "IMPOSSIBLE ATTACK ORDER\n";
continue;
}

View File

@ -741,11 +741,9 @@ void game_display::highlight_reach(const paths &paths_list)
void game_display::highlight_another_reach(const paths &paths_list)
{
paths::routes_map::const_iterator r;
// Fold endpoints of routes into reachability map.
for (r = paths_list.routes.begin(); r != paths_list.routes.end(); ++r) {
reach_map_[r->first]++;
foreach (const paths::step &dest, paths_list.destinations) {
reach_map_[dest.curr]++;
}
reach_map_changed_ = true;
}

View File

@ -111,7 +111,7 @@ void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update)
if (update) {
if (reachmap_invalid_) {
reachmap_invalid_ = false;
if (!current_paths_.routes.empty() && !show_partial_move_) {
if (!current_paths_.destinations.empty() && !show_partial_move_) {
unit_map::iterator u = find_unit(selected_hex_);
if(selected_hex_.valid() && u != units_.end() ) {
// reselect the unit without firing events (updates current_paths_)
@ -155,7 +155,10 @@ void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update)
&& !selected_unit->second.incapacitated() && !browse) {
if (attack_from.valid()) {
cursor::set(dragging_started_ ? cursor::ATTACK_DRAG : cursor::ATTACK);
} else if (mouseover_unit==units_.end() && current_paths_.routes.count(new_hex)) {
}
else if (mouseover_unit==units_.end() &&
current_paths_.destinations.contains(new_hex))
{
cursor::set(dragging_started_ ? cursor::MOVE_DRAG : cursor::MOVE);
} else {
// selecte unit can't attack or move there
@ -189,9 +192,10 @@ void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update)
if(dest == selected_hex_ || dest_un != units_.end()) {
current_route_.steps.clear();
gui().set_route(NULL);
} else if(!current_paths_.routes.empty() && map_.on_board(selected_hex_) &&
map_.on_board(new_hex)) {
}
else if (!current_paths_.destinations.empty() &&
map_.on_board(selected_hex_) && map_.on_board(new_hex))
{
if(selected_unit != units_.end() && !selected_unit->second.incapacitated()) {
// the movement_reset is active only if it's not the unit's turn
unit_movement_resetter move_reset(selected_unit->second,
@ -205,7 +209,9 @@ void mouse_handler::mouse_motion(int x, int y, const bool browse, bool update)
unit_map::iterator un = mouseover_unit;
if(un != units_.end() && current_paths_.routes.empty() && !gui().fogged(un->first)) {
if (un != units_.end() && current_paths_.destinations.empty() &&
!gui().fogged(un->first))
{
if (un->second.side() != team_num_) {
//unit under cursor is not on our team, highlight reach
unit_movement_resetter move_reset(un->second);
@ -280,7 +286,8 @@ map_location mouse_handler::current_unit_attacks_from(const map_location& loc)
continue;
}
if(current_paths_.routes.count(adj[n])) {
if (current_paths_.destinations.contains(adj[n]))
{
static const size_t NDIRECTIONS = map_location::NDIRECTIONS;
unsigned int difference = abs(int(preferred - n));
if(difference > NDIRECTIONS/2) {
@ -691,6 +698,7 @@ bool mouse_handler::attack_enemy_(unit_map::iterator attacker, unit_map::iterato
void mouse_handler::show_attack_options(unit_map::const_iterator u)
{
#if 0
team& current_team = teams_[team_num_-1];
if(u == units_.end() || u->second.attacks_left() == 0)
@ -702,6 +710,7 @@ void mouse_handler::show_attack_options(unit_map::const_iterator u)
current_paths_.routes[target->first] = paths::route();
}
}
#endif
}
bool mouse_handler::unit_in_cycle(unit_map::const_iterator it)

View File

@ -150,7 +150,7 @@ struct comp {
static void find_routes(const gamemap& map, const unit_map& units,
const unit& u, const map_location& loc,
int move_left, paths::routes_map& routes,
int move_left, paths::dest_vect &destinations,
std::vector<team> const &teams,
bool force_ignore_zocs, bool allow_teleport, int turns_left,
const team &viewing_team,
@ -173,7 +173,7 @@ static void find_routes(const gamemap& map, const unit_map& units,
indexer index(map.w(), map.h());
comp node_comp(nodes);
int xmin = map.w(), xmax = 0, ymin = map.h(), ymax = 0;
int xmin = loc.x, xmax = loc.x, ymin = loc.y, ymax = loc.y;
nodes[index(loc)] = node(move_left, turns_left, map_location::null_location, loc);
std::vector<int> pq;
@ -247,38 +247,67 @@ static void find_routes(const gamemap& map, const unit_map& units,
}
}
}
// build the routes for every map_location that we reached
for (int y = ymin; y <= ymax; ++y) {
for (int x = xmin; x <= xmax; ++x)
// Build the routes for every map_location that we reached.
// The ordering must be compatible with map_location::operator<.
for (int x = xmin; x <= xmax; ++x) {
for (int y = ymin; y <= ymax; ++y)
{
const node &n = nodes[index(map_location(x, y))];
if (n.in - search_counter > 1u) continue;
paths::route route;
route.move_left = n.movement_left + n.turns_left * total_movement;
// the ai expects that the destination map_location not actually be in the route...
if (n.prev.valid())
{
for (const node *curr = &nodes[index(n.prev)];
curr->prev.valid(); curr = &nodes[index(curr->prev)])
{
assert(curr->curr.valid());
route.steps.push_back(curr->curr);
}
}
route.steps.push_back(loc);
std::reverse(route.steps.begin(), route.steps.end());
routes[n.curr] = route;
paths::step s =
{ n.curr, n.prev, n.movement_left + n.turns_left * total_movement };
destinations.push_back(s);
}
}
}
paths::dest_vect::const_iterator paths::dest_vect::find(const map_location &loc) const
{
size_t sz = size(), pos = 0;
while (sz)
{
if ((*this)[pos + sz / 2].curr < loc) {
pos = pos + sz / 2 + 1;
sz = sz - sz / 2 - 1;
} else sz = sz / 2;
}
const_iterator i_end = end(), i = begin() + pos;
if (i != i_end && i->curr != loc) i = i_end;
return i;
}
/**
* Returns the path going from the source point (included) to the
* destination point @a j (excluded).
*/
std::vector<map_location> paths::dest_vect::get_path(const const_iterator &j) const
{
std::vector<map_location> path;
if (!j->prev.valid()) {
path.push_back(j->curr);
} else {
const_iterator i = j;
do {
i = find(i->prev);
assert(i != end());
path.push_back(i->curr);
} while (i->prev.valid());
}
std::reverse(path.begin(), path.end());
return path;
}
bool paths::dest_vect::contains(const map_location &loc) const
{
return find(loc) != end();
}
paths::paths(gamemap const &map, unit_map const &units,
map_location const &loc, std::vector<team> const &teams,
bool force_ignore_zoc, bool allow_teleport, const team &viewing_team,
int additional_turns, bool see_all, bool ignore_units) :
routes()
int additional_turns, bool see_all, bool ignore_units)
{
const unit_map::const_iterator i = units.find(loc);
if(i == units.end()) {
@ -290,9 +319,8 @@ paths::paths(gamemap const &map, unit_map const &units,
return;
}
routes[loc].move_left = i->second.movement_left();
find_routes(map,units,i->second,loc,
i->second.movement_left(),routes,teams,force_ignore_zoc,
i->second.movement_left(), destinations, teams, force_ignore_zoc,
allow_teleport,additional_turns,viewing_team,
see_all, ignore_units);
}
@ -461,18 +489,3 @@ double dummy_path_calculator::cost(const map_location&, const map_location&, con
{
return 0.0;
}
std::ostream& operator << (std::ostream& outstream, const paths::route& rt) {
outstream << "\n[route]\n\tsteps=\"";
bool first_loop = true;
foreach(map_location const& loc, rt.steps) {
if(first_loop) {
first_loop = false;
} else {
outstream << "->";
}
outstream << '(' << loc << ')';
}
outstream << "\"\n\tmove_left=\"" << rt.move_left << "\"\n[/route]";
return outstream;
}

View File

@ -87,7 +87,7 @@ struct cost_calculator
*/
struct paths
{
paths() : routes() {}
paths() {}
// Construct a list of paths for the unit at loc.
// - force_ignore_zocs: find the path ignoring ZOC entirely,
@ -104,16 +104,20 @@ struct paths
const team &viewing_team,int additional_turns = 0,
bool see_all = false, bool ignore_units = false);
/** Structure which holds a single route between one location and another. */
struct route
struct step
{
route() : steps(), move_left(0) {}
std::vector<map_location> steps;
int move_left; // movement unit will have left at end of the route.
map_location curr, prev;
int move_left;
};
typedef std::map<map_location,route> routes_map;
routes_map routes;
/** Ordered vector of possible destinations. */
struct dest_vect : std::vector<step>
{
const_iterator find(const map_location &) const;
bool contains(const map_location &) const;
std::vector<map_location> get_path(const const_iterator &) const;
};
dest_vect destinations;
};
/** Structure which holds a single route and waypoints for special events. */
@ -135,9 +139,6 @@ struct marked_route
waypoint_map waypoints;
};
//std::ostream& operator << (std::ostream& os, const paths::route& rt);
/** Structure which holds a single route between one location and another. */
struct plain_route
{