ai/contexts.cpp

Go to the documentation of this file.
00001 /* $Id: contexts.cpp 53940 2012-04-15 16:33:36Z nephro $ */
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  * Helper functions for the object which operates in the context of AI for specific side
00018  * This is part of AI interface
00019  * @file
00020  */
00021 
00022 #include "actions.hpp"
00023 #include "contexts.hpp"
00024 #include "manager.hpp"
00025 
00026 #include "composite/aspect.hpp"
00027 #include "composite/engine.hpp"
00028 #include "composite/goal.hpp"
00029 
00030 #include "default/ai.hpp"
00031 
00032 #include "../callable_objects.hpp"
00033 #include "../formula.hpp"
00034 #include "../formula_callable.hpp"
00035 #include "../formula_function.hpp"
00036 #include "../formula_fwd.hpp"
00037 #include "../game_display.hpp"
00038 #include "../game_events.hpp"
00039 #include "../log.hpp"
00040 #include "../mouse_handler_base.hpp"
00041 #include "../resources.hpp"
00042 #include "../tod_manager.hpp"
00043 
00044 static lg::log_domain log_ai("ai/general");
00045 #define DBG_AI LOG_STREAM(debug, log_ai)
00046 #define LOG_AI LOG_STREAM(info, log_ai)
00047 #define WRN_AI LOG_STREAM(warn, log_ai)
00048 #define ERR_AI LOG_STREAM(err, log_ai)
00049 
00050 // =======================================================================
00051 //
00052 // =======================================================================
00053 namespace ai {
00054 
00055 int side_context_impl::get_recursion_count() const
00056 {
00057     return recursion_counter_.get_count();
00058 }
00059 
00060 
00061 int readonly_context_impl::get_recursion_count() const
00062 {
00063     return recursion_counter_.get_count();
00064 }
00065 
00066 
00067 int readwrite_context_impl::get_recursion_count() const
00068 {
00069     return recursion_counter_.get_count();
00070 }
00071 
00072 
00073 void readonly_context_impl::raise_user_interact() const
00074 {
00075     manager::raise_user_interact();
00076 }
00077 
00078 
00079 void readwrite_context_impl::raise_gamestate_changed() const
00080 {
00081     manager::raise_gamestate_changed();
00082 }
00083 
00084 
00085 team& readwrite_context_impl::current_team_w()
00086 {
00087     return (*resources::teams)[get_side()-1];
00088 }
00089 
00090 attack_result_ptr readwrite_context_impl::execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){
00091     unit_map::iterator i = resources::units->find(attacker_loc);
00092     double m_aggression = i.valid() && i->can_recruit() ? get_leader_aggression() : get_aggression();
00093     return actions::execute_attack_action(get_side(),true,attacker_loc,defender_loc,attacker_weapon, m_aggression);
00094 }
00095 
00096 
00097 attack_result_ptr readonly_context_impl::check_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){
00098     unit_map::iterator i = resources::units->find(attacker_loc);
00099     double m_aggression = i.valid() && i->can_recruit() ? get_leader_aggression() : get_aggression();
00100     return actions::execute_attack_action(get_side(),false,attacker_loc,defender_loc,attacker_weapon, m_aggression);
00101 }
00102 
00103 
00104 move_result_ptr readwrite_context_impl::execute_move_action(const map_location& from, const map_location& to, bool remove_movement){
00105     return actions::execute_move_action(get_side(),true,from,to,remove_movement);
00106 }
00107 
00108 
00109 move_result_ptr readonly_context_impl::check_move_action(const map_location& from, const map_location& to, bool remove_movement){
00110     return actions::execute_move_action(get_side(),false,from,to,remove_movement);
00111 }
00112 
00113 
00114 recall_result_ptr readwrite_context_impl::execute_recall_action(const std::string& id, const map_location &where, const map_location &from){
00115     return actions::execute_recall_action(get_side(),true,id,where,from);
00116 }
00117 
00118 
00119 recruit_result_ptr readwrite_context_impl::execute_recruit_action(const std::string& unit_name, const map_location &where, const map_location &from){
00120     return actions::execute_recruit_action(get_side(),true,unit_name,where,from);
00121 }
00122 
00123 
00124 recall_result_ptr readonly_context_impl::check_recall_action(const std::string& id, const map_location &where, const map_location &from){
00125     return actions::execute_recall_action(get_side(),false,id,where,from);
00126 }
00127 
00128 
00129 recruit_result_ptr readonly_context_impl::check_recruit_action(const std::string& unit_name, const map_location &where, const map_location &from){
00130     return actions::execute_recruit_action(get_side(),false,unit_name,where,from);
00131 }
00132 
00133 
00134 stopunit_result_ptr readwrite_context_impl::execute_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){
00135     return actions::execute_stopunit_action(get_side(),true,unit_location,remove_movement,remove_attacks);
00136 }
00137 
00138 
00139 stopunit_result_ptr readonly_context_impl::check_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){
00140     return actions::execute_stopunit_action(get_side(),false,unit_location,remove_movement,remove_attacks);
00141 }
00142 
00143 
00144 template<typename T>
00145 void readonly_context_impl::add_known_aspect(const std::string &name, boost::shared_ptr< typesafe_aspect <T> > &where)
00146 {
00147     boost::shared_ptr< typesafe_known_aspect <T> > ka_ptr(new typesafe_known_aspect<T>(name,where,aspects_));
00148     known_aspects_.insert(make_pair(name,ka_ptr));
00149 }
00150 
00151 readonly_context_impl::readonly_context_impl(side_context &context, const config &cfg)
00152         : cfg_(cfg),
00153         engines_(),
00154         known_aspects_(),
00155         aggression_(),
00156         attack_depth_(),
00157         aspects_(),
00158         attacks_(),
00159         avoid_(),
00160         caution_(),
00161         defensive_position_cache_(),
00162         dstsrc_(),enemy_dstsrc_(),
00163         enemy_possible_moves_(),
00164         enemy_srcdst_(),
00165         grouping_(),
00166         goals_(),
00167         keeps_(),
00168         leader_aggression_(),
00169         leader_goal_(),
00170         leader_value_(),
00171         move_maps_enemy_valid_(false),      
00172         move_maps_valid_(false),
00173         dst_src_valid_lua_(false),
00174         dst_src_enemy_valid_lua_(false),
00175         src_dst_valid_lua_(false),
00176         src_dst_enemy_valid_lua_(false),
00177         number_of_possible_recruits_to_force_recruit_(),
00178         passive_leader_(),
00179         passive_leader_shares_keep_(),
00180         possible_moves_(),
00181         recruitment_(),
00182         recruitment_ignore_bad_combat_(),
00183         recruitment_ignore_bad_movement_(),
00184         recruitment_pattern_(),
00185         recursion_counter_(context.get_recursion_count()),
00186         scout_village_targeting_(),
00187         simple_targeting_(),
00188         srcdst_(),
00189         support_villages_(),
00190         unit_stats_cache_(),
00191         village_value_(),
00192         villages_per_scout_()
00193     {
00194         init_side_context_proxy(context);
00195         manager::add_gamestate_observer(this);
00196 
00197         add_known_aspect("aggression",aggression_);
00198         add_known_aspect("attack_depth",attack_depth_);
00199         add_known_aspect("attacks",attacks_);
00200         add_known_aspect("avoid",avoid_);
00201         add_known_aspect("caution",caution_);
00202         add_known_aspect("grouping",grouping_);
00203         add_known_aspect("leader_aggression",leader_aggression_);
00204         add_known_aspect("leader_goal",leader_goal_);
00205         add_known_aspect("leader_value",leader_value_);
00206         add_known_aspect("number_of_possible_recruits_to_force_recruit",number_of_possible_recruits_to_force_recruit_);
00207         add_known_aspect("passive_leader",passive_leader_);
00208         add_known_aspect("passive_leader_shares_keep",passive_leader_shares_keep_);
00209         add_known_aspect("recruitment",recruitment_);
00210         add_known_aspect("recruitment_ignore_bad_combat",recruitment_ignore_bad_combat_);
00211         add_known_aspect("recruitment_ignore_bad_movement",recruitment_ignore_bad_movement_);
00212         add_known_aspect("recruitment_pattern",recruitment_pattern_);
00213         add_known_aspect("scout_village_targeting",scout_village_targeting_);
00214         add_known_aspect("simple_targeting",simple_targeting_);
00215         add_known_aspect("support_villages",support_villages_);
00216         add_known_aspect("village_value",village_value_);
00217         add_known_aspect("villages_per_scout",villages_per_scout_);
00218         keeps_.init(*resources::game_map);
00219 
00220     }
00221 
00222 void readonly_context_impl::on_readonly_context_create() {
00223     //init the composite ai engines
00224     foreach(const config &cfg_element, cfg_.child_range("engine")){
00225         engine::parse_engine_from_config(*this,cfg_element,std::back_inserter(engines_));
00226     }
00227 
00228     // init the composite ai aspects
00229     foreach(const config &cfg_element, cfg_.child_range("aspect")){
00230         std::vector<aspect_ptr> aspects;
00231         engine::parse_aspect_from_config(*this,cfg_element,cfg_element["id"],std::back_inserter(aspects));
00232         add_aspects(aspects);
00233     }
00234 
00235     // init the composite ai goals
00236     foreach(const config &cfg_element, cfg_.child_range("goal")){
00237         engine::parse_goal_from_config(*this,cfg_element,std::back_inserter(get_goals()));
00238     }
00239 }
00240 
00241 
00242 config side_context_impl::to_side_context_config() const
00243 {
00244     return config();
00245 }
00246 
00247 config readwrite_context_impl::to_readwrite_context_config() const
00248 {
00249     return config();
00250 }
00251 
00252 
00253 config readonly_context_impl::to_readonly_context_config() const
00254 {
00255     config cfg;
00256     foreach(const engine_ptr e, engines_) {
00257         cfg.add_child("engine",e->to_config());
00258     }
00259     foreach(const aspect_map::value_type a, aspects_) {
00260         cfg.add_child("aspect",a.second->to_config());
00261     }
00262     foreach(const goal_ptr g, goals_) {
00263         cfg.add_child("goal",g->to_config());
00264     }
00265     return cfg;
00266 }
00267 
00268 readonly_context_impl::~readonly_context_impl()
00269 {
00270     manager::remove_gamestate_observer(this);
00271 }
00272 
00273 void readonly_context_impl::handle_generic_event(const std::string& /*event_name*/)
00274 {
00275     invalidate_move_maps();
00276 }
00277 
00278 
00279 const game_info& readonly_context_impl::get_info() const{
00280     return manager::get_active_ai_info_for_side(get_side());
00281 }
00282 
00283 
00284 game_info& readwrite_context_impl::get_info_w(){
00285     return manager::get_active_ai_info_for_side(get_side());
00286 }
00287 
00288 void readonly_context_impl::diagnostic(const std::string& msg)
00289 {
00290     if(game_config::debug) {
00291         resources::screen->set_diagnostic(msg);
00292     }
00293 }
00294 
00295 
00296 const team& readonly_context_impl::current_team() const
00297 {
00298     return (*resources::teams)[get_side()-1];
00299 }
00300 
00301 
00302 void readonly_context_impl::log_message(const std::string& msg)
00303 {
00304     if(game_config::debug) {
00305         resources::screen->add_chat_message(time(NULL), "ai", get_side(), msg,
00306                 events::chat_handler::MESSAGE_PUBLIC, false);
00307     }
00308 }
00309 
00310 
00311 void readonly_context_impl::calculate_possible_moves(std::map<map_location,pathfind::paths>& res, move_map& srcdst,
00312         move_map& dstsrc, bool enemy, bool assume_full_movement,
00313         const terrain_filter* remove_destinations) const
00314 {
00315   calculate_moves(*resources::units,res,srcdst,dstsrc,enemy,assume_full_movement,remove_destinations);
00316 }
00317 
00318 void readonly_context_impl::calculate_moves(const unit_map& units, std::map<map_location,pathfind::paths>& res, move_map& srcdst,
00319         move_map& dstsrc, bool enemy, bool assume_full_movement,
00320         const terrain_filter* remove_destinations,
00321         bool see_all
00322           ) const
00323 {
00324 
00325     for(unit_map::const_iterator un_it = units.begin(); un_it != units.end(); ++un_it) {
00326         // If we are looking for the movement of enemies, then this unit must be an enemy unit.
00327         // If we are looking for movement of our own units, it must be on our side.
00328         // If we are assuming full movement, then it may be a unit on our side, or allied.
00329         if ((enemy && current_team().is_enemy(un_it->side()) == false) ||
00330             (!enemy && !assume_full_movement && un_it->side() != get_side()) ||
00331             (!enemy && assume_full_movement && current_team().is_enemy(un_it->side()))) {
00332             continue;
00333         }
00334         // Discount incapacitated units
00335         if (un_it->incapacitated() ||
00336             (!assume_full_movement && un_it->movement_left() == 0)) {
00337             continue;
00338         }
00339 
00340         // We can't see where invisible enemy units might move.
00341         if (enemy && un_it->invisible(un_it->get_location()) && !see_all) {
00342             continue;
00343         }
00344         // If it's an enemy unit, reset its moves while we do the calculations.
00345         unit* held_unit = const_cast<unit *>(&*un_it);
00346         const unit_movement_resetter move_resetter(*held_unit,enemy || assume_full_movement);
00347 
00348         // Insert the trivial moves of staying on the same map location.
00349         if (un_it->movement_left() > 0) {
00350             std::pair<map_location,map_location> trivial_mv(un_it->get_location(), un_it->get_location());
00351             srcdst.insert(trivial_mv);
00352             dstsrc.insert(trivial_mv);
00353         }
00354         res.insert(std::pair<map_location,pathfind::paths>(
00355             un_it->get_location(), pathfind::paths(*resources::game_map,
00356             units, *un_it, *resources::teams, false,
00357             true, current_team(), 0, see_all)));
00358     }
00359 
00360     // deactivate terrain filtering if it's just the dummy 'matches nothing'
00361     static const config only_not_tag("not");
00362     if(remove_destinations && remove_destinations->to_config() == only_not_tag) {
00363         remove_destinations = NULL;
00364     }
00365 
00366     for(std::map<map_location,pathfind::paths>::iterator m = res.begin(); m != res.end(); ++m) {
00367         foreach (const pathfind::paths::step &dest, m->second.destinations)
00368         {
00369             const map_location& src = m->first;
00370             const map_location& dst = dest.curr;
00371 
00372             if(remove_destinations != NULL && remove_destinations->match(dst)) {
00373                 continue;
00374             }
00375 
00376             bool friend_owns = false;
00377 
00378             // Don't take friendly villages
00379             if(!enemy && resources::game_map->is_village(dst)) {
00380                 for(size_t n = 0; n != resources::teams->size(); ++n) {
00381                     if((*resources::teams)[n].owns_village(dst)) {
00382                         int side = n + 1;
00383                         if (get_side() != side && !current_team().is_enemy(side)) {
00384                             friend_owns = true;
00385                         }
00386 
00387                         break;
00388                     }
00389                 }
00390             }
00391 
00392             if(friend_owns) {
00393                 continue;
00394             }
00395 
00396             if(src != dst && (find_visible_unit(dst, current_team()) == resources::units->end()) ) {
00397                 srcdst.insert(std::pair<map_location,map_location>(src,dst));
00398                 dstsrc.insert(std::pair<map_location,map_location>(dst,src));
00399             }
00400         }
00401     }
00402 }
00403 
00404 
00405 void readonly_context_impl::add_aspects(std::vector< aspect_ptr > &aspects )
00406 {
00407     foreach (aspect_ptr a, aspects) {
00408         const std::string id = a->get_id();
00409         known_aspect_map::iterator i = known_aspects_.find(id);
00410         if (i != known_aspects_.end()) {
00411             i->second->set(a);
00412         } else {
00413             ERR_AI << "when adding aspects, unknown aspect id["<<id<<"]"<<std::endl;
00414         }
00415     }
00416 }
00417 
00418 void readonly_context_impl::add_facet(const std::string &id, const config &cfg) const
00419 {
00420     known_aspect_map::const_iterator i = known_aspects_.find(id);
00421     if (i != known_aspects_.end()) {
00422         i->second->add_facet(cfg);
00423     } else {
00424         ERR_AI << "when adding aspects, unknown aspect id["<<id<<"]"<<std::endl;
00425     }
00426 }
00427 
00428 const defensive_position& readonly_context_impl::best_defensive_position(const map_location& loc,
00429         const move_map& dstsrc, const move_map& srcdst, const move_map& enemy_dstsrc) const
00430 {
00431     const unit_map::const_iterator itor = resources::units->find(loc);
00432     if(itor == resources::units->end()) {
00433         static defensive_position pos;
00434         pos.chance_to_hit = 0;
00435         pos.vulnerability = pos.support = 0;
00436         return pos;
00437     }
00438 
00439     const std::map<map_location,defensive_position>::const_iterator position =
00440         defensive_position_cache_.find(loc);
00441 
00442     if(position != defensive_position_cache_.end()) {
00443         return position->second;
00444     }
00445 
00446     defensive_position pos;
00447     pos.chance_to_hit = 100;
00448     pos.vulnerability = 10000.0;
00449     pos.support = 0.0;
00450 
00451     typedef move_map::const_iterator Itor;
00452     const std::pair<Itor,Itor> itors = srcdst.equal_range(loc);
00453     for(Itor i = itors.first; i != itors.second; ++i) {
00454         const int defense = itor->defense_modifier(resources::game_map->get_terrain(i->second));
00455         if(defense > pos.chance_to_hit) {
00456             continue;
00457         }
00458 
00459         const double vulnerability = power_projection(i->second,enemy_dstsrc);
00460         const double support = power_projection(i->second,dstsrc);
00461 
00462         if(defense < pos.chance_to_hit || support - vulnerability > pos.support - pos.vulnerability) {
00463             pos.loc = i->second;
00464             pos.chance_to_hit = defense;
00465             pos.vulnerability = vulnerability;
00466             pos.support = support;
00467         }
00468     }
00469 
00470     defensive_position_cache_.insert(std::pair<map_location,defensive_position>(loc,pos));
00471     return defensive_position_cache_[loc];
00472 }
00473 
00474 
00475 std::map<map_location,defensive_position>& readonly_context_impl::defensive_position_cache() const
00476 {
00477     return defensive_position_cache_;
00478 }
00479 
00480 
00481 double readonly_context_impl::get_aggression() const
00482 {
00483     if (aggression_) {
00484         return aggression_->get();
00485     }
00486     return 0;
00487 }
00488 
00489 
00490 int readonly_context_impl::get_attack_depth() const
00491 {
00492     if (attack_depth_) {
00493         return std::max<int>(1,attack_depth_->get()); ///@todo 1.9: add validators, such as minmax filters to aspects
00494     }
00495     return 1;
00496 }
00497 
00498 
00499 const aspect_map& readonly_context_impl::get_aspects() const
00500 {
00501     return aspects_;
00502 }
00503 
00504 
00505 aspect_map& readonly_context_impl::get_aspects()
00506 {
00507     return aspects_;
00508 }
00509 
00510 
00511 const attacks_vector& readonly_context_impl::get_attacks() const
00512 {
00513     if (attacks_) {
00514         return attacks_->get();
00515     }
00516     static attacks_vector av;
00517     return av;
00518 }
00519 
00520 
00521 const variant& readonly_context_impl::get_attacks_as_variant() const
00522 {
00523     if (attacks_) {
00524         return attacks_->get_variant();
00525     }
00526     static variant v;///@todo 1.9: replace with variant::null_variant;
00527     return v;
00528 }
00529 
00530 const terrain_filter& readonly_context_impl::get_avoid() const
00531 {
00532     if (avoid_) {
00533         return avoid_->get();
00534     }
00535     config cfg;
00536     cfg.add_child("not");
00537     static terrain_filter tf(vconfig(cfg),*resources::units);
00538     return tf;
00539 }
00540 
00541 
00542 double readonly_context_impl::get_caution() const
00543 {
00544     if (caution_) {
00545         return caution_->get();
00546     }
00547     return 0;
00548 }
00549 
00550 const move_map& readonly_context_impl::get_dstsrc() const
00551 {
00552     if (!move_maps_valid_) {
00553         recalculate_move_maps();
00554     }
00555     return dstsrc_;
00556 }
00557 
00558 
00559 const move_map& readonly_context_impl::get_enemy_dstsrc() const
00560 {
00561     if (!move_maps_enemy_valid_) {
00562         recalculate_move_maps_enemy();
00563     }
00564     return enemy_dstsrc_;
00565 }
00566 
00567 
00568 const moves_map& readonly_context_impl::get_enemy_possible_moves() const
00569 {
00570     if (!move_maps_enemy_valid_) {
00571         recalculate_move_maps_enemy();
00572     }
00573     return enemy_possible_moves_;
00574 }
00575 
00576 
00577 const move_map& readonly_context_impl::get_enemy_srcdst() const
00578 {
00579     if (!move_maps_enemy_valid_) {
00580         recalculate_move_maps_enemy();
00581     }
00582     return enemy_srcdst_;
00583 }
00584 
00585 
00586 engine_ptr readonly_context_impl::get_engine_by_cfg(const config& cfg)
00587 {
00588     std::string engine_name = cfg["engine"];
00589     if (engine_name.empty()) {
00590         engine_name="cpp";//default engine
00591     }
00592 
00593     std::vector<engine_ptr>::iterator en = engines_.begin();
00594     while (en!=engines_.end() && ((*en)->get_name()!=engine_name) && ((*en)->get_id()!=engine_name)) {
00595         ++en;
00596     }
00597 
00598     if (en != engines_.end()){
00599         return *en;
00600     }
00601 
00602     //TODO: fix, removing some code duplication
00603     engine_factory::factory_map::iterator eng = engine_factory::get_list().find(engine_name);
00604     if (eng == engine_factory::get_list().end()){
00605         ERR_AI << "side "<<get_side()<<" : UNABLE TO FIND engine["<<
00606             engine_name <<"]" << std::endl;
00607         DBG_AI << "config snippet contains: " << std::endl << cfg << std::endl;
00608         return engine_ptr();
00609     }
00610 
00611     engine_ptr new_engine = eng->second->get_new_instance(*this,engine_name);
00612     if (!new_engine) {
00613         ERR_AI << "side "<<get_side()<<" : UNABLE TO CREATE engine["<<
00614             engine_name <<"] " << std::endl;
00615         DBG_AI << "config snippet contains: " << std::endl << cfg << std::endl;
00616         return engine_ptr();
00617     }
00618     engines_.push_back(new_engine);
00619     return engines_.back();
00620 }
00621 
00622 
00623 const std::vector<engine_ptr>& readonly_context_impl::get_engines() const
00624 {
00625     return engines_;
00626 }
00627 
00628 
00629 std::vector<engine_ptr>& readonly_context_impl::get_engines()
00630 {
00631     return engines_;
00632 }
00633 
00634 
00635 std::string readonly_context_impl::get_grouping() const
00636 {
00637     if (grouping_) {
00638         return grouping_->get();
00639     }
00640     return std::string();
00641 }
00642 
00643 
00644 const std::vector<goal_ptr>& readonly_context_impl::get_goals() const
00645 {
00646     return goals_;
00647 }
00648 
00649 
00650 std::vector<goal_ptr>& readonly_context_impl::get_goals()
00651 {
00652     return goals_;
00653 }
00654 
00655 
00656 
00657 double readonly_context_impl::get_leader_aggression() const
00658 {
00659     if (leader_aggression_) {
00660         return leader_aggression_->get();
00661     }
00662     return 0;
00663 }
00664 
00665 
00666 config readonly_context_impl::get_leader_goal() const
00667 {
00668     if (leader_goal_) {
00669         return leader_goal_->get();
00670     }
00671     return config();
00672 }
00673 
00674 
00675 double readonly_context_impl::get_leader_value() const
00676 {
00677     if (leader_value_) {
00678         return leader_value_->get();
00679     }
00680     return 0;
00681 }
00682 
00683 
00684 double readonly_context_impl::get_number_of_possible_recruits_to_force_recruit() const
00685 {
00686     if (number_of_possible_recruits_to_force_recruit_) {
00687         return number_of_possible_recruits_to_force_recruit_->get();
00688     }
00689     return 0;
00690 }
00691 
00692 
00693 bool readonly_context_impl::get_passive_leader() const
00694 {
00695     if (passive_leader_) {
00696         return passive_leader_->get();
00697     }
00698     return false;
00699 }
00700 
00701 
00702 bool readonly_context_impl::get_passive_leader_shares_keep() const
00703 {
00704     if (passive_leader_shares_keep_) {
00705         return passive_leader_shares_keep_->get();
00706     }
00707     return false;
00708 }
00709 
00710 
00711 const moves_map& readonly_context_impl::get_possible_moves() const
00712 {
00713     if (!move_maps_valid_) {
00714         recalculate_move_maps();
00715     }
00716     return possible_moves_;
00717 }
00718 
00719 
00720 const std::vector<unit>& readonly_context_impl::get_recall_list() const
00721 {
00722     static std::vector<unit> dummy_units;
00723     ///@todo 1.9: check for (level_["disallow_recall"]))
00724     if(!current_team().persistent()) {
00725         return dummy_units;
00726     }
00727 
00728     return current_team().recall_list();
00729 }
00730 
00731 stage_ptr readonly_context_impl::get_recruitment(ai_context &context) const
00732 {
00733     if (recruitment_) {
00734         ministage_ptr m = recruitment_->get_ptr();
00735         if (m) {
00736             return m->get_stage_ptr(context);
00737         }
00738     }
00739     return stage_ptr();
00740 }
00741 
00742 
00743 bool readonly_context_impl::get_recruitment_ignore_bad_combat() const
00744 {
00745     if (recruitment_ignore_bad_combat_) {
00746         return recruitment_ignore_bad_combat_->get();
00747     }
00748     return false;
00749 }
00750 
00751 
00752 bool readonly_context_impl::get_recruitment_ignore_bad_movement() const
00753 {
00754     if (recruitment_ignore_bad_movement_) {
00755         return recruitment_ignore_bad_movement_->get();
00756     }
00757     return false;
00758 }
00759 
00760 
00761 const std::vector<std::string> readonly_context_impl::get_recruitment_pattern() const
00762 {
00763     if (recruitment_pattern_) {
00764         return recruitment_pattern_->get();
00765     }
00766     return std::vector<std::string>();
00767 }
00768 
00769 
00770 double readonly_context_impl::get_scout_village_targeting() const
00771 {
00772     if (scout_village_targeting_) {
00773         return scout_village_targeting_->get();
00774     }
00775     return 1;
00776 }
00777 
00778 
00779 bool readonly_context_impl::get_simple_targeting() const
00780 {
00781     if (simple_targeting_) {
00782         return simple_targeting_->get();
00783     }
00784     return false;
00785 }
00786 
00787 
00788 const move_map& readonly_context_impl::get_srcdst() const
00789 {
00790     if (!move_maps_valid_) {
00791         recalculate_move_maps();
00792     }
00793     return srcdst_;
00794 }
00795 
00796 
00797 bool readonly_context_impl::get_support_villages() const
00798 {
00799     if (support_villages_) {
00800         return support_villages_->get();
00801     }
00802     return false;
00803 }
00804 
00805 
00806 double readonly_context_impl::get_village_value() const
00807 {
00808     if (village_value_) {
00809         return village_value_->get();
00810     }
00811     return 0;
00812 }
00813 
00814 
00815 int readonly_context_impl::get_villages_per_scout() const
00816 {
00817     if (villages_per_scout_) {
00818         return villages_per_scout_->get();
00819     }
00820     return 0;
00821 }
00822 
00823 bool readonly_context_impl::is_dst_src_valid_lua() const
00824 {
00825     return dst_src_valid_lua_;
00826 }
00827 
00828 bool readonly_context_impl::is_dst_src_enemy_valid_lua() const
00829 {
00830     return dst_src_enemy_valid_lua_;
00831 }
00832 
00833 bool readonly_context_impl::is_src_dst_valid_lua() const
00834 {
00835     return src_dst_valid_lua_;
00836 }
00837 
00838 bool readonly_context_impl::is_src_dst_enemy_valid_lua() const
00839 {
00840     return src_dst_enemy_valid_lua_;
00841 }
00842 
00843 void readonly_context_impl::invalidate_defensive_position_cache() const
00844 {
00845     defensive_position_cache_.clear();
00846 }
00847 
00848 
00849 void readonly_context_impl::invalidate_keeps_cache() const
00850 {
00851     keeps_.clear();
00852 }
00853 
00854 
00855 void keeps_cache::handle_generic_event(const std::string &/*event_name*/)
00856 {
00857     clear();
00858 }
00859 
00860 
00861 void readonly_context_impl::invalidate_move_maps() const
00862 {
00863     move_maps_valid_ = false;
00864     move_maps_enemy_valid_ = false;
00865     
00866     dst_src_valid_lua_ = false;
00867     dst_src_enemy_valid_lua_ = false;
00868     
00869     src_dst_valid_lua_ = false;
00870     src_dst_enemy_valid_lua_ = false;
00871 }
00872 
00873 
00874 const std::set<map_location>& readonly_context_impl::keeps() const
00875 {
00876     return keeps_.get();
00877 }
00878 
00879 
00880 keeps_cache::keeps_cache()
00881     : map_(NULL)
00882     , keeps_()
00883 {
00884     ai::manager::add_turn_started_observer(this);
00885     ai::manager::add_map_changed_observer(this);
00886 }
00887 
00888 
00889 keeps_cache::~keeps_cache()
00890 {
00891     ai::manager::remove_turn_started_observer(this);
00892     ai::manager::remove_map_changed_observer(this);
00893 }
00894 
00895 void keeps_cache::clear()
00896 {
00897     keeps_.clear();
00898 }
00899 
00900 
00901 void keeps_cache::init(gamemap &map)
00902 {
00903     map_ = &map;
00904 }
00905 
00906 const std::set<map_location>& keeps_cache::get()
00907 {
00908     if(keeps_.empty()) {
00909         // Generate the list of keeps:
00910         // iterate over the entire map and find all keeps.
00911         for(size_t x = 0; x != size_t(map_->w()); ++x) {
00912             for(size_t y = 0; y != size_t(map_->h()); ++y) {
00913                 const map_location loc(x,y);
00914                 if(map_->is_keep(loc)) {
00915                     map_location adj[6];
00916                     get_adjacent_tiles(loc,adj);
00917                     for(size_t n = 0; n != 6; ++n) {
00918                         if(map_->is_castle(adj[n])) {
00919                             keeps_.insert(loc);
00920                             break;
00921                         }
00922                     }
00923                 }
00924             }
00925         }
00926     }
00927 
00928     return keeps_;
00929 }
00930 
00931 
00932 bool readonly_context_impl::leader_can_reach_keep() const
00933 {
00934     const unit_map::iterator leader = resources::units->find_leader(get_side());
00935     if(leader == resources::units->end() || leader->incapacitated()) {
00936         return false;
00937     }
00938 
00939     const map_location &start_pos = nearest_keep(leader->get_location());
00940     if(start_pos.valid() == false) {
00941         return false;
00942     }
00943 
00944     if (leader->get_location() == start_pos) {
00945         return true;
00946     }
00947 
00948     // Find where the leader can move
00949     const pathfind::paths leader_paths(*resources::game_map, *resources::units,
00950         *leader, *resources::teams, false, true, current_team());
00951 
00952     return leader_paths.destinations.contains(start_pos);
00953 }
00954 
00955 
00956 const map_location& readonly_context_impl::nearest_keep(const map_location& loc) const
00957 {
00958     std::set<map_location> avoided_locations;
00959     get_avoid().get_locations(avoided_locations);
00960     const std::set<map_location>& keeps = this->keeps();
00961     if(keeps.empty()) {
00962         static const map_location dummy;
00963         return dummy;
00964     }
00965 
00966     const map_location* res = NULL;
00967     int closest = -1;
00968     for(std::set<map_location>::const_iterator i = keeps.begin(); i != keeps.end(); ++i) {
00969         if (avoided_locations.find(*i)!=avoided_locations.end()) {
00970             continue;
00971         }
00972         const int distance = distance_between(*i,loc);
00973         if(res == NULL || distance < closest) {
00974             closest = distance;
00975             res = &*i;
00976         }
00977     }
00978     if (res) {
00979         return *res;
00980     } else {
00981         return map_location::null_location;
00982     }
00983 }
00984 
00985 
00986 double readonly_context_impl::power_projection(const map_location& loc, const move_map& dstsrc) const
00987 {
00988     map_location used_locs[6];
00989     int ratings[6];
00990     int num_used_locs = 0;
00991 
00992     map_location locs[6];
00993     get_adjacent_tiles(loc,locs);
00994 
00995     gamemap& map_ = *resources::game_map;
00996     unit_map& units_ = *resources::units;
00997 
00998     int res = 0;
00999 
01000     bool changed = false;
01001     for (int i = 0;; ++i) {
01002         if (i == 6) {
01003             if (!changed) break;
01004             // Loop once again, in case a unit found a better spot
01005             // and freed the place for another unit.
01006             changed = false;
01007             i = 0;
01008         }
01009 
01010         if (map_.on_board(locs[i]) == false) {
01011             continue;
01012         }
01013 
01014         const t_translation::t_terrain terrain = map_[locs[i]];
01015 
01016         typedef move_map::const_iterator Itor;
01017         typedef std::pair<Itor,Itor> Range;
01018         Range its = dstsrc.equal_range(locs[i]);
01019 
01020         map_location* const beg_used = used_locs;
01021         map_location* end_used = used_locs + num_used_locs;
01022 
01023         int best_rating = 0;
01024         map_location best_unit;
01025 
01026         for(Itor it = its.first; it != its.second; ++it) {
01027             const unit_map::const_iterator u = units_.find(it->second);
01028 
01029             // Unit might have been killed, and no longer exist
01030             if(u == units_.end()) {
01031                 continue;
01032             }
01033 
01034             const unit& un = *u;
01035 
01036             // The unit might play on the next turn
01037             int attack_turn = resources::tod_manager->turn();
01038             if(un.side() < get_side()) {
01039                 ++attack_turn;
01040             }
01041             // Considering the unit location would be too slow, we only apply the bonus granted by the global ToD
01042             const int lawful_bonus = resources::tod_manager->get_time_of_day(attack_turn).lawful_bonus;
01043             int tod_modifier = 0;
01044             if(un.alignment() == unit_type::LAWFUL) {
01045                 tod_modifier = lawful_bonus;
01046             } else if(un.alignment() == unit_type::CHAOTIC) {
01047                 tod_modifier = -lawful_bonus;
01048             } else if(un.alignment() == unit_type::LIMINAL) {
01049                 tod_modifier = -(abs(lawful_bonus));
01050             }
01051 
01052             // The 0.5 power avoids underestimating too much the damage of a wounded unit.
01053             int hp = int(sqrt(double(un.hitpoints()) / un.max_hitpoints()) * 1000);
01054             int most_damage = 0;
01055             foreach (const attack_type &att, un.attacks())
01056             {
01057                 int damage = att.damage() * att.num_attacks() * (100 + tod_modifier);
01058                 if (damage > most_damage) {
01059                     most_damage = damage;
01060                 }
01061             }
01062 
01063             int village_bonus = map_.is_village(terrain) ? 3 : 2;
01064             int defense = 100 - un.defense_modifier(terrain);
01065             int rating = hp * defense * most_damage * village_bonus / 200;
01066             if(rating > best_rating) {
01067                 map_location *pos = std::find(beg_used, end_used, it->second);
01068                 // Check if the spot is the same or better than an older one.
01069                 if (pos == end_used || rating >= ratings[pos - beg_used]) {
01070                     best_rating = rating;
01071                     best_unit = it->second;
01072                 }
01073             }
01074         }
01075 
01076         if (!best_unit.valid()) continue;
01077         map_location *pos = std::find(beg_used, end_used, best_unit);
01078         int index = pos - beg_used;
01079         if (index == num_used_locs)
01080             ++num_used_locs;
01081         else if (best_rating == ratings[index])
01082             continue;
01083         else {
01084             // The unit was in another spot already, so remove its older rating
01085             // from the final result, and require a new run to fill its old spot.
01086             res -= ratings[index];
01087             changed = true;
01088         }
01089         used_locs[index] = best_unit;
01090         ratings[index] = best_rating;
01091         res += best_rating;
01092     }
01093 
01094     return res / 100000.;
01095 }
01096 
01097 void readonly_context_impl::recalculate_move_maps() const
01098 {
01099     dstsrc_ = move_map();
01100     possible_moves_ = moves_map();
01101     srcdst_ = move_map();
01102     calculate_possible_moves(possible_moves_,srcdst_,dstsrc_,false,false,&get_avoid());
01103     if (get_passive_leader()||get_passive_leader_shares_keep()) {
01104         unit_map::iterator i = resources::units->find_leader(get_side());
01105         if (i.valid()) {
01106             map_location loc = i->get_location();
01107             srcdst_.erase(loc);
01108             for(move_map::iterator i = dstsrc_.begin(); i != dstsrc_.end(); ) {
01109                 if(i->second == loc) {
01110                     dstsrc_.erase(i++);
01111                 } else {
01112                     ++i;
01113                 }
01114             }
01115         ///@todo 1.9: shall possible moves be modified as well ?
01116         }
01117     }
01118     move_maps_valid_ = true;
01119     
01120     // invalidate lua cache
01121     dst_src_valid_lua_ = false;
01122     src_dst_valid_lua_ = false;
01123 }
01124 
01125 
01126 void readonly_context_impl::recalculate_move_maps_enemy() const
01127 {
01128     enemy_dstsrc_ = move_map();
01129     enemy_srcdst_ = move_map();
01130     enemy_possible_moves_ = moves_map();
01131     calculate_possible_moves(enemy_possible_moves_,enemy_srcdst_,enemy_dstsrc_,true);
01132     move_maps_enemy_valid_ = true;
01133     
01134     // invalidate lua cache
01135     dst_src_enemy_valid_lua_ = false;
01136     src_dst_enemy_valid_lua_ = false;
01137 }
01138 
01139 void readonly_context_impl::set_dst_src_valid_lua()
01140 {
01141     dst_src_valid_lua_ = true;
01142 }
01143 
01144 void readonly_context_impl::set_dst_src_enemy_valid_lua()
01145 {
01146     dst_src_enemy_valid_lua_ = true;
01147 }
01148 
01149 void readonly_context_impl::set_src_dst_valid_lua()
01150 {
01151     src_dst_valid_lua_ = true;
01152 }
01153 
01154 void readonly_context_impl::set_src_dst_enemy_valid_lua()
01155 {
01156     src_dst_enemy_valid_lua_ = true;
01157 }
01158 
01159 const map_location& readonly_context_impl::suitable_keep(const map_location& leader_location, const pathfind::paths& leader_paths){
01160     if (resources::game_map->is_keep(leader_location)) {
01161         return leader_location; //if leader already on keep, then return leader_location
01162     }
01163 
01164     map_location const* best_free_keep = &map_location::null_location;
01165     double move_left_at_best_free_keep = 0.0;
01166 
01167     map_location const* best_occupied_keep = &map_location::null_location;
01168     double move_left_at_best_occupied_keep = 0.0;
01169 
01170     foreach (const pathfind::paths::step &dest, leader_paths.destinations)
01171     {
01172         const map_location &loc = dest.curr;
01173         if (keeps().find(loc)!=keeps().end()){
01174 
01175             const int move_left_at_loc = dest.move_left;
01176             if (resources::units->count(loc) == 0) {
01177                 if ((*best_free_keep==map_location::null_location)||(move_left_at_loc>move_left_at_best_free_keep)){
01178                     best_free_keep = &loc;
01179                     move_left_at_best_free_keep = move_left_at_loc;
01180                 }
01181             } else {
01182                 if ((*best_occupied_keep==map_location::null_location)||(move_left_at_loc>move_left_at_best_occupied_keep)){
01183                     best_occupied_keep = &loc;
01184                         move_left_at_best_occupied_keep = move_left_at_loc;
01185                 }
01186             }
01187         }
01188     }
01189 
01190     if (*best_free_keep != map_location::null_location){
01191         return *best_free_keep; // if there is a free keep reachable during current turn, return it
01192     }
01193 
01194     if (*best_occupied_keep != map_location::null_location){
01195         return *best_occupied_keep; // if there is an occupied keep reachable during current turn, return it
01196     }
01197 
01198     return nearest_keep(leader_location); // return nearest keep
01199 }
01200 
01201 
01202     /** Weapon choice cache, to speed simulations. */
01203 std::map<std::pair<map_location,const unit_type *>,
01204         std::pair<battle_context_unit_stats,battle_context_unit_stats> >& readonly_context_impl::unit_stats_cache() const
01205 {
01206     return unit_stats_cache_;
01207 }
01208 
01209 
01210 bool readonly_context_impl::is_active(const std::string &time_of_day, const std::string &turns) const
01211 {
01212         if(time_of_day.empty() == false) {
01213             const std::vector<std::string>& times = utils::split(time_of_day);
01214             if(std::count(times.begin(),times.end(),resources::tod_manager->get_time_of_day().name) == 0) {
01215                 return false;
01216             }
01217         }
01218 
01219         if(turns.empty() == false) {
01220             int turn = resources::tod_manager->turn();
01221             const std::vector<std::string>& turns_list = utils::split(turns);
01222             for(std::vector<std::string>::const_iterator j = turns_list.begin(); j != turns_list.end() ; ++j ) {
01223                 const std::pair<int,int> range = utils::parse_range(*j);
01224                 if(turn >= range.first && turn <= range.second) {
01225                       return true;
01226                 }
01227             }
01228             return false;
01229         }
01230         return true;
01231 }
01232 
01233 } //of namespace ai
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Thu May 24 2012 01:02:28 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs