various AI improvements, tweaks to the campaign

This commit is contained in:
Dave White 2004-05-14 21:21:08 +00:00
parent effdcfdc1e
commit 187adbcf9b
10 changed files with 167 additions and 98 deletions

View file

@ -1,30 +1,30 @@
gggggggrrgggccsssssssssssssssssssssccg
gCCCgggrrgggccsssssssssssssssssssssctg
gC3Cgtgrrgggccsssssssssssssssssssscggg
gCCCgggrrgggccssssssssssssssssssssscvg
gC3Cgvgrrgggccsssssssssssssssssssscggg
ggCggggrrggggcssssssssccccsssssssccCgg
gggggggrrgggggcsssssscctccssssssccg2Cg
gggggggrrgggggcssssssccvccssssssccg2Cg
ggggggrrrgggggcsssssccccccscccsccggggr
ggggggrrggtgggccsssccccccccccccccggrrr
ggtggrrrgggggggfcccccccccccccccggrrrrg
gggggrrgggggffffcccccccccccccggrrrrggg
gggggrrfffgfffffffcfcfffcfgggggrrfgtgg
gggggrrffffffffftffffffffftggrrrrffffg
gggggrrrrrffffffffffffffffffrrrfffffff
gggtggrrrrrrrrfrfffffffffffrrrffffffff
ggggggggggrfrrrrrrfffffffffrrfffffffff
gggggggggggfffrfrrrrfffffrrrrfffffffff
gggggggggggfffffffrrrrfrrrrfffffffffff
gfffffggtgffffffftffrrrrrfffffffffffff
fffffffffffffffffffffrrrrrffffffffffff
fffffffffffffffrrrrrrrrfrrrffffffftfff
ffftfffffffrfrrrrrrrrrfffrrfffffffffff
rrfffffrfrrrrrrffffffffffrrfffffffffff
rrrrrrrrrrrfrffffffffffffrrfffffffffff
ffrrrrrfrfffffftfffffftffrrfffffffffff
ffffffffffffffffffffffffrrrffffffffffg
fffgfffffffffffffffffffrrrfffgggfgfggg
ffftggfffffffffffffffffrrgfgggggggggtg
ffffgffffgfgfgfffffffffrrggggggCgggggg
ggfffffggggtggggfgggfffrrgggggC1Cggggg
gggggggggggggggggggfffrrrgtgggCCCggggg
gggggggggggggggggggffrrrgggggggggggggg
ggggggrrggvgggccsssccccccccccccccggrrr
ggvggrrrggggggghcccccccccccccccggrrrrg
gggggrrggggghhhhcccccccccccccggrrrrggg
gggggrrhhhghhhhhhhchchhhchgggggrrhgvgg
gggggrrhhhhhhhhhvhhhhhhhhhvggrrrrhhhhg
gggggrrrrrhhhhhhhhhhhhhhhhhhrrrhhhhhhh
gggvggrrrrrrrrhrhhhhhhhhhhhrrrhhhhhhhh
ggggggggggrhrrrrrrhhhhhhhhhrrhhhhhhhhh
ggggggggggghhhrhrrrrhhhhhrrrrhhhhhhhhh
ggggggggggghhhhhhhrrrrhrrrrhhhhhhhhhhh
ghhhhhggvghhhhhhhvhhrrrrrhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhrrrrrhhhhhhhhhhhh
hhhhhhhhhhhhhhhrrrrrrrrhrrrhhhhhhhvhhh
hhhvhhhhhhhrhrrrrrrrrrhhhrrhhhhhhhhhhh
rrhhhhhrhrrrrrrhhhhhhhhhhrrhhhhhhhhhhh
rrrrrrrrrrrhrhhhhhhhhhhhhrrhhhhhhhhhhh
hhrrrrrhrhhhhhhvhhhhhhvhhrrhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhrrrhhhhhhhhhhg
hhhghhhhhhhhhhhhhhhhhhhrrrhhhggghghggg
hhhvgghhhhhhhhhhhhhhhhhrrghgggggggggvg
hhhhghhhhghghghhhhhhhhhrrggggggCgggggg
gghhhhhggggvgggghggghhhrrgggggC1Cggggg
ggggggggggggggggggghhhrrrgvgggCCCggggg
ggggggggggggggggggghhrrrgggggggggggggg

View file

