restructure of AI code
This commit is contained in:
parent
ccf31d5a30
commit
1c3e548d92
12 changed files with 374 additions and 414 deletions
|
@ -12,7 +12,7 @@ advanceto=null
|
|||
cost=39
|
||||
ability=leadership
|
||||
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
|
||||
[attack]
|
||||
name=axe
|
||||
|
|
309
src/ai.cpp
309
src/ai.cpp
|
@ -25,13 +25,18 @@
|
|||
|
||||
#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,
|
||||
const std::string& usage, const game_data& gameinfo,
|
||||
int team_num, team& tm, int min_gold,
|
||||
std::map<gamemap::location,unit>& units, display& disp)
|
||||
bool ai::recruit(const std::string& usage)
|
||||
{
|
||||
const int min_gold = 0;
|
||||
|
||||
log_scope("recruiting troops");
|
||||
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
|
||||
//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 =
|
||||
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())
|
||||
&& tm.gold() - i->second.cost() > min_gold) {
|
||||
&& current_team().gold() - i->second.cost() > min_gold) {
|
||||
|
||||
options.push_back(i);
|
||||
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
|
||||
if(options.empty() == false) {
|
||||
|
||||
const gamemap::location loc = gamemap::location::null_location;
|
||||
const int option = rand()%options.size();
|
||||
|
||||
|
@ -67,13 +71,11 @@ bool recruit(const gamemap& map, const gamemap::location& leader,
|
|||
replay_undo replay_guard(recorder);
|
||||
|
||||
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)
|
||||
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";
|
||||
|
||||
tm.spend_gold(u.cost());
|
||||
if(recruit_unit(map_,team_num_,units_,new_unit,loc,&disp_).empty()) {
|
||||
current_team().spend_gold(u.cost());
|
||||
|
||||
//confirm the transaction - i.e. don't undo recruitment
|
||||
replay_guard.confirm_transaction();
|
||||
|
@ -86,25 +88,21 @@ bool recruit(const gamemap& map, const gamemap::location& leader,
|
|||
return false;
|
||||
}
|
||||
|
||||
team& ai::current_team()
|
||||
{
|
||||
return teams_[team_num_-1];
|
||||
}
|
||||
|
||||
namespace ai {
|
||||
|
||||
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)
|
||||
void ai::move_unit(const location& from, const location& to, std::map<location,paths>& possible_moves)
|
||||
{
|
||||
assert(units.find(to) == units.end() || from == to);
|
||||
assert(units_.find(to) == units_.end() || from == to);
|
||||
|
||||
disp.select_hex(from);
|
||||
disp.update_display();
|
||||
disp_.select_hex(from);
|
||||
disp_.update_display();
|
||||
|
||||
log_scope("move_unit");
|
||||
std::map<location,unit>::iterator u_it = units.find(from);
|
||||
if(u_it == units.end()) {
|
||||
unit_map::iterator u_it = units_.find(from);
|
||||
if(u_it == units_.end()) {
|
||||
std::cout << "Could not find unit at " << from.x << ", "
|
||||
<< from.y << "\n";
|
||||
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 teleport = u_it->second.type().teleports();
|
||||
paths current_paths = paths(map,gameinfo,units,from,teams,
|
||||
ignore_zocs,teleport);
|
||||
paths_wiper wiper(disp);
|
||||
paths current_paths = paths(map_,gameinfo_,units_,from,teams_,ignore_zocs,teleport);
|
||||
paths_wiper wiper(disp_);
|
||||
|
||||
if(!disp.fogged(from.x,from.y))
|
||||
disp.set_paths(¤t_paths);
|
||||
if(!disp_.fogged(from.x,from.y))
|
||||
disp_.set_paths(¤t_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;
|
||||
units.erase(u_it);
|
||||
units_.erase(u_it);
|
||||
|
||||
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()) {
|
||||
std::vector<location> steps = rt->second.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);
|
||||
units.insert(std::pair<location,unit>(to,current_unit));
|
||||
if(map.underlying_terrain(map[to.x][to.y]) == gamemap::TOWER)
|
||||
get_tower(to,teams,team_num-1,units);
|
||||
units_.insert(std::pair<location,unit>(to,current_unit));
|
||||
if(map_.underlying_terrain(map_[to.x][to.y]) == gamemap::TOWER)
|
||||
get_tower(to,teams_,team_num_-1,units_);
|
||||
|
||||
disp.draw_tile(to.x,to.y);
|
||||
disp.draw();
|
||||
disp_.draw_tile(to.x,to.y);
|
||||
disp_.draw();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void do_move(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)
|
||||
|
||||
void ai::do_move()
|
||||
{
|
||||
std::vector<target> tgts;
|
||||
if(additional_targets == NULL)
|
||||
additional_targets = &tgts;
|
||||
|
||||
log_scope("doing ai move");
|
||||
|
||||
team& current_team = teams[team_num-1];
|
||||
|
||||
typedef paths::route route;
|
||||
|
||||
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;
|
||||
moves_map possible_moves;
|
||||
|
||||
for(std::map<gamemap::location,unit>::iterator un_it = units.begin();
|
||||
un_it != units.end(); ++un_it) {
|
||||
for(std::map<gamemap::location,unit>::iterator un_it = units_.begin();
|
||||
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);
|
||||
enemy_srcdst.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 teleports = un_it->second.type().teleports();
|
||||
const paths new_paths(map,gameinfo,units,
|
||||
un_it->first,teams,ignore_zocs,teleports);
|
||||
const paths new_paths(map_,gameinfo_,units_,
|
||||
un_it->first,teams_,ignore_zocs,teleports);
|
||||
for(paths::routes_map::const_iterator rt = new_paths.routes.begin();
|
||||
rt != new_paths.routes.end(); ++rt) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 teleports = un_it->second.type().teleports();
|
||||
possible_moves.insert(std::pair<gamemap::location,paths>(
|
||||
un_it->first,paths(map,gameinfo,units,
|
||||
un_it->first,teams,ignore_zocs,teleports)));
|
||||
un_it->first,paths(map_,gameinfo_,units_,
|
||||
un_it->first,teams_,ignore_zocs,teleports)));
|
||||
}
|
||||
|
||||
|
||||
for(moves_map::iterator m = possible_moves.begin();
|
||||
m != possible_moves.end(); ++m) {
|
||||
for(paths::routes_map::iterator rtit =
|
||||
m->second.routes.begin(); rtit != m->second.routes.end();
|
||||
++rtit) {
|
||||
m->second.routes.begin(); rtit != m->second.routes.end(); ++rtit) {
|
||||
const location& src = m->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));
|
||||
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
|
||||
//take stock of our current set of units
|
||||
if(srcdst.empty() || leader != units.end() && srcdst.count(leader->first) == srcdst.size()) {
|
||||
if(leader == units.end()) {
|
||||
if(srcdst.empty() || leader != units_.end() && srcdst.count(leader->first) == srcdst.size()) {
|
||||
if(leader == units_.end()) {
|
||||
recorder.end_turn();
|
||||
return;
|
||||
}
|
||||
|
||||
//find where the leader can move
|
||||
const paths leader_paths(map,gameinfo,units,leader->first,teams,false,false);
|
||||
const gamemap::location& start_pos = map.starting_position(leader->second.side());
|
||||
const paths leader_paths(map_,gameinfo_,units_,leader->first,teams_,false,false);
|
||||
const gamemap::location& start_pos = map_.starting_position(leader->second.side());
|
||||
|
||||
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) {
|
||||
leader_moved = true;
|
||||
const paths::routes_map::const_iterator itor = leader_paths.routes.find(start_pos);
|
||||
if(itor != leader_paths.routes.end() && units.count(start_pos) == 0) {
|
||||
move_unit(gameinfo,disp,map,units,leader->first,start_pos,
|
||||
possible_moves,teams,team_num);
|
||||
if(itor != leader_paths.routes.end() && units_.count(start_pos) == 0) {
|
||||
move_unit(leader->first,start_pos,possible_moves);
|
||||
|
||||
leader = find_leader(units,team_num);
|
||||
assert(leader != units.end());
|
||||
leader = find_leader(units_,team_num_);
|
||||
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!
|
||||
const int min_gold = 0;
|
||||
|
||||
const int towers = map.towers().size();
|
||||
const int towers = map_.towers().size();
|
||||
int taken_towers = 0;
|
||||
for(size_t j = 0; j != teams.size(); ++j) {
|
||||
taken_towers += teams[j].towers().size();
|
||||
for(size_t j = 0; j != teams_.size(); ++j) {
|
||||
taken_towers += teams_[j].towers().size();
|
||||
}
|
||||
|
||||
const int neutral_towers = towers - taken_towers;
|
||||
|
||||
//get scouts depending on how many neutral villages there are
|
||||
int scouts_wanted = current_team.villages_per_scout() > 0 ?
|
||||
neutral_towers/current_team.villages_per_scout() : 0;
|
||||
int scouts_wanted = current_team().villages_per_scout() > 0 ?
|
||||
neutral_towers/current_team().villages_per_scout() : 0;
|
||||
|
||||
std::map<std::string,int> unit_types;
|
||||
while(unit_types["scout"] < scouts_wanted) {
|
||||
if(recruit(map,leader->first,"scout",gameinfo,team_num,current_team,
|
||||
min_gold,units,disp) == false)
|
||||
if(recruit("scout") == false)
|
||||
break;
|
||||
|
||||
++unit_types["scout"];
|
||||
}
|
||||
|
||||
const std::vector<std::string>& options =
|
||||
current_team.recruitment_pattern();
|
||||
const std::vector<std::string>& options = current_team().recruitment_pattern();
|
||||
|
||||
if(options.empty()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
//buy fighters as long as we have room and can afford it
|
||||
while(recruit(map,leader->first,options[rand()%options.size()].c_str(),
|
||||
gameinfo,team_num,current_team,min_gold,units,disp)) {
|
||||
//buy units as long as we have room and can afford it
|
||||
while(recruit(options[rand()%options.size()])) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -334,16 +318,16 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
|
||||
//search through villages finding one to capture
|
||||
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();
|
||||
v != villages.end(); ++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;
|
||||
}
|
||||
|
||||
const int owner = tower_owner(*v,teams);
|
||||
if(owner == -1 || current_team.is_enemy(owner+1) || leader->second.hitpoints() < leader->second.max_hitpoints()) {
|
||||
const int owner = tower_owner(*v,teams_);
|
||||
if(owner == -1 || current_team().is_enemy(owner+1) || leader->second.hitpoints() < leader->second.max_hitpoints()) {
|
||||
|
||||
//check that no enemies can reach the village
|
||||
gamemap::location adj[6];
|
||||
|
@ -357,8 +341,7 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
if(n != 6)
|
||||
continue;
|
||||
|
||||
move_unit(gameinfo,disp,map,units,leader->first,*v,
|
||||
possible_moves,teams,team_num);
|
||||
move_unit(leader->first,*v,possible_moves);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -373,9 +356,8 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
|
||||
std::vector<attack_analysis> analysis;
|
||||
|
||||
if(consider_combat) {
|
||||
analysis = analyze_targets(map,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
units,current_team,team_num,state,gameinfo);
|
||||
if(consider_combat_) {
|
||||
analysis = analyze_targets(srcdst,dstsrc,enemy_srcdst,enemy_dstsrc);
|
||||
}
|
||||
|
||||
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();
|
||||
double choice_rating = -1000.0;
|
||||
for(std::vector<attack_analysis>::iterator it = analysis.begin();
|
||||
it != analysis.end(); ++it) {
|
||||
if(skip_num > 0 && ((it - analysis.begin())%skip_num) &&
|
||||
it->movements.size() > 1)
|
||||
for(std::vector<attack_analysis>::iterator it = analysis.begin(); it != analysis.end(); ++it) {
|
||||
if(skip_num > 0 && ((it - analysis.begin())%skip_num) && it->movements.size() > 1)
|
||||
continue;
|
||||
|
||||
const double rating = it->rating(current_team.aggression());
|
||||
std::cout << "attack option rated at " << rating << " (" << current_team.aggression() << ")\n";
|
||||
const double rating = it->rating(current_team().aggression());
|
||||
std::cout << "attack option rated at " << rating << " (" << current_team().aggression() << ")\n";
|
||||
if(rating > choice_rating) {
|
||||
choice_it = it;
|
||||
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 int weapon = choice_it->weapons[0];
|
||||
|
||||
const std::map<gamemap::location,unit>::const_iterator tgt =
|
||||
units.find(target_loc);
|
||||
const unit_map::const_iterator tgt = units_.find(target_loc);
|
||||
|
||||
const bool defender_human = (tgt != units.end()) ?
|
||||
teams[tgt->second.side()-1].is_human() : false;
|
||||
const bool defender_human = (tgt != units_.end()) ?
|
||||
teams_[tgt->second.side()-1].is_human() : false;
|
||||
|
||||
move_unit(gameinfo,disp,map,units,from,to,
|
||||
possible_moves,teams,team_num);
|
||||
move_unit(from,to,possible_moves);
|
||||
|
||||
std::cerr << "attacking...\n";
|
||||
recorder.add_attack(to,target_loc,weapon);
|
||||
|
||||
game_events::fire("attack",to,target_loc);
|
||||
if(units.count(to) && units.count(target_loc)) {
|
||||
attack(disp,map,to,target_loc,weapon,units,state,gameinfo,false);
|
||||
check_victory(units,teams);
|
||||
if(units_.count(to) && units_.count(target_loc)) {
|
||||
attack(disp_,map_,to,target_loc,weapon,units_,state_,gameinfo_,false);
|
||||
check_victory(units_,teams_);
|
||||
}
|
||||
std::cerr << "done attacking...\n";
|
||||
|
||||
//if this is the only unit in the planned attack, and the target
|
||||
//is still alive, then also summon reinforcements
|
||||
if(choice_it->movements.size() == 1 && units.count(target_loc)) {
|
||||
additional_targets->push_back(target(target_loc,3.0));
|
||||
if(choice_it->movements.size() == 1 && units_.count(target_loc)) {
|
||||
additional_targets_.push_back(target(target_loc,3.0));
|
||||
}
|
||||
|
||||
dialogs::advance_unit(gameinfo,units,to,disp,true);
|
||||
dialogs::advance_unit(gameinfo,units,target_loc,disp,!defender_human);
|
||||
dialogs::advance_unit(gameinfo_,units_,to,disp_,true);
|
||||
dialogs::advance_unit(gameinfo_,units_,target_loc,disp_,!defender_human);
|
||||
|
||||
do_move(disp,map,gameinfo,units,teams,team_num,state,consider_combat,
|
||||
additional_targets);
|
||||
do_move();
|
||||
return;
|
||||
|
||||
} else {
|
||||
log_scope("summoning reinforcements...\n");
|
||||
consider_combat = false;
|
||||
consider_combat_ = false;
|
||||
|
||||
std::set<gamemap::location> already_done;
|
||||
|
||||
for(std::vector<attack_analysis>::iterator it = analysis.begin();
|
||||
it != analysis.end(); ++it) {
|
||||
for(std::vector<attack_analysis>::iterator it = analysis.begin(); it != analysis.end(); ++it) {
|
||||
assert(it->movements.empty() == false);
|
||||
const gamemap::location& loc = it->movements.front().first;
|
||||
if(already_done.count(loc) > 0)
|
||||
continue;
|
||||
|
||||
additional_targets->push_back(target(loc,3.0));
|
||||
additional_targets_.push_back(target(loc,3.0));
|
||||
already_done.insert(loc);
|
||||
}
|
||||
}
|
||||
|
@ -481,13 +457,13 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
//try to acquire towers
|
||||
for(std::multimap<location,location>::const_iterator i = dstsrc.begin();
|
||||
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;
|
||||
|
||||
bool want_tower = true, owned = false;
|
||||
for(size_t j = 0; j != teams.size(); ++j) {
|
||||
owned = teams[j].owns_tower(i->first);
|
||||
if(owned && !current_team.is_enemy(j+1)) {
|
||||
for(size_t j = 0; j != teams_.size(); ++j) {
|
||||
owned = teams_[j].owns_tower(i->first);
|
||||
if(owned && !current_team().is_enemy(j+1)) {
|
||||
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,
|
||||
//and we don't want it.
|
||||
if(!owned && leader == units.end())
|
||||
if(!owned && leader == units_.end())
|
||||
want_tower = false;
|
||||
|
||||
if(want_tower) {
|
||||
std::cerr << "trying to acquire village: " << i->first.x
|
||||
<< ", " << i->first.y << "\n";
|
||||
|
||||
const std::map<location,unit>::iterator un = units.find(i->second);
|
||||
if(un == units.end()) {
|
||||
const std::map<location,unit>::iterator un = units_.find(i->second);
|
||||
if(un == units_.end()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
@ -514,24 +490,21 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
if(un->second.is_guardian())
|
||||
continue;
|
||||
|
||||
move_unit(gameinfo,disp,map,units,i->second,i->first,
|
||||
possible_moves,teams,team_num);
|
||||
|
||||
do_move(disp,map,gameinfo,units,teams,team_num,
|
||||
state,consider_combat,additional_targets);
|
||||
move_unit(i->second,i->first,possible_moves);
|
||||
do_move();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//find units in need of healing
|
||||
std::map<location,unit>::iterator u_it = units.begin();
|
||||
for(; u_it != units.end(); ++u_it) {
|
||||
unit_map::iterator u_it = units_.begin();
|
||||
for(; u_it != units_.end(); ++u_it) {
|
||||
unit& u = u_it->second;
|
||||
|
||||
//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
|
||||
//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().regenerates()) {
|
||||
|
||||
|
@ -542,10 +515,10 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
Itor best_loc = it.second;
|
||||
while(it.first != it.second) {
|
||||
const location& dst = it.first->second;
|
||||
if(map.underlying_terrain(map[dst.x][dst.y]) == gamemap::TOWER &&
|
||||
units.find(dst) == units.end()) {
|
||||
if(map_.underlying_terrain(map_[dst.x][dst.y]) == gamemap::TOWER &&
|
||||
units_.find(dst) == units_.end()) {
|
||||
const double vuln = power_projection(it.first->first,
|
||||
enemy_srcdst,enemy_dstsrc,units,map);
|
||||
enemy_srcdst,enemy_dstsrc);
|
||||
if(vuln < best_vulnerability) {
|
||||
best_vulnerability = vuln;
|
||||
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";
|
||||
|
||||
move_unit(gameinfo,disp,map,units,src,dst,
|
||||
possible_moves,teams,team_num);
|
||||
do_move(disp,map,gameinfo,units,teams,team_num,state,
|
||||
consider_combat,additional_targets);
|
||||
move_unit(src,dst,possible_moves);
|
||||
do_move();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(dstsrc.empty()) {
|
||||
do_move(disp,map,gameinfo,units,teams,team_num,state,
|
||||
consider_combat,additional_targets);
|
||||
do_move();
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << "finding targets...\n";
|
||||
std::vector<target> targets = find_targets(map,units,teams,team_num,leader != units.end());
|
||||
targets.insert(targets.end(),additional_targets->begin(),
|
||||
additional_targets->end());
|
||||
std::vector<target> targets = find_targets(leader != units_.end());
|
||||
targets.insert(targets.end(),additional_targets_.begin(),
|
||||
additional_targets_.end());
|
||||
for(;;) {
|
||||
if(targets.empty()) {
|
||||
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::pair<location,location> move = choose_move(targets,dstsrc,units,
|
||||
map,teams,team_num,
|
||||
gameinfo);
|
||||
for(std::vector<target>::const_iterator ittg = targets.begin();
|
||||
ittg != targets.end(); ++ittg) {
|
||||
assert(map.on_board(ittg->loc));
|
||||
std::pair<location,location> move = choose_move(targets,dstsrc);
|
||||
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;
|
||||
int weapon = -1;
|
||||
for(int n = 0; n != 6; ++n) {
|
||||
const unit_map::iterator enemy = units.find(adj[n]);
|
||||
if(enemy != units.end() &&
|
||||
current_team.is_enemy(enemy->second.side()) &&
|
||||
!enemy->second.invisible(map[enemy->first.x][enemy->first.y])) {
|
||||
const unit_map::iterator enemy = units_.find(adj[n]);
|
||||
if(enemy != units_.end() &&
|
||||
current_team().is_enemy(enemy->second.side()) &&
|
||||
!enemy->second.invisible(map_[enemy->first.x][enemy->first.y])) {
|
||||
target = adj[n];
|
||||
weapon = choose_weapon(map,units,state,gameinfo,move.first,
|
||||
target,bat_stats,
|
||||
map[move.second.x][move.second.y]);
|
||||
weapon = choose_weapon(move.first,target,bat_stats,
|
||||
map_[move.second.x][move.second.y]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
move_unit(gameinfo,disp,map,units,move.first,move.second,
|
||||
possible_moves,teams,team_num);
|
||||
move_unit(move.first,move.second,possible_moves);
|
||||
|
||||
//if we're going to attack someone
|
||||
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);
|
||||
|
||||
game_events::fire("attack",attacker,target);
|
||||
if(units.count(attacker) && units.count(target)) {
|
||||
attack(disp,map,attacker,target,weapon,units,state,
|
||||
gameinfo,false);
|
||||
if(units_.count(attacker) && units_.count(target)) {
|
||||
attack(disp_,map_,attacker,target,weapon,units_,state_,gameinfo_,false);
|
||||
|
||||
const std::map<gamemap::location,unit>::const_iterator tgt =
|
||||
units.find(target);
|
||||
const std::map<gamemap::location,unit>::const_iterator tgt = units_.find(target);
|
||||
|
||||
const bool defender_human = (tgt != units.end()) ?
|
||||
teams[tgt->second.side()-1].is_human() : false;
|
||||
const bool defender_human = (tgt != units_.end()) ?
|
||||
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);
|
||||
dialogs::advance_unit(gameinfo,units,target,disp,!defender_human);
|
||||
|
||||
check_victory(units,teams);
|
||||
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);
|
||||
}
|
||||
|
||||
do_move(disp,map,gameinfo,units,teams,team_num,state,
|
||||
consider_combat,additional_targets);
|
||||
do_move();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
110
src/ai.hpp
110
src/ai.hpp
|
@ -13,6 +13,7 @@
|
|||
#ifndef AI_HPP_INCLUDED
|
||||
#define AI_HPP_INCLUDED
|
||||
|
||||
#include "actions.hpp"
|
||||
#include "ai_move.hpp"
|
||||
#include "display.hpp"
|
||||
#include "map.hpp"
|
||||
|
@ -21,15 +22,116 @@
|
|||
|
||||
#include <map>
|
||||
|
||||
namespace ai {
|
||||
typedef gamemap::location location;
|
||||
typedef std::multimap<location,location> move_map;
|
||||
class ai {
|
||||
public:
|
||||
|
||||
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,
|
||||
std::map<gamemap::location,unit>& units,
|
||||
std::vector<team>& teams, int team_num, const gamestatus& state,
|
||||
bool consider_combat=true,
|
||||
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
|
||||
|
|
|
@ -25,26 +25,20 @@
|
|||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
namespace {
|
||||
|
||||
const int max_positions = 10000;
|
||||
|
||||
using namespace ai;
|
||||
|
||||
//analyze possibility of attacking target on 'loc'
|
||||
void do_analysis(
|
||||
const gamemap& map,
|
||||
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::map<gamemap::location,unit>& units_map,
|
||||
std::vector<attack_analysis>& result,
|
||||
const game_data& data, const gamestatus& status,
|
||||
attack_analysis& cur_analysis
|
||||
)
|
||||
void ai::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
|
||||
)
|
||||
{
|
||||
std::cerr << "doing attack analysis...\n";
|
||||
if(cur_analysis.movements.size() >= 4)
|
||||
return;
|
||||
|
||||
|
@ -93,28 +87,27 @@ void do_analysis(
|
|||
if(its.first == its.second)
|
||||
continue;
|
||||
|
||||
cur_analysis.movements.push_back(
|
||||
std::pair<location,location>(current_unit,tiles[j]));
|
||||
cur_analysis.movements.push_back(std::pair<location,location>(current_unit,tiles[j]));
|
||||
|
||||
//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;
|
||||
|
||||
//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
|
||||
//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.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) {
|
||||
|
||||
result.push_back(cur_analysis);
|
||||
used_locations[j] = true;
|
||||
do_analysis(map,loc,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
tiles,used_locations,
|
||||
units,units_map,result,data,status,cur_analysis);
|
||||
do_attack_analysis(loc,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
tiles,used_locations,
|
||||
units,result,cur_analysis);
|
||||
used_locations[j] = false;
|
||||
}
|
||||
|
||||
|
@ -128,9 +121,6 @@ void do_analysis(
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ai {
|
||||
|
||||
struct battle_type {
|
||||
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;
|
||||
|
||||
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)
|
||||
int ai::choose_weapon(const location& att, const location& def,
|
||||
battle_stats& cur_stats, gamemap::TERRAIN terrain)
|
||||
{
|
||||
const std::map<location,unit>::const_iterator itor = units.find(att);
|
||||
if(itor == units.end())
|
||||
const std::map<location,unit>::const_iterator itor = units_.find(att);
|
||||
if(itor == units_.end())
|
||||
return -1;
|
||||
|
||||
static int cache_hits = 0;
|
||||
static int cache_misses = 0;
|
||||
|
||||
battle_type battle(att,def,terrain);
|
||||
const std::set<battle_type>::const_iterator cache_itor
|
||||
= weapon_choice_cache.find(battle);
|
||||
const std::set<battle_type>::const_iterator cache_itor = weapon_choice_cache.find(battle);
|
||||
|
||||
if(cache_itor != weapon_choice_cache.end()) {
|
||||
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();
|
||||
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 a_hitpoints = itor->second.hitpoints();
|
||||
|
||||
for(size_t a = 0; a != attacks.size(); ++a) {
|
||||
const battle_stats stats = evaluate_battle_stats(map,att,def,a,units,
|
||||
status,info,terrain,false);
|
||||
const battle_stats stats = evaluate_battle_stats(map_,att,def,a,units_,
|
||||
state_,gameinfo_,terrain,false);
|
||||
|
||||
//TODO: improve this rating formula!
|
||||
const double rating =
|
||||
|
@ -233,12 +220,12 @@ int choose_weapon(const gamemap& map, std::map<location,unit>& units,
|
|||
return current_choice;
|
||||
}
|
||||
|
||||
void attack_analysis::analyze(const gamemap& map,
|
||||
std::map<location,unit>& units,
|
||||
const gamestatus& status,
|
||||
const game_data& info, int num_sims)
|
||||
void ai::attack_analysis::analyze(const gamemap& map,
|
||||
unit_map& units,
|
||||
const gamestatus& status,
|
||||
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());
|
||||
|
||||
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;
|
||||
for(m = movements.begin(); m != movements.end(); ++m) {
|
||||
battle_stats bat_stats;
|
||||
const int weapon = choose_weapon(map,units,status,info,
|
||||
m->first,target, bat_stats,
|
||||
map[m->second.x][m->second.y]);
|
||||
const int weapon = ai_obj.choose_weapon(m->first,target, bat_stats, map[m->second.x][m->second.y]);
|
||||
|
||||
assert(weapon != -1);
|
||||
weapons.push_back(weapon);
|
||||
|
@ -289,8 +274,7 @@ void attack_analysis::analyze(const gamemap& map,
|
|||
int attacks = stat.nattacks;
|
||||
int defends = stat.ndefends;
|
||||
|
||||
std::map<location,unit>::const_iterator att
|
||||
= units.find(movements[i].first);
|
||||
unit_map::const_iterator att = units.find(movements[i].first);
|
||||
double cost = att->second.type().cost();
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
double attack_analysis::rating(double aggression) const
|
||||
double ai::attack_analysis::rating(double aggression) const
|
||||
{
|
||||
double value = chance_to_kill*target_value - avg_losses;
|
||||
|
||||
|
@ -414,14 +398,10 @@ double attack_analysis::rating(double aggression) const
|
|||
return value;
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
std::vector<ai::attack_analysis> ai::analyze_targets(
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc
|
||||
)
|
||||
{
|
||||
log_scope("analyzing targets...");
|
||||
|
||||
|
@ -430,9 +410,8 @@ std::vector<attack_analysis> analyze_targets(
|
|||
std::vector<attack_analysis> res;
|
||||
|
||||
std::vector<location> unit_locs;
|
||||
for(std::map<location,unit>::const_iterator i = units.begin();
|
||||
i != units.end(); ++i) {
|
||||
if(i->second.side() == team_num) {
|
||||
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
|
||||
if(i->second.side() == team_num_) {
|
||||
unit_locs.push_back(i->first);
|
||||
}
|
||||
}
|
||||
|
@ -440,13 +419,12 @@ std::vector<attack_analysis> analyze_targets(
|
|||
bool used_locations[6];
|
||||
std::fill(used_locations,used_locations+6,false);
|
||||
|
||||
for(std::map<location,unit>::const_iterator j = units.begin();
|
||||
j != units.end(); ++j) {
|
||||
for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
|
||||
|
||||
//attack anyone who is on the enemy side, and who is not invisible
|
||||
if(current_team.is_enemy(j->second.side()) &&
|
||||
j->second.invisible(map.underlying_terrain(
|
||||
map[j->first.x][j->first.y])) == false) {
|
||||
if(current_team().is_enemy(j->second.side()) &&
|
||||
j->second.invisible(map_.underlying_terrain(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];
|
||||
get_adjacent_tiles(j->first,adjacent);
|
||||
attack_analysis analysis;
|
||||
|
@ -456,9 +434,8 @@ std::vector<attack_analysis> analyze_targets(
|
|||
|
||||
const int ticks = SDL_GetTicks();
|
||||
|
||||
do_analysis(map,j->first,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
adjacent,used_locations,unit_locs,units,
|
||||
res,data,status,analysis);
|
||||
do_attack_analysis(j->first,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
adjacent,used_locations,unit_locs,res,analysis);
|
||||
|
||||
const int time_taken = SDL_GetTicks() - ticks;
|
||||
static int max_time = 0;
|
||||
|
@ -472,9 +449,7 @@ std::vector<attack_analysis> analyze_targets(
|
|||
return res;
|
||||
}
|
||||
|
||||
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)
|
||||
double ai::power_projection(const gamemap::location& loc, const move_map& srcdst, const move_map& dstsrc, bool use_terrain)
|
||||
{
|
||||
static gamemap::location used_locs[6];
|
||||
static double ratings[6];
|
||||
|
@ -486,11 +461,11 @@ double power_projection(const gamemap::location& loc,
|
|||
double res = 0.0;
|
||||
|
||||
for(int i = 0; i != 6; ++i) {
|
||||
if(map.on_board(locs[i]) == false) {
|
||||
if(map_.on_board(locs[i]) == false) {
|
||||
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 std::pair<Itor,Itor> Range;
|
||||
|
@ -508,10 +483,10 @@ double power_projection(const gamemap::location& loc,
|
|||
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
|
||||
if(u == units.end()) {
|
||||
if(u == units_.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -527,10 +502,10 @@ double power_projection(const gamemap::location& loc,
|
|||
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 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);
|
||||
if(rating > best_rating) {
|
||||
best_rating = rating;
|
||||
|
@ -543,8 +518,7 @@ double power_projection(const gamemap::location& loc,
|
|||
//a better position to attack from
|
||||
if(n == 1 && best_unit.valid()) {
|
||||
end_used = beg_used + num_used_locs;
|
||||
gamemap::location* const pos
|
||||
= std::find(beg_used,end_used,best_unit);
|
||||
gamemap::location* const pos = std::find(beg_used,end_used,best_unit);
|
||||
const int index = pos - beg_used;
|
||||
if(best_rating >= ratings[index]) {
|
||||
res -= ratings[index];
|
||||
|
@ -573,6 +547,4 @@ double power_projection(const gamemap::location& loc,
|
|||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -23,67 +23,4 @@
|
|||
#include <map>
|
||||
#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
|
||||
|
|
|
@ -14,31 +14,30 @@
|
|||
#include "display.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "log.hpp"
|
||||
#include "map.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ai {
|
||||
|
||||
struct move_cost_calculator
|
||||
{
|
||||
move_cost_calculator(const unit& u, const gamemap& map,
|
||||
const game_data& data,
|
||||
const std::map<location,unit>& units,
|
||||
const unit_map& units,
|
||||
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),
|
||||
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))
|
||||
return 1000.0;
|
||||
|
||||
//if this unit can move to that location this turn, it has a very
|
||||
//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);
|
||||
while(range.first != range.second) {
|
||||
if(range.first->second == loc_)
|
||||
|
@ -83,52 +82,47 @@ private:
|
|||
const unit& unit_;
|
||||
const gamemap& map_;
|
||||
const game_data& data_;
|
||||
const std::map<location,unit>& units_;
|
||||
const unit_map& units_;
|
||||
const unit_movement_type& move_type_;
|
||||
const gamemap::location loc_;
|
||||
const std::multimap<location,location> dstsrc_;
|
||||
const ai::move_map dstsrc_;
|
||||
|
||||
};
|
||||
|
||||
std::vector<target> find_targets(
|
||||
const gamemap& map, std::map<location,unit>& units,
|
||||
std::vector<team>& teams, int current_team, bool has_leader
|
||||
)
|
||||
std::vector<ai::target> ai::find_targets(bool has_leader)
|
||||
{
|
||||
log_scope("finding targets...");
|
||||
|
||||
team& tm = teams[current_team-1];
|
||||
|
||||
std::vector<target> targets;
|
||||
|
||||
if(has_leader && tm.village_value() > 0.0) {
|
||||
const std::vector<location>& towers = map.towers();
|
||||
if(has_leader && current_team().village_value() > 0.0) {
|
||||
const std::vector<location>& towers = map_.towers();
|
||||
for(std::vector<location>::const_iterator t = towers.begin(); t != towers.end(); ++t) {
|
||||
assert(map.on_board(*t));
|
||||
bool get_tower = true;
|
||||
for(size_t i = 0; i != teams.size(); ++i) {
|
||||
if(!tm.is_enemy(i+1) && teams[i].owns_tower(*t)) {
|
||||
for(size_t i = 0; i != teams_.size(); ++i) {
|
||||
if(!current_team().is_enemy(i+1) && teams_[i].owns_tower(*t)) {
|
||||
get_tower = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
std::map<location,unit>::const_iterator u;
|
||||
for(u = units.begin(); u != units.end(); ++u) {
|
||||
unit_map::const_iterator u;
|
||||
for(u = units_.begin(); u != units_.end(); ++u) {
|
||||
|
||||
//is an enemy leader
|
||||
if(u->second.can_recruit() && tm.is_enemy(u->second.side())) {
|
||||
assert(map.on_board(u->first));
|
||||
targets.push_back(target(u->first,tm.leader_value()));
|
||||
if(u->second.can_recruit() && current_team().is_enemy(u->second.side())) {
|
||||
assert(map_.on_board(u->first));
|
||||
targets.push_back(target(u->first,current_team().leader_value()));
|
||||
}
|
||||
|
||||
//explicit targets for this team
|
||||
|
@ -148,8 +142,7 @@ std::vector<target> find_targets(
|
|||
|
||||
new_values.push_back(i->value);
|
||||
|
||||
for(std::vector<target>::const_iterator j = targets.begin();
|
||||
j != targets.end(); ++j) {
|
||||
for(std::vector<target>::const_iterator j = targets.begin(); j != targets.end(); ++j) {
|
||||
if(i->loc == j->loc) {
|
||||
continue;
|
||||
}
|
||||
|
@ -169,14 +162,7 @@ std::vector<target> find_targets(
|
|||
return targets;
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
std::pair<gamemap::location,gamemap::location> ai::choose_move(std::vector<ai::target>& targets,const std::multimap<location,location>& dstsrc)
|
||||
{
|
||||
log_scope("choosing move");
|
||||
|
||||
|
@ -186,21 +172,20 @@ std::pair<location,location> choose_move(
|
|||
}
|
||||
|
||||
paths::route best_route;
|
||||
std::map<location,unit>::iterator best = units.end();
|
||||
unit_map::iterator best = units_.end();
|
||||
double best_rating = 0.1;
|
||||
std::vector<target>::iterator best_target = targets.end();
|
||||
|
||||
std::map<location,unit>::iterator u;
|
||||
unit_map::iterator u;
|
||||
|
||||
//find the first eligible unit
|
||||
for(u = units.begin(); u != units.end(); ++u) {
|
||||
if(!(u->second.side() != current_team || u->second.can_recruit() ||
|
||||
u->second.movement_left() <= 0)) {
|
||||
for(u = units_.begin(); u != units_.end(); ++u) {
|
||||
if(!(u->second.side() != team_num_ || u->second.can_recruit() || u->second.movement_left() <= 0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(u == units.end()) {
|
||||
if(u == units_.end()) {
|
||||
std::cout << "no eligible units found\n";
|
||||
return std::pair<location,location>();
|
||||
}
|
||||
|
@ -211,8 +196,7 @@ std::pair<location,location> choose_move(
|
|||
return std::pair<location,location>(u->first,u->first);
|
||||
}
|
||||
|
||||
const move_cost_calculator cost_calc(u->second,map,data,units,
|
||||
u->first,dstsrc);
|
||||
const move_cost_calculator cost_calc(u->second,map_,gameinfo_,units_,u->first,dstsrc);
|
||||
|
||||
//choose the best target for that unit
|
||||
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
|
||||
for(++u; u != units.end(); ++u) {
|
||||
if(u->second.side() != current_team || u->second.can_recruit() ||
|
||||
for(++u; u != units_.end(); ++u) {
|
||||
if(u->second.side() != team_num_ || u->second.can_recruit() ||
|
||||
u->second.movement_left() <= 0 || u->second.is_guardian()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const move_cost_calculator calc(u->second,map,data,units,
|
||||
u->first,dstsrc);
|
||||
const move_cost_calculator calc(u->second,map_,gameinfo_,units_,u->first,dstsrc);
|
||||
const paths::route cur_route = a_star_search(u->first,best_target->loc,
|
||||
minimum(best_target->value/best_rating,100.0),calc);
|
||||
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 = u;
|
||||
best_route = cur_route;
|
||||
|
@ -259,8 +242,7 @@ std::pair<location,location> choose_move(
|
|||
if(best_target->value <= 0.0)
|
||||
targets.erase(best_target);
|
||||
|
||||
for(ittg = targets.begin();
|
||||
ittg != targets.end(); ++ittg) {
|
||||
for(ittg = targets.begin(); ittg != targets.end(); ++ittg) {
|
||||
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";
|
||||
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";
|
||||
return std::pair<location,location>();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,37 +14,11 @@
|
|||
#define AI_MOVE_H_INCLUDED
|
||||
|
||||
#include "map.hpp"
|
||||
#include "ai.hpp"
|
||||
#include "pathfind.hpp"
|
||||
#include "unit.hpp"
|
||||
#include "unit_types.hpp"
|
||||
|
||||
#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
|
||||
|
|
|
@ -161,7 +161,7 @@ void find_routes(const gamemap& map, const game_data& gamedata,
|
|||
//teleport to
|
||||
for(std::vector<gamemap::location>::const_iterator t = towers.begin();
|
||||
t != towers.end(); ++t) {
|
||||
if(!current_team.owns_tower(*t))
|
||||
if(!current_team.owns_tower(*t) || units.count(*t))
|
||||
continue;
|
||||
|
||||
locs.push_back(*t);
|
||||
|
|
|
@ -391,8 +391,7 @@ redo_turn:
|
|||
|
||||
update_locker lock(gui,!preferences::show_ai_moves());
|
||||
|
||||
ai::do_move(gui,map,gameinfo,units,teams,
|
||||
player_number,status);
|
||||
ai(gui,map,gameinfo,units,teams,player_number,status).do_move();
|
||||
|
||||
if(network::nconnections() > 0) {
|
||||
config cfg;
|
||||
|
|
|
@ -92,8 +92,15 @@ void play_turn(game_data& gameinfo, game_state& state_of_game,
|
|||
unit u = ui->second;
|
||||
const shortest_path_calculator calc(u,current_team,units,map);
|
||||
|
||||
const std::set<gamemap::location>* const teleports =
|
||||
u.type().teleports() ? ¤t_team.towers() : NULL;
|
||||
const std::set<gamemap::location>* teleports = 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(),
|
||||
10000.0,calc,teleports);
|
||||
|
@ -275,8 +282,15 @@ void turn_info::mouse_motion(const SDL_MouseMotionEvent& event)
|
|||
units_,map_);
|
||||
const bool can_teleport = un->second.type().teleports();
|
||||
|
||||
const std::set<gamemap::location>* const teleports =
|
||||
can_teleport ? ¤t_team.towers() : NULL;
|
||||
const std::set<gamemap::location>* teleports = 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,
|
||||
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,
|
||||
units_,map_);
|
||||
|
||||
const std::set<gamemap::location>* const teleports =
|
||||
teleport ? ¤t_team.towers() : NULL;
|
||||
const std::set<gamemap::location>* teleports = 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,
|
||||
10000.0,calc,teleports);
|
||||
|
|
13
src/team.cpp
13
src/team.cpp
|
@ -510,4 +510,17 @@ int team::nteams()
|
|||
} else {
|
||||
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;
|
||||
}
|
|
@ -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);
|
||||
|
||||
const std::set<gamemap::location> vacant_towers(const std::set<gamemap::location>& towers, const unit_map& units);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue