ai/testing/aspect_attacks.cpp

Go to the documentation of this file.
00001 /* $Id: aspect_attacks.cpp 53754 2012-04-02 18:07:01Z crab $ */
00002 /*
00003    Copyright (C) 2009 - 2012 by Yurii Chernyi <terraninfo@terraninfo.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * Stage: fallback to other AI
00018  * @file
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         // Attack anyone who is on the enemy side,
00098         // and who is not invisible or petrified.
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 &current_team
00131                     ) const
00132 {
00133     // This function is called fairly frequently, so interact with the user here.
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         //std::cerr << "ANALYSIS " << cur_analysis.movements.size() << " >= " << get_attack_depth() << "\n";
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         // See if the unit has the backstab ability.
00159         // Units with backstab will want to try to have a
00160         // friendly unit opposite the position they move to.
00161         //
00162         // See if the unit has the slow ability -- units with slow only attack first.
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                // Check if the friendly unit is surrounded,
00181                // A unit is surrounded if it is flanked by enemy units
00182                // and at least one other enemy unit is nearby
00183                // or if the unit is totaly surrounded by enemies
00184                // with max. one tile to escape.
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         // Iterate over positions adjacent to the unit, finding the best rated one.
00233         for(int j = 0; j != 6; ++j) {
00234 
00235             // If in this planned attack, a unit is already in this location.
00236             if(used_locations[j]) {
00237                 continue;
00238             }
00239 
00240             // See if the current unit can reach that position.
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                 // If the unit can't move to this location.
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             // Check to see whether this move would be a backstab.
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                 // Note that we *could* also check if a unit plans to move there
00271                 // before we're at this stage, but we don't because, since the
00272                 // attack calculations don't actually take backstab into account (too complicated),
00273                 // this could actually make our analysis look *worse* instead of better.
00274                 // So we only check for 'concrete' backstab opportunities.
00275                 // That would also break backstab_check, since it assumes
00276                 // the defender is in place.
00277                 if(itor != units_.end() &&
00278                     backstab_check(tiles[j], loc, units_, teams_)) {
00279                     if(backstab) {
00280                         backstab_bonus = 2;
00281                     }
00282 
00283                     // No surround bonus if target is skirmisher
00284                     if (!itor->get_ability_bool("skirmisher"))
00285                         surround_bonus = 1.2;
00286                 }
00287 
00288 
00289             }
00290 
00291             // See if this position is the best rated we've seen so far.
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             // Find out how vulnerable we are to attack from enemy units in this hex.
00298             //FIXME: suokko's r29531 multiplied this by a constant 1.5. ?
00299             const double vulnerability = power_projection(tiles[j],enemy_dstsrc);//?
00300 
00301             // Calculate how much support we have on this hex from allies.
00302             const double support = power_projection(tiles[j], fullmove_dstsrc);//?
00303 
00304             // If this is a position with equal defense to another position,
00305             // but more vulnerability then we don't want to use it.
00306 #ifdef SUOKKO
00307             //FIXME: this code was in sukko's r29531  Correct?
00308             // scale vulnerability to 60 hp unit
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             //FIXME: this code was in sukko's r29531  Correct?
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 } // end of namespace testing_ai_default
00406 
00407 } // end of namespace ai
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Wed May 23 2012 01:02:32 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs