restructure of AI code

This commit is contained in:
uid68803 2004-01-13 18:21:10 +00:00
parent ccf31d5a30
commit 1c3e548d92
12 changed files with 374 additions and 414 deletions

View file

@ -12,7 +12,7 @@ advanceto=null
cost=39 cost=39
ability=leadership ability=leadership
usage=fighter usage=fighter
unit_description="Smetimes the migthiest warriors and generals cursed with hate and angst came back to this world as Death Knights." unit_description="Sometimes the mightiest warriors and generals cursed with hate and angst came back to this world as Death Knights."
get_hit_sound=groan.wav get_hit_sound=groan.wav
[attack] [attack]
name=axe name=axe

View file

@ -25,13 +25,18 @@
#include <iostream> #include <iostream>
namespace { ai::ai(display& disp, const gamemap& map, const game_data& gameinfo,
std::map<gamemap::location,unit>& units,
std::vector<team>& teams, int team_num, const gamestatus& state)
: disp_(disp), map_(map), gameinfo_(gameinfo), units_(units),
teams_(teams), team_num_(team_num), state_(state),
consider_combat_(true)
{}
bool recruit(const gamemap& map, const gamemap::location& leader, bool ai::recruit(const std::string& usage)
const std::string& usage, const game_data& gameinfo,
int team_num, team& tm, int min_gold,
std::map<gamemap::location,unit>& units, display& disp)
{ {
const int min_gold = 0;
log_scope("recruiting troops"); log_scope("recruiting troops");
std::cerr << "recruiting " << usage << "\n"; std::cerr << "recruiting " << usage << "\n";
@ -42,12 +47,12 @@ bool recruit(const gamemap& map, const gamemap::location& leader,
//find an available unit that can be recruited, matches the desired //find an available unit that can be recruited, matches the desired
//usage type, and comes in under budget //usage type, and comes in under budget
const std::set<std::string>& recruits = tm.recruits(); const std::set<std::string>& recruits = current_team().recruits();
for(std::map<std::string,unit_type>::const_iterator i = for(std::map<std::string,unit_type>::const_iterator i =
gameinfo.unit_types.begin(); i != gameinfo.unit_types.end(); ++i) { gameinfo_.unit_types.begin(); i != gameinfo_.unit_types.end(); ++i) {
if(i->second.usage() == usage && recruits.count(i->second.name()) if(i->second.usage() == usage && recruits.count(i->second.name())
&& tm.gold() - i->second.cost() > min_gold) { && current_team().gold() - i->second.cost() > min_gold) {
options.push_back(i); options.push_back(i);
option_numbers.push_back(std::distance(recruits.begin(), option_numbers.push_back(std::distance(recruits.begin(),
@ -57,7 +62,6 @@ bool recruit(const gamemap& map, const gamemap::location& leader,
//from the available options, choose one at random //from the available options, choose one at random
if(options.empty() == false) { if(options.empty() == false) {
const gamemap::location loc = gamemap::location::null_location; const gamemap::location loc = gamemap::location::null_location;
const int option = rand()%options.size(); const int option = rand()%options.size();
@ -67,13 +71,11 @@ bool recruit(const gamemap& map, const gamemap::location& leader,
replay_undo replay_guard(recorder); replay_undo replay_guard(recorder);
const unit_type& u = options[option]->second; const unit_type& u = options[option]->second;
unit new_unit(&u,team_num,true); unit new_unit(&u,team_num_,true);
//see if we can actually recruit (i.e. have enough room etc) //see if we can actually recruit (i.e. have enough room etc)
if(recruit_unit(map,team_num,units,new_unit,loc,&disp).empty()) { if(recruit_unit(map_,team_num_,units_,new_unit,loc,&disp_).empty()) {
std::cerr << "recruiting a " << u.name() << " for " << u.cost() << " have " << tm.gold() << " left\n"; current_team().spend_gold(u.cost());
tm.spend_gold(u.cost());
//confirm the transaction - i.e. don't undo recruitment //confirm the transaction - i.e. don't undo recruitment
replay_guard.confirm_transaction(); replay_guard.confirm_transaction();
@ -86,25 +88,21 @@ bool recruit(const gamemap& map, const gamemap::location& leader,
return false; return false;
} }
team& ai::current_team()
{
return teams_[team_num_-1];
} }
namespace ai { void ai::move_unit(const location& from, const location& to, std::map<location,paths>& possible_moves)
void move_unit(const game_data& gameinfo, display& disp,
const gamemap& map,
std::map<gamemap::location,unit>& units,
const location& from, const location& to,
std::map<location,paths>& possible_moves,
std::vector<team>& teams, int team_num)
{ {
assert(units.find(to) == units.end() || from == to); assert(units_.find(to) == units_.end() || from == to);
disp.select_hex(from); disp_.select_hex(from);
disp.update_display(); disp_.update_display();
log_scope("move_unit"); log_scope("move_unit");
std::map<location,unit>::iterator u_it = units.find(from); unit_map::iterator u_it = units_.find(from);
if(u_it == units.end()) { if(u_it == units_.end()) {
std::cout << "Could not find unit at " << from.x << ", " std::cout << "Could not find unit at " << from.x << ", "
<< from.y << "\n"; << from.y << "\n";
assert(false); assert(false);
@ -115,17 +113,16 @@ void move_unit(const game_data& gameinfo, display& disp,
const bool ignore_zocs = u_it->second.type().is_skirmisher(); const bool ignore_zocs = u_it->second.type().is_skirmisher();
const bool teleport = u_it->second.type().teleports(); const bool teleport = u_it->second.type().teleports();
paths current_paths = paths(map,gameinfo,units,from,teams, paths current_paths = paths(map_,gameinfo_,units_,from,teams_,ignore_zocs,teleport);
ignore_zocs,teleport); paths_wiper wiper(disp_);
paths_wiper wiper(disp);
if(!disp.fogged(from.x,from.y)) if(!disp_.fogged(from.x,from.y))
disp.set_paths(&current_paths); disp_.set_paths(&current_paths);
disp.scroll_to_tiles(from.x,from.y,to.x,to.y); disp_.scroll_to_tiles(from.x,from.y,to.x,to.y);
unit current_unit = u_it->second; unit current_unit = u_it->second;
units.erase(u_it); units_.erase(u_it);
const std::map<location,paths>::iterator p_it = possible_moves.find(from); const std::map<location,paths>::iterator p_it = possible_moves.find(from);
@ -141,38 +138,30 @@ void move_unit(const game_data& gameinfo, display& disp,
if(rt != p.routes.end()) { if(rt != p.routes.end()) {
std::vector<location> steps = rt->second.steps; std::vector<location> steps = rt->second.steps;
steps.push_back(to); //add the destination to the steps steps.push_back(to); //add the destination to the steps
disp.move_unit(steps,current_unit); disp_.move_unit(steps,current_unit);
} }
} }
current_unit.set_movement(0); current_unit.set_movement(0);
units.insert(std::pair<location,unit>(to,current_unit)); units_.insert(std::pair<location,unit>(to,current_unit));
if(map.underlying_terrain(map[to.x][to.y]) == gamemap::TOWER) if(map_.underlying_terrain(map_[to.x][to.y]) == gamemap::TOWER)
get_tower(to,teams,team_num-1,units); get_tower(to,teams_,team_num_-1,units_);
disp.draw_tile(to.x,to.y); disp_.draw_tile(to.x,to.y);
disp.draw(); disp_.draw();
game_events::fire("moveto",to); game_events::fire("moveto",to);
if((teams.front().uses_fog() || teams.front().uses_shroud()) && !teams.front().fogged(to.x,to.y)) { if((teams_.front().uses_fog() || teams_.front().uses_shroud()) && !teams_.front().fogged(to.x,to.y)) {
game_events::fire("sighted",to); game_events::fire("sighted",to);
} }
} }
void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
unit_map& units, void ai::do_move()
std::vector<team>& teams, int team_num, const gamestatus& state,
bool consider_combat, std::vector<target>* additional_targets)
{ {
std::vector<target> tgts;
if(additional_targets == NULL)
additional_targets = &tgts;
log_scope("doing ai move"); log_scope("doing ai move");
team& current_team = teams[team_num-1];
typedef paths::route route; typedef paths::route route;
std::multimap<location,location> enemy_srcdst; std::multimap<location,location> enemy_srcdst;
@ -186,10 +175,10 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
typedef std::map<location,paths> moves_map; typedef std::map<location,paths> moves_map;
moves_map possible_moves; moves_map possible_moves;
for(std::map<gamemap::location,unit>::iterator un_it = units.begin(); for(std::map<gamemap::location,unit>::iterator un_it = units_.begin();
un_it != units.end(); ++un_it) { un_it != units_.end(); ++un_it) {
if(current_team.is_enemy(un_it->second.side())) { if(current_team().is_enemy(un_it->second.side())) {
std::pair<location,location> trivial_mv(un_it->first,un_it->first); std::pair<location,location> trivial_mv(un_it->first,un_it->first);
enemy_srcdst.insert(trivial_mv); enemy_srcdst.insert(trivial_mv);
enemy_dstsrc.insert(trivial_mv); enemy_dstsrc.insert(trivial_mv);
@ -198,8 +187,8 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
const bool ignore_zocs = un_it->second.type().is_skirmisher(); const bool ignore_zocs = un_it->second.type().is_skirmisher();
const bool teleports = un_it->second.type().teleports(); const bool teleports = un_it->second.type().teleports();
const paths new_paths(map,gameinfo,units, const paths new_paths(map_,gameinfo_,units_,
un_it->first,teams,ignore_zocs,teleports); un_it->first,teams_,ignore_zocs,teleports);
for(paths::routes_map::const_iterator rt = new_paths.routes.begin(); for(paths::routes_map::const_iterator rt = new_paths.routes.begin();
rt != new_paths.routes.end(); ++rt) { rt != new_paths.routes.end(); ++rt) {
const std::pair<location,location> item(un_it->first,rt->first); const std::pair<location,location> item(un_it->first,rt->first);
@ -209,7 +198,7 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
} }
} }
if(un_it->second.side() != team_num) { if(un_it->second.side() != team_num_) {
continue; continue;
} }
@ -229,39 +218,38 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
const bool ignore_zocs = un_it->second.type().is_skirmisher(); const bool ignore_zocs = un_it->second.type().is_skirmisher();
const bool teleports = un_it->second.type().teleports(); const bool teleports = un_it->second.type().teleports();
possible_moves.insert(std::pair<gamemap::location,paths>( possible_moves.insert(std::pair<gamemap::location,paths>(
un_it->first,paths(map,gameinfo,units, un_it->first,paths(map_,gameinfo_,units_,
un_it->first,teams,ignore_zocs,teleports))); un_it->first,teams_,ignore_zocs,teleports)));
} }
for(moves_map::iterator m = possible_moves.begin(); for(moves_map::iterator m = possible_moves.begin();
m != possible_moves.end(); ++m) { m != possible_moves.end(); ++m) {
for(paths::routes_map::iterator rtit = for(paths::routes_map::iterator rtit =
m->second.routes.begin(); rtit != m->second.routes.end(); m->second.routes.begin(); rtit != m->second.routes.end(); ++rtit) {
++rtit) {
const location& src = m->first; const location& src = m->first;
const location& dst = rtit->first; const location& dst = rtit->first;
if(src != dst && units.find(dst) == units.end()) { if(src != dst && units_.find(dst) == units_.end()) {
srcdst.insert(std::pair<location,location>(src,dst)); srcdst.insert(std::pair<location,location>(src,dst));
dstsrc.insert(std::pair<location,location>(dst,src)); dstsrc.insert(std::pair<location,location>(dst,src));
} }
} }
} }
unit_map::iterator leader = find_leader(units,team_num); unit_map::iterator leader = find_leader(units_,team_num_);
//no moves left, recruitment phase and leader movement phase //no moves left, recruitment phase and leader movement phase
//take stock of our current set of units //take stock of our current set of units
if(srcdst.empty() || leader != units.end() && srcdst.count(leader->first) == srcdst.size()) { if(srcdst.empty() || leader != units_.end() && srcdst.count(leader->first) == srcdst.size()) {
if(leader == units.end()) { if(leader == units_.end()) {
recorder.end_turn(); recorder.end_turn();
return; return;
} }
//find where the leader can move //find where the leader can move
const paths leader_paths(map,gameinfo,units,leader->first,teams,false,false); const paths leader_paths(map_,gameinfo_,units_,leader->first,teams_,false,false);
const gamemap::location& start_pos = map.starting_position(leader->second.side()); const gamemap::location& start_pos = map_.starting_position(leader->second.side());
possible_moves.insert(std::pair<gamemap::location,paths>(leader->first,leader_paths)); possible_moves.insert(std::pair<gamemap::location,paths>(leader->first,leader_paths));
@ -270,12 +258,11 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
if(leader->first != start_pos) { if(leader->first != start_pos) {
leader_moved = true; leader_moved = true;
const paths::routes_map::const_iterator itor = leader_paths.routes.find(start_pos); const paths::routes_map::const_iterator itor = leader_paths.routes.find(start_pos);
if(itor != leader_paths.routes.end() && units.count(start_pos) == 0) { if(itor != leader_paths.routes.end() && units_.count(start_pos) == 0) {
move_unit(gameinfo,disp,map,units,leader->first,start_pos, move_unit(leader->first,start_pos,possible_moves);
possible_moves,teams,team_num);
leader = find_leader(units,team_num); leader = find_leader(units_,team_num_);
assert(leader != units.end()); assert(leader != units_.end());
} }
} }
@ -284,38 +271,35 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
//currently just spend all the gold we can! //currently just spend all the gold we can!
const int min_gold = 0; const int min_gold = 0;
const int towers = map.towers().size(); const int towers = map_.towers().size();
int taken_towers = 0; int taken_towers = 0;
for(size_t j = 0; j != teams.size(); ++j) { for(size_t j = 0; j != teams_.size(); ++j) {
taken_towers += teams[j].towers().size(); taken_towers += teams_[j].towers().size();
} }
const int neutral_towers = towers - taken_towers; const int neutral_towers = towers - taken_towers;
//get scouts depending on how many neutral villages there are //get scouts depending on how many neutral villages there are
int scouts_wanted = current_team.villages_per_scout() > 0 ? int scouts_wanted = current_team().villages_per_scout() > 0 ?
neutral_towers/current_team.villages_per_scout() : 0; neutral_towers/current_team().villages_per_scout() : 0;
std::map<std::string,int> unit_types; std::map<std::string,int> unit_types;
while(unit_types["scout"] < scouts_wanted) { while(unit_types["scout"] < scouts_wanted) {
if(recruit(map,leader->first,"scout",gameinfo,team_num,current_team, if(recruit("scout") == false)
min_gold,units,disp) == false)
break; break;
++unit_types["scout"]; ++unit_types["scout"];
} }
const std::vector<std::string>& options = const std::vector<std::string>& options = current_team().recruitment_pattern();
current_team.recruitment_pattern();
if(options.empty()) { if(options.empty()) {
assert(false); assert(false);
return; return;
} }
//buy fighters as long as we have room and can afford it //buy units as long as we have room and can afford it
while(recruit(map,leader->first,options[rand()%options.size()].c_str(), while(recruit(options[rand()%options.size()])) {
gameinfo,team_num,current_team,min_gold,units,disp)) {
} }
@ -334,16 +318,16 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
//search through villages finding one to capture //search through villages finding one to capture
if(!leader_moved) { if(!leader_moved) {
const std::vector<gamemap::location>& villages = map.towers(); const std::vector<gamemap::location>& villages = map_.towers();
for(std::vector<gamemap::location>::const_iterator v = villages.begin(); for(std::vector<gamemap::location>::const_iterator v = villages.begin();
v != villages.end(); ++v) { v != villages.end(); ++v) {
const paths::routes_map::const_iterator itor = leader_paths.routes.find(*v); const paths::routes_map::const_iterator itor = leader_paths.routes.find(*v);
if(itor == leader_paths.routes.end() || units.count(*v) != 0) { if(itor == leader_paths.routes.end() || units_.count(*v) != 0) {
continue; continue;
} }
const int owner = tower_owner(*v,teams); const int owner = tower_owner(*v,teams_);
if(owner == -1 || current_team.is_enemy(owner+1) || leader->second.hitpoints() < leader->second.max_hitpoints()) { if(owner == -1 || current_team().is_enemy(owner+1) || leader->second.hitpoints() < leader->second.max_hitpoints()) {
//check that no enemies can reach the village //check that no enemies can reach the village
gamemap::location adj[6]; gamemap::location adj[6];
@ -357,8 +341,7 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
if(n != 6) if(n != 6)
continue; continue;
move_unit(gameinfo,disp,map,units,leader->first,*v, move_unit(leader->first,*v,possible_moves);
possible_moves,teams,team_num);
break; break;
} }
@ -373,9 +356,8 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
std::vector<attack_analysis> analysis; std::vector<attack_analysis> analysis;
if(consider_combat) { if(consider_combat_) {
analysis = analyze_targets(map,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc, analysis = analyze_targets(srcdst,dstsrc,enemy_srcdst,enemy_dstsrc);
units,current_team,team_num,state,gameinfo);
} }
int time_taken = SDL_GetTicks() - ticks; int time_taken = SDL_GetTicks() - ticks;
@ -397,14 +379,12 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
std::vector<attack_analysis>::iterator choice_it = analysis.end(); std::vector<attack_analysis>::iterator choice_it = analysis.end();
double choice_rating = -1000.0; double choice_rating = -1000.0;
for(std::vector<attack_analysis>::iterator it = analysis.begin(); for(std::vector<attack_analysis>::iterator it = analysis.begin(); it != analysis.end(); ++it) {
it != analysis.end(); ++it) { if(skip_num > 0 && ((it - analysis.begin())%skip_num) && it->movements.size() > 1)
if(skip_num > 0 && ((it - analysis.begin())%skip_num) &&
it->movements.size() > 1)
continue; continue;
const double rating = it->rating(current_team.aggression()); const double rating = it->rating(current_team().aggression());
std::cout << "attack option rated at " << rating << " (" << current_team.aggression() << ")\n"; std::cout << "attack option rated at " << rating << " (" << current_team().aggression() << ")\n";
if(rating > choice_rating) { if(rating > choice_rating) {
choice_it = it; choice_it = it;
choice_rating = rating; choice_rating = rating;
@ -420,52 +400,48 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
const location& target_loc = choice_it->target; const location& target_loc = choice_it->target;
const int weapon = choice_it->weapons[0]; const int weapon = choice_it->weapons[0];
const std::map<gamemap::location,unit>::const_iterator tgt = const unit_map::const_iterator tgt = units_.find(target_loc);
units.find(target_loc);
const bool defender_human = (tgt != units.end()) ? const bool defender_human = (tgt != units_.end()) ?
teams[tgt->second.side()-1].is_human() : false; teams_[tgt->second.side()-1].is_human() : false;
move_unit(gameinfo,disp,map,units,from,to, move_unit(from,to,possible_moves);
possible_moves,teams,team_num);
std::cerr << "attacking...\n"; std::cerr << "attacking...\n";
recorder.add_attack(to,target_loc,weapon); recorder.add_attack(to,target_loc,weapon);
game_events::fire("attack",to,target_loc); game_events::fire("attack",to,target_loc);
if(units.count(to) && units.count(target_loc)) { if(units_.count(to) && units_.count(target_loc)) {
attack(disp,map,to,target_loc,weapon,units,state,gameinfo,false); attack(disp_,map_,to,target_loc,weapon,units_,state_,gameinfo_,false);
check_victory(units,teams); check_victory(units_,teams_);
} }
std::cerr << "done attacking...\n"; std::cerr << "done attacking...\n";
//if this is the only unit in the planned attack, and the target //if this is the only unit in the planned attack, and the target
//is still alive, then also summon reinforcements //is still alive, then also summon reinforcements
if(choice_it->movements.size() == 1 && units.count(target_loc)) { if(choice_it->movements.size() == 1 && units_.count(target_loc)) {
additional_targets->push_back(target(target_loc,3.0)); additional_targets_.push_back(target(target_loc,3.0));
} }
dialogs::advance_unit(gameinfo,units,to,disp,true); dialogs::advance_unit(gameinfo_,units_,to,disp_,true);
dialogs::advance_unit(gameinfo,units,target_loc,disp,!defender_human); dialogs::advance_unit(gameinfo_,units_,target_loc,disp_,!defender_human);
do_move(disp,map,gameinfo,units,teams,team_num,state,consider_combat, do_move();
additional_targets);
return; return;
} else { } else {
log_scope("summoning reinforcements...\n"); log_scope("summoning reinforcements...\n");
consider_combat = false; consider_combat_ = false;
std::set<gamemap::location> already_done; std::set<gamemap::location> already_done;
for(std::vector<attack_analysis>::iterator it = analysis.begin(); for(std::vector<attack_analysis>::iterator it = analysis.begin(); it != analysis.end(); ++it) {
it != analysis.end(); ++it) {
assert(it->movements.empty() == false); assert(it->movements.empty() == false);
const gamemap::location& loc = it->movements.front().first; const gamemap::location& loc = it->movements.front().first;
if(already_done.count(loc) > 0) if(already_done.count(loc) > 0)
continue; continue;
additional_targets->push_back(target(loc,3.0)); additional_targets_.push_back(target(loc,3.0));
already_done.insert(loc); already_done.insert(loc);
} }
} }
@ -481,13 +457,13 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
//try to acquire towers //try to acquire towers
for(std::multimap<location,location>::const_iterator i = dstsrc.begin(); for(std::multimap<location,location>::const_iterator i = dstsrc.begin();
i != dstsrc.end(); ++i) { i != dstsrc.end(); ++i) {
if(map.underlying_terrain(map[i->first.x][i->first.y]) != gamemap::TOWER) if(map_.underlying_terrain(map_[i->first.x][i->first.y]) != gamemap::TOWER)
continue; continue;
bool want_tower = true, owned = false; bool want_tower = true, owned = false;
for(size_t j = 0; j != teams.size(); ++j) { for(size_t j = 0; j != teams_.size(); ++j) {
owned = teams[j].owns_tower(i->first); owned = teams_[j].owns_tower(i->first);
if(owned && !current_team.is_enemy(j+1)) { if(owned && !current_team().is_enemy(j+1)) {
want_tower = false; want_tower = false;
} }
@ -498,15 +474,15 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
//if it's a neutral tower, and we have no leader, then the village is no use to us, //if it's a neutral tower, and we have no leader, then the village is no use to us,
//and we don't want it. //and we don't want it.
if(!owned && leader == units.end()) if(!owned && leader == units_.end())
want_tower = false; want_tower = false;
if(want_tower) { if(want_tower) {
std::cerr << "trying to acquire village: " << i->first.x std::cerr << "trying to acquire village: " << i->first.x
<< ", " << i->first.y << "\n"; << ", " << i->first.y << "\n";
const std::map<location,unit>::iterator un = units.find(i->second); const std::map<location,unit>::iterator un = units_.find(i->second);
if(un == units.end()) { if(un == units_.end()) {
assert(false); assert(false);
return; return;
} }
@ -514,24 +490,21 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
if(un->second.is_guardian()) if(un->second.is_guardian())
continue; continue;
move_unit(gameinfo,disp,map,units,i->second,i->first, move_unit(i->second,i->first,possible_moves);
possible_moves,teams,team_num); do_move();
do_move(disp,map,gameinfo,units,teams,team_num,
state,consider_combat,additional_targets);
return; return;
} }
} }
//find units in need of healing //find units in need of healing
std::map<location,unit>::iterator u_it = units.begin(); unit_map::iterator u_it = units_.begin();
for(; u_it != units.end(); ++u_it) { for(; u_it != units_.end(); ++u_it) {
unit& u = u_it->second; unit& u = u_it->second;
//if the unit is on our side, has lost as many or more than 1/2 round //if the unit is on our side, has lost as many or more than 1/2 round
//worth of healing, and doesn't regenerate itself, then try to //worth of healing, and doesn't regenerate itself, then try to
//find a vacant village for it to rest in //find a vacant village for it to rest in
if(u.side() == team_num && if(u.side() == team_num_ &&
u.type().hitpoints() - u.hitpoints() >= game_config::cure_amount/2 && u.type().hitpoints() - u.hitpoints() >= game_config::cure_amount/2 &&
!u.type().regenerates()) { !u.type().regenerates()) {
@ -542,10 +515,10 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
Itor best_loc = it.second; Itor best_loc = it.second;
while(it.first != it.second) { while(it.first != it.second) {
const location& dst = it.first->second; const location& dst = it.first->second;
if(map.underlying_terrain(map[dst.x][dst.y]) == gamemap::TOWER && if(map_.underlying_terrain(map_[dst.x][dst.y]) == gamemap::TOWER &&
units.find(dst) == units.end()) { units_.find(dst) == units_.end()) {
const double vuln = power_projection(it.first->first, const double vuln = power_projection(it.first->first,
enemy_srcdst,enemy_dstsrc,units,map); enemy_srcdst,enemy_dstsrc);
if(vuln < best_vulnerability) { if(vuln < best_vulnerability) {
best_vulnerability = vuln; best_vulnerability = vuln;
best_loc = it.first; best_loc = it.first;
@ -562,25 +535,22 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
std::cerr << "moving unit to village for healing...\n"; std::cerr << "moving unit to village for healing...\n";
move_unit(gameinfo,disp,map,units,src,dst, move_unit(src,dst,possible_moves);
possible_moves,teams,team_num); do_move();
do_move(disp,map,gameinfo,units,teams,team_num,state,
consider_combat,additional_targets);
return; return;
} }
} }
} }
if(dstsrc.empty()) { if(dstsrc.empty()) {
do_move(disp,map,gameinfo,units,teams,team_num,state, do_move();
consider_combat,additional_targets);
return; return;
} }
std::cout << "finding targets...\n"; std::cout << "finding targets...\n";
std::vector<target> targets = find_targets(map,units,teams,team_num,leader != units.end()); std::vector<target> targets = find_targets(leader != units_.end());
targets.insert(targets.end(),additional_targets->begin(), targets.insert(targets.end(),additional_targets_.begin(),
additional_targets->end()); additional_targets_.end());
for(;;) { for(;;) {
if(targets.empty()) { if(targets.empty()) {
recorder.end_turn(); recorder.end_turn();
@ -588,12 +558,9 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
} }
std::cout << "choosing move...\n"; std::cout << "choosing move...\n";
std::pair<location,location> move = choose_move(targets,dstsrc,units, std::pair<location,location> move = choose_move(targets,dstsrc);
map,teams,team_num, for(std::vector<target>::const_iterator ittg = targets.begin(); ittg != targets.end(); ++ittg) {
gameinfo); assert(map_.on_board(ittg->loc));
for(std::vector<target>::const_iterator ittg = targets.begin();
ittg != targets.end(); ++ittg) {
assert(map.on_board(ittg->loc));
} }
@ -610,20 +577,18 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
gamemap::location target; gamemap::location target;
int weapon = -1; int weapon = -1;
for(int n = 0; n != 6; ++n) { for(int n = 0; n != 6; ++n) {
const unit_map::iterator enemy = units.find(adj[n]); const unit_map::iterator enemy = units_.find(adj[n]);
if(enemy != units.end() && if(enemy != units_.end() &&
current_team.is_enemy(enemy->second.side()) && current_team().is_enemy(enemy->second.side()) &&
!enemy->second.invisible(map[enemy->first.x][enemy->first.y])) { !enemy->second.invisible(map_[enemy->first.x][enemy->first.y])) {
target = adj[n]; target = adj[n];
weapon = choose_weapon(map,units,state,gameinfo,move.first, weapon = choose_weapon(move.first,target,bat_stats,
target,bat_stats, map_[move.second.x][move.second.y]);
map[move.second.x][move.second.y]);
break; break;
} }
} }
move_unit(gameinfo,disp,map,units,move.first,move.second, move_unit(move.first,move.second,possible_moves);
possible_moves,teams,team_num);
//if we're going to attack someone //if we're going to attack someone
if(weapon != -1) { if(weapon != -1) {
@ -633,21 +598,18 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
recorder.add_attack(attacker,target,weapon); recorder.add_attack(attacker,target,weapon);
game_events::fire("attack",attacker,target); game_events::fire("attack",attacker,target);
if(units.count(attacker) && units.count(target)) { if(units_.count(attacker) && units_.count(target)) {
attack(disp,map,attacker,target,weapon,units,state, attack(disp_,map_,attacker,target,weapon,units_,state_,gameinfo_,false);
gameinfo,false);
const std::map<gamemap::location,unit>::const_iterator tgt = const std::map<gamemap::location,unit>::const_iterator tgt = units_.find(target);
units.find(target);
const bool defender_human = (tgt != units.end()) ? const bool defender_human = (tgt != units_.end()) ?
teams[tgt->second.side()-1].is_human() : false; teams_[tgt->second.side()-1].is_human() : false;
dialogs::advance_unit(gameinfo_,units_,attacker,disp_,true);
dialogs::advance_unit(gameinfo_,units_,target,disp_,!defender_human);
dialogs::advance_unit(gameinfo,units,attacker,disp,true); check_victory(units_,teams_);
dialogs::advance_unit(gameinfo,units,target,disp,!defender_human);
check_victory(units,teams);
} }
} }
@ -659,9 +621,6 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
dstsrc.erase(del.first,del.second); dstsrc.erase(del.first,del.second);
} }
do_move(disp,map,gameinfo,units,teams,team_num,state, do_move();
consider_combat,additional_targets);
return; return;
} }
}

View file

@ -13,6 +13,7 @@
#ifndef AI_HPP_INCLUDED #ifndef AI_HPP_INCLUDED
#define AI_HPP_INCLUDED #define AI_HPP_INCLUDED
#include "actions.hpp"
#include "ai_move.hpp" #include "ai_move.hpp"
#include "display.hpp" #include "display.hpp"
#include "map.hpp" #include "map.hpp"
@ -21,15 +22,116 @@
#include <map> #include <map>
namespace ai { class ai {
typedef gamemap::location location; public:
typedef std::multimap<location,location> move_map;
typedef gamemap::location location;
typedef std::multimap<location,location> move_map;
ai(display& disp, const gamemap& map, const game_data& gameinfo,
std::map<gamemap::location,unit>& units,
std::vector<team>& teams, int team_num, const gamestatus& state);
void do_move();
/*
void do_move(display& disp, const gamemap& map, const game_data& gameinfo, void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
std::map<gamemap::location,unit>& units, std::map<gamemap::location,unit>& units,
std::vector<team>& teams, int team_num, const gamestatus& state, std::vector<team>& teams, int team_num, const gamestatus& state,
bool consider_combat=true, bool consider_combat=true,
std::vector<target>* additional_targets=NULL); std::vector<target>* additional_targets=NULL);
} */
private:
bool recruit(const std::string& usage);
void move_unit(const location& from, const location& to, std::map<location,paths>& possible_moves);
team& current_team();
struct attack_analysis
{
void analyze(const gamemap& map, std::map<location,unit>& units,
const gamestatus& status, const game_data& info, int sims,
class ai& ai_obj);
double rating(double aggression) const;
gamemap::location target;
std::vector<std::pair<gamemap::location,gamemap::location> > movements;
std::vector<int> weapons;
//the value of the unit being targeted
double target_value;
//the value on average, of units lost in the combat
double avg_losses;
//estimated % chance to kill the unit
double chance_to_kill;
//the average hitpoints damage inflicted
double avg_damage_inflicted;
int target_starting_damage;
//the average hitpoints damage taken
double avg_damage_taken;
//the sum of the values of units used in the attack
double resources_used;
//the weighted average of the % chance to hit each attacking unit
double terrain_quality;
//the ratio of the attacks the unit being attacked will get to
//the strength of its most powerful attack
double counter_strength_ratio;
double vulnerability, support;
};
void do_attack_analysis(
const location& loc,
const move_map& srcdst, const move_map& dstsrc,
const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
const location* tiles, bool* used_locations,
std::vector<location>& units,
std::vector<attack_analysis>& result,
attack_analysis& cur_analysis
);
double power_projection(const gamemap::location& loc, const move_map& srcdst, const move_map& dstsrc, bool use_terrain=true);
public:
int choose_weapon(const location& att, const location& def,
battle_stats& cur_stats, gamemap::TERRAIN terrain);
private:
std::vector<attack_analysis> analyze_targets(
const move_map& srcdst, const move_map& dstsrc,
const move_map& enemy_srcdst, const move_map& enemy_dstsrc
);
struct target {
target(const location& pos, double val) : loc(pos), value(val)
{}
location loc;
double value;
};
std::vector<target> find_targets(bool has_leader);
std::pair<location,location> choose_move(std::vector<target>& targets,const move_map& dstsrc);
display& disp_;
const gamemap& map_;
const game_data& gameinfo_;
unit_map& units_;
std::vector<team>& teams_;
int team_num_;
const gamestatus& state_;
bool consider_combat_;
std::vector<target> additional_targets_;
};
#endif #endif

View file

@ -25,26 +25,20 @@
#include <iostream> #include <iostream>
#include <set> #include <set>
namespace {
const int max_positions = 10000; const int max_positions = 10000;
using namespace ai;
//analyze possibility of attacking target on 'loc' //analyze possibility of attacking target on 'loc'
void do_analysis( void ai::do_attack_analysis(
const gamemap& map,
const location& loc, const location& loc,
const move_map& srcdst, const move_map& dstsrc, const move_map& srcdst, const move_map& dstsrc,
const move_map& enemy_srcdst, const move_map& enemy_dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
const location* tiles, bool* used_locations, const location* tiles, bool* used_locations,
std::vector<location>& units, std::vector<location>& units,
std::map<gamemap::location,unit>& units_map,
std::vector<attack_analysis>& result, std::vector<attack_analysis>& result,
const game_data& data, const gamestatus& status,
attack_analysis& cur_analysis attack_analysis& cur_analysis
) )
{ {
std::cerr << "doing attack analysis...\n";
if(cur_analysis.movements.size() >= 4) if(cur_analysis.movements.size() >= 4)
return; return;
@ -93,28 +87,27 @@ void do_analysis(
if(its.first == its.second) if(its.first == its.second)
continue; continue;
cur_analysis.movements.push_back( cur_analysis.movements.push_back(std::pair<location,location>(current_unit,tiles[j]));
std::pair<location,location>(current_unit,tiles[j]));
//find out how vulnerable we are to attack from enemy units in this hex //find out how vulnerable we are to attack from enemy units in this hex
const double vulnerability = power_projection(tiles[j],enemy_srcdst,enemy_dstsrc,units_map,map); const double vulnerability = power_projection(tiles[j],enemy_srcdst,enemy_dstsrc);
cur_analysis.vulnerability += vulnerability; cur_analysis.vulnerability += vulnerability;
//calculate how much support we have on this hex from allies. Support does not //calculate how much support we have on this hex from allies. Support does not
//take into account terrain, because we don't want to move into a hex that is //take into account terrain, because we don't want to move into a hex that is
//surrounded by good defensive terrain //surrounded by good defensive terrain
const double support = power_projection(tiles[j],srcdst,dstsrc,units_map,map,false); const double support = power_projection(tiles[j],srcdst,dstsrc,false);
cur_analysis.support += support; cur_analysis.support += support;
cur_analysis.analyze(map,units_map,status,data,50); cur_analysis.analyze(map_,units_,state_,gameinfo_,50,*this);
if(cur_analysis.rating(0.0) > rating_to_beat) { if(cur_analysis.rating(0.0) > rating_to_beat) {
result.push_back(cur_analysis); result.push_back(cur_analysis);
used_locations[j] = true; used_locations[j] = true;
do_analysis(map,loc,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc, do_attack_analysis(loc,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,
tiles,used_locations, tiles,used_locations,
units,units_map,result,data,status,cur_analysis); units,result,cur_analysis);
used_locations[j] = false; used_locations[j] = false;
} }
@ -128,9 +121,6 @@ void do_analysis(
} }
} }
}
namespace ai {
struct battle_type { struct battle_type {
battle_type(const gamemap::location& a, const gamemap::location& d, battle_type(const gamemap::location& a, const gamemap::location& d,
@ -161,21 +151,18 @@ bool operator==(const battle_type& a, const battle_type& b)
std::set<battle_type> weapon_choice_cache; std::set<battle_type> weapon_choice_cache;
int choose_weapon(const gamemap& map, std::map<location,unit>& units, int ai::choose_weapon(const location& att, const location& def,
const gamestatus& status, const game_data& info,
const location& att, const location& def,
battle_stats& cur_stats, gamemap::TERRAIN terrain) battle_stats& cur_stats, gamemap::TERRAIN terrain)
{ {
const std::map<location,unit>::const_iterator itor = units.find(att); const std::map<location,unit>::const_iterator itor = units_.find(att);
if(itor == units.end()) if(itor == units_.end())
return -1; return -1;
static int cache_hits = 0; static int cache_hits = 0;
static int cache_misses = 0; static int cache_misses = 0;
battle_type battle(att,def,terrain); battle_type battle(att,def,terrain);
const std::set<battle_type>::const_iterator cache_itor const std::set<battle_type>::const_iterator cache_itor = weapon_choice_cache.find(battle);
= weapon_choice_cache.find(battle);
if(cache_itor != weapon_choice_cache.end()) { if(cache_itor != weapon_choice_cache.end()) {
assert(*cache_itor == battle); assert(*cache_itor == battle);
@ -203,13 +190,13 @@ int choose_weapon(const gamemap& map, std::map<location,unit>& units,
const std::vector<attack_type>& attacks = itor->second.attacks(); const std::vector<attack_type>& attacks = itor->second.attacks();
assert(!attacks.empty()); assert(!attacks.empty());
const std::map<location,unit>::const_iterator d_itor = units.find(def); const unit_map::const_iterator d_itor = units_.find(def);
int d_hitpoints = d_itor->second.hitpoints(); int d_hitpoints = d_itor->second.hitpoints();
int a_hitpoints = itor->second.hitpoints(); int a_hitpoints = itor->second.hitpoints();
for(size_t a = 0; a != attacks.size(); ++a) { for(size_t a = 0; a != attacks.size(); ++a) {
const battle_stats stats = evaluate_battle_stats(map,att,def,a,units, const battle_stats stats = evaluate_battle_stats(map_,att,def,a,units_,
status,info,terrain,false); state_,gameinfo_,terrain,false);
//TODO: improve this rating formula! //TODO: improve this rating formula!
const double rating = const double rating =
@ -233,12 +220,12 @@ int choose_weapon(const gamemap& map, std::map<location,unit>& units,
return current_choice; return current_choice;
} }
void attack_analysis::analyze(const gamemap& map, void ai::attack_analysis::analyze(const gamemap& map,
std::map<location,unit>& units, unit_map& units,
const gamestatus& status, const gamestatus& status,
const game_data& info, int num_sims) const game_data& info, int num_sims, ai& ai_obj)
{ {
const std::map<location,unit>::const_iterator defend_it =units.find(target); const std::map<location,unit>::const_iterator defend_it = units.find(target);
assert(defend_it != units.end()); assert(defend_it != units.end());
target_value = defend_it->second.type().cost(); target_value = defend_it->second.type().cost();
@ -266,9 +253,7 @@ void attack_analysis::analyze(const gamemap& map,
std::vector<std::pair<location,location> >::const_iterator m; std::vector<std::pair<location,location> >::const_iterator m;
for(m = movements.begin(); m != movements.end(); ++m) { for(m = movements.begin(); m != movements.end(); ++m) {
battle_stats bat_stats; battle_stats bat_stats;
const int weapon = choose_weapon(map,units,status,info, const int weapon = ai_obj.choose_weapon(m->first,target, bat_stats, map[m->second.x][m->second.y]);
m->first,target, bat_stats,
map[m->second.x][m->second.y]);
assert(weapon != -1); assert(weapon != -1);
weapons.push_back(weapon); weapons.push_back(weapon);
@ -289,8 +274,7 @@ void attack_analysis::analyze(const gamemap& map,
int attacks = stat.nattacks; int attacks = stat.nattacks;
int defends = stat.ndefends; int defends = stat.ndefends;
std::map<location,unit>::const_iterator att unit_map::const_iterator att = units.find(movements[i].first);
= units.find(movements[i].first);
double cost = att->second.type().cost(); double cost = att->second.type().cost();
//up to double the value of a unit based on experience //up to double the value of a unit based on experience
@ -398,7 +382,7 @@ void attack_analysis::analyze(const gamemap& map,
avg_losses /= num_sims; avg_losses /= num_sims;
} }
double attack_analysis::rating(double aggression) const double ai::attack_analysis::rating(double aggression) const
{ {
double value = chance_to_kill*target_value - avg_losses; double value = chance_to_kill*target_value - avg_losses;
@ -414,13 +398,9 @@ double attack_analysis::rating(double aggression) const
return value; return value;
} }
std::vector<attack_analysis> analyze_targets( std::vector<ai::attack_analysis> ai::analyze_targets(
const gamemap& map,
const move_map& srcdst, const move_map& dstsrc, const move_map& srcdst, const move_map& dstsrc,
const move_map& enemy_srcdst, const move_map& enemy_dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc
std::map<location,unit>& units,
const team& current_team, int team_num,
const gamestatus& status, const game_data& data
) )
{ {
log_scope("analyzing targets..."); log_scope("analyzing targets...");
@ -430,9 +410,8 @@ std::vector<attack_analysis> analyze_targets(
std::vector<attack_analysis> res; std::vector<attack_analysis> res;
std::vector<location> unit_locs; std::vector<location> unit_locs;
for(std::map<location,unit>::const_iterator i = units.begin(); for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
i != units.end(); ++i) { if(i->second.side() == team_num_) {
if(i->second.side() == team_num) {
unit_locs.push_back(i->first); unit_locs.push_back(i->first);
} }
} }
@ -440,13 +419,12 @@ std::vector<attack_analysis> analyze_targets(
bool used_locations[6]; bool used_locations[6];
std::fill(used_locations,used_locations+6,false); std::fill(used_locations,used_locations+6,false);
for(std::map<location,unit>::const_iterator j = units.begin(); for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
j != units.end(); ++j) {
//attack anyone who is on the enemy side, and who is not invisible //attack anyone who is on the enemy side, and who is not invisible
if(current_team.is_enemy(j->second.side()) && if(current_team().is_enemy(j->second.side()) &&
j->second.invisible(map.underlying_terrain( j->second.invisible(map_.underlying_terrain(map_[j->first.x][j->first.y])) == false) {
map[j->first.x][j->first.y])) == false) { std::cerr << "analyzing attack on " << j->first.x+1 << "," << j->first.y+1 << "\n";
location adjacent[6]; location adjacent[6];
get_adjacent_tiles(j->first,adjacent); get_adjacent_tiles(j->first,adjacent);
attack_analysis analysis; attack_analysis analysis;
@ -456,9 +434,8 @@ std::vector<attack_analysis> analyze_targets(
const int ticks = SDL_GetTicks(); const int ticks = SDL_GetTicks();
do_analysis(map,j->first,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc, do_attack_analysis(j->first,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,
adjacent,used_locations,unit_locs,units, adjacent,used_locations,unit_locs,res,analysis);
res,data,status,analysis);
const int time_taken = SDL_GetTicks() - ticks; const int time_taken = SDL_GetTicks() - ticks;
static int max_time = 0; static int max_time = 0;
@ -472,9 +449,7 @@ std::vector<attack_analysis> analyze_targets(
return res; return res;
} }
double power_projection(const gamemap::location& loc, double ai::power_projection(const gamemap::location& loc, const move_map& srcdst, const move_map& dstsrc, bool use_terrain)
const move_map& srcdst, const move_map& dstsrc,
const unit_map& units, const gamemap& map, bool use_terrain)
{ {
static gamemap::location used_locs[6]; static gamemap::location used_locs[6];
static double ratings[6]; static double ratings[6];
@ -486,11 +461,11 @@ double power_projection(const gamemap::location& loc,
double res = 0.0; double res = 0.0;
for(int i = 0; i != 6; ++i) { for(int i = 0; i != 6; ++i) {
if(map.on_board(locs[i]) == false) { if(map_.on_board(locs[i]) == false) {
continue; continue;
} }
const gamemap::TERRAIN terrain = map[locs[i].x][locs[i].y]; const gamemap::TERRAIN terrain = map_[locs[i].x][locs[i].y];
typedef move_map::const_iterator Itor; typedef move_map::const_iterator Itor;
typedef std::pair<Itor,Itor> Range; typedef std::pair<Itor,Itor> Range;
@ -508,10 +483,10 @@ double power_projection(const gamemap::location& loc,
continue; continue;
} }
const unit_map::const_iterator u = units.find(it->second); const unit_map::const_iterator u = units_.find(it->second);
//unit might have been killed, and no longer exist //unit might have been killed, and no longer exist
if(u == units.end()) { if(u == units_.end()) {
continue; continue;
} }
@ -527,10 +502,10 @@ double power_projection(const gamemap::location& loc,
most_damage = damage; most_damage = damage;
} }
const bool village = map.underlying_terrain(terrain) == gamemap::TOWER; const bool village = map_.underlying_terrain(terrain) == gamemap::TOWER;
const double village_bonus = (use_terrain && village) ? 1.5 : 1.0; const double village_bonus = (use_terrain && village) ? 1.5 : 1.0;
const double defense = use_terrain ? double(100 - un.defense_modifier(map,terrain))/100.0 : 0.5; const double defense = use_terrain ? double(100 - un.defense_modifier(map_,terrain))/100.0 : 0.5;
const double rating = village_bonus*hp*defense*double(most_damage); const double rating = village_bonus*hp*defense*double(most_damage);
if(rating > best_rating) { if(rating > best_rating) {
best_rating = rating; best_rating = rating;
@ -543,8 +518,7 @@ double power_projection(const gamemap::location& loc,
//a better position to attack from //a better position to attack from
if(n == 1 && best_unit.valid()) { if(n == 1 && best_unit.valid()) {
end_used = beg_used + num_used_locs; end_used = beg_used + num_used_locs;
gamemap::location* const pos gamemap::location* const pos = std::find(beg_used,end_used,best_unit);
= std::find(beg_used,end_used,best_unit);
const int index = pos - beg_used; const int index = pos - beg_used;
if(best_rating >= ratings[index]) { if(best_rating >= ratings[index]) {
res -= ratings[index]; res -= ratings[index];
@ -574,5 +548,3 @@ double power_projection(const gamemap::location& loc,
return res; return res;
} }
}

View file

@ -23,67 +23,4 @@
#include <map> #include <map>
#include <vector> #include <vector>
namespace ai {
double power_projection(const gamemap::location& loc,
const move_map& srcdst, const move_map& dstsrc,
const unit_map& units, const gamemap& map, bool use_terrain=true);
int choose_weapon(const gamemap& map, std::map<location,unit>& units,
const gamestatus& status, const game_data& info,
const location& att, const location& def,
battle_stats& cur_stats, gamemap::TERRAIN terrain);
struct attack_analysis
{
void analyze(const gamemap& map, std::map<location,unit>& units,
const gamestatus& status, const game_data& info, int sims);
double rating(double aggression) const;
gamemap::location target;
std::vector<std::pair<gamemap::location,gamemap::location> > movements;
std::vector<int> weapons;
//the value of the unit being targeted
double target_value;
//the value on average, of units lost in the combat
double avg_losses;
//estimated % chance to kill the unit
double chance_to_kill;
//the average hitpoints damage inflicted
double avg_damage_inflicted;
int target_starting_damage;
//the average hitpoints damage taken
double avg_damage_taken;
//the sum of the values of units used in the attack
double resources_used;
//the weighted average of the % chance to hit each attacking unit
double terrain_quality;
//the ratio of the attacks the unit being attacked will get to
//the strength of its most powerful attack
double counter_strength_ratio;
double vulnerability, support;
};
std::vector<attack_analysis> analyze_targets(
const gamemap& map,
const move_map& srcdst, const move_map& dstsrc,
const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
std::map<location,unit>& units,
const team& current_team, int team_num,
const gamestatus& status, const game_data& data
);
}
#endif #endif

View file

@ -14,31 +14,30 @@
#include "display.hpp" #include "display.hpp"
#include "game_config.hpp" #include "game_config.hpp"
#include "log.hpp" #include "log.hpp"
#include "map.hpp"
#include "util.hpp" #include "util.hpp"
#include <iostream> #include <iostream>
namespace ai {
struct move_cost_calculator struct move_cost_calculator
{ {
move_cost_calculator(const unit& u, const gamemap& map, move_cost_calculator(const unit& u, const gamemap& map,
const game_data& data, const game_data& data,
const std::map<location,unit>& units, const unit_map& units,
const gamemap::location& loc, const gamemap::location& loc,
const std::multimap<location,location>& dstsrc) const std::multimap<gamemap::location,gamemap::location>& dstsrc)
: unit_(u), map_(map), data_(data), units_(units), : unit_(u), map_(map), data_(data), units_(units),
move_type_(u.type().movement_type()), loc_(loc), dstsrc_(dstsrc) move_type_(u.type().movement_type()), loc_(loc), dstsrc_(dstsrc)
{} {}
double cost(const location& loc, double so_far) const double cost(const gamemap::location& loc, double so_far) const
{ {
if(!map_.on_board(loc)) if(!map_.on_board(loc))
return 1000.0; return 1000.0;
//if this unit can move to that location this turn, it has a very //if this unit can move to that location this turn, it has a very
//very low cost //very low cost
typedef std::multimap<location,location>::const_iterator Itor; typedef std::multimap<gamemap::location,gamemap::location>::const_iterator Itor;
std::pair<Itor,Itor> range = dstsrc_.equal_range(loc); std::pair<Itor,Itor> range = dstsrc_.equal_range(loc);
while(range.first != range.second) { while(range.first != range.second) {
if(range.first->second == loc_) if(range.first->second == loc_)
@ -83,52 +82,47 @@ private:
const unit& unit_; const unit& unit_;
const gamemap& map_; const gamemap& map_;
const game_data& data_; const game_data& data_;
const std::map<location,unit>& units_; const unit_map& units_;
const unit_movement_type& move_type_; const unit_movement_type& move_type_;
const gamemap::location loc_; const gamemap::location loc_;
const std::multimap<location,location> dstsrc_; const ai::move_map dstsrc_;
}; };
std::vector<target> find_targets( std::vector<ai::target> ai::find_targets(bool has_leader)
const gamemap& map, std::map<location,unit>& units,
std::vector<team>& teams, int current_team, bool has_leader
)
{ {
log_scope("finding targets..."); log_scope("finding targets...");
team& tm = teams[current_team-1];
std::vector<target> targets; std::vector<target> targets;
if(has_leader && tm.village_value() > 0.0) { if(has_leader && current_team().village_value() > 0.0) {
const std::vector<location>& towers = map.towers(); const std::vector<location>& towers = map_.towers();
for(std::vector<location>::const_iterator t = towers.begin(); t != towers.end(); ++t) { for(std::vector<location>::const_iterator t = towers.begin(); t != towers.end(); ++t) {
assert(map.on_board(*t)); assert(map.on_board(*t));
bool get_tower = true; bool get_tower = true;
for(size_t i = 0; i != teams.size(); ++i) { for(size_t i = 0; i != teams_.size(); ++i) {
if(!tm.is_enemy(i+1) && teams[i].owns_tower(*t)) { if(!current_team().is_enemy(i+1) && teams_[i].owns_tower(*t)) {
get_tower = false; get_tower = false;
break; break;
} }
} }
if(get_tower) { if(get_tower) {
targets.push_back(target(*t,tm.village_value())); targets.push_back(target(*t,current_team().village_value()));
} }
} }
} }
std::vector<team::target>& team_targets = tm.targets(); std::vector<team::target>& team_targets = current_team().targets();
//find the enemy leaders and explicit targets //find the enemy leaders and explicit targets
std::map<location,unit>::const_iterator u; unit_map::const_iterator u;
for(u = units.begin(); u != units.end(); ++u) { for(u = units_.begin(); u != units_.end(); ++u) {
//is an enemy leader //is an enemy leader
if(u->second.can_recruit() && tm.is_enemy(u->second.side())) { if(u->second.can_recruit() && current_team().is_enemy(u->second.side())) {
assert(map.on_board(u->first)); assert(map_.on_board(u->first));
targets.push_back(target(u->first,tm.leader_value())); targets.push_back(target(u->first,current_team().leader_value()));
} }
//explicit targets for this team //explicit targets for this team
@ -148,8 +142,7 @@ std::vector<target> find_targets(
new_values.push_back(i->value); new_values.push_back(i->value);
for(std::vector<target>::const_iterator j = targets.begin(); for(std::vector<target>::const_iterator j = targets.begin(); j != targets.end(); ++j) {
j != targets.end(); ++j) {
if(i->loc == j->loc) { if(i->loc == j->loc) {
continue; continue;
} }
@ -169,14 +162,7 @@ std::vector<target> find_targets(
return targets; return targets;
} }
std::pair<location,location> choose_move( std::pair<gamemap::location,gamemap::location> ai::choose_move(std::vector<ai::target>& targets,const std::multimap<location,location>& dstsrc)
std::vector<target>& targets,
const std::multimap<location,location>& dstsrc,
std::map<location,unit>& units,
const gamemap& map, const std::vector<team>& teams,
int current_team,
const game_data& data
)
{ {
log_scope("choosing move"); log_scope("choosing move");
@ -186,21 +172,20 @@ std::pair<location,location> choose_move(
} }
paths::route best_route; paths::route best_route;
std::map<location,unit>::iterator best = units.end(); unit_map::iterator best = units_.end();
double best_rating = 0.1; double best_rating = 0.1;
std::vector<target>::iterator best_target = targets.end(); std::vector<target>::iterator best_target = targets.end();
std::map<location,unit>::iterator u; unit_map::iterator u;
//find the first eligible unit //find the first eligible unit
for(u = units.begin(); u != units.end(); ++u) { for(u = units_.begin(); u != units_.end(); ++u) {
if(!(u->second.side() != current_team || u->second.can_recruit() || if(!(u->second.side() != team_num_ || u->second.can_recruit() || u->second.movement_left() <= 0)) {
u->second.movement_left() <= 0)) {
break; break;
} }
} }
if(u == units.end()) { if(u == units_.end()) {
std::cout << "no eligible units found\n"; std::cout << "no eligible units found\n";
return std::pair<location,location>(); return std::pair<location,location>();
} }
@ -211,8 +196,7 @@ std::pair<location,location> choose_move(
return std::pair<location,location>(u->first,u->first); return std::pair<location,location>(u->first,u->first);
} }
const move_cost_calculator cost_calc(u->second,map,data,units, const move_cost_calculator cost_calc(u->second,map_,gameinfo_,units_,u->first,dstsrc);
u->first,dstsrc);
//choose the best target for that unit //choose the best target for that unit
for(std::vector<target>::iterator tg = targets.begin(); tg != targets.end(); ++tg) { for(std::vector<target>::iterator tg = targets.begin(); tg != targets.end(); ++tg) {
@ -236,18 +220,17 @@ std::pair<location,location> choose_move(
} }
//now see if any other unit can put a better bid forward //now see if any other unit can put a better bid forward
for(++u; u != units.end(); ++u) { for(++u; u != units_.end(); ++u) {
if(u->second.side() != current_team || u->second.can_recruit() || if(u->second.side() != team_num_ || u->second.can_recruit() ||
u->second.movement_left() <= 0 || u->second.is_guardian()) { u->second.movement_left() <= 0 || u->second.is_guardian()) {
continue; continue;
} }
const move_cost_calculator calc(u->second,map,data,units, const move_cost_calculator calc(u->second,map_,gameinfo_,units_,u->first,dstsrc);
u->first,dstsrc);
const paths::route cur_route = a_star_search(u->first,best_target->loc, const paths::route cur_route = a_star_search(u->first,best_target->loc,
minimum(best_target->value/best_rating,100.0),calc); minimum(best_target->value/best_rating,100.0),calc);
const double rating = best_target->value/cur_route.move_left; const double rating = best_target->value/cur_route.move_left;
if(best == units.end() || rating > best_rating) { if(best == units_.end() || rating > best_rating) {
best_rating = rating; best_rating = rating;
best = u; best = u;
best_route = cur_route; best_route = cur_route;
@ -259,8 +242,7 @@ std::pair<location,location> choose_move(
if(best_target->value <= 0.0) if(best_target->value <= 0.0)
targets.erase(best_target); targets.erase(best_target);
for(ittg = targets.begin(); for(ittg = targets.begin(); ittg != targets.end(); ++ittg) {
ittg != targets.end(); ++ittg) {
assert(map.on_board(ittg->loc)); assert(map.on_board(ittg->loc));
} }
@ -283,7 +265,7 @@ std::pair<location,location> choose_move(
} }
} }
if(best != units.end()) { if(best != units_.end()) {
std::cout << "Could not make good move, staying still\n"; std::cout << "Could not make good move, staying still\n";
return std::pair<location,location>(best->first,best->first); return std::pair<location,location>(best->first,best->first);
} }
@ -291,5 +273,3 @@ std::pair<location,location> choose_move(
std::cout << "Could not find anywhere to move!\n"; std::cout << "Could not find anywhere to move!\n";
return std::pair<location,location>(); return std::pair<location,location>();
} }
}

View file

@ -14,37 +14,11 @@
#define AI_MOVE_H_INCLUDED #define AI_MOVE_H_INCLUDED
#include "map.hpp" #include "map.hpp"
#include "ai.hpp"
#include "pathfind.hpp" #include "pathfind.hpp"
#include "unit.hpp" #include "unit.hpp"
#include "unit_types.hpp" #include "unit_types.hpp"
#include <map> #include <map>
namespace ai {
typedef gamemap::location location;
struct target {
target(const location& pos, double val) : loc(pos), value(val)
{}
location loc;
double value;
};
std::vector<target> find_targets(
const gamemap& map, std::map<location,unit>& units,
std::vector<team>& teams, int current_team, bool has_leader
);
std::pair<location,location> choose_move(
std::vector<target>& targets,
const std::multimap<location,location>& dstsrc,
std::map<location,unit>& units,
const gamemap& map, const std::vector<team>& teams,
int current_team,
const game_data& data
);
}
#endif #endif

View file

@ -161,7 +161,7 @@ void find_routes(const gamemap& map, const game_data& gamedata,
//teleport to //teleport to
for(std::vector<gamemap::location>::const_iterator t = towers.begin(); for(std::vector<gamemap::location>::const_iterator t = towers.begin();
t != towers.end(); ++t) { t != towers.end(); ++t) {
if(!current_team.owns_tower(*t)) if(!current_team.owns_tower(*t) || units.count(*t))
continue; continue;
locs.push_back(*t); locs.push_back(*t);

View file

@ -391,8 +391,7 @@ redo_turn:
update_locker lock(gui,!preferences::show_ai_moves()); update_locker lock(gui,!preferences::show_ai_moves());
ai::do_move(gui,map,gameinfo,units,teams, ai(gui,map,gameinfo,units,teams,player_number,status).do_move();
player_number,status);
if(network::nconnections() > 0) { if(network::nconnections() > 0) {
config cfg; config cfg;

View file

@ -92,8 +92,15 @@ void play_turn(game_data& gameinfo, game_state& state_of_game,
unit u = ui->second; unit u = ui->second;
const shortest_path_calculator calc(u,current_team,units,map); const shortest_path_calculator calc(u,current_team,units,map);
const std::set<gamemap::location>* const teleports = const std::set<gamemap::location>* teleports = NULL;
u.type().teleports() ? &current_team.towers() : NULL;
std::set<gamemap::location> allowed_teleports;
if(u.type().teleports()) {
allowed_teleports = vacant_towers(current_team.towers(),units);
teleports = &allowed_teleports;
if(current_team.towers().count(ui->first))
allowed_teleports.insert(ui->first);
}
paths::route route = a_star_search(ui->first,ui->second.get_goto(), paths::route route = a_star_search(ui->first,ui->second.get_goto(),
10000.0,calc,teleports); 10000.0,calc,teleports);
@ -275,8 +282,15 @@ void turn_info::mouse_motion(const SDL_MouseMotionEvent& event)
units_,map_); units_,map_);
const bool can_teleport = un->second.type().teleports(); const bool can_teleport = un->second.type().teleports();
const std::set<gamemap::location>* const teleports = const std::set<gamemap::location>* teleports = NULL;
can_teleport ? &current_team.towers() : NULL;
std::set<gamemap::location> allowed_teleports;
if(can_teleport) {
allowed_teleports = vacant_towers(current_team.towers(),units_);
teleports = &allowed_teleports;
if(current_team.towers().count(un->first))
allowed_teleports.insert(un->first);
}
current_route_ = a_star_search(selected_hex_,new_hex, current_route_ = a_star_search(selected_hex_,new_hex,
10000.0,calc,teleports); 10000.0,calc,teleports);
@ -594,8 +608,16 @@ void turn_info::left_click(const SDL_MouseButtonEvent& event)
const shortest_path_calculator calc(u,current_team, const shortest_path_calculator calc(u,current_team,
units_,map_); units_,map_);
const std::set<gamemap::location>* const teleports = const std::set<gamemap::location>* teleports = NULL;
teleport ? &current_team.towers() : NULL;
std::set<gamemap::location> allowed_teleports;
if(u.type().teleports()) {
allowed_teleports = vacant_towers(current_team.towers(),units_);
teleports = &allowed_teleports;
if(current_team.towers().count(it->first))
allowed_teleports.insert(it->first);
}
paths::route route = a_star_search(it->first,go_to, paths::route route = a_star_search(it->first,go_to,
10000.0,calc,teleports); 10000.0,calc,teleports);

View file

@ -511,3 +511,16 @@ int team::nteams()
return teams->size(); return teams->size();
} }
} }
const std::set<gamemap::location> vacant_towers(const std::set<gamemap::location>& towers, const unit_map& units)
{
std::set<gamemap::location> res;
for(std::set<gamemap::location>::const_iterator i = towers.begin(); i != towers.end(); ++i) {
if(units.count(*i) == 0) {
res.insert(*i);
}
}
return res;
}

View file

@ -202,4 +202,6 @@ team_data calculate_team_data(const class team& tm, int side, const unit_map& un
std::string get_team_name(int side, const unit_map& units); std::string get_team_name(int side, const unit_map& units);
const std::set<gamemap::location> vacant_towers(const std::set<gamemap::location>& towers, const unit_map& units);
#endif #endif