used 'attacks' aspect to keep a cache of attacks...
...shared between ai_default/formula_ai/composite_ai code
This commit is contained in:
parent
15abe931ea
commit
ba6a7aa2c4
17 changed files with 644 additions and 394 deletions
|
@ -41,6 +41,17 @@
|
|||
|
||||
{DEFAULT_ASPECT_VALUE aggression 0.5}
|
||||
{DEFAULT_ASPECT_VALUE attack_depth 5}
|
||||
[aspect]
|
||||
id=attacks
|
||||
engine=cpp
|
||||
name=composite_aspect
|
||||
invalidate_on_gamestate_change=yes
|
||||
[default]
|
||||
engine=cpp
|
||||
name=testing_ai_default::aspect_attacks
|
||||
invalidate_on_gamestate_change=yes
|
||||
[/default]
|
||||
[/aspect]
|
||||
{DEFAULT_ASPECT_EMPTY_SLF avoid}
|
||||
{DEFAULT_ASPECT_VALUE caution 0.25}
|
||||
{DEFAULT_ASPECT_VALUE grouping offensive}
|
||||
|
|
|
@ -98,6 +98,8 @@
|
|||
<Unit filename="..\..\src\ai\registry.hpp" />
|
||||
<Unit filename="..\..\src\ai\testing.cpp" />
|
||||
<Unit filename="..\..\src\ai\testing.hpp" />
|
||||
<Unit filename="..\..\src\ai\testing\aspect_attacks.cpp" />
|
||||
<Unit filename="..\..\src\ai\testing\aspect_attacks.hpp" />
|
||||
<Unit filename="..\..\src\ai\testing\ca.cpp" />
|
||||
<Unit filename="..\..\src\ai\testing\ca.hpp" />
|
||||
<Unit filename="..\..\src\ai\testing\stage_fallback.cpp" />
|
||||
|
|
|
@ -127,6 +127,8 @@
|
|||
<Unit filename="..\..\src\ai\registry.hpp" />
|
||||
<Unit filename="..\..\src\ai\testing.cpp" />
|
||||
<Unit filename="..\..\src\ai\testing.hpp" />
|
||||
<Unit filename="..\..\src\ai\testing\aspect_attacks.cpp" />
|
||||
<Unit filename="..\..\src\ai\testing\aspect_attacks.hpp" />
|
||||
<Unit filename="..\..\src\ai\testing\ca.cpp" />
|
||||
<Unit filename="..\..\src\ai\testing\ca.hpp" />
|
||||
<Unit filename="..\..\src\ai\testing\stage_fallback.cpp" />
|
||||
|
|
|
@ -4395,6 +4395,34 @@
|
|||
<Filter
|
||||
Name="testing"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\src\ai\testing\aspect_attacks.cpp"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)\ai\testing\"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)\ai\testing\"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug (fast)|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
ObjectFile="$(IntDir)\ai\testing\"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\ai\testing\ca.cpp"
|
||||
>
|
||||
|
@ -5658,6 +5686,10 @@
|
|||
<Filter
|
||||
Name="testing"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\src\ai\testing\aspect_attacks.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\ai\testing\ca.hpp"
|
||||
>
|
||||
|
|
|
@ -245,6 +245,7 @@ SET(wesnoth-main_SRC
|
|||
ai/interface.cpp
|
||||
ai/manager.cpp
|
||||
ai/registry.cpp
|
||||
ai/testing/aspect_attacks.cpp
|
||||
ai/testing/ca.cpp
|
||||
ai/testing/stage_rca.cpp
|
||||
ai/testing/stage_fallback.cpp
|
||||
|
|
|
@ -68,6 +68,7 @@ wesnoth_source = \
|
|||
ai/interface.cpp \
|
||||
ai/manager.cpp \
|
||||
ai/registry.cpp \
|
||||
ai/testing/aspect_attacks.cpp \
|
||||
ai/testing/ca.cpp \
|
||||
ai/testing/stage_rca.cpp \
|
||||
ai/testing/stage_fallback.cpp \
|
||||
|
|
|
@ -173,6 +173,7 @@ wesnoth_sources = Split("""
|
|||
ai/interface.cpp
|
||||
ai/manager.cpp
|
||||
ai/registry.cpp
|
||||
ai/testing/aspect_attacks.cpp
|
||||
ai/testing/ca.cpp
|
||||
ai/testing/stage_rca.cpp
|
||||
ai/testing/stage_fallback.cpp
|
||||
|
|
|
@ -316,7 +316,7 @@ void attack_result::do_execute()
|
|||
check_victory();
|
||||
set_gamestate_changed();
|
||||
//start of ugly hack. @todo 1.8 rework that via extended event system
|
||||
//until event system is reworked, we note the attack this way
|
||||
//until event system is reworked, we note the attack this way
|
||||
get_info().recent_attacks.insert(defender_loc_);
|
||||
//end of ugly hack
|
||||
try {
|
||||
|
|
|
@ -798,17 +798,10 @@ void ai_default::do_move()
|
|||
bool ai_default::do_combat(std::map<map_location,paths>& possible_moves, const move_map& srcdst,
|
||||
const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc)
|
||||
{
|
||||
|
||||
const std::vector<attack_analysis> &analysis = get_attacks();
|
||||
int ticks = SDL_GetTicks();
|
||||
|
||||
std::vector<attack_analysis> analysis = analyze_targets(srcdst, dstsrc,
|
||||
enemy_srcdst, enemy_dstsrc);
|
||||
|
||||
int time_taken = SDL_GetTicks() - ticks;
|
||||
LOG_AI << "took " << time_taken << " ticks for " << analysis.size()
|
||||
<< " positions. Analyzing...\n";
|
||||
|
||||
ticks = SDL_GetTicks();
|
||||
|
||||
const int max_sims = 50000;
|
||||
int num_sims = analysis.empty() ? 0 : max_sims/analysis.size();
|
||||
if(num_sims < 20)
|
||||
|
@ -821,9 +814,9 @@ bool ai_default::do_combat(std::map<map_location,paths>& possible_moves, const m
|
|||
const int max_positions = 30000;
|
||||
const int skip_num = analysis.size()/max_positions;
|
||||
|
||||
std::vector<attack_analysis>::iterator choice_it = analysis.end();
|
||||
std::vector<attack_analysis>::const_iterator choice_it = analysis.end();
|
||||
double choice_rating = -1000.0;
|
||||
for(std::vector<attack_analysis>::iterator it = analysis.begin();
|
||||
for(std::vector<attack_analysis>::const_iterator it = analysis.begin();
|
||||
it != analysis.end(); ++it) {
|
||||
|
||||
if(skip_num > 0 && ((it - analysis.begin())%skip_num) && it->movements.size() > 1)
|
||||
|
@ -849,7 +842,7 @@ bool ai_default::do_combat(std::map<map_location,paths>& possible_moves, const m
|
|||
}
|
||||
}
|
||||
|
||||
time_taken = SDL_GetTicks() - ticks;
|
||||
int time_taken = SDL_GetTicks() - ticks;
|
||||
LOG_AI << "analysis took " << time_taken << " ticks\n";
|
||||
|
||||
// suokko tested the rating against current_team().caution()
|
||||
|
|
|
@ -34,7 +34,7 @@ static lg::log_domain log_ai("ai/attack");
|
|||
namespace ai {
|
||||
|
||||
void attack_analysis::analyze(const gamemap& map, unit_map& units,
|
||||
class readonly_context& ai_obj,
|
||||
const readonly_context& ai_obj,
|
||||
const move_map& dstsrc, const move_map& srcdst,
|
||||
const move_map& enemy_dstsrc, double aggression)
|
||||
{
|
||||
|
@ -260,7 +260,7 @@ bool attack_analysis::attack_close(const map_location& loc) const
|
|||
}
|
||||
|
||||
|
||||
double attack_analysis::rating(double aggression, readonly_context& ai_obj) const
|
||||
double attack_analysis::rating(double aggression, const readonly_context& ai_obj) const
|
||||
{
|
||||
if(leader_threat) {
|
||||
aggression = 1.0;
|
||||
|
@ -292,10 +292,6 @@ double attack_analysis::rating(double aggression, readonly_context& ai_obj) cons
|
|||
#endif
|
||||
LOG_AI << "attack option has base value " << value << " with exposure " << exposure << ": "
|
||||
<< vulnerability << "/" << support << " = " << (vulnerability/std::max<double>(support,0.1)) << "\n";
|
||||
if(uses_leader) {
|
||||
ai_obj.log_message("attack option has value " + str_cast(value) + " with exposure " + str_cast(exposure) + ": " + str_cast(vulnerability) + "/" + str_cast(support));
|
||||
}
|
||||
|
||||
value -= exposure*(1.0-aggression);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,60 +59,6 @@ void default_ai_context_proxy::init_default_ai_context_proxy(default_ai_context
|
|||
|
||||
const int max_positions = 10000;
|
||||
|
||||
std::vector<attack_analysis> default_ai_context_impl::analyze_targets(
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc
|
||||
)
|
||||
{
|
||||
log_scope2(log_ai, "analyzing targets...");
|
||||
|
||||
std::vector<attack_analysis> res;
|
||||
unit_map units_ = get_info().units;
|
||||
|
||||
std::vector<map_location> unit_locs;
|
||||
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
|
||||
if(i->second.side() == get_side() && i->second.attacks_left()) {
|
||||
unit_locs.push_back(i->first);
|
||||
}
|
||||
}
|
||||
|
||||
bool used_locations[6];
|
||||
std::fill(used_locations,used_locations+6,false);
|
||||
|
||||
moves_map dummy_moves;
|
||||
move_map fullmove_srcdst, fullmove_dstsrc;
|
||||
calculate_possible_moves(dummy_moves,fullmove_srcdst,fullmove_dstsrc,false,true);
|
||||
|
||||
unit_stats_cache().clear();
|
||||
|
||||
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 or petrified.
|
||||
if(current_team().is_enemy(j->second.side()) && !j->second.incapacitated() &&
|
||||
j->second.invisible(j->first,units_,get_info().teams) == false) {
|
||||
map_location adjacent[6];
|
||||
get_adjacent_tiles(j->first,adjacent);
|
||||
attack_analysis analysis;
|
||||
analysis.target = j->first;
|
||||
analysis.vulnerability = 0.0;
|
||||
analysis.support = 0.0;
|
||||
|
||||
// const int ticks = SDL_GetTicks();
|
||||
|
||||
do_attack_analysis(j->first,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
adjacent,used_locations,unit_locs,res,analysis);
|
||||
|
||||
// const int time_taken = SDL_GetTicks() - ticks;
|
||||
// static int max_time = 0;
|
||||
// if(time_taken > max_time)
|
||||
// max_time = time_taken;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
default_ai_context_impl::~default_ai_context_impl()
|
||||
{
|
||||
|
@ -145,270 +91,6 @@ int default_ai_context_impl::count_free_hexes_in_castle(const map_location &loc,
|
|||
}
|
||||
|
||||
|
||||
void default_ai_context_impl::do_attack_analysis(
|
||||
const map_location& loc,
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
|
||||
const map_location* tiles, bool* used_locations,
|
||||
std::vector<map_location>& units,
|
||||
std::vector<attack_analysis>& result,
|
||||
attack_analysis& cur_analysis
|
||||
)
|
||||
{
|
||||
// This function is called fairly frequently, so interact with the user here.
|
||||
raise_user_interact();
|
||||
|
||||
if(cur_analysis.movements.size() >= size_t(get_attack_depth())) {
|
||||
//std::cerr << "ANALYSIS " << cur_analysis.movements.size() << " >= " << get_attack_depth() << "\n";
|
||||
return;
|
||||
}
|
||||
gamemap &map_ = get_info().map;
|
||||
unit_map &units_ = get_info().units;
|
||||
std::vector<team> &teams_ = get_info().teams;
|
||||
|
||||
static double best_results[6];
|
||||
if(result.empty()) {
|
||||
for(int i = 0; i != 6; ++i) {
|
||||
best_results[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t max_positions = 1000;
|
||||
if(result.size() > max_positions && !cur_analysis.movements.empty()) {
|
||||
LOG_AI << "cut analysis short with number of positions\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const double cur_rating = cur_analysis.movements.empty() ? -1.0 :
|
||||
cur_analysis.rating(get_aggression(),*this);
|
||||
|
||||
double rating_to_beat = cur_rating;
|
||||
|
||||
if(!cur_analysis.movements.empty()) {
|
||||
assert(cur_analysis.movements.size() < 6);
|
||||
double& best_res = best_results[cur_analysis.movements.size()-1];
|
||||
rating_to_beat = best_res = std::max(best_res,cur_rating);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i != units.size(); ++i) {
|
||||
const map_location current_unit = units[i];
|
||||
|
||||
unit_map::iterator unit_itor = units_.find(current_unit);
|
||||
assert(unit_itor != units_.end());
|
||||
|
||||
// See if the unit has the backstab ability.
|
||||
// Units with backstab will want to try to have a
|
||||
// friendly unit opposite the position they move to.
|
||||
//
|
||||
// See if the unit has the slow ability -- units with slow only attack first.
|
||||
bool backstab = false, slow = false;
|
||||
std::vector<attack_type>& attacks = unit_itor->second.attacks();
|
||||
for(std::vector<attack_type>::iterator a = attacks.begin(); a != attacks.end(); ++a) {
|
||||
a->set_specials_context(map_location(), map_location(), units_, true, NULL);
|
||||
if(a->get_special_bool("backstab")) {
|
||||
backstab = true;
|
||||
}
|
||||
|
||||
if(a->get_special_bool("slow")) {
|
||||
slow = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(slow && cur_analysis.movements.empty() == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the friendly unit is surrounded,
|
||||
// A unit is surrounded if it is flanked by enemy units
|
||||
// and at least one other enemy unit is nearby
|
||||
// or if the unit is totaly surrounded by enemies
|
||||
// with max. one tile to escape.
|
||||
bool is_surrounded = false;
|
||||
bool is_flanked = false;
|
||||
int enemy_units_around = 0;
|
||||
int accessible_tiles = 0;
|
||||
map_location adj[6];
|
||||
get_adjacent_tiles(current_unit, adj);
|
||||
|
||||
size_t tile;
|
||||
for(tile = 0; tile != 3; ++tile) {
|
||||
|
||||
const unit_map::const_iterator tmp_unit = units_.find(adj[tile]);
|
||||
bool possible_flanked = false;
|
||||
|
||||
if(map_.on_board(adj[tile]))
|
||||
{
|
||||
accessible_tiles++;
|
||||
if(tmp_unit != units_.end() && get_side() != tmp_unit->second.side())
|
||||
{
|
||||
enemy_units_around++;
|
||||
possible_flanked = true;
|
||||
}
|
||||
}
|
||||
|
||||
const unit_map::const_iterator tmp_opposite_unit = units_.find(adj[tile + 3]);
|
||||
if(map_.on_board(adj[tile + 3]))
|
||||
{
|
||||
accessible_tiles++;
|
||||
if(tmp_opposite_unit != units_.end() && get_side() != tmp_opposite_unit->second.side())
|
||||
{
|
||||
enemy_units_around++;
|
||||
if(possible_flanked)
|
||||
{
|
||||
is_flanked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if((is_flanked && enemy_units_around > 2) || enemy_units_around >= accessible_tiles - 1)
|
||||
is_surrounded = true;
|
||||
|
||||
|
||||
|
||||
double best_vulnerability = 0.0, best_support = 0.0;
|
||||
int best_rating = 0;
|
||||
int cur_position = -1;
|
||||
|
||||
// Iterate over positions adjacent to the unit, finding the best rated one.
|
||||
for(int j = 0; j != 6; ++j) {
|
||||
|
||||
// If in this planned attack, a unit is already in this location.
|
||||
if(used_locations[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// See if the current unit can reach that position.
|
||||
if (tiles[j] != current_unit) {
|
||||
typedef std::multimap<map_location,map_location>::const_iterator Itor;
|
||||
std::pair<Itor,Itor> its = dstsrc.equal_range(tiles[j]);
|
||||
while(its.first != its.second) {
|
||||
if(its.first->second == current_unit)
|
||||
break;
|
||||
++its.first;
|
||||
}
|
||||
|
||||
// If the unit can't move to this location.
|
||||
if(its.first == its.second || units_.find(tiles[j]) != units_.end()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
unit_ability_list abil = unit_itor->second.get_abilities("leadership",tiles[j]);
|
||||
int best_leadership_bonus = abil.highest("value").first;
|
||||
double leadership_bonus = static_cast<double>(best_leadership_bonus+100)/100.0;
|
||||
if (leadership_bonus > 1.1) {
|
||||
LOG_AI << unit_itor->second.name() << " is getting leadership " << leadership_bonus << "\n";
|
||||
}
|
||||
|
||||
// Check to see whether this move would be a backstab.
|
||||
int backstab_bonus = 1;
|
||||
double surround_bonus = 1.0;
|
||||
|
||||
if(tiles[(j+3)%6] != current_unit) {
|
||||
const unit_map::const_iterator itor = units_.find(tiles[(j+3)%6]);
|
||||
|
||||
// Note that we *could* also check if a unit plans to move there
|
||||
// before we're at this stage, but we don't because, since the
|
||||
// attack calculations don't actually take backstab into account (too complicated),
|
||||
// this could actually make our analysis look *worse* instead of better.
|
||||
// So we only check for 'concrete' backstab opportunities.
|
||||
// That would also break backstab_check, since it assumes
|
||||
// the defender is in place.
|
||||
if(itor != units_.end() &&
|
||||
backstab_check(tiles[j], loc, units_, teams_)) {
|
||||
if(backstab) {
|
||||
backstab_bonus = 2;
|
||||
}
|
||||
|
||||
// No surround bonus if target is skirmisher
|
||||
if (!itor->second.get_ability_bool("skirmisker"))
|
||||
surround_bonus = 1.2;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// See if this position is the best rated we've seen so far.
|
||||
const int rating = static_cast<int>(rate_terrain(unit_itor->second,tiles[j]) * backstab_bonus * leadership_bonus);
|
||||
if(cur_position >= 0 && rating < best_rating) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find out how vulnerable we are to attack from enemy units in this hex.
|
||||
//FIXME: suokko's r29531 multiplied this by a constant 1.5. ?
|
||||
const double vulnerability = power_projection(tiles[j],enemy_dstsrc);
|
||||
|
||||
// Calculate how much support we have on this hex from allies.
|
||||
const double support = power_projection(tiles[j], fullmove_dstsrc);
|
||||
|
||||
// If this is a position with equal defense to another position,
|
||||
// but more vulnerability then we don't want to use it.
|
||||
#ifdef SUOKKO
|
||||
//FIXME: this code was in sukko's r29531 Correct?
|
||||
// scale vulnerability to 60 hp unit
|
||||
if(cur_position >= 0 && rating < best_rating
|
||||
&& (vulnerability/surround_bonus*30.0)/unit_itor->second.hitpoints() -
|
||||
(support*surround_bonus*30.0)/unit_itor->second.max_hitpoints()
|
||||
> best_vulnerability - best_support) {
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if(cur_position >= 0 && rating == best_rating && vulnerability/surround_bonus - support*surround_bonus >= best_vulnerability - best_support) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
cur_position = j;
|
||||
best_rating = rating;
|
||||
#ifdef SUOKKO
|
||||
//FIXME: this code was in sukko's r29531 Correct?
|
||||
best_vulnerability = (vulnerability/surround_bonus*30.0)/unit_itor->second.hitpoints();
|
||||
best_support = (support*surround_bonus*30.0)/unit_itor->second.max_hitpoints();
|
||||
#else
|
||||
best_vulnerability = vulnerability/surround_bonus;
|
||||
best_support = support*surround_bonus;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(cur_position != -1) {
|
||||
units.erase(units.begin() + i);
|
||||
|
||||
cur_analysis.movements.push_back(std::pair<map_location,map_location>(current_unit,tiles[cur_position]));
|
||||
|
||||
cur_analysis.vulnerability += best_vulnerability;
|
||||
|
||||
cur_analysis.support += best_support;
|
||||
|
||||
cur_analysis.is_surrounded = is_surrounded;
|
||||
|
||||
cur_analysis.analyze(map_, units_, *this, dstsrc, srcdst, enemy_dstsrc, get_aggression());
|
||||
|
||||
//This logic to sometimes not add the attack because it doesn't
|
||||
//rate high enough seems to remove attacks from consideration
|
||||
//that should not be removed, so it has been removed.
|
||||
// -- David.
|
||||
// if(cur_analysis.rating(get_aggression(),*this) > rating_to_beat) {
|
||||
|
||||
result.push_back(cur_analysis);
|
||||
used_locations[cur_position] = true;
|
||||
do_attack_analysis(loc,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
tiles,used_locations,
|
||||
units,result,cur_analysis);
|
||||
used_locations[cur_position] = false;
|
||||
// }
|
||||
|
||||
cur_analysis.vulnerability -= best_vulnerability;
|
||||
cur_analysis.support -= best_support;
|
||||
|
||||
cur_analysis.movements.pop_back();
|
||||
|
||||
units.insert(units.begin() + i, current_unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
default_ai_context& default_ai_context_impl::get_default_ai_context(){
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -59,11 +59,11 @@ public:
|
|||
}
|
||||
|
||||
void analyze(const gamemap& map, unit_map& units,
|
||||
class readonly_context& ai_obj,
|
||||
const readonly_context& ai_obj,
|
||||
const move_map& dstsrc, const move_map& srcdst,
|
||||
const move_map& enemy_dstsrc, double aggression);
|
||||
|
||||
double rating(double aggression, class readonly_context& ai_obj) const;
|
||||
double rating(double aggression, const readonly_context& ai_obj) const;
|
||||
variant get_value(const std::string& key) const;
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
|
||||
|
||||
|
@ -124,12 +124,6 @@ class default_ai_context;
|
|||
class default_ai_context : public virtual readwrite_context{
|
||||
public:
|
||||
|
||||
/** Return a vector of all possible attack analysisis */
|
||||
virtual std::vector<attack_analysis> analyze_targets(
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc) = 0;
|
||||
|
||||
|
||||
virtual int count_free_hexes_in_castle(const map_location& loc, std::set<map_location> &checked_hexes) = 0;
|
||||
|
||||
|
||||
|
@ -141,16 +135,6 @@ public:
|
|||
virtual ~default_ai_context();
|
||||
|
||||
|
||||
/** Analyze possibility of attacking target on 'loc'. */
|
||||
virtual void do_attack_analysis(
|
||||
const map_location& loc, const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
|
||||
const map_location* tiles, bool* used_locations,
|
||||
std::vector<map_location>& units,
|
||||
std::vector<attack_analysis>& result, attack_analysis& cur_analysis) = 0;
|
||||
|
||||
|
||||
virtual default_ai_context& get_default_ai_context() = 0;
|
||||
|
||||
|
||||
|
@ -171,14 +155,6 @@ public:
|
|||
class default_ai_context_proxy : public virtual default_ai_context, public virtual readwrite_context_proxy {
|
||||
public:
|
||||
|
||||
virtual std::vector<attack_analysis> analyze_targets(
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc)
|
||||
{
|
||||
return target_->analyze_targets(srcdst,dstsrc,enemy_srcdst,enemy_dstsrc);
|
||||
}
|
||||
|
||||
|
||||
int count_free_hexes_in_castle(const map_location& loc, std::set<map_location> &checked_hexes)
|
||||
{
|
||||
return target_->count_free_hexes_in_castle(loc, checked_hexes);
|
||||
|
@ -194,19 +170,6 @@ public:
|
|||
virtual ~default_ai_context_proxy();
|
||||
|
||||
|
||||
|
||||
virtual void do_attack_analysis(
|
||||
const map_location& loc, const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
|
||||
const map_location* tiles, bool* used_locations,
|
||||
std::vector<map_location>& units,
|
||||
std::vector<attack_analysis>& result, attack_analysis& cur_analysis)
|
||||
{
|
||||
target_->do_attack_analysis(loc,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,tiles,used_locations,units,result,cur_analysis);
|
||||
}
|
||||
|
||||
|
||||
virtual default_ai_context& get_default_ai_context()
|
||||
{
|
||||
return target_->get_default_ai_context();
|
||||
|
@ -244,11 +207,6 @@ private:
|
|||
class default_ai_context_impl : public virtual readwrite_context_proxy, public default_ai_context {
|
||||
public:
|
||||
|
||||
virtual std::vector<attack_analysis> analyze_targets(
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc);
|
||||
|
||||
|
||||
int count_free_hexes_in_castle(const map_location& loc, std::set<map_location> &checked_hexes);
|
||||
|
||||
|
||||
|
@ -262,15 +220,6 @@ public:
|
|||
virtual ~default_ai_context_impl();
|
||||
|
||||
|
||||
virtual void do_attack_analysis(
|
||||
const map_location& loc, const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
|
||||
const map_location* tiles, bool* used_locations,
|
||||
std::vector<map_location>& units,
|
||||
std::vector<attack_analysis>& result, attack_analysis& cur_analysis);
|
||||
|
||||
|
||||
virtual default_ai_context& get_default_ai_context();
|
||||
|
||||
|
||||
|
|
|
@ -789,7 +789,7 @@ variant formula_ai::get_value(const std::string& key) const
|
|||
return attacks_cache_;
|
||||
}
|
||||
|
||||
std::vector<attack_analysis> attacks = const_cast<formula_ai*>(this)->analyze_targets(srcdst_, dstsrc_, enemy_srcdst_, enemy_dstsrc_);
|
||||
const std::vector<attack_analysis> attacks = get_attacks();
|
||||
std::vector<variant> vars;
|
||||
for(std::vector<attack_analysis>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
|
||||
vars.push_back(variant(new attack_analysis(*i)));
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "dfool/ai.hpp"
|
||||
#include "formula/ai.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "testing/aspect_attacks.hpp"
|
||||
#include "testing/ca.hpp"
|
||||
#include "testing/stage_rca.hpp"
|
||||
#include "testing/stage_fallback.hpp"
|
||||
|
@ -108,6 +109,9 @@ static register_aspect_factory< composite_aspect<double> >
|
|||
static register_aspect_factory< composite_aspect<int> >
|
||||
attack_depth__composite_aspect_factory("attack_depth*composite_aspect");
|
||||
|
||||
static register_aspect_factory< composite_aspect< attacks_vector > >
|
||||
attacks__composite_aspect_factory("attacks*composite_aspect");
|
||||
|
||||
static register_aspect_factory< composite_aspect< terrain_filter > >
|
||||
avoid__composite_aspect_factory("avoid*composite_aspect");
|
||||
|
||||
|
@ -164,6 +168,9 @@ static register_aspect_factory< standard_aspect<double> >
|
|||
static register_aspect_factory< standard_aspect<int> >
|
||||
attack_depth__standard_aspect_factory("attack_depth*standard_aspect");
|
||||
|
||||
static register_aspect_factory< testing_ai_default::aspect_attacks >
|
||||
attacks__testing_ai_default_aspect_attacks_factory("attacks*testing_ai_default::aspect_attacks");
|
||||
|
||||
static register_aspect_factory< standard_aspect< terrain_filter > >
|
||||
avoid__standard_aspect_factory("avoid*standard_aspect");
|
||||
|
||||
|
@ -220,6 +227,9 @@ static register_aspect_factory< standard_aspect<double> >
|
|||
static register_aspect_factory< standard_aspect<int> >
|
||||
attack_depth__standard_aspect_factory2("attack_depth*");
|
||||
|
||||
static register_aspect_factory< testing_ai_default::aspect_attacks >
|
||||
attacks__testing_ai_default_aspect_attacks_factory2("attacks*");
|
||||
|
||||
static register_aspect_factory< standard_aspect< terrain_filter > >
|
||||
avoid__standard_aspect_factory2("avoid*");
|
||||
|
||||
|
|
488
src/ai/testing/aspect_attacks.cpp
Normal file
488
src/ai/testing/aspect_attacks.cpp
Normal file
|
@ -0,0 +1,488 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2009 by Yurii Chernyi <terraninfo@terraninfo.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
or at your option any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stage: fallback to other AI
|
||||
* @file ai/testing/aspect_attacks.cpp
|
||||
*/
|
||||
|
||||
#include "aspect_attacks.hpp"
|
||||
|
||||
#include "../manager.hpp"
|
||||
#include "../../foreach.hpp"
|
||||
#include "../../log.hpp"
|
||||
|
||||
namespace ai {
|
||||
|
||||
namespace testing_ai_default {
|
||||
|
||||
static lg::log_domain log_ai_testing_aspect_attacks("ai/aspect/attacks");
|
||||
#define DBG_AI LOG_STREAM(debug, log_ai_testing_aspect_attacks)
|
||||
#define LOG_AI LOG_STREAM(info, log_ai_testing_aspect_attacks)
|
||||
#define ERR_AI LOG_STREAM(err, log_ai_testing_aspect_attacks)
|
||||
|
||||
aspect_attacks::aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
|
||||
: typesafe_aspect<attacks_vector>(context,cfg,id)
|
||||
{
|
||||
}
|
||||
|
||||
aspect_attacks::~aspect_attacks()
|
||||
{
|
||||
}
|
||||
|
||||
void aspect_attacks::recalculate() const
|
||||
{
|
||||
this->value_ = analyze_targets();
|
||||
}
|
||||
|
||||
boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
|
||||
{
|
||||
const move_map& srcdst = get_srcdst();
|
||||
const move_map& dstsrc = get_dstsrc();
|
||||
const move_map& enemy_srcdst = get_enemy_srcdst();
|
||||
const move_map& enemy_dstsrc = get_enemy_dstsrc();
|
||||
|
||||
boost::shared_ptr<attacks_vector> res(new attacks_vector());
|
||||
unit_map& units_ = get_info().units;
|
||||
|
||||
std::vector<map_location> unit_locs;
|
||||
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
|
||||
if(i->second.side() == get_side() && i->second.attacks_left()) {
|
||||
unit_locs.push_back(i->first);
|
||||
}
|
||||
}
|
||||
|
||||
bool used_locations[6];
|
||||
std::fill(used_locations,used_locations+6,false);
|
||||
|
||||
moves_map dummy_moves;
|
||||
move_map fullmove_srcdst, fullmove_dstsrc;
|
||||
calculate_possible_moves(dummy_moves,fullmove_srcdst,fullmove_dstsrc,false,true);
|
||||
|
||||
unit_stats_cache().clear();
|
||||
|
||||
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 or petrified.
|
||||
if(current_team().is_enemy(j->second.side()) && !j->second.incapacitated() &&
|
||||
j->second.invisible(j->first,units_,get_info().teams) == false) {
|
||||
map_location adjacent[6];
|
||||
get_adjacent_tiles(j->first,adjacent);
|
||||
attack_analysis analysis;
|
||||
analysis.target = j->first;
|
||||
analysis.vulnerability = 0.0;
|
||||
analysis.support = 0.0;
|
||||
do_attack_analysis(j->first,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
adjacent,used_locations,unit_locs,*res,analysis);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void aspect_attacks::do_attack_analysis(
|
||||
const map_location& loc,
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
|
||||
const map_location* tiles, bool* used_locations,
|
||||
std::vector<map_location>& units,
|
||||
std::vector<attack_analysis>& result,
|
||||
attack_analysis& cur_analysis
|
||||
) const
|
||||
{
|
||||
// This function is called fairly frequently, so interact with the user here.
|
||||
raise_user_interact();
|
||||
|
||||
if(cur_analysis.movements.size() >= size_t(get_attack_depth())) {
|
||||
//std::cerr << "ANALYSIS " << cur_analysis.movements.size() << " >= " << get_attack_depth() << "\n";
|
||||
return;
|
||||
}
|
||||
gamemap &map_ = get_info().map;
|
||||
unit_map &units_ = get_info().units;
|
||||
std::vector<team> &teams_ = get_info().teams;
|
||||
|
||||
static double best_results[6];
|
||||
if(result.empty()) {
|
||||
for(int i = 0; i != 6; ++i) {
|
||||
best_results[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t max_positions = 1000;
|
||||
if(result.size() > max_positions && !cur_analysis.movements.empty()) {
|
||||
LOG_AI << "cut analysis short with number of positions\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const double cur_rating = cur_analysis.movements.empty() ? -1.0 :
|
||||
cur_analysis.rating(get_aggression(),*this);
|
||||
|
||||
double rating_to_beat = cur_rating;
|
||||
|
||||
if(!cur_analysis.movements.empty()) {
|
||||
assert(cur_analysis.movements.size() < 6);
|
||||
double& best_res = best_results[cur_analysis.movements.size()-1];
|
||||
rating_to_beat = best_res = std::max(best_res,cur_rating);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i != units.size(); ++i) {
|
||||
const map_location current_unit = units[i];
|
||||
|
||||
unit_map::iterator unit_itor = units_.find(current_unit);
|
||||
assert(unit_itor != units_.end());
|
||||
|
||||
// See if the unit has the backstab ability.
|
||||
// Units with backstab will want to try to have a
|
||||
// friendly unit opposite the position they move to.
|
||||
//
|
||||
// See if the unit has the slow ability -- units with slow only attack first.
|
||||
bool backstab = false, slow = false;
|
||||
std::vector<attack_type>& attacks = unit_itor->second.attacks();
|
||||
for(std::vector<attack_type>::iterator a = attacks.begin(); a != attacks.end(); ++a) {
|
||||
a->set_specials_context(map_location(), map_location(), units_, true, NULL);
|
||||
if(a->get_special_bool("backstab")) {
|
||||
backstab = true;
|
||||
}
|
||||
|
||||
if(a->get_special_bool("slow")) {
|
||||
slow = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(slow && cur_analysis.movements.empty() == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the friendly unit is surrounded,
|
||||
// A unit is surrounded if it is flanked by enemy units
|
||||
// and at least one other enemy unit is nearby
|
||||
// or if the unit is totaly surrounded by enemies
|
||||
// with max. one tile to escape.
|
||||
bool is_surrounded = false;
|
||||
bool is_flanked = false;
|
||||
int enemy_units_around = 0;
|
||||
int accessible_tiles = 0;
|
||||
map_location adj[6];
|
||||
get_adjacent_tiles(current_unit, adj);
|
||||
|
||||
size_t tile;
|
||||
for(tile = 0; tile != 3; ++tile) {
|
||||
|
||||
const unit_map::const_iterator tmp_unit = units_.find(adj[tile]);
|
||||
bool possible_flanked = false;
|
||||
|
||||
if(map_.on_board(adj[tile]))
|
||||
{
|
||||
accessible_tiles++;
|
||||
if(tmp_unit != units_.end() && get_side() != tmp_unit->second.side())
|
||||
{
|
||||
enemy_units_around++;
|
||||
possible_flanked = true;
|
||||
}
|
||||
}
|
||||
|
||||
const unit_map::const_iterator tmp_opposite_unit = units_.find(adj[tile + 3]);
|
||||
if(map_.on_board(adj[tile + 3]))
|
||||
{
|
||||
accessible_tiles++;
|
||||
if(tmp_opposite_unit != units_.end() && get_side() != tmp_opposite_unit->second.side())
|
||||
{
|
||||
enemy_units_around++;
|
||||
if(possible_flanked)
|
||||
{
|
||||
is_flanked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if((is_flanked && enemy_units_around > 2) || enemy_units_around >= accessible_tiles - 1)
|
||||
is_surrounded = true;
|
||||
|
||||
|
||||
|
||||
double best_vulnerability = 0.0, best_support = 0.0;
|
||||
int best_rating = 0;
|
||||
int cur_position = -1;
|
||||
|
||||
// Iterate over positions adjacent to the unit, finding the best rated one.
|
||||
for(int j = 0; j != 6; ++j) {
|
||||
|
||||
// If in this planned attack, a unit is already in this location.
|
||||
if(used_locations[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// See if the current unit can reach that position.
|
||||
if (tiles[j] != current_unit) {
|
||||
typedef std::multimap<map_location,map_location>::const_iterator Itor;
|
||||
std::pair<Itor,Itor> its = dstsrc.equal_range(tiles[j]);
|
||||
while(its.first != its.second) {
|
||||
if(its.first->second == current_unit)
|
||||
break;
|
||||
++its.first;
|
||||
}
|
||||
|
||||
// If the unit can't move to this location.
|
||||
if(its.first == its.second || units_.find(tiles[j]) != units_.end()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
unit_ability_list abil = unit_itor->second.get_abilities("leadership",tiles[j]);
|
||||
int best_leadership_bonus = abil.highest("value").first;
|
||||
double leadership_bonus = static_cast<double>(best_leadership_bonus+100)/100.0;
|
||||
if (leadership_bonus > 1.1) {
|
||||
LOG_AI << unit_itor->second.name() << " is getting leadership " << leadership_bonus << "\n";
|
||||
}
|
||||
|
||||
// Check to see whether this move would be a backstab.
|
||||
int backstab_bonus = 1;
|
||||
double surround_bonus = 1.0;
|
||||
|
||||
if(tiles[(j+3)%6] != current_unit) {
|
||||
const unit_map::const_iterator itor = units_.find(tiles[(j+3)%6]);
|
||||
|
||||
// Note that we *could* also check if a unit plans to move there
|
||||
// before we're at this stage, but we don't because, since the
|
||||
// attack calculations don't actually take backstab into account (too complicated),
|
||||
// this could actually make our analysis look *worse* instead of better.
|
||||
// So we only check for 'concrete' backstab opportunities.
|
||||
// That would also break backstab_check, since it assumes
|
||||
// the defender is in place.
|
||||
if(itor != units_.end() &&
|
||||
backstab_check(tiles[j], loc, units_, teams_)) {
|
||||
if(backstab) {
|
||||
backstab_bonus = 2;
|
||||
}
|
||||
|
||||
// No surround bonus if target is skirmisher
|
||||
if (!itor->second.get_ability_bool("skirmisker"))
|
||||
surround_bonus = 1.2;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// See if this position is the best rated we've seen so far.
|
||||
const int rating = static_cast<int>(rate_terrain(unit_itor->second,tiles[j]) * backstab_bonus * leadership_bonus);
|
||||
if(cur_position >= 0 && rating < best_rating) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find out how vulnerable we are to attack from enemy units in this hex.
|
||||
//FIXME: suokko's r29531 multiplied this by a constant 1.5. ?
|
||||
const double vulnerability = power_projection(tiles[j],enemy_dstsrc);//?
|
||||
|
||||
// Calculate how much support we have on this hex from allies.
|
||||
const double support = power_projection(tiles[j], fullmove_dstsrc);//?
|
||||
|
||||
// If this is a position with equal defense to another position,
|
||||
// but more vulnerability then we don't want to use it.
|
||||
#ifdef SUOKKO
|
||||
//FIXME: this code was in sukko's r29531 Correct?
|
||||
// scale vulnerability to 60 hp unit
|
||||
if(cur_position >= 0 && rating < best_rating
|
||||
&& (vulnerability/surround_bonus*30.0)/unit_itor->second.hitpoints() -
|
||||
(support*surround_bonus*30.0)/unit_itor->second.max_hitpoints()
|
||||
> best_vulnerability - best_support) {
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if(cur_position >= 0 && rating == best_rating && vulnerability/surround_bonus - support*surround_bonus >= best_vulnerability - best_support) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
cur_position = j;
|
||||
best_rating = rating;
|
||||
#ifdef SUOKKO
|
||||
//FIXME: this code was in sukko's r29531 Correct?
|
||||
best_vulnerability = (vulnerability/surround_bonus*30.0)/unit_itor->second.hitpoints();
|
||||
best_support = (support*surround_bonus*30.0)/unit_itor->second.max_hitpoints();
|
||||
#else
|
||||
best_vulnerability = vulnerability/surround_bonus;
|
||||
best_support = support*surround_bonus;
|
||||
#endif
|
||||
}
|
||||
|
||||
if(cur_position != -1) {
|
||||
units.erase(units.begin() + i);
|
||||
|
||||
cur_analysis.movements.push_back(std::pair<map_location,map_location>(current_unit,tiles[cur_position]));
|
||||
|
||||
cur_analysis.vulnerability += best_vulnerability;
|
||||
|
||||
cur_analysis.support += best_support;
|
||||
|
||||
cur_analysis.is_surrounded = is_surrounded;
|
||||
|
||||
cur_analysis.analyze(map_, units_, *this, dstsrc, srcdst, enemy_dstsrc, get_aggression());
|
||||
result.push_back(cur_analysis);
|
||||
used_locations[cur_position] = true;
|
||||
do_attack_analysis(loc,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
tiles,used_locations,
|
||||
units,result,cur_analysis);
|
||||
used_locations[cur_position] = false;
|
||||
|
||||
|
||||
cur_analysis.vulnerability -= best_vulnerability;
|
||||
cur_analysis.support -= best_support;
|
||||
|
||||
cur_analysis.movements.pop_back();
|
||||
|
||||
units.insert(units.begin() + i, current_unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int aspect_attacks::rate_terrain(const unit& u, const map_location& loc) const
|
||||
{
|
||||
gamemap &map_ = get_info().map;
|
||||
const t_translation::t_terrain terrain = map_.get_terrain(loc);
|
||||
const int defense = u.defense_modifier(terrain);
|
||||
int rating = 100 - defense;
|
||||
|
||||
const int healing_value = 10;
|
||||
const int friendly_village_value = 5;
|
||||
const int neutral_village_value = 10;
|
||||
const int enemy_village_value = 15;
|
||||
|
||||
if(map_.gives_healing(terrain) && u.get_ability_bool("regenerates",loc) == false) {
|
||||
rating += healing_value;
|
||||
}
|
||||
|
||||
if(map_.is_village(terrain)) {
|
||||
int owner = village_owner(loc, get_info().teams) + 1;
|
||||
|
||||
if(owner == get_side()) {
|
||||
rating += friendly_village_value;
|
||||
} else if(owner == 0) {
|
||||
rating += neutral_village_value;
|
||||
} else {
|
||||
rating += enemy_village_value;
|
||||
}
|
||||
}
|
||||
|
||||
return rating;
|
||||
}
|
||||
|
||||
|
||||
double aspect_attacks::power_projection(const map_location& loc, const move_map& dstsrc) const
|
||||
{
|
||||
map_location used_locs[6];
|
||||
int ratings[6];
|
||||
int num_used_locs = 0;
|
||||
|
||||
map_location locs[6];
|
||||
get_adjacent_tiles(loc,locs);
|
||||
|
||||
const int lawful_bonus = get_info().tod_manager_.get_time_of_day().lawful_bonus;
|
||||
gamemap& map_ = get_info().map;
|
||||
unit_map& units_ = get_info().units;
|
||||
|
||||
int res = 0;
|
||||
|
||||
bool changed = false;
|
||||
for (int i = 0;; ++i) {
|
||||
if (i == 6) {
|
||||
if (!changed) break;
|
||||
// Loop once again, in case a unit found a better spot
|
||||
// and freed the place for another unit.
|
||||
changed = false;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (map_.on_board(locs[i]) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const t_translation::t_terrain terrain = map_[locs[i]];
|
||||
|
||||
typedef move_map::const_iterator Itor;
|
||||
typedef std::pair<Itor,Itor> Range;
|
||||
Range its = dstsrc.equal_range(locs[i]);
|
||||
|
||||
map_location* const beg_used = used_locs;
|
||||
map_location* end_used = used_locs + num_used_locs;
|
||||
|
||||
int best_rating = 0;
|
||||
map_location best_unit;
|
||||
|
||||
for(Itor it = its.first; it != its.second; ++it) {
|
||||
const unit_map::const_iterator u = units_.find(it->second);
|
||||
|
||||
// Unit might have been killed, and no longer exist
|
||||
if(u == units_.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const unit& un = u->second;
|
||||
|
||||
int tod_modifier = 0;
|
||||
if(un.alignment() == unit_type::LAWFUL) {
|
||||
tod_modifier = lawful_bonus;
|
||||
} else if(un.alignment() == unit_type::CHAOTIC) {
|
||||
tod_modifier = -lawful_bonus;
|
||||
}
|
||||
|
||||
// The 0.5 power avoids underestimating too much the damage of a wounded unit.
|
||||
int hp = int(sqrt(double(un.hitpoints()) / un.max_hitpoints()) * 1000);
|
||||
int most_damage = 0;
|
||||
foreach (const attack_type &att, un.attacks())
|
||||
{
|
||||
int damage = att.damage() * att.num_attacks() * (100 + tod_modifier);
|
||||
if (damage > most_damage) {
|
||||
most_damage = damage;
|
||||
}
|
||||
}
|
||||
|
||||
int village_bonus = map_.is_village(terrain) ? 3 : 2;
|
||||
int defense = 100 - un.defense_modifier(terrain);
|
||||
int rating = hp * defense * most_damage * village_bonus / 200;
|
||||
if(rating > best_rating) {
|
||||
map_location *pos = std::find(beg_used, end_used, it->second);
|
||||
// Check if the spot is the same or better than an older one.
|
||||
if (pos == end_used || rating >= ratings[pos - beg_used]) {
|
||||
best_rating = rating;
|
||||
best_unit = it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!best_unit.valid()) continue;
|
||||
map_location *pos = std::find(beg_used, end_used, best_unit);
|
||||
int index = pos - beg_used;
|
||||
if (index == num_used_locs)
|
||||
++num_used_locs;
|
||||
else if (best_rating == ratings[index])
|
||||
continue;
|
||||
else {
|
||||
// The unit was in another spot already, so remove its older rating
|
||||
// from the final result, and require a new run to fill its old spot.
|
||||
res -= ratings[index];
|
||||
changed = true;
|
||||
}
|
||||
used_locs[index] = best_unit;
|
||||
ratings[index] = best_rating;
|
||||
res += best_rating;
|
||||
}
|
||||
|
||||
return res / 100000.;
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace testing_ai_default
|
||||
|
||||
} // end of namespace ai
|
82
src/ai/testing/aspect_attacks.hpp
Normal file
82
src/ai/testing/aspect_attacks.hpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2009 by Yurii Chernyi <terraninfo@terraninfo.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
or at your option any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Aspect: attacks
|
||||
* @file ai/testing/aspect_attacks.hpp
|
||||
*/
|
||||
|
||||
#ifndef AI_TESTING_ASPECT_ATTACKS_HPP_INCLUDED
|
||||
#define AI_TESTING_ASPECT_ATTACKS_HPP_INCLUDED
|
||||
|
||||
#include "../../global.hpp"
|
||||
|
||||
#include "../composite/aspect.hpp"
|
||||
#include "../interface.hpp"
|
||||
#include "../../config.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
//silence "inherits via dominance" warnings
|
||||
#pragma warning(disable:4250)
|
||||
#endif
|
||||
|
||||
namespace ai {
|
||||
|
||||
|
||||
namespace testing_ai_default {
|
||||
|
||||
class aspect_attacks: public typesafe_aspect<attacks_vector> {
|
||||
public:
|
||||
aspect_attacks(readonly_context &context, const config &cfg, const std::string &id);
|
||||
|
||||
|
||||
virtual ~aspect_attacks();
|
||||
|
||||
|
||||
virtual void recalculate() const;
|
||||
|
||||
|
||||
protected:
|
||||
boost::shared_ptr<attacks_vector> analyze_targets() const;
|
||||
|
||||
void do_attack_analysis(
|
||||
const map_location& loc,
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
|
||||
const map_location* tiles, bool* used_locations,
|
||||
std::vector<map_location>& units,
|
||||
std::vector<attack_analysis>& result,
|
||||
attack_analysis& cur_analysis
|
||||
) const;
|
||||
|
||||
int rate_terrain(const unit& u, const map_location& loc) const;
|
||||
double power_projection(const map_location& loc, const move_map& dstsrc) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // end of namespace testing_ai_default
|
||||
|
||||
} // end of namespace ai
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -559,7 +559,7 @@ double combat_phase::evaluate()
|
|||
choice_rating_ = -1000.0;
|
||||
int ticks = SDL_GetTicks();
|
||||
|
||||
std::vector<attack_analysis> analysis = analyze_targets(get_srcdst(), get_dstsrc(), get_enemy_srcdst(), get_enemy_dstsrc());
|
||||
const std::vector<attack_analysis> analysis = get_attacks();
|
||||
|
||||
int time_taken = SDL_GetTicks() - ticks;
|
||||
LOG_AI_TESTING_AI_DEFAULT << "took " << time_taken << " ticks for " << analysis.size()
|
||||
|
@ -579,8 +579,8 @@ double combat_phase::evaluate()
|
|||
const int max_positions = 30000;
|
||||
const int skip_num = analysis.size()/max_positions;
|
||||
|
||||
std::vector<attack_analysis>::iterator choice_it = analysis.end();
|
||||
for(std::vector<attack_analysis>::iterator it = analysis.begin();
|
||||
std::vector<attack_analysis>::const_iterator choice_it = analysis.end();
|
||||
for(std::vector<attack_analysis>::const_iterator it = analysis.begin();
|
||||
it != analysis.end(); ++it) {
|
||||
|
||||
if(skip_num > 0 && ((it - analysis.begin())%skip_num) && it->movements.size() > 1)
|
||||
|
|
Loading…
Add table
Reference in a new issue