00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "aspect_attacks.hpp"
00022
00023 #include "../manager.hpp"
00024 #include "../../actions.hpp"
00025 #include "../../foreach.hpp"
00026 #include "../../log.hpp"
00027 #include "../../map.hpp"
00028 #include "../../team.hpp"
00029 #include "../../tod_manager.hpp"
00030 #include "../../resources.hpp"
00031 #include "../../unit.hpp"
00032 #include "../../pathfind/pathfind.hpp"
00033
00034 namespace ai {
00035
00036 namespace testing_ai_default {
00037
00038 static lg::log_domain log_ai_testing_aspect_attacks("ai/aspect/attacks");
00039 #define DBG_AI LOG_STREAM(debug, log_ai_testing_aspect_attacks)
00040 #define LOG_AI LOG_STREAM(info, log_ai_testing_aspect_attacks)
00041 #define ERR_AI LOG_STREAM(err, log_ai_testing_aspect_attacks)
00042
00043 aspect_attacks::aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
00044 : typesafe_aspect<attacks_vector>(context,cfg,id)
00045 , filter_own_()
00046 , filter_enemy_()
00047 {
00048 if (const config &filter_own = cfg.child("filter_own")) {
00049 filter_own_ = filter_own;
00050 }
00051 if (const config &filter_enemy = cfg.child("filter_enemy")) {
00052 filter_enemy_ = filter_enemy;
00053 }
00054 }
00055
00056 aspect_attacks::~aspect_attacks()
00057 {
00058 }
00059
00060 void aspect_attacks::recalculate() const
00061 {
00062 this->value_ = analyze_targets();
00063 this->valid_ = true;
00064 }
00065
00066 boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
00067 {
00068 const move_map& srcdst = get_srcdst();
00069 const move_map& dstsrc = get_dstsrc();
00070 const move_map& enemy_srcdst = get_enemy_srcdst();
00071 const move_map& enemy_dstsrc = get_enemy_dstsrc();
00072
00073 boost::shared_ptr<attacks_vector> res(new attacks_vector());
00074 unit_map& units_ = *resources::units;
00075
00076 std::vector<map_location> unit_locs;
00077 for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
00078 if (i->side() == get_side() && i->attacks_left() && !(i->can_recruit() && get_passive_leader())) {
00079 if (!i->matches_filter(vconfig(filter_own_), i->get_location())) {
00080 continue;
00081 }
00082 unit_locs.push_back(i->get_location());
00083 }
00084 }
00085
00086 bool used_locations[6];
00087 std::fill(used_locations,used_locations+6,false);
00088
00089 moves_map dummy_moves;
00090 move_map fullmove_srcdst, fullmove_dstsrc;
00091 calculate_possible_moves(dummy_moves,fullmove_srcdst,fullmove_dstsrc,false,true);
00092
00093 unit_stats_cache().clear();
00094
00095 for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
00096
00097
00098
00099 if (current_team().is_enemy(j->side()) && !j->incapacitated() &&
00100 !j->invisible(j->get_location()))
00101 {
00102 if (!j->matches_filter(vconfig(filter_enemy_), j->get_location())) {
00103 continue;
00104 }
00105 map_location adjacent[6];
00106 get_adjacent_tiles(j->get_location(), adjacent);
00107 attack_analysis analysis;
00108 analysis.target = j->get_location();
00109 analysis.vulnerability = 0.0;
00110 analysis.support = 0.0;
00111 do_attack_analysis(j->get_location(), srcdst, dstsrc,
00112 fullmove_srcdst, fullmove_dstsrc, enemy_srcdst, enemy_dstsrc,
00113 adjacent,used_locations,unit_locs,*res,analysis, current_team());
00114 }
00115 }
00116 return res;
00117 }
00118
00119
00120
00121 void aspect_attacks::do_attack_analysis(
00122 const map_location& loc,
00123 const move_map& srcdst, const move_map& dstsrc,
00124 const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
00125 const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
00126 const map_location* tiles, bool* used_locations,
00127 std::vector<map_location>& units,
00128 std::vector<attack_analysis>& result,
00129 attack_analysis& cur_analysis,
00130 const team ¤t_team
00131 ) const
00132 {
00133
00134
00135 ai::manager::raise_user_interact();
00136 const int default_attack_depth = 5;
00137 if(cur_analysis.movements.size() >= size_t(default_attack_depth)) {
00138
00139 return;
00140 }
00141 gamemap &map_ = *resources::game_map;
00142 unit_map &units_ = *resources::units;
00143 std::vector<team> &teams_ = *resources::teams;
00144
00145
00146 const size_t max_positions = 1000;
00147 if(result.size() > max_positions && !cur_analysis.movements.empty()) {
00148 LOG_AI << "cut analysis short with number of positions\n";
00149 return;
00150 }
00151
00152 for(size_t i = 0; i != units.size(); ++i) {
00153 const map_location current_unit = units[i];
00154
00155 unit_map::iterator unit_itor = units_.find(current_unit);
00156 assert(unit_itor != units_.end());
00157
00158
00159
00160
00161
00162
00163 bool backstab = false, slow = false;
00164 std::vector<attack_type>& attacks = unit_itor->attacks();
00165 for(std::vector<attack_type>::iterator a = attacks.begin(); a != attacks.end(); ++a) {
00166 a->set_specials_context(map_location(), map_location(), units_, true, NULL);
00167 if(a->get_special_bool("backstab")) {
00168 backstab = true;
00169 }
00170
00171 if(a->get_special_bool("slow")) {
00172 slow = true;
00173 }
00174 }
00175
00176 if(slow && cur_analysis.movements.empty() == false) {
00177 continue;
00178 }
00179
00180
00181
00182
00183
00184
00185 bool is_surrounded = false;
00186 bool is_flanked = false;
00187 int enemy_units_around = 0;
00188 int accessible_tiles = 0;
00189 map_location adj[6];
00190 get_adjacent_tiles(current_unit, adj);
00191
00192 size_t tile;
00193 for(tile = 0; tile != 3; ++tile) {
00194
00195 const unit_map::const_iterator tmp_unit = units_.find(adj[tile]);
00196 bool possible_flanked = false;
00197
00198 if(map_.on_board(adj[tile]))
00199 {
00200 accessible_tiles++;
00201 if (tmp_unit != units_.end() && current_team.is_enemy(tmp_unit->side()))
00202 {
00203 enemy_units_around++;
00204 possible_flanked = true;
00205 }
00206 }
00207
00208 const unit_map::const_iterator tmp_opposite_unit = units_.find(adj[tile + 3]);
00209 if(map_.on_board(adj[tile + 3]))
00210 {
00211 accessible_tiles++;
00212 if (tmp_opposite_unit != units_.end() && current_team.is_enemy(tmp_opposite_unit->side()))
00213 {
00214 enemy_units_around++;
00215 if(possible_flanked)
00216 {
00217 is_flanked = true;
00218 }
00219 }
00220 }
00221 }
00222
00223 if((is_flanked && enemy_units_around > 2) || enemy_units_around >= accessible_tiles - 1)
00224 is_surrounded = true;
00225
00226
00227
00228 double best_vulnerability = 0.0, best_support = 0.0;
00229 int best_rating = 0;
00230 int cur_position = -1;
00231
00232
00233 for(int j = 0; j != 6; ++j) {
00234
00235
00236 if(used_locations[j]) {
00237 continue;
00238 }
00239
00240
00241 if (tiles[j] != current_unit) {
00242 typedef std::multimap<map_location,map_location>::const_iterator Itor;
00243 std::pair<Itor,Itor> its = dstsrc.equal_range(tiles[j]);
00244 while(its.first != its.second) {
00245 if(its.first->second == current_unit)
00246 break;
00247 ++its.first;
00248 }
00249
00250
00251 if(its.first == its.second || units_.find(tiles[j]) != units_.end()) {
00252 continue;
00253 }
00254 }
00255
00256 unit_ability_list abil = unit_itor->get_abilities("leadership",tiles[j]);
00257 int best_leadership_bonus = abil.highest("value").first;
00258 double leadership_bonus = static_cast<double>(best_leadership_bonus+100)/100.0;
00259 if (leadership_bonus > 1.1) {
00260 LOG_AI << unit_itor->name() << " is getting leadership " << leadership_bonus << "\n";
00261 }
00262
00263
00264 int backstab_bonus = 1;
00265 double surround_bonus = 1.0;
00266
00267 if(tiles[(j+3)%6] != current_unit) {
00268 const unit_map::const_iterator itor = units_.find(tiles[(j+3)%6]);
00269
00270
00271
00272
00273
00274
00275
00276
00277 if(itor != units_.end() &&
00278 backstab_check(tiles[j], loc, units_, teams_)) {
00279 if(backstab) {
00280 backstab_bonus = 2;
00281 }
00282
00283
00284 if (!itor->get_ability_bool("skirmisher"))
00285 surround_bonus = 1.2;
00286 }
00287
00288
00289 }
00290
00291
00292 int rating = static_cast<int>(rate_terrain(*unit_itor, tiles[j]) * backstab_bonus * leadership_bonus);
00293 if(cur_position >= 0 && rating < best_rating) {
00294 continue;
00295 }
00296
00297
00298
00299 const double vulnerability = power_projection(tiles[j],enemy_dstsrc);
00300
00301
00302 const double support = power_projection(tiles[j], fullmove_dstsrc);
00303
00304
00305
00306 #ifdef SUOKKO
00307
00308
00309 if(cur_position >= 0 && rating < best_rating
00310 && (vulnerability/surround_bonus*30.0)/unit_itor->second.hitpoints() -
00311 (support*surround_bonus*30.0)/unit_itor->second.max_hitpoints()
00312 > best_vulnerability - best_support) {
00313 continue;
00314 }
00315 #else
00316 if(cur_position >= 0 && rating == best_rating && vulnerability/surround_bonus - support*surround_bonus >= best_vulnerability - best_support) {
00317 continue;
00318 }
00319 #endif
00320 cur_position = j;
00321 best_rating = rating;
00322 #ifdef SUOKKO
00323
00324 best_vulnerability = (vulnerability/surround_bonus*30.0)/unit_itor->second.hitpoints();
00325 best_support = (support*surround_bonus*30.0)/unit_itor->second.max_hitpoints();
00326 #else
00327 best_vulnerability = vulnerability/surround_bonus;
00328 best_support = support*surround_bonus;
00329 #endif
00330 }
00331
00332 if(cur_position != -1) {
00333 units.erase(units.begin() + i);
00334
00335 cur_analysis.movements.push_back(std::pair<map_location,map_location>(current_unit,tiles[cur_position]));
00336
00337 cur_analysis.vulnerability += best_vulnerability;
00338
00339 cur_analysis.support += best_support;
00340
00341 cur_analysis.is_surrounded = is_surrounded;
00342 cur_analysis.analyze(map_, units_, *this, dstsrc, srcdst, enemy_dstsrc, get_aggression());
00343 result.push_back(cur_analysis);
00344 used_locations[cur_position] = true;
00345 do_attack_analysis(loc,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
00346 tiles,used_locations,
00347 units,result,cur_analysis, current_team);
00348 used_locations[cur_position] = false;
00349
00350
00351 cur_analysis.vulnerability -= best_vulnerability;
00352 cur_analysis.support -= best_support;
00353
00354 cur_analysis.movements.pop_back();
00355
00356 units.insert(units.begin() + i, current_unit);
00357 }
00358 }
00359 }
00360
00361 int aspect_attacks::rate_terrain(const unit& u, const map_location& loc)
00362 {
00363 gamemap &map_ = *resources::game_map;
00364 const t_translation::t_terrain terrain = map_.get_terrain(loc);
00365 const int defense = u.defense_modifier(terrain);
00366 int rating = 100 - defense;
00367
00368 const int healing_value = 10;
00369 const int friendly_village_value = 5;
00370 const int neutral_village_value = 10;
00371 const int enemy_village_value = 15;
00372
00373 if(map_.gives_healing(terrain) && u.get_ability_bool("regenerate",loc) == false) {
00374 rating += healing_value;
00375 }
00376
00377 if(map_.is_village(terrain)) {
00378 int owner = village_owner(loc, *resources::teams) + 1;
00379
00380 if(owner == u.side()) {
00381 rating += friendly_village_value;
00382 } else if(owner == 0) {
00383 rating += neutral_village_value;
00384 } else {
00385 rating += enemy_village_value;
00386 }
00387 }
00388
00389 return rating;
00390 }
00391
00392
00393 config aspect_attacks::to_config() const
00394 {
00395 config cfg = typesafe_aspect<attacks_vector>::to_config();
00396 if (!filter_own_.empty()) {
00397 cfg.add_child("filter_own",filter_own_);
00398 }
00399 if (!filter_enemy_.empty()) {
00400 cfg.add_child("filter_enemy",filter_enemy_);
00401 }
00402 return cfg;
00403 }
00404
00405 }
00406
00407 }