Speed up by keeping unit pointers in map, not units:

...makes AI sims much faster (class unit are now far too large to copy
cheaply).

2p Blitz 800 gold turn 7 used to test:
  Before: 20 seconds to first AI move, 3:40 for AI turn.
  After: 7 seconds to first move, 1:02 for entire AI turn.
  (note: most of the remainder is animation time).

To avoid everyone having to deal with pointers, new unit_map class
works exactly like the old std::map unless you are
adding/deleting/moving units.

dfool: I removed a couple of redundant clear() calls in your AI, too.
This commit is contained in:
Rusty Russell 2006-05-28 11:57:45 +00:00
parent b6a9d353b2
commit 4197721363
17 changed files with 346 additions and 116 deletions

View file

@ -108,9 +108,10 @@ wesnoth_SOURCES = \
tooltips.cpp \
unit.cpp \
unit_abilities.cpp \
unit_display.cpp \
unit_types.cpp \
unit_animation.cpp \
unit_display.cpp \
unit_map.cpp \
unit_types.cpp \
upload_log.cpp \
variable.cpp \
video.cpp \
@ -401,6 +402,7 @@ noinst_HEADERS = \
intro.hpp \
preferences.hpp \
unit_types.hpp \
unit_map.hpp \
unit_animation.hpp \
unit_abilities.hpp \
unit_frame.hpp \

View file