@ -87,7 +87,7 @@ Defeat
[message]
id=msgcrossroads4
description=Kalenz
message="Beware! These forests are not safe! The roads are important to Asheviere's strategy, and she has hired Orcs to guard them. We shall have to fight to travel them."
message="Beware! These hills are not safe! The roads are important to Asheviere's strategy, and she has hired Orcs to guard them. We shall have to fight to travel them."
[/message]
[message]
id=msgcrossroads5
@ -127,7 +127,7 @@ Defeat
[message]
id=msgcrossroads1
description=Niodien
message="Stay on the path! The forests here are not safe!"
message="Stay on the path! The hills here are not safe!"
[/message]
[/event]
@ -153,7 +153,7 @@ Defeat
[message]
description=Loflar
id=msgcrossroads2
message="Beware the forest! There are many Orcs in hiding, preparing to ambush you!"
message="Beware the hills! There are many Orcs in hiding, preparing to ambush you!"
[/message]
[/event]

View file

@ -22,6 +22,12 @@ Defeat:
#Death of Konrad
#Turns run out"
[label]
x,y=11,17
text="Elensefar"
id=elensefar
[/label]
[bigmap]
image=misc/map.png
{DOT 131 332}

View file

@ -445,6 +445,8 @@ void ai::do_move()
{
log_scope("doing ai move");
invalidate_defensive_position_cache();
user_interact();
typedef paths::route route;
@ -791,16 +793,22 @@ bool ai::get_healing(std::map<gamemap::location,paths>& possible_moves, const mo
return false;
}
bool ai::should_retreat(const gamemap::location& loc, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc) const
bool ai::should_retreat(const gamemap::location& loc, const unit_map::const_iterator un, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc) const
{
const double caution = current_team().caution();
if(caution <= 0.0) {
return false;
}
const double optimal_terrain = best_defensive_position(un->first,dstsrc,srcdst,enemy_dstsrc,enemy_srcdst).chance_to_hit/100.0;
const double proposed_terrain = un->second.defense_modifier(map_,map_.get_terrain(loc))/100.0;
//the 'exposure' is the additional % chance to hit this unit receives from being on a sub-optimal defensive terrain
const double exposure = proposed_terrain - optimal_terrain;
const double our_power = power_projection(loc,srcdst,dstsrc);
const double their_power = power_projection(loc,enemy_srcdst,enemy_dstsrc);
return caution*their_power > 2.0*our_power + our_power*current_team().aggression();
return caution*their_power*(1.0+exposure) > our_power;
}
bool ai::retreat_units(std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc, unit_map::const_iterator leader)
@ -821,7 +829,7 @@ bool ai::retreat_units(std::map<gamemap::location,paths>& possible_moves, const
//this unit still has movement left, and is a candidate to retreat. We see the amount
//of power of each side on the situation, and decide whether it should retreat.
if(should_retreat(i->first,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc)) {
if(should_retreat(i->first,i,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc)) {
bool can_reach_leader = false;
@ -1269,48 +1277,9 @@ void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
}
}
void ai::leader_attack()
{
std::cerr << "leader attack analysis...\n";
const unit_map::iterator leader = find_leader(units_,team_num_);
if(leader == units_.end() || leader->second.stone() || leader->second.can_attack() == false)
return;
gamemap::location choice;
double rating = 0.0;
int weapon = -1;
gamemap::location adj[6];
get_adjacent_tiles(leader->first,adj);
for(size_t n = 0; n != 6; ++n) {
const unit_map::const_iterator u = units_.find(adj[n]);
if(u != units_.end() && current_team().is_enemy(u->second.side()) && u->second.stone() == false) {
attack_analysis analysis;
analysis.target = adj[n];
analysis.movements.push_back(std::pair<location,location>(leader->first,leader->first));
analysis.analyze(map_,units_,state_,gameinfo_,20,*this);
const double value = analysis.chance_to_kill*analysis.target_value - analysis.avg_losses*10.0 - analysis.avg_damage_taken;
if(value >= rating) {
rating = value;
choice = adj[n];
assert(analysis.weapons.size() == 1);
weapon = analysis.weapons.front();
}
}
}
if(choice.valid()) {
attack_enemy(leader->first,choice,weapon);
}
std::cerr << "end leader attack analysis...\n";
}
void ai::move_leader_after_recruit(const move_map& enemy_dstsrc)
{
std::cerr << "moving leader after recruit...\n";
leader_attack();
const unit_map::iterator leader = find_leader(units_,team_num_);
if(leader == units_.end() || leader->second.stone())
@ -1391,3 +1360,53 @@ int ai::rate_terrain(const unit& u, const gamemap::location& loc)
return rating;
}
const ai::defensive_position& ai::best_defensive_position(const gamemap::location& loc,
const move_map& dstsrc, const move_map& srcdst,
const move_map& enemy_dstsrc, const move_map& enemy_srcdst) const
{
const unit_map::const_iterator itor = units_.find(loc);
if(itor == units_.end()) {
static defensive_position pos;
pos.chance_to_hit = 0;
pos.vulnerability = pos.support = 0;
return pos;
}
const std::map<location,defensive_position>::const_iterator position = defensive_position_cache_.find(loc);
if(position != defensive_position_cache_.end()) {
return position->second;
}
defensive_position pos;
pos.chance_to_hit = 100;
pos.vulnerability = 10000.0;
pos.support = 0.0;
typedef move_map::const_iterator Itor;
const std::pair<Itor,Itor> itors = srcdst.equal_range(loc);
for(Itor i = itors.first; i != itors.second; ++i) {
const int defense = itor->second.defense_modifier(map_,map_.get_terrain(i->second));
if(defense > pos.chance_to_hit) {
continue;
}
const double vulnerability = power_projection(i->second,enemy_srcdst,enemy_dstsrc);
const double support = power_projection(i->second,srcdst,dstsrc);
if(defense < pos.chance_to_hit || support - vulnerability > pos.support - pos.vulnerability) {
pos.loc = i->second;
pos.chance_to_hit = defense;
pos.vulnerability = vulnerability;
pos.support = support;
}
}
defensive_position_cache_.insert(std::pair<location,defensive_position>(loc,pos));
return defensive_position_cache_[loc];
}
void ai::invalidate_defensive_position_cache()
{
defensive_position_cache_.clear();
}

View file

@ -305,8 +305,20 @@ public:
double value;
};
struct defensive_position {
location loc;
int chance_to_hit;
double vulnerability, support;
};
const defensive_position& best_defensive_position(const location& unit, const move_map& dstsrc, const move_map& srcdst,
const move_map& enemy_dstsrc, const move_map& enemy_srcdst) const;
void invalidate_defensive_position_cache();
protected:
mutable std::map<location,defensive_position> defensive_position_cache_;
virtual void do_move();
virtual bool do_combat(std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc);
@ -315,13 +327,12 @@ protected:
virtual bool retreat_units(std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc, unit_map::const_iterator leader);
virtual bool move_to_targets(std::map<gamemap::location,paths>& possible_moves, move_map& srcdst, move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc, unit_map::const_iterator leader);
virtual bool should_retreat(const gamemap::location& loc, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc) const;
virtual bool should_retreat(const gamemap::location& loc, unit_map::const_iterator un, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc) const;
virtual void do_recruitment();
virtual void move_leader_to_keep(const move_map& enemy_dstsrc);
virtual void move_leader_after_recruit(const move_map& enemy_dstsrc);
virtual void leader_attack();
virtual bool recruit_usage(const std::string& usage);
@ -343,7 +354,8 @@ protected:
{
void analyze(const gamemap& map, std::map<location,unit>& units,
const gamestatus& status, const game_data& info, int sims,
class ai& ai_obj);
class ai& ai_obj, const move_map& dstsrc, const move_map& srcdst,
const move_map& enemy_dstsrc, const move_map& enemy_srcdst);
double rating(double aggression) const;
@ -374,6 +386,11 @@ protected:
//the weighted average of the % chance to hit each attacking unit
double terrain_quality;
//the weighted average of the % defense of the best possible terrain
//that the attacking units could reach this turn, without attacking
//(good for comparison to see just how good/bad 'terrain_quality' is)
double alternative_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;

View file

@ -168,7 +168,7 @@ void ai::do_attack_analysis(
cur_analysis.support += best_support;
cur_analysis.analyze(map_,units_,state_,gameinfo_,50,*this);
cur_analysis.analyze(map_,units_,state_,gameinfo_,50,*this,dstsrc,srcdst,enemy_dstsrc,enemy_srcdst);
if(cur_analysis.rating(0.0) > rating_to_beat) {
@ -292,7 +292,9 @@ int ai::choose_weapon(const location& att, const location& def,
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 game_data& info, int num_sims, ai& ai_obj,
const ai::move_map& dstsrc, const ai::move_map& srcdst,
const ai::move_map& enemy_dstsrc, const ai::move_map& enemy_srcdst)
{
const unit_map::const_iterator defend_it = units.find(target);
assert(defend_it != units.end());
@ -463,12 +465,32 @@ void ai::attack_analysis::analyze(const gamemap& map,
avg_damage_inflicted += target_hp - defhp;
}
//calculate the 'alternative_terrain_quality' -- the best possible defensive values
//the attacking units could hope to achieve if they didn't attack and moved somewhere.
//this is could for comparative purposes to see just how vulnerable the AI is
//making itself
alternative_terrain_quality = 0.0;
double cost_sum = 0.0;
for(size_t i = 0; i != movements.size(); ++i) {
const unit_map::const_iterator att = units.find(movements[i].first);
const double cost = att->second.type().cost();
cost_sum += cost;
alternative_terrain_quality += cost*ai_obj.best_defensive_position(att->first,dstsrc,srcdst,enemy_dstsrc,enemy_srcdst).chance_to_hit;
}
alternative_terrain_quality /= cost_sum*100;
chance_to_kill /= num_sims;
avg_damage_inflicted /= num_sims;
avg_damage_taken /= num_sims;
terrain_quality /= resources_used;
resources_used /= num_sims;
avg_losses /= num_sims;
if(uses_leader) {
leader_threat = false;
}
}
double ai::attack_analysis::rating(double aggression) const
@ -479,12 +501,24 @@ double ai::attack_analysis::rating(double aggression) const
//only use the leader if we do a serious amount of damage
//compared to how much they do to us.
if(uses_leader && aggression > -1.0) {
aggression = -1.0;
if(uses_leader && aggression > -4.0) {
std::cerr << "uses leader..\n";
aggression = -4.0;
}
double value = chance_to_kill*target_value - avg_losses;
if(terrain_quality > alternative_terrain_quality) {
//this situation looks like it might be a bad move: we are moving our attackers out
//of their optimal terrain into sub-optimal terrain.
//calculate the 'exposure' of our units to risk
const double exposure = resources_used*(terrain_quality - alternative_terrain_quality);
std::cerr << "attack option has base value " << value << " with exposure " << exposure << "\n";
value -= exposure;
}
//prefer to attack already damaged targets
value += ((target_starting_damage/3 + avg_damage_inflicted)*
(target_value/resources_used) -
@ -500,6 +534,8 @@ double ai::attack_analysis::rating(double aggression) const
value *= 5.0;
}
std::cerr << "value: " << value << " vulnerability: " << vulnerability << " quality: " << terrain_quality << " alternative quality: " << alternative_terrain_quality << "\n";
return value;
}

View file

@ -302,7 +302,7 @@ std::pair<gamemap::location,gamemap::location> ai::choose_move(std::vector<targe
std::pair<Itor,Itor> its = dstsrc.equal_range(*ri);
while(its.first != its.second) {
if(its.first->second == best->first) {
if(!should_retreat(its.first->first,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc)) {
if(!should_retreat(its.first->first,best,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc)) {
const double value = best_target->value - best->second.type().cost()/20.0;
if(value > 0.0) {

View file

@ -65,9 +65,9 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
config* scenario = NULL;
//'starting_pos' will contain the position we start the game from.
//'replay_starting_pos' will contain the position as at the start of the scenario
//which is useful for saving replays
config starting_pos, replay_starting_pos;
config starting_pos;
recorder.set_save_info(state);
//see if we load the scenario from the scenario data -- if there is
//no snapshot data available from a save, or if the user has selected
@ -79,7 +79,6 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
std::cerr << "loading starting position: '" << state.starting_pos.write() << "'\n";
starting_pos = state.starting_pos;
scenario = &starting_pos;
replay_starting_pos = state.starting_pos;
} else {
scenario = game_config.find_child(type,"id",state.scenario);
}
@ -91,7 +90,6 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
std::cerr << "loading snapshot...\n";
//load from a save-snapshot.
starting_pos = state.snapshot;
replay_starting_pos = state.starting_pos;
scenario = &starting_pos;
state = read_game(units_data,&state.snapshot);
}
@ -102,18 +100,9 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
const std::string current_scenario = state.scenario;
try {
LEVEL_RESULT res = REPLAY;
state.label = translate_string_default((*scenario)["id"],(*scenario)["name"]);
recorder.set_save_info(state);
while(res == REPLAY) {
state = recorder.get_save_info();
res = play_level(units_data,game_config,scenario,
video,state,story);
}
const LEVEL_RESULT res = play_level(units_data,game_config,scenario,video,state,story);
state.snapshot = config();
@ -137,7 +126,7 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
try {
config snapshot;
recorder.save_game(units_data,label,snapshot,replay_starting_pos);
recorder.save_game(units_data,label,snapshot,state.starting_pos);
} catch(gamestatus::save_game_failed& e) {
gui::show_dialog(disp,NULL,"",string_table["save_game_failed"],gui::MESSAGE);
retry = true;
@ -202,6 +191,8 @@ LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
state.starting_pos = *scenario;
}
recorder.set_save_info(state);
}
if(state.scenario != "" && state.scenario != "null") {

View file

@ -606,7 +606,7 @@ redo_turn:
return end_level.result;
}
if(end_level.result == QUIT || end_level.result == REPLAY) {
if(end_level.result == QUIT) {
return end_level.result;
} else if(end_level.result == DEFEAT) {
try {

View file

@ -34,7 +34,7 @@
#include <sstream>
#include <string>
enum LEVEL_RESULT { VICTORY, DEFEAT, QUIT, REPLAY, CONTINUE };
enum LEVEL_RESULT { VICTORY, DEFEAT, QUIT, CONTINUE };
struct end_level_exception {
end_level_exception(LEVEL_RESULT res, bool bonus=true)