@ -151,7 +151,7 @@ std::string recruit_unit(const gamemap& map, int side,
disp->draw(true,true);
}
units.insert(std::pair<gamemap::location,unit>( recruit_location,new_unit));
units.add(new std::pair<gamemap::location,unit>(recruit_location,new_unit));
if(show) {
unit_map::iterator un = disp->get_units().find(recruit_location);
@ -894,7 +894,7 @@ void attack(display& gui, const gamemap& map,
newunit.heal_all();
}
units.insert(std::pair<gamemap::location,unit>(loc,newunit));
units.replace(new std::pair<gamemap::location,unit>(loc,newunit));
if (update_display){
gui.invalidate(loc);
}
@ -1125,7 +1125,7 @@ void attack(display& gui, const gamemap& map,
newunit.add_modification("variation",mod);
}
units.insert(std::pair<gamemap::location,unit>(loc,newunit));
units.replace(new std::pair<gamemap::location,unit>(loc,newunit));
if (update_display){
gui.invalidate(loc);
}
@ -1514,8 +1514,7 @@ void advance_unit(const game_data& info,
preferences::encountered_units().insert(new_unit.id());
LOG_STREAM(info, config) << "Added '" << new_unit.id() << "' to encountered units\n";
units.erase(loc);
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
units.replace(new std::pair<gamemap::location,unit>(loc,new_unit));
LOG_NG << "firing post_advance event\n";
game_events::fire("post_advance",loc);
}
@ -1679,8 +1678,7 @@ bool clear_shroud_unit(const gamemap& map,
return false;
}
unit_map temp_units;
temp_units.insert(*u);
unit_map temp_units(u->first, u->second);
paths p(map,status,gamedata,temp_units,loc,teams,true,false,teams[team]);
for(paths::routes_map::const_iterator i = p.routes.begin();
@ -1903,9 +1901,10 @@ size_t move_unit(display* disp, const game_data& gamedata,
u.set_movement(moves_left);
units.erase(ui);
ui = units.insert(std::pair<gamemap::location,unit>(steps.back(),u)).first;
std::pair<gamemap::location,unit> *p = units.extract(ui->first);
p->first = steps.back();
units.add(p);
ui = units.find(p->first);
if(disp != NULL) {
disp->invalidate_unit();
disp->invalidate(steps.back());

View file

@ -414,8 +414,7 @@ gamemap::location ai_interface::move_unit_partial(location from, location to, st
paths current_paths(info_.map,info_.state,info_.gameinfo,info_.units,from,info_.teams,ignore_zocs,teleport,current_team());
const std::map<location,paths>::iterator p_it = possible_moves.find(from);
unit current_unit = u_it->second;
std::pair<gamemap::location,unit> *up = NULL;
if(p_it != possible_moves.end()) {
paths& p = p_it->second;
@ -427,7 +426,7 @@ gamemap::location ai_interface::move_unit_partial(location from, location to, st
}
if(rt != p.routes.end()) {
current_unit.set_movement(rt->second.move_left);
u_it->second.set_movement(rt->second.move_left);
std::vector<location> steps = rt->second.steps;
@ -457,7 +456,7 @@ gamemap::location ai_interface::move_unit_partial(location from, location to, st
}
if(n != 6) {
current_unit.set_movement(0); //enter enemy ZoC, no movement left
u_it->second.set_movement(0); //enter enemy ZoC, no movement left
break;
}
}
@ -469,20 +468,18 @@ gamemap::location ai_interface::move_unit_partial(location from, location to, st
info_.disp.scroll_to_tiles(from.x,from.y,to.x,to.y);
u_it->second.set_hidden(true);
unit_display::move_unit(info_.disp,info_.map,steps,current_unit,info_.units,info_.teams);
u_it->second.set_hidden(false);
info_.units.erase(u_it);
u_it = info_.units.end();
up = info_.units.extract(u_it->first);
unit_display::move_unit(info_.disp,info_.map,steps,up->second,info_.units,info_.teams);
}
}
}
if(u_it != info_.units.end()) {
info_.units.erase(u_it);
if (!up) {
up = info_.units.extract(u_it->first);
}
info_.units.insert(std::pair<location,unit>(to,current_unit));
up->first = to;
info_.units.add(up);
if(info_.map.is_village(to)) {
// if a new village is captured, disallow any future movement
if (!info_.teams[info_.team_num-1].owns_village(to))
@ -1852,8 +1849,7 @@ void ai::move_leader_after_recruit(const move_map& srcdst, const move_map& dstsr
if(current_loc.valid()) {
LOG_AI << "considering movement to " << str_cast(current_loc.x + 1)
<< "," << str_cast(current_loc.y+1);
unit_map temp_units;
temp_units.insert(std::pair<location,unit>(current_loc,leader->second));
unit_map temp_units(current_loc,leader->second);
const paths p(map_,state_,gameinfo_,temp_units,current_loc,teams_,false,false,current_team());
if(p.routes.count(i->first)) {
@ -1869,8 +1865,9 @@ void ai::move_leader_after_recruit(const move_map& srcdst, const move_map& dstsr
//can recruit if they want.
if(nearest_keep(leader->first) == leader->first) {
const location keep = leader->first;
const std::pair<location,unit> temp_leader = *leader;
units_.erase(leader);
std::pair<gamemap::location,unit> *temp_leader;
temp_leader = units_.extract(keep);
bool friend_can_reach_keep = false;
@ -1887,7 +1884,7 @@ void ai::move_leader_after_recruit(const move_map& srcdst, const move_map& dstsr
}
}
units_.insert(temp_leader);
units_.add(temp_leader);
if(friend_can_reach_keep) {
//find a location for our leader to vacate the keep to

View file

@ -340,12 +340,11 @@ void ai::attack_analysis::analyze(const gamemap& map, unit_map& units,
std::vector<std::pair<location,location> >::const_iterator m;
for (m = movements.begin(); m != movements.end(); ++m) {
// We fix up units map to reflect what this would look like.
unit_map::iterator att_it = units.find(m->first);
unit att_u = att_it->second;
units.erase(att_it);
units.insert(std::pair<location,unit>(m->second, att_u));
std::pair<gamemap::location,unit> *up = units.extract(m->first);
up->first = m->second;
units.add(up);
if (att_u.can_recruit()) {
if (up->second.can_recruit()) {
uses_leader = true;
leader_threat = false;
}
@ -368,10 +367,10 @@ void ai::attack_analysis::analyze(const gamemap& map, unit_map& units,
double prob_died = att.hp_dist[0];
double prob_survived = (1.0 - prob_died) * prob_fought;
double cost = att_u.cost();
double cost = up->second.cost();
const bool on_village = map.is_village(m->second);
//up to double the value of a unit based on experience
cost += (double(att_u.experience())/double(att_u.max_experience()))*cost;
cost += (double(up->second.experience())/double(up->second.max_experience()))*cost;
resources_used += cost;
avg_losses += cost * prob_died;
@ -384,8 +383,8 @@ void ai::attack_analysis::analyze(const gamemap& map, unit_map& units,
double advance_prob = 0.0;
//the reward for advancing a unit is to get a 'negative' loss of that unit
if (!att_u.advances_to().empty()) {
int xp_for_advance = att_u.max_experience() - att_u.experience();
if (!up->second.advances_to().empty()) {
int xp_for_advance = up->second.max_experience() - up->second.experience();
int kill_xp, fight_xp;
fight_xp = defend_it->second.level();
@ -395,29 +394,29 @@ void ai::attack_analysis::analyze(const gamemap& map, unit_map& units,
advance_prob = prob_fought;
else if (kill_xp >= xp_for_advance)
advance_prob = prob_killed;
avg_losses -= att_u.cost() * advance_prob;
avg_losses -= up->second.cost() * advance_prob;
//the reward for getting a unit closer to advancement (if
//it didn't advance) is to get the proportion of remaining
//experience needed, and multiply it by a quarter of the
//unit cost. This will cause the AI to heavily favor
//getting xp for close-to-advance units.
avg_losses -= (att_u.cost()*fight_xp)/(xp_for_advance*4) * (prob_fought - prob_killed);
avg_losses -= (att_u.cost()*kill_xp)/(xp_for_advance*4) * prob_killed;
avg_losses -= (up->second.cost()*fight_xp)/(xp_for_advance*4) * (prob_fought - prob_killed);
avg_losses -= (up->second.cost()*kill_xp)/(xp_for_advance*4) * prob_killed;
//the reward for killing with a unit that
//plagues is to get a 'negative' loss of that unit
if (bc.get_attacker_stats().plagues) {
avg_losses -= prob_killed * att_u.cost();
avg_losses -= prob_killed * up->second.cost();
}
}
// If we didn't advance, we took this damage.
avg_damage_taken += (att_u.hitpoints() - att.average_hp()) * (1.0 - advance_prob);
avg_damage_taken += (up->second.hitpoints() - att.average_hp()) * (1.0 - advance_prob);
// FIXME: attack_prediction.cpp should understand advancement directly.
// For each level of attacker def gets 1 xp or kill_experience.
def_avg_experience += att_u.level() *
def_avg_experience += up->second.level() *
(1.0 - att.hp_dist[0] + game_config::kill_experience * att.hp_dist[0]);
if (m == movements.begin()) {
first_chance_kill = def.hp_dist[0];
@ -440,10 +439,9 @@ void ai::attack_analysis::analyze(const gamemap& map, unit_map& units,
// Restore the units to their original positions.
for (m = movements.begin(); m != movements.end(); ++m) {
unit_map::iterator att_it = units.find(m->second);
unit att_u = att_it->second;
units.erase(att_it);
units.insert(std::pair<location,unit>(m->first, att_u));
std::pair<gamemap::location,unit> *up = units.extract(m->second);
up->first = m->first;
units.add(up);
}
}

View file

@ -97,7 +97,6 @@ namespace dfool {
unit_map matching_units;
unit_map assigned_units;
assigned_units.clear();
//find units assigned to this order;
for(config::child_list::const_iterator at = order_assignments.begin(); at != order_assignments.end(); ++at) {
LOG_STREAM(info, ai)<<"\tchecking for assignments\n";
@ -110,7 +109,7 @@ namespace dfool {
if(clear_assignment(i->first,clear_assign,info_.map)){
LOG_STREAM(info, ai)<<"\tclear existing assignments\n";
}else{
assigned_units.insert(*i);
assigned_units.add(new std::pair<gamemap::location,unit>(*i));
LOG_STREAM(info, ai)<<"\t\tAssignment: "<<(**at)["unit_id"]<<" to order: "<<id<<std::endl;
}
}
@ -118,7 +117,6 @@ namespace dfool {
}
}
matching_units.clear();
//find units that match any filter. If no filters then accept all units.
if(filter.size()){
for(config::child_list::const_iterator f = filter.begin(); f != filter.end(); ++f) {
@ -137,7 +135,7 @@ namespace dfool {
if(found){
LOG_STREAM(info, ai)<<"\t\talready assigned: "<<i->second.underlying_description()<<std::endl;
}else{
matching_units.insert(*i);
matching_units.add(new std::pair<gamemap::location,unit>(*i));
LOG_STREAM(info, ai)<<"\t\tmatching: "<<i->second.underlying_description()<<" to order: "<<id<<std::endl;
}
}
@ -149,7 +147,7 @@ namespace dfool {
//should add sorting functionality here in future
//bring assigned units up to maximum number
for(unit_map::const_iterator mu = matching_units.begin(); mu != matching_units.end() && order_assignments.size()<num; ++mu) {
assigned_units.insert(*mu);
assigned_units.add(new std::pair<gamemap::location,unit>(*mu));
LOG_STREAM(info, ai)<<"\tassigned unit:\t"<<mu->second.underlying_description()<<"\n";
}
@ -211,7 +209,7 @@ namespace dfool {
}else{
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
if(current_team().fogged(i->first.x,i->first.y) == false) {
visible_units_.insert(*i);
visible_units_.add(new std::pair<gamemap::location,unit>(*i));
}
}
}
@ -226,7 +224,7 @@ namespace dfool {
unit_map filtered_units_;
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
if(i->second.matches_filter(filter,i->first)) {
filtered_units_.insert(*i);
filtered_units_.add(new std::pair<gamemap::location,unit>(*i));
}
}
return filtered_units_;

View file

@ -770,8 +770,7 @@ void ai::access_points(const move_map& srcdst, const location& u, const location
return;
}
unit_map single_unit;
single_unit.insert(*u_it);
unit_map single_unit(u_it->first, u_it->second);
const std::pair<move_map::const_iterator,move_map::const_iterator> locs = srcdst.equal_range(u);
for(move_map::const_iterator i = locs.first; i != locs.second; ++i) {

View file

@ -122,8 +122,7 @@ void get_player_info(const config& cfg, game_state& gamestate, std::string save_
}
new_unit.new_turn();
units.insert(std::pair<gamemap::location,unit>(
map.starting_position(new_unit.side()), new_unit));
units.add(new std::pair<gamemap::location,unit>(map.starting_position(new_unit.side()), new_unit));
LOG_NG << "initializing side '" << cfg["side"] << "' at "
<< start_pos << '\n';
}
@ -168,7 +167,7 @@ void get_player_info(const config& cfg, game_state& gamestate, std::string save_
") for a unit on side " +
lexical_cast<std::string>(side) + ".");
} else {
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
units.add(new std::pair<gamemap::location,unit>(loc,new_unit));
LOG_NG << "inserting unit for side " << new_unit.side() << "\n";
}
}

View file

@ -329,10 +329,9 @@ bool event_handler::handle_event_command(const queued_event& event_info,
if(game_map->on_board(vacant_dst)) {
const int side = u->second.side();
//note that inserting into a map does not invalidate iterators
//into the map, so this sequence is fine.
units->insert(std::pair<gamemap::location,unit>(vacant_dst,u->second));
units->erase(u);
std::pair<gamemap::location,unit> *up = units->extract(u->first);
up->first = dst;
units->add(up);
if(game_map->is_village(vacant_dst)) {
get_village(vacant_dst,*teams,side,*units);
@ -1110,7 +1109,7 @@ bool event_handler::handle_event_command(const queued_event& event_info,
screen->draw(true,true);
}
units->insert(std::pair<gamemap::location,unit>(loc,new_unit));
units->add(new std::pair<gamemap::location,unit>(loc,new_unit));
if(game_map->is_village(loc)) {
get_village(loc,*teams,new_unit.side()-1,*units);
}
@ -1557,7 +1556,7 @@ bool event_handler::handle_event_command(const queued_event& event_info,
}
units->erase(loc);
units->insert(std::pair<gamemap::location,unit>(loc,u));
units->add(new std::pair<gamemap::location,unit>(loc,u));
std::string text = cfg["text"];
text = utils::interpolate_variables_into_string(text, *state_of_game);

View file

@ -18,6 +18,7 @@ class config;
class gamestatus;
class unit;
class vconfig;
class unit_map;
#include "terrain.hpp"
@ -104,7 +105,6 @@ public:
private:
void init(const std::string &x, const std::string &y);
};
typedef std::map<location,unit> unit_map;
const std::string& underlying_mvt_terrain(const location& loc) const
{ return underlying_mvt_terrain(get_terrain(loc)); }

View file

@ -829,16 +829,12 @@ namespace events{
action.starting_moves = u->second.movement_left();
unit un = u->second;
un.set_goto(gamemap::location());
u->second.set_hidden(true);
unit_display::move_unit(*gui_,map_,route,un,units_,teams_);
u->second.set_hidden(false);
units_.erase(u);
un.set_movement(starting_moves);
units_.insert(std::pair<gamemap::location,unit>(route.back(),un));
std::pair<gamemap::location,unit> *up = units_.extract(u->first);
unit_display::move_unit(*gui_,map_,route,up->second,units_,teams_);
up->second.set_goto(gamemap::location());
up->second.set_movement(starting_moves);
up->first = route.back();
units_.add(up);
gui_->invalidate(route.back());
gui_->draw();
}
@ -952,19 +948,15 @@ namespace events{
action.starting_moves = u->second.movement_left();
unit un = u->second;
un.set_goto(gamemap::location());
u->second.set_hidden(true);
unit_display::move_unit(*gui_,map_,route,un,units_,teams_);
u->second.set_hidden(false);
units_.erase(u);
un.set_movement(starting_moves);
units_.insert(std::pair<gamemap::location,unit>(route.back(),un));
std::pair<gamemap::location,unit> *up = units_.extract(u->first);
unit_display::move_unit(*gui_,map_,route,up->second,units_,teams_);
up->second.set_goto(gamemap::location());
up->second.set_movement(starting_moves);
up->first = route.back();
units_.add(up);
if(map_.is_village(route.back())) {
get_village(route.back(),teams_,un.side()-1,units_);
get_village(route.back(),teams_,up->second.side()-1,units_);
//MP_COUNTDOWN restore capture bonus
if(action.countdown_time_bonus)
{
@ -1011,8 +1003,7 @@ namespace events{
const unit_movement_resetter move_reset(u->second);
const bool is_skirmisher = u->second.get_ability_bool("skirmisher",u->first);
const bool teleports = u->second.get_ability_bool("teleport",u->first);
unit_map units;
units.insert(*u);
unit_map units(u->first, u->second);
const paths& path = paths(map_,status_,gameinfo_,ignore_units?units:units_,
u->first,teams_,is_skirmisher,teleports,teams_[gui_->viewing_team()]);
@ -1162,7 +1153,7 @@ namespace events{
if (size_t(choice) < unit_choices.size()) {
units_.erase(mousehandler.get_last_hex());
units_.insert(std::pair<gamemap::location,unit>(mousehandler.get_last_hex(),unit_choices[choice]));
units_.add(new std::pair<gamemap::location,unit>(mousehandler.get_last_hex(),unit_choices[choice]));
gui_->invalidate(mousehandler.get_last_hex());
gui_->invalidate_unit();
}
@ -1673,7 +1664,7 @@ namespace events{
}
units_.erase(mousehandler.get_last_hex());
units_.insert(std::pair<gamemap::location,unit>(mousehandler.get_last_hex(),unit(&gameinfo_,&units_,&map_,&status_,&teams_,&i->second,1,false)));
units_.add(new std::pair<gamemap::location,unit>(mousehandler.get_last_hex(),unit(&gameinfo_,&units_,&map_,&status_,&teams_,&i->second,1,false)));
gui_->invalidate(mousehandler.get_last_hex());
gui_->invalidate_unit();
} else if(game_config::debug && cmd == "gold") {

View file

@ -873,6 +873,7 @@ gamemap::location mouse_handler::current_unit_attacks_from(const gamemap::locati
return res;
}
// FIXME: Should borrow unit pointers, not copy units.
const unit_map& mouse_handler::visible_units()
{
if(viewing_team().uses_shroud() == false && viewing_team().uses_fog() == false) {
@ -883,7 +884,7 @@ const unit_map& mouse_handler::visible_units()
visible_units_.clear();
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
if((*gui_).fogged(i->first.x,i->first.y) == false) {
visible_units_.insert(*i);
visible_units_.add(new std::pair<gamemap::location,unit>(*i));
}
}

View file

@ -812,8 +812,6 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
paths paths_list(map,state,gameinfo,units,src,teams,ignore_zocs,teleport,current_team);
unit current_unit = u->second;
std::map<gamemap::location,paths::route>::iterator rt = paths_list.routes.find(dst);
if(rt == paths_list.routes.end()) {
@ -821,30 +819,31 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
ERR_NW << "can get to: " << rt->first << '\n';
}
ERR_NW << "src cannot get to dst: " << current_unit.movement_left() << ' '
ERR_NW << "src cannot get to dst: " << u->second.movement_left() << ' '
<< paths_list.routes.size() << ' ' << src << " -> " << dst << '\n';
if (!game_config::ignore_replay_errors) throw replay::error();
}
rt->second.steps.push_back(dst);
if(!replayer.is_skipping() && unit_display::unit_visible_on_path(disp,rt->second.steps,current_unit,units,teams)) {
if(!replayer.is_skipping() && unit_display::unit_visible_on_path(disp,rt->second.steps,u->second,units,teams)) {
disp.scroll_to_tiles(src.x,src.y,dst.x,dst.y);
}
units.erase(u);
std::pair<gamemap::location,unit> *up = units.extract(u->first);
if(!replayer.is_skipping()) {
unit_display::move_unit(disp,map,rt->second.steps,current_unit,units,teams);
unit_display::move_unit(disp,map,rt->second.steps,up->second,units,teams);
}
else{
//unit location needs to be updated
current_unit.set_goto(*(rt->second.steps.end() - 1));
up->second.set_goto(*(rt->second.steps.end() - 1));
}
current_unit.set_movement(rt->second.move_left);
u = units.insert(std::pair<gamemap::location,unit>(dst,current_unit)).first;
up->second.set_movement(rt->second.move_left);
up->first = dst;
units.add(up);
if(map.is_village(dst)) {
const int orig_owner = village_owner(dst,teams) + 1;
if(orig_owner != team_num) {

View file

@ -2844,19 +2844,15 @@ std::string get_team_name(unsigned int side, const unit_map& units)
}
temporary_unit_placer::temporary_unit_placer(unit_map& m, const gamemap::location& loc, const unit& u)
: m_(m), loc_(loc), temp_(m.count(loc) == 1 ? m.find(loc)->second : u), use_temp_(m.count(loc) == 1)
: m_(m), loc_(loc), temp_(m.extract(loc))
{
if(use_temp_) {
m.erase(loc);
}
m.insert(std::pair<gamemap::location,unit>(loc,u));
m.add(new std::pair<gamemap::location,unit>(loc,u));
}
temporary_unit_placer::~temporary_unit_placer()
{
m_.erase(loc_);
if(use_temp_) {
m_.insert(std::pair<gamemap::location,unit>(loc_,temp_));
if(temp_) {
m_.add(temp_);
}
}

View file

@ -19,9 +19,7 @@
#include "team.hpp"
#include "unit_types.hpp"
#include "image.hpp"
#include "unit_map.hpp"
class unit;
class display;
@ -423,8 +421,7 @@ struct temporary_unit_placer
private:
unit_map& m_;
const gamemap::location& loc_;
const unit temp_;
bool use_temp_;
std::pair<gamemap::location,unit> *temp_;
};
#endif

104
src/unit_map.cpp Normal file
View file

@ -0,0 +1,104 @@
/* $Id$ */
/*
Copyright (C) 2006 by Rusty Russell <rusty@rustcorp.com.au>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "unit.hpp"
#include "unit_map.hpp"
#include "wassert.hpp"
// A unit map with a copy of a single unit in it.
unit_map::unit_map(const gamemap::location &loc, const unit &u)
{
add(new std::pair<gamemap::location,unit>(loc, u));
}
unit_map::unit_map(const unit_map &that)
{
*this = that;
}
unit_map::unit_map &unit_map::operator =(const unit_map &that)
{
delete_all();
for (pmap::const_iterator i = that.map_.begin(); i != that.map_.end(); i++) {
add(new std::pair<gamemap::location,unit>(*i->second));
}
return *this;
}
unit_map::~unit_map()
{
delete_all();
}
// Due to unit <-> unit_map dependencies, must be out of line.
std::pair<gamemap::location,unit> unit_map::iterator::operator*() const
{
return *i_->second;
}
std::pair<gamemap::location,unit> unit_map::const_iterator::operator*() const
{
return *i_->second;
}
void unit_map::add(std::pair<gamemap::location,unit> *p)
{
std::pair<pmap::iterator,bool> res = map_.insert(std::pair<gamemap::location,std::pair<gamemap::location,unit>*>(p->first, p));
wassert(res.second);
}
void unit_map::replace(std::pair<gamemap::location,unit> *p)
{
if (erase(p->first) != 1)
assert(0);
map_.insert(std::pair<gamemap::location,std::pair<gamemap::location,unit>*>(p->first, p));
}
void unit_map::delete_all()
{
for (pmap::iterator i = map_.begin(); i != map_.end(); i++) {
delete(i->second);
}
}
// Extract (like erase, only don't delete).
std::pair<gamemap::location,unit> *unit_map::extract(const gamemap::location &loc)
{
pmap::iterator i = map_.find(loc);
if (i == map_.end())
return NULL;
std::pair<gamemap::location,unit> *res = i->second;
map_.erase(i);
return res;
}
size_t unit_map::erase(const gamemap::location &loc)
{
pmap::iterator i = map_.find(loc);
if (i == map_.end())
return 0;
delete i->second;
map_.erase(i);
return 1;
}
void unit_map::erase(iterator pos)
{
if (erase(pos->first) != 1)
assert(0);
}
void unit_map::clear()
{
delete_all();
map_.clear();
}

152
src/unit_map.hpp Normal file
View file

@ -0,0 +1,152 @@
/* $Id$ */
/*
Copyright (C) 2006 by Rusty Russell <rusty@rustcorp.com.au>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef UNIT_MAP_H_INCLUDED
#define UNIT_MAP_H_INCLUDED
#include <cstring>
#include "map.hpp"
#include "unit.hpp"
// We used to just open-code a std::map<location,unit>, but as unit
// gained weight leading up to 1.1.3, manipulating the map caused
// significant performance issues for the AI, which had to actually
// move units for accurate simulation with the new, more powerful
// filtering. This class eases the transition, by providing a wrapper
// which acts like a map of units, not unit pointers, except
// implemented with pointers and hence providing a cheap a move
// function.
class unit_map
{
public:
unit_map() { };
unit_map(const unit_map &that);
unit_map &operator =(const unit_map &that);
// A unit map with a single unit in it.
explicit unit_map(const gamemap::location &loc, const unit &u);
~unit_map();
// We actually keep map to pointers to pairs. Easy to fake iterators.
typedef std::map<gamemap::location,std::pair<gamemap::location,unit>*> pmap;
struct iterator;
struct const_iterator {
const_iterator() { }
const_iterator(const iterator &i) : i_(i.i_) { }
const std::pair<gamemap::location,unit> *operator->() const
{ return i_->second; }
std::pair<gamemap::location,unit> operator*() const;
const_iterator &operator++()
{ ++i_; return *this; }
const_iterator &operator++(int)
{ i_++; return *this; }
const_iterator &operator--()
{ --i_; return *this; }
bool operator==(const const_iterator &that) const
{ return that.i_ == this->i_; }
bool operator!=(const const_iterator &that) const
{ return that.i_ != this->i_; }
explicit const_iterator(pmap::const_iterator i) : i_(i) { }
private:
pmap::const_iterator i_;
};
struct iterator {
iterator() { }
std::pair<gamemap::location,unit> *operator->() const
{ return i_->second; }
std::pair<gamemap::location,unit> operator*() const;
iterator &operator++()
{ ++i_; return *this; }
iterator &operator++(int)
{ i_++; return *this; }
bool operator==(const iterator &that) const
{ return that.i_ == this->i_; }
bool operator!=(const iterator &that) const
{ return that.i_ != this->i_; }
explicit iterator(pmap::iterator i) : i_(i) { }
friend struct const_iterator;
private:
pmap::iterator i_;
};
iterator find(const gamemap::location &loc) {
return iterator(map_.find(loc));
}
const_iterator find(const gamemap::location &loc) const {
return const_iterator(map_.find(loc));
}
size_t count(const gamemap::location &loc) const {
return map_.count(loc);
}
iterator begin() {
return iterator(map_.begin());
}
const_iterator begin() const {
return const_iterator(map_.begin());
}
iterator end() {
return iterator(map_.end());
}
const_iterator end() const {
return const_iterator(map_.end());
}
size_t size() const {
return map_.size();
}
void clear();
// Extract (like erase, only don't delete).
std::pair<gamemap::location,unit> *extract(const gamemap::location &loc);
// Map owns pointer after this. Loc must be currently empty.
void add(std::pair<gamemap::location,unit> *p);
// Like add, but loc must be occupied (implicitly erased).
void replace(std::pair<gamemap::location,unit> *p);
void erase(iterator pos);
size_t erase(const gamemap::location &loc);
private:
void delete_all();
// A map of pairs is redundant, but makes it possible to imitate a map of location,unit.
std::map<gamemap::location,std::pair<gamemap::location,unit>*> map_;
};
#endif // UNIT_MAP_H_INCLUDED

View file

@ -30,7 +30,6 @@ class unit_ability_list;
struct game_data;
class gamestatus;
class team;
typedef std::map<gamemap::location,unit> unit_map;
//and how much damage it does.
class attack_type