From 4d1ea0035a2e1aac51e1043c6ef2d625b1014d53 Mon Sep 17 00:00:00 2001 From: Iurii Chernyi Date: Thu, 21 May 2009 00:18:13 +0000 Subject: [PATCH] AI Refactoring - hierarchial AI contexts, ...a step needed for easier writing of composite ai components. --- src/ai/ai.cpp | 205 ++++++++++++++-------- src/ai/ai.hpp | 31 +++- src/ai/ai2.hpp | 15 +- src/ai/ai_actions.cpp | 16 +- src/ai/ai_actions.hpp | 15 +- src/ai/ai_attack.cpp | 14 +- src/ai/ai_dfool.cpp | 12 +- src/ai/ai_dfool.hpp | 16 +- src/ai/ai_interface.hpp | 29 +--- src/ai/ai_manager.cpp | 45 +++-- src/ai/ai_manager.hpp | 12 +- src/ai/ai_move.cpp | 28 +-- src/ai/ai_village.cpp | 6 +- src/ai/contexts.cpp | 96 +++++----- src/ai/contexts.hpp | 376 ++++++++++++++++++++++++++++++++++++---- src/ai/formula_ai.cpp | 37 ++-- src/ai/formula_ai.hpp | 17 +- src/ai/game_info.hpp | 11 ++ src/team.hpp | 2 +- 19 files changed, 712 insertions(+), 271 deletions(-) diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp index b393c097a52..0325743e1db 100644 --- a/src/ai/ai.cpp +++ b/src/ai/ai.cpp @@ -46,14 +46,26 @@ static lg::log_domain log_ai("ai/general"); typedef util::array adjacent_tiles_array; -idle_ai::idle_ai(int side, bool master) : ai_readwrite_context(side,master) +idle_ai::idle_ai(ai::readwrite_context &context) : ai::side_context_proxy(context), ai::readonly_context_proxy(context), ai::readwrite_context_proxy(context), recursion_counter_(context.get_recursion_count()) { } -std::string idle_ai::describe_self(){ +std::string idle_ai::describe_self() +{ return "[idle_ai]"; } +void idle_ai::switch_side(ai::side_number side) +{ + set_side(side); +} + +int idle_ai::get_recursion_count() const +{ + return recursion_counter_.get_count(); +} + + void idle_ai::play_turn() { game_events::fire("ai turn"); @@ -61,11 +73,12 @@ void idle_ai::play_turn() /** Sample ai, with simple strategy. */ -class sample_ai : public ai_readwrite_context { +class sample_ai : public ai::readwrite_context_proxy, public ai_interface { public: - sample_ai(int side, bool master) : ai_readwrite_context(side,master) {} + sample_ai(ai::readwrite_context &context) + : ai::side_context_proxy(context), ai::readonly_context_proxy(context), ai::readwrite_context_proxy(context), recursion_counter_(context.get_recursion_count()) {} - void play_turn() { + virtual void play_turn() { game_events::fire("ai turn"); do_attacks(); get_villages(); @@ -73,31 +86,36 @@ public: do_recruitment(); } - std::string describe_self(){ + virtual std::string describe_self(){ return "[sample_ai]"; } + virtual int get_recursion_count() const + { + return recursion_counter_.get_count(); + } + protected: void do_attacks() { - std::map possible_moves; - move_map srcdst, dstsrc; + std::map possible_moves; + ai::move_map srcdst, dstsrc; calculate_possible_moves(possible_moves,srcdst,dstsrc,false); for(unit_map::const_iterator i = get_info().units.begin(); i != get_info().units.end(); ++i) { if(current_team().is_enemy(i->second.side())) { - location adjacent_tiles[6]; + map_location adjacent_tiles[6]; get_adjacent_tiles(i->first,adjacent_tiles); int best_defense = -1; - std::pair best_movement; + std::pair best_movement; for(size_t n = 0; n != 6; ++n) { - typedef move_map::const_iterator Itor; + typedef ai::move_map::const_iterator Itor; std::pair range = dstsrc.equal_range(adjacent_tiles[n]); while(range.first != range.second) { - const location& dst = range.first->first; - const location& src = range.first->second; + const map_location& dst = range.first->first; + const map_location& src = range.first->second; const unit_map::const_iterator un = get_info().units.find(src); const t_translation::t_terrain terrain = get_info().map.get_terrain(dst); @@ -130,11 +148,11 @@ protected: } void get_villages() { - std::map possible_moves; - move_map srcdst, dstsrc; + std::map possible_moves; + ai::move_map srcdst, dstsrc; calculate_possible_moves(possible_moves,srcdst,dstsrc,false); - for(move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) { + for(ai::move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) { if(get_info().map.is_village(i->first) && current_team().owns_village(i->first) == false) { move_unit(i->second,i->first,possible_moves); get_villages(); @@ -154,14 +172,14 @@ protected: if(leader == get_info().units.end()) return; - std::map possible_moves; - move_map srcdst, dstsrc; + std::map possible_moves; + ai::move_map srcdst, dstsrc; calculate_possible_moves(possible_moves,srcdst,dstsrc,false); int closest_distance = -1; - std::pair closest_move; + std::pair closest_move; - for(move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) { + for(ai::move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) { const int distance = distance_between(i->first,leader->first); if(closest_distance == -1 || distance < closest_distance) { closest_distance = distance; @@ -175,6 +193,11 @@ protected: } } + void switch_side(ai::side_number side){ + set_side(side); + } + + bool do_recruitment() { const std::set& options = current_team().recruits(); if (!options.empty()) { @@ -190,10 +213,16 @@ protected: } return false; } +private: + ai::recursion_counter recursion_counter_; }; -ai::ai(int side, bool master) : - ai_readwrite_context(side,master), +ai_default::ai_default(ai::readwrite_context &context) : + ai::side_context_proxy(context), + ai::readonly_context_proxy(context), + ai::readwrite_context_proxy(context), + game_logic::formula_callable(), + recursion_counter_(context.get_recursion_count()), defensive_position_cache_(), threats_found_(false), attacks_(), @@ -216,13 +245,18 @@ ai::ai(int side, bool master) : { } -ai::~ai(){ +ai_default::~ai_default(){ if (formula_ai_!=NULL) { delete formula_ai_; } } -void ai::new_turn() +void ai_default::switch_side(ai::side_number side){ + set_side(side); +} + + +void ai_default::new_turn() { defensive_position_cache_.clear(); threats_found_ = false; @@ -242,11 +276,16 @@ void ai::new_turn() ai_interface::new_turn(); } -std::string ai::describe_self(){ +std::string ai_default::describe_self(){ return "[default_ai]"; } -bool ai::recruit_usage(const std::string& usage) +int ai_default::get_recursion_count() const +{ + return recursion_counter_.get_count(); +} + +bool ai_default::recruit_usage(const std::string& usage) { raise_user_interact(); @@ -316,14 +355,14 @@ bool ai::recruit_usage(const std::string& usage) WRN_AI << warning; // Uncommented until the recruitment limiting macro can be fixed to not trigger this warning. //lg::wml_error << warning; - return current_team().remove_recruitment_pattern_entry(usage); + return current_team_w().remove_recruitment_pattern_entry(usage); } return false; } -bool ai::multistep_move_possible(const location& from, - const location& to, const location& via, - const std::map& possible_moves) const +bool ai_default::multistep_move_possible(const map_location& from, + const map_location& to, const map_location& via, + const std::map& possible_moves) const { const unit_map::const_iterator i = units_.find(from); if(i != units_.end()) { @@ -331,7 +370,7 @@ bool ai::multistep_move_possible(const location& from, LOG_AI << "when seeing if leader can move from " << from << " -> " << to << " seeing if can detour to keep at " << via << '\n'; - const std::map::const_iterator moves = possible_moves.find(from); + const std::map::const_iterator moves = possible_moves.find(from); if(moves != possible_moves.end()) { LOG_AI << "found leader moves..\n"; @@ -363,10 +402,10 @@ bool ai::multistep_move_possible(const location& from, return false; } -map_location ai::move_unit(location from, location to, std::map& possible_moves) +map_location ai_default::move_unit(map_location from, map_location to, ai::moves_map& possible_moves) { - std::map temp_possible_moves; - std::map* possible_moves_ptr = &possible_moves; + ai::moves_map temp_possible_moves; + ai::moves_map* possible_moves_ptr = &possible_moves; const unit_map::const_iterator i = units_.find(from); if(i != units_.end() && i->second.can_recruit()) { @@ -377,7 +416,7 @@ map_location ai::move_unit(location from, location to, std::map& // If we can make it back to the keep and then to our original destination, do so. if(multistep_move_possible(from,to,start_pos,possible_moves)) { - from = ai_readwrite_context::move_unit(from,start_pos,possible_moves); + from = readwrite_context_proxy::move_unit(from,start_pos,possible_moves); if(from != start_pos) { return from; } @@ -399,7 +438,7 @@ map_location ai::move_unit(location from, location to, std::map& } if(units_.count(to) == 0 || from == to) { - const location res = ai_readwrite_context::move_unit(from,to,*possible_moves_ptr); + const map_location res = readwrite_context_proxy::move_unit(from,to,*possible_moves_ptr); if(res != to) { // We've been ambushed; find the ambushing unit and attack them. adjacent_tiles_array locs; @@ -422,9 +461,9 @@ map_location ai::move_unit(location from, location to, std::map& } } -bool ai::attack_close(const map_location& loc) const +bool ai_default::attack_close(const map_location& loc) const { - for(std::set::const_iterator i = attacks_.begin(); i != attacks_.end(); ++i) { + for(std::set::const_iterator i = attacks_.begin(); i != attacks_.end(); ++i) { if(distance_between(*i,loc) < 4) { return true; } @@ -433,15 +472,15 @@ bool ai::attack_close(const map_location& loc) const return false; } -void ai::attack_enemy(const location& attacking_unit, const location& target, +void ai_default::attack_enemy(const map_location& attacking_unit, const map_location& target, int att_weapon, int def_weapon) { attacks_.insert(attacking_unit); - ai_readwrite_context::attack_enemy(attacking_unit,target,att_weapon,def_weapon); + readwrite_context_proxy::attack_enemy(attacking_unit,target,att_weapon,def_weapon); } -void ai::remove_unit_from_moves(const map_location& loc, move_map& srcdst, move_map& dstsrc) +void ai_default::remove_unit_from_moves(const map_location& loc, move_map& srcdst, move_map& dstsrc) { srcdst.erase(loc); for(move_map::iterator i = dstsrc.begin(); i != dstsrc.end(); ) { @@ -468,7 +507,7 @@ struct protected_item { } -void ai::find_threats() +void ai_default::find_threats() { if(threats_found_) { return; @@ -529,7 +568,7 @@ void ai::find_threats() } } -void ai::play_turn() +void ai_default::play_turn() { // Protect against a memory over commitment: /** @@ -546,7 +585,7 @@ void ai::play_turn() } } -void ai::evaluate_recruiting_value(unit_map::iterator leader) +void ai_default::evaluate_recruiting_value(unit_map::iterator leader) { if (recruiting_preferred_ == 2) { @@ -560,10 +599,10 @@ void ai::evaluate_recruiting_value(unit_map::iterator leader) float free_slots = 0.0f; const float gold = current_team().gold(); - const float unit_price = current_team().average_recruit_price(); + const float unit_price = current_team_w().average_recruit_price(); if (map_.is_keep(leader->first)) { - std::set checked_hexes; + std::set checked_hexes; checked_hexes.insert(leader->first); free_slots = count_free_hexes_in_castle(leader->first, checked_hexes); } else { @@ -581,7 +620,7 @@ void ai::evaluate_recruiting_value(unit_map::iterator leader) " limit: " << current_team().num_pos_recruits_to_force() << "\n"; } -void ai::do_move() +void ai_default::do_move() { log_scope2(log_ai, "doing ai move"); @@ -745,7 +784,7 @@ void ai::do_move() } } -bool ai::do_combat(std::map& possible_moves, const move_map& srcdst, +bool ai_default::do_combat(std::map& possible_moves, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc) { int ticks = SDL_GetTicks(); @@ -806,14 +845,14 @@ bool ai::do_combat(std::map& possible_moves, const move_map& // Bad mistake -- the AI became extremely reluctant to attack anything. // Documenting this in case someone has this bright idea again...*don't*... if(choice_rating > 0.0) { - location from = choice_it->movements[0].first; - location to = choice_it->movements[0].second; - location target_loc = choice_it->target; + map_location from = choice_it->movements[0].first; + map_location to = choice_it->movements[0].second; + map_location target_loc = choice_it->target; // Never used: // const unit_map::const_iterator tgt = units_.find(target_loc); - const location arrived_at = move_unit(from,to,possible_moves); + const map_location arrived_at = move_unit(from,to,possible_moves); if(arrived_at != to || units_.find(to) == units_.end()) { WRN_AI << "unit moving to attack has ended up unexpectedly at " << arrived_at << " when moving to " << to << " from " << from << '\n'; @@ -843,7 +882,7 @@ bool ai::do_combat(std::map& possible_moves, const move_map& } -bool ai::get_healing(std::map& possible_moves, +bool ai_default::get_healing(std::map& possible_moves, const move_map& srcdst, const move_map& enemy_dstsrc) { // Find units in need of healing. @@ -900,7 +939,7 @@ bool ai::get_healing(std::map& possible_moves, return false; } -bool ai::should_retreat(const map_location& loc, const unit_map::const_iterator un, +bool ai_default::should_retreat(const map_location& loc, const unit_map::const_iterator un, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_dstsrc, double caution) { @@ -922,7 +961,7 @@ bool ai::should_retreat(const map_location& loc, const unit_map::const_iterator return caution*their_power*(1.0+exposure) > our_power; } -bool ai::retreat_units(std::map& possible_moves, +bool ai_default::retreat_units(std::map& possible_moves, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_dstsrc, unit_map::const_iterator leader) { @@ -1033,7 +1072,7 @@ bool ai::retreat_units(std::map& possible_moves, return false; } -bool ai::move_to_targets(std::map& possible_moves, +bool ai_default::move_to_targets(std::map& possible_moves, move_map& srcdst, move_map& dstsrc, const move_map& enemy_dstsrc, unit_map::const_iterator leader) { @@ -1153,7 +1192,7 @@ bool ai::move_to_targets(std::map& possible_moves, return false; } -int ai::average_resistance_against(const unit_type& a, const unit_type& b) const +int ai_default::average_resistance_against(const unit_type& a, const unit_type& b) const { int weighting_sum = 0, defense = 0; const std::map& terrain = @@ -1231,7 +1270,7 @@ int ai::average_resistance_against(const unit_type& a, const unit_type& b) const return sum/weight_sum; } -int ai::compare_unit_types(const unit_type& a, const unit_type& b) const +int ai_default::compare_unit_types(const unit_type& a, const unit_type& b) const { const int a_effectiveness_vs_b = average_resistance_against(b,a); const int b_effectiveness_vs_a = average_resistance_against(a,b); @@ -1242,7 +1281,7 @@ int ai::compare_unit_types(const unit_type& a, const unit_type& b) const return a_effectiveness_vs_b - b_effectiveness_vs_a; } -void ai::analyze_potential_recruit_combat() +void ai_default::analyze_potential_recruit_combat() { if(unit_combat_scores_.empty() == false || utils::string_bool(current_team().ai_parameters()["recruitment_ignore_bad_combat"])) { @@ -1313,7 +1352,7 @@ namespace { struct target_comparer_distance { target_comparer_distance(const map_location& loc) : loc_(loc) {} - bool operator()(const ai::target& a, const ai::target& b) const { + bool operator()(const ai_default::target& a, const ai_default::target& b) const { return distance_between(a.loc,loc_) < distance_between(b.loc,loc_); } @@ -1323,7 +1362,7 @@ private: } -void ai::analyze_potential_recruit_movements() +void ai_default::analyze_potential_recruit_movements() { if(unit_movement_scores_.empty() == false || utils::string_bool(current_team().ai_parameters()["recruitment_ignore_bad_movement"])) { @@ -1433,7 +1472,7 @@ void ai::analyze_potential_recruit_movements() } } -bool ai::do_recruitment() +bool ai_default::do_recruitment() { const unit_map::const_iterator leader = units_.find_leader(get_side()); if(leader == units_.end()) { @@ -1442,11 +1481,11 @@ bool ai::do_recruitment() raise_user_interact(); // Let formula ai to do recruiting first - if (get_master()) + if (get_recursion_count()(ai_manager::create_transient_ai(ai_manager::AI_TYPE_FORMULA_AI, get_side(),false)); + formula_ai_ = static_cast(ai_manager::create_transient_ai(ai_manager::AI_TYPE_FORMULA_AI, this)); } assert(formula_ai_ != NULL); @@ -1541,7 +1580,7 @@ bool ai::do_recruitment() return ret; } -void ai::move_leader_to_goals( const move_map& enemy_dstsrc) +void ai_default::move_leader_to_goals( const move_map& enemy_dstsrc) { const config &goal = current_team().ai_parameters().child("leader_goal"); @@ -1596,7 +1635,7 @@ void ai::move_leader_to_goals( const move_map& enemy_dstsrc) } } -void ai::move_leader_after_recruit(const move_map& /*srcdst*/, +void ai_default::move_leader_after_recruit(const move_map& /*srcdst*/, const move_map& /*dstsrc*/, const move_map& enemy_dstsrc) { @@ -1717,7 +1756,7 @@ void ai::move_leader_after_recruit(const move_map& /*srcdst*/, } } -bool ai::leader_can_reach_keep() +bool ai_default::leader_can_reach_keep() { const unit_map::iterator leader = units_.find_leader(get_side()); if(leader == units_.end() || leader->second.incapacitated()) { @@ -1740,7 +1779,7 @@ bool ai::leader_can_reach_keep() return leader_paths.destinations.contains(start_pos); } -int ai::rate_terrain(const unit& u, const map_location& loc) +int ai_default::rate_terrain(const unit& u, const map_location& loc) { const t_translation::t_terrain terrain = map_.get_terrain(loc); const int defense = u.defense_modifier(terrain); @@ -1770,7 +1809,7 @@ int ai::rate_terrain(const unit& u, const map_location& loc) return rating; } -const ai::defensive_position& ai::best_defensive_position(const map_location& loc, +const ai_default::defensive_position& ai_default::best_defensive_position(const map_location& loc, const move_map& dstsrc, const move_map& srcdst, const move_map& enemy_dstsrc) { const unit_map::const_iterator itor = units_.find(loc); @@ -1816,7 +1855,7 @@ const ai::defensive_position& ai::best_defensive_position(const map_location& lo return defensive_position_cache_[loc]; } -bool ai::is_accessible(const location& loc, const move_map& dstsrc) const +bool ai_default::is_accessible(const location& loc, const move_map& dstsrc) const { map_location adj[6]; get_adjacent_tiles(loc,adj); @@ -1830,7 +1869,7 @@ bool ai::is_accessible(const location& loc, const move_map& dstsrc) const } -const std::set& ai::keeps() +const std::set& ai_default::keeps() { if(keeps_.empty()) { // Generate the list of keeps: @@ -1855,7 +1894,7 @@ const std::set& ai::keeps() return keeps_; } -const map_location& ai::nearest_keep(const map_location& loc) +const map_location& ai_default::nearest_keep(const map_location& loc) { const std::set& keeps = this->keeps(); if(keeps.empty()) { @@ -1876,7 +1915,7 @@ const map_location& ai::nearest_keep(const map_location& loc) return *res; } -const std::set& ai::avoided_locations() +const std::set& ai_default::avoided_locations() { if(avoid_.empty()) { foreach (const config &av, current_team().ai_parameters().child_range("avoid")) @@ -1894,7 +1933,7 @@ const std::set& ai::avoided_locations() return avoid_; } -int ai::attack_depth() +int ai_default::attack_depth() { if(attack_depth_ > 0) { return attack_depth_; @@ -1905,8 +1944,22 @@ int ai::attack_depth() return attack_depth_; } +variant ai_default::get_value(const std::string& key) const +{ + if(key == "map") { + return variant(new gamemap_callable(get_info().map)); + } + return variant(); +} -variant ai::attack_analysis::get_value(const std::string& key) const +void ai_default::get_inputs(std::vector* inputs) const +{ + using game_logic::FORMULA_READ_ONLY; + inputs->push_back(game_logic::formula_input("map", FORMULA_READ_ONLY)); +} + + +variant ai_default::attack_analysis::get_value(const std::string& key) const { using namespace game_logic; if(key == "target") { @@ -1961,7 +2014,7 @@ variant ai::attack_analysis::get_value(const std::string& key) const } } -void ai::attack_analysis::get_inputs(std::vector* inputs) const +void ai_default::attack_analysis::get_inputs(std::vector* inputs) const { using namespace game_logic; inputs->push_back(formula_input("target", FORMULA_READ_ONLY)); diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp index 4e57138e0b2..14be2eabe91 100644 --- a/src/ai/ai.hpp +++ b/src/ai/ai.hpp @@ -20,28 +20,36 @@ #include "../global.hpp" #include "../actions.hpp" +#include "ai_interface.hpp" #include "contexts.hpp" #include "../formula_callable.hpp" class formula_ai; /** A trivial ai that sits around doing absolutely nothing. */ -class idle_ai : public ai_readwrite_context { +class idle_ai : public ai::readwrite_context_proxy, public ai_interface { public: - idle_ai(int side, bool master); + idle_ai(ai::readwrite_context &context); void play_turn(); virtual std::string describe_self(); + void switch_side(ai::side_number side); + int get_recursion_count() const; +private: + ai::recursion_counter recursion_counter_; }; -class ai : public ai_readwrite_context { +class ai_default : public virtual ai::readwrite_context_proxy, public ai_interface, public game_logic::formula_callable { public: + typedef ai::move_map move_map; + typedef map_location location;//will get rid of this later - ai(int side, bool master); - virtual ~ai(); + ai_default(ai::readwrite_context &context); + virtual ~ai_default(); virtual void play_turn(); virtual void new_turn(); virtual std::string describe_self(); + void switch_side(ai::side_number side); struct target { enum TYPE { VILLAGE, LEADER, EXPLICIT, THREAT, BATTLE_AID, MASS, SUPPORT }; @@ -72,14 +80,21 @@ public: void invalidate_defensive_position_cache() { defensive_position_cache_.clear(); } + virtual variant get_value(const std::string& key) const; + virtual void get_inputs(std::vector* inputs) const; + bool leader_can_reach_keep(); - /** Return true iff there has been another attack this turn 'close' to this one. */ + /** Return true if there has been another attack this turn 'close' to this one. */ bool attack_close(const location& loc) const; /** get most suitable keep for leader - nearest free that can be reached in 1 turn, if none - return nearest occupied that can be reached in 1 turn, if none - return nearest keep, if none - return null_location */ const map_location& suitable_keep( const location& leader_location, const paths& leader_paths ); + /** get the recursion counter */ + int get_recursion_count() const; +private: + ai::recursion_counter recursion_counter_; protected: std::map defensive_position_cache_; @@ -175,11 +190,11 @@ public: void analyze(const gamemap& map, unit_map& units, const std::vector& teams, const gamestatus& status, - class ai& ai_obj, + class ai_default& ai_obj, const move_map& dstsrc, const move_map& srcdst, const move_map& enemy_dstsrc, double aggression); - double rating(double aggression, class ai& ai_obj) const; + double rating(double aggression, class ai_default& ai_obj) const; variant get_value(const std::string& key) const; void get_inputs(std::vector* inputs) const; diff --git a/src/ai/ai2.hpp b/src/ai/ai2.hpp index d7f9ba55fe5..8c933b68481 100644 --- a/src/ai/ai2.hpp +++ b/src/ai/ai2.hpp @@ -20,15 +20,24 @@ #ifndef AI_AI2_HPP_INCLUDED #define AI_AI2_HPP_INCLUDED +#include "ai_interface.hpp" #include "contexts.hpp" -class ai2 : public ai_readwrite_context -{ +class ai2 : public ai::readwrite_context_proxy, public ai_interface { public: - ai2(int side, bool master) : ai_readwrite_context(side, master) + ai2(ai::readwrite_context &context) + : ai::side_context_proxy(context), ai::readonly_context_proxy(context), ai::readwrite_context_proxy(context),recursion_counter_(context.get_recursion_count()) {} virtual ~ai2() {} virtual void play_turn() {} + virtual void switch_side(ai::side_number side){ + set_side(side); + } + int get_recursion_count() const{ + return recursion_counter_.get_count(); + } +private: + ai::recursion_counter recursion_counter_; }; diff --git a/src/ai/ai_actions.cpp b/src/ai/ai_actions.cpp index 9e431bebf55..c6a4ef21ccf 100644 --- a/src/ai/ai_actions.cpp +++ b/src/ai/ai_actions.cpp @@ -669,50 +669,50 @@ std::ostream &operator<<(std::ostream &s, ai_stopunit_result const &r) { // STATELESS INTERFACE TO AI ACTIONS // ======================================================================= -std::auto_ptr< ai_attack_result > ai_actions::execute_attack_action( unsigned int side, +ai_attack_result_ptr ai_actions::execute_attack_action( unsigned int side, bool execute, const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon) { - std::auto_ptr< ai_attack_result > ai_action(new ai_attack_result(side,attacker_loc,defender_loc,attacker_weapon)); + ai_attack_result_ptr ai_action(new ai_attack_result(side,attacker_loc,defender_loc,attacker_weapon)); execute ? ai_action->execute() : ai_action->check_before(); return ai_action; } -std::auto_ptr< ai_move_result > ai_actions::execute_move_action( unsigned int side, +ai_move_result_ptr ai_actions::execute_move_action( unsigned int side, bool execute, const map_location& from, const map_location& to, bool remove_movement) { - std::auto_ptr< ai_move_result > ai_action(new ai_move_result(side,from,to,remove_movement)); + ai_move_result_ptr ai_action(new ai_move_result(side,from,to,remove_movement)); execute ? ai_action->execute() : ai_action->check_before(); return ai_action; } -std::auto_ptr< ai_recruit_result > ai_actions::execute_recruit_action( unsigned int side, +ai_recruit_result_ptr ai_actions::execute_recruit_action( unsigned int side, bool execute, const std::string& unit_name, const map_location& where) { - std::auto_ptr< ai_recruit_result > ai_action(new ai_recruit_result(side,unit_name,where)); + ai_recruit_result_ptr ai_action(new ai_recruit_result(side,unit_name,where)); execute ? ai_action->execute() : ai_action->check_before(); return ai_action; } -std::auto_ptr< ai_stopunit_result > ai_actions::execute_stopunit_action( unsigned int side, +ai_stopunit_result_ptr ai_actions::execute_stopunit_action( unsigned int side, bool execute, const map_location& unit_location, bool remove_movement, bool remove_attacks) { - std::auto_ptr< ai_stopunit_result > ai_action(new ai_stopunit_result(side,unit_location,remove_movement,remove_attacks)); + ai_stopunit_result_ptr ai_action(new ai_stopunit_result(side,unit_location,remove_movement,remove_attacks)); execute ? ai_action->execute() : ai_action->check_before(); return ai_action; diff --git a/src/ai/ai_actions.hpp b/src/ai/ai_actions.hpp index b1d8634af70..46942973c6e 100644 --- a/src/ai/ai_actions.hpp +++ b/src/ai/ai_actions.hpp @@ -240,6 +240,11 @@ std::ostream &operator<<(std::ostream &s, ai_move_result const &r); std::ostream &operator<<(std::ostream &s, ai_recruit_result const &r); std::ostream &operator<<(std::ostream &s, ai_stopunit_result const &r); +typedef boost::shared_ptr ai_attack_result_ptr; +typedef boost::shared_ptr ai_move_result_ptr; +typedef boost::shared_ptr ai_recruit_result_ptr; +typedef boost::shared_ptr ai_stopunit_result_ptr; + class ai_actions { public: @@ -260,7 +265,7 @@ public: * @retval possible result: attacker and/or defender are invalid * @retval possible result: attacker doesn't have the specified weapon */ -static std::auto_ptr execute_attack_action( unsigned int side, +static ai_attack_result_ptr execute_attack_action( unsigned int side, bool execute, const map_location& attacker_loc, const map_location& defender_loc, @@ -279,7 +284,7 @@ static std::auto_ptr execute_attack_action( unsigned int side, * @retval possible result: move is interrupted * @retval possible result: move is impossible */ -static std::auto_ptr execute_move_action( unsigned int side, +static ai_move_result_ptr execute_move_action( unsigned int side, bool execute, const map_location& from, const map_location& to, @@ -298,7 +303,7 @@ static std::auto_ptr execute_move_action( unsigned int side, * @retval possible_result: no free space on keep * @retval possible_result: not enough gold */ -static std::auto_ptr execute_recruit_action( unsigned int side, +static ai_recruit_result_ptr execute_recruit_action( unsigned int side, bool execute, const std::string& unit_name, const map_location& where ); @@ -315,7 +320,7 @@ static std::auto_ptr execute_recruit_action( unsigned int sid * @retval possible_result: something wrong * @retval possible_result: nothing to do */ -static std::auto_ptr execute_stopunit_action( unsigned int side, +static ai_stopunit_result_ptr execute_stopunit_action( unsigned int side, bool execute, const map_location& unit_location, bool remove_movement, @@ -325,5 +330,7 @@ static std::auto_ptr execute_stopunit_action( unsigned int s }; +//@todo 1.7 Add an ai action to set a goto on a unit //@todo 1.7 Add an ai action to send a chat message to a player + #endif diff --git a/src/ai/ai_attack.cpp b/src/ai/ai_attack.cpp index fb7cfda63b4..74dd9bdd410 100644 --- a/src/ai/ai_attack.cpp +++ b/src/ai/ai_attack.cpp @@ -32,7 +32,7 @@ static lg::log_domain log_ai("ai/attack"); const int max_positions = 10000; /** Analyze possibility of attacking target on 'loc'. */ -void ai::do_attack_analysis( +void ai_default::do_attack_analysis( const location& loc, const move_map& srcdst, const move_map& dstsrc, const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc, @@ -296,10 +296,10 @@ void ai::do_attack_analysis( } -void ai::attack_analysis::analyze(const gamemap& map, unit_map& units, +void ai_default::attack_analysis::analyze(const gamemap& map, unit_map& units, const std::vector& teams, const gamestatus& status, - class ai& ai_obj, + class ai_default& ai_obj, const move_map& dstsrc, const move_map& srcdst, const move_map& enemy_dstsrc, double aggression) { @@ -512,7 +512,7 @@ void ai::attack_analysis::analyze(const gamemap& map, unit_map& units, } } -double ai::attack_analysis::rating(double aggression, ai& ai_obj) const +double ai_default::attack_analysis::rating(double aggression, ai_default& ai_obj) const { if(leader_threat) { aggression = 1.0; @@ -598,7 +598,7 @@ double ai::attack_analysis::rating(double aggression, ai& ai_obj) const return value; } -std::vector ai::analyze_targets( +std::vector ai_default::analyze_targets( const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc ) @@ -651,7 +651,7 @@ std::vector ai::analyze_targets( return res; } -double ai::power_projection(const map_location& loc, const move_map& dstsrc, bool use_terrain) const +double ai_default::power_projection(const map_location& loc, const move_map& dstsrc, bool use_terrain) const { map_location used_locs[6]; int ratings[6]; @@ -762,7 +762,7 @@ double ai::power_projection(const map_location& loc, const move_map& dstsrc, bo * There is no real hope for us: we should try to do some damage to the enemy. * We can spend some cycles here, since it's rare. */ -bool ai::desperate_attack(const map_location &loc) +bool ai_default::desperate_attack(const map_location &loc) { const unit &u = units_.find(loc)->second; LOG_AI << "desperate attack by '" << u.type_id() << "' " << loc << "\n"; diff --git a/src/ai/ai_dfool.cpp b/src/ai/ai_dfool.cpp index 236689f5f67..d8b5807d5cd 100644 --- a/src/ai/ai_dfool.cpp +++ b/src/ai/ai_dfool.cpp @@ -179,7 +179,7 @@ namespace dfool { } unit_memory_.write(ai_mem); - current_team().set_ai_memory(ai_mem); + current_team_w().set_ai_memory(ai_mem); return; } @@ -239,11 +239,11 @@ namespace dfool { bool dfool_ai::moveto(const config &o, unit_map::const_iterator m) { - location target(atoi(o["target_x"].c_str()) - 1, atoi(o["target_y"].c_str()) - 1); + map_location target(atoi(o["target_x"].c_str()) - 1, atoi(o["target_y"].c_str()) - 1); LOG_AI << "\tmoving to:(" << target.x << ',' << target.y << ")\n"; if(m->second.movement_left()){ - std::map possible_moves; - move_map srcdst, dstsrc; + ai::moves_map possible_moves; + ai::move_map srcdst, dstsrc; unit_map known_units; // unit_memory_.known_map(known_units, get_info().state.turn()); @@ -257,10 +257,10 @@ bool dfool_ai::moveto(const config &o, unit_map::const_iterator m) calculate_moves(known_units,possible_moves,srcdst,dstsrc,false,false,NULL,true); int closest_distance = -1; - std::pair closest_move; + std::pair closest_move; /** @todo 2.0 This undoubtedly could be done more cleanly */ - for(move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) { + for(ai::move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) { // Must restrict move_map to only unit that is moving. if(i->second==m->first){ const int distance = distance_between(target,i->first); diff --git a/src/ai/ai_dfool.hpp b/src/ai/ai_dfool.hpp index c31774eb653..4f91f3f4166 100644 --- a/src/ai/ai_dfool.hpp +++ b/src/ai/ai_dfool.hpp @@ -22,6 +22,7 @@ #include "../global.hpp" +#include "ai_interface.hpp" #include "contexts.hpp" #include "../map_location.hpp" #include "../unit_map.hpp" @@ -98,15 +99,22 @@ namespace dfool { * does not target units that it has not "seen", * and does not make decisions based on unseen units. */ - class dfool_ai : public ai_readwrite_context { + class dfool_ai : public ai::readwrite_context_proxy, public ai_interface { public: - dfool_ai(int side, bool master) : ai_readwrite_context(side, master), unit_memory_(current_team().ai_memory()){} - void play_turn(); - virtual std::string describe_self(); + dfool_ai(ai::readwrite_context &context) + : ai::side_context_proxy(context), ai::readonly_context_proxy(context), ai::readwrite_context_proxy(context), recursion_counter_(context.get_recursion_count()), unit_memory_(current_team().ai_memory()){} + void play_turn(); + virtual std::string describe_self(); + virtual int get_recursion_count() const{ + return recursion_counter_.get_count(); + } private: + ai::recursion_counter recursion_counter_; // std::map target_map_; unit_list all_units(); unit_list visible_units(); + void switch_side(ai::side_number /*side*/) + {} unit_list my_units(); unit_list filter_units(const config& filter,unit_list& ul, unit_map& um); bool moveto(const config &o, unit_map::const_iterator m); diff --git a/src/ai/ai_interface.hpp b/src/ai/ai_interface.hpp index f5017cde229..4e5558c7bf3 100644 --- a/src/ai/ai_interface.hpp +++ b/src/ai/ai_interface.hpp @@ -21,32 +21,14 @@ #define AI_AI_INTERFACE_HPP_INCLUDED #include "../formula_callable.hpp" +#include "game_info.hpp" -class side_context { -public: - - side_context(int side) : side_(side) {} - - virtual ~side_context() {} - - /** get the 1-based side number which is controlled by this AI */ - int get_side() const { return side_;} - - /** Set the side */ - virtual void set_side(int side) { side_ = side; } - -private: - int side_; - -}; - - -class ai_interface : public side_context { +class ai_interface { public: /** * The constructor. */ - ai_interface(unsigned int side, bool master) : side_context(side), master_(master) { + ai_interface() { } virtual ~ai_interface() {} @@ -63,8 +45,7 @@ public: virtual void new_turn() { } - /** get the 'master' flag of the AI. 'master' AI is the top-level-AI. */ - bool get_master() const { return master_;} + virtual void switch_side(ai::side_number side) = 0; /** Evaluate */ virtual std::string evaluate(const std::string& /*str*/) @@ -73,8 +54,6 @@ public: /** Describe self*/ virtual std::string describe_self() const; -private: - bool master_; }; #endif diff --git a/src/ai/ai_manager.cpp b/src/ai/ai_manager.cpp index 8ff85604d9c..fb347d8d6ab 100644 --- a/src/ai/ai_manager.cpp +++ b/src/ai/ai_manager.cpp @@ -22,6 +22,7 @@ #include "ai_configuration.hpp" #include "ai_manager.hpp" #include "ai_dfool.hpp" +#include "contexts.hpp" #include "formula_ai.hpp" #include "../game_events.hpp" #include "../game_preferences.hpp" @@ -48,7 +49,7 @@ static lg::log_domain log_ai_manager("ai/manager"); #define ERR_AI_MANAGER LOG_STREAM(err, log_ai_manager) ai_holder::ai_holder( int side, const std::string& ai_algorithm_type ) - : ai_(NULL), ai_algorithm_type_(ai_algorithm_type), ai_effective_parameters_(), ai_global_parameters_(), ai_memory_(), ai_parameters_(), side_(side) + : ai_(NULL), side_context_(NULL), readonly_context_(NULL), readwrite_context_(NULL), ai_algorithm_type_(ai_algorithm_type), ai_effective_parameters_(), ai_global_parameters_(), ai_memory_(), ai_parameters_(), side_(side) { DBG_AI_MANAGER << describe_ai() << "Preparing new AI holder" << std::endl; } @@ -57,6 +58,17 @@ ai_holder::ai_holder( int side, const std::string& ai_algorithm_type ) void ai_holder::init( int side ) { LOG_AI_MANAGER << describe_ai() << "Preparing to create new managed master AI" << std::endl; + if (side_context_ == NULL) { + side_context_ = new ai::side_context_impl(side); + } else { + side_context_->set_side(side); + } + if (readonly_context_ == NULL){ + readonly_context_ = new ai::readonly_context_impl(*side_context_); + } + if (readwrite_context_ == NULL){ + readwrite_context_ = new ai::readwrite_context_impl(*readonly_context_); + } this->ai_ = create_ai(side); if (this->ai_ == NULL) { ERR_AI_MANAGER << describe_ai()<<"AI lazy initialization error!" << std::endl; @@ -69,8 +81,11 @@ ai_holder::~ai_holder() { if (this->ai_ != NULL) { LOG_AI_MANAGER << describe_ai() << "Managed AI will be deleted" << std::endl; - delete this->ai_; } + delete this->ai_; + delete this->readwrite_context_; + delete this->readonly_context_; + delete this->side_context_; } @@ -181,8 +196,9 @@ bool ai_holder::is_mandate_ok() ai_interface* ai_holder::create_ai( int side ) { assert (side > 0); - //@note: ai_params and ai_algorithm_type are supposed to be set before calling init( ); - return ai_manager::create_transient_ai(ai_algorithm_type_,side,true); + assert (readwrite_context_!=NULL); + //@note: ai_params, contexts, and ai_algorithm_type are supposed to be set before calling init( ); + return ai_manager::create_transient_ai(ai_algorithm_type_,readwrite_context_); } @@ -523,42 +539,45 @@ bool ai_manager::add_ai_for_side( int side, const std::string& ai_algorithm_type } -ai_interface* ai_manager::create_transient_ai( const std::string& ai_algorithm_type, int side, bool master ) +ai_interface* ai_manager::create_transient_ai( const std::string &ai_algorithm_type, ai::readwrite_context *rw_context ) { + assert(rw_context!=NULL); //@todo 1.7 modify this code to use a 'factory lookup' pattern - - //a singleton which holds a map of all functors which can create AIs. + //a singleton which holds a map of all functors which can create AIs. //this will allow individual AI implementations to 'register' themselves. + + //: To add an AI of your own, put // if(ai_algorithm_type == "my_ai") { // LOG_AI_MANAGER << "Creating new AI of type [" << "my_ai" << "]"<< std::endl; - // return new my_ai(side,master); + // return new my_ai(rw_context); // } // at the top of this function //if(ai_algorithm_type == ai_manager::AI_TYPE_SAMPLE_AI) { // LOG_AI_MANAGER << "Creating new AI of type [" << ai_manager::AI_TYPE_IDLE_AI << "]"<< std::endl; - // return new sample_ai(side,master); + // return new sample_ai(*rw_context); //} if(ai_algorithm_type == ai_manager::AI_TYPE_IDLE_AI) { LOG_AI_MANAGER << "Creating new AI of type [" << ai_manager::AI_TYPE_IDLE_AI << "]"<< std::endl; - return new idle_ai(side,master); + return new idle_ai(*rw_context); } if(ai_algorithm_type == ai_manager::AI_TYPE_FORMULA_AI) { LOG_AI_MANAGER << "Creating new AI of type [" << ai_manager::AI_TYPE_FORMULA_AI << "]"<< std::endl; - return new formula_ai(side,master); + return new formula_ai(*rw_context); } if(ai_algorithm_type == ai_manager::AI_TYPE_DFOOL_AI) { LOG_AI_MANAGER << "Creating new AI of type [" << ai_manager::AI_TYPE_DFOOL_AI << "]"<< std::endl; - return new dfool::dfool_ai(side,master); + return new dfool::dfool_ai(*rw_context); } if(ai_algorithm_type == ai_manager::AI_TYPE_AI2) { LOG_AI_MANAGER << "Creating new AI of type [" << ai_manager::AI_TYPE_AI2 << "]"<< std::endl; - return new ai2(side,master); + return new ai2(*rw_context); } if (!ai_algorithm_type.empty() && ai_algorithm_type != ai_manager::AI_TYPE_DEFAULT) { @@ -566,7 +585,7 @@ ai_interface* ai_manager::create_transient_ai( const std::string& ai_algorithm_t } LOG_AI_MANAGER << "Creating new AI of type [" << ai_manager::AI_TYPE_DEFAULT << "]"<< std::endl; - return new ai(side,master); + return new ai_default(*rw_context); } std::vector ai_manager::get_available_ais() diff --git a/src/ai/ai_manager.hpp b/src/ai/ai_manager.hpp index cc0ee2a0ddd..bce22d5e4fe 100644 --- a/src/ai/ai_manager.hpp +++ b/src/ai/ai_manager.hpp @@ -24,6 +24,7 @@ #define AI_AI_MANAGER_HPP_INCLUDED #include "../global.hpp" +#include "contexts.hpp" #include "game_info.hpp" #include @@ -73,7 +74,10 @@ public: bool is_mandate_ok(); private: - ai_interface* ai_; + ai_interface *ai_; + ai::side_context *side_context_; + ai::readonly_context *readonly_context_; + ai::readwrite_context *readwrite_context_; std::string ai_algorithm_type_; config ai_effective_parameters_; config ai_global_parameters_; @@ -262,12 +266,10 @@ public: * Returns a pointer to a new AI. It is the sole responsibility of the caller * to manage its lifetime. * @param ai_algorithm_type type of AI algorithm to create - * @param side side number (1-based) - * @param master should this AI be a master AI ? Only master AIs are - * allowed to fallback. If you are not sure, pick false here. + * @param context context in which this ai is created * @return the reference to the created AI */ - static ai_interface* create_transient_ai( const std::string& ai_algorithm_type, int side, bool master = false); + static ai_interface* create_transient_ai( const std::string& ai_algorithm_type, ai::readwrite_context *rw_context); /** diff --git a/src/ai/ai_move.cpp b/src/ai/ai_move.cpp index 3875e488bae..a598b28f3da 100644 --- a/src/ai/ai_move.cpp +++ b/src/ai/ai_move.cpp @@ -91,7 +91,7 @@ private: const bool avoid_enemies_; }; -std::vector ai::find_targets(unit_map::const_iterator leader, const move_map& enemy_dstsrc) +std::vector ai_default::find_targets(unit_map::const_iterator leader, const move_map& enemy_dstsrc) { log_scope2(log_ai, "finding targets..."); @@ -182,7 +182,7 @@ std::vector ai::find_targets(unit_map::const_iterator leader, const } } - std::vector& team_targets = current_team().targets(); + std::vector& team_targets = current_team_w().targets(); //find the enemy leaders and explicit targets unit_map::const_iterator u; @@ -233,7 +233,7 @@ std::vector ai::find_targets(unit_map::const_iterator leader, const return targets; } -map_location ai::form_group(const std::vector& route, const move_map& dstsrc, std::set& res) +map_location ai_default::form_group(const std::vector& route, const move_map& dstsrc, std::set& res) { if(route.empty()) { return location(); @@ -274,7 +274,7 @@ map_location ai::form_group(const std::vector& route, const move_map& return *i; } -void ai::enemies_along_path(const std::vector& route, const move_map& dstsrc, std::set& res) +void ai_default::enemies_along_path(const std::vector& route, const move_map& dstsrc, std::set& res) { for(std::vector::const_iterator i = route.begin(); i != route.end(); ++i) { map_location adj[6]; @@ -288,7 +288,7 @@ void ai::enemies_along_path(const std::vector& route, const move_map& } } -bool ai::move_group(const location& dst, const std::vector& route, const std::set& units) +bool ai_default::move_group(const location& dst, const std::vector& route, const std::set& units) { const std::vector::const_iterator itor = std::find(route.begin(),route.end(),dst); if(itor == route.end()) { @@ -388,7 +388,7 @@ bool ai::move_group(const location& dst, const std::vector& route, con return res; } -double ai::rate_group(const std::set& group, const std::vector& battlefield) const +double ai_default::rate_group(const std::set& group, const std::vector& battlefield) const { double strength = 0.0; for(std::set::const_iterator i = group.begin(); i != group.end(); ++i) { @@ -420,14 +420,14 @@ double ai::rate_group(const std::set& group, const std::vector& our_group, const std::set& their_group, const std::vector& battlefield) const +double ai_default::compare_groups(const std::set& our_group, const std::set& their_group, const std::vector& battlefield) const { const double a = rate_group(our_group,battlefield); const double b = std::max(rate_group(their_group,battlefield),0.01); return a/b; } -std::pair ai::choose_move(std::vector& targets, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_dstsrc) +std::pair ai_default::choose_move(std::vector& targets, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_dstsrc) { log_scope2(log_ai, "choosing move"); @@ -531,7 +531,7 @@ std::pair ai::choose_move(std::vector& target if(tg->type == target::VILLAGE) { if(current_team().ai_parameters().has_attribute("scout_village_targetting")) { rating *= lexical_cast_default(current_team().ai_parameters()["scout_village_targetting"],3); - lg::wml_error << "[ai] the 'scout_village_targetting' attribute is deprecated, support will be removed in version 1.7.0; use 'scout_village_targeting' instead\n"; + lg::wml_error << "[ai_default] the 'scout_village_targetting' attribute is deprecated, support will be removed in version 1.7.0; use 'scout_village_targeting' instead\n"; } else { rating *= lexical_cast_default(current_team().ai_parameters()["scout_village_targeting"],3); @@ -566,7 +566,7 @@ std::pair ai::choose_move(std::vector& target bool simple_targeting = false; if(current_team().ai_parameters().has_attribute("simple_targetting")) { simple_targeting = utils::string_bool(current_team().ai_parameters()["simple_targetting"]); - lg::wml_error << "[ai] the 'simple_targetting' attribute is deprecated, support will be removed in version 1.7.0; use 'simple_targeting' instead\n"; + lg::wml_error << "[ai_default] the 'simple_targetting' attribute is deprecated, support will be removed in version 1.7.0; use 'simple_targeting' instead\n"; } else { simple_targeting = utils::string_bool(current_team().ai_parameters()["simple_targeting"]); @@ -844,7 +844,7 @@ std::pair ai::choose_move(std::vector& target return std::pair(); } -void ai::access_points(const move_map& srcdst, const location& u, const location& dst, std::vector& out) +void ai_default::access_points(const move_map& srcdst, const location& u, const location& dst, std::vector& out) { const unit_map::const_iterator u_it = units_.find(u); if(u_it == units_.end()) { @@ -866,7 +866,7 @@ void ai::access_points(const move_map& srcdst, const location& u, const location } } -const map_location& ai::suitable_keep(const map_location& leader_location, const paths& leader_paths){ +const map_location& ai_default::suitable_keep(const map_location& leader_location, const paths& leader_paths){ if (map_.is_keep(leader_location)) { return leader_location; //if leader already on keep, then return leader_location } @@ -908,7 +908,7 @@ const map_location& ai::suitable_keep(const map_location& leader_location, const return nearest_keep(leader_location); // return nearest keep } -void ai::move_leader_to_keep(const move_map& enemy_dstsrc) +void ai_default::move_leader_to_keep(const move_map& enemy_dstsrc) { const unit_map::iterator leader = units_.find_leader(get_side()); if(leader == units_.end() || leader->second.incapacitated()) { @@ -957,7 +957,7 @@ void ai::move_leader_to_keep(const move_map& enemy_dstsrc) } } -int ai::count_free_hexes_in_castle(const map_location& loc, std::set& checked_hexes) +int ai_default::count_free_hexes_in_castle(const map_location& loc, std::set& checked_hexes) { int ret = 0; location adj[6]; diff --git a/src/ai/ai_village.cpp b/src/ai/ai_village.cpp index 346e582024a..fbd5c2ea9a1 100644 --- a/src/ai/ai_village.cpp +++ b/src/ai/ai_village.cpp @@ -15,7 +15,7 @@ /** * @file ai/ai_village.cpp * The village capturing part of the AI. - * ai::get_villages and ai::find_villages are based on ai::get_villages in ai.cpp + * ai_default::get_villages and ai_default::find_villages are based on ai_default::get_villages in ai.cpp */ #include "../global.hpp" @@ -151,7 +151,7 @@ static void full_dispatch(treachmap& reachmap, tmoves& moves); /** Shows which villages every unit can reach (debug function). */ static void dump_reachmap(treachmap& reachmap); -bool ai::get_villages(std::map& possible_moves, +bool ai_default::get_villages(std::map& possible_moves, const move_map& dstsrc, const move_map& enemy_dstsrc, unit_map::iterator &leader) { @@ -255,7 +255,7 @@ bool ai::get_villages(std::map& possible_moves, return false; } -void ai::find_villages( +void ai_default::find_villages( treachmap& reachmap, tmoves& moves, const std::multimap& dstsrc, diff --git a/src/ai/contexts.cpp b/src/ai/contexts.cpp index b7903288863..8024e3f7dfa 100644 --- a/src/ai/contexts.cpp +++ b/src/ai/contexts.cpp @@ -45,65 +45,91 @@ static lg::log_domain log_ai("ai/general"); // ======================================================================= // // ======================================================================= +namespace ai { + +int side_context_impl::get_recursion_count() const +{ + return recursion_counter_.get_count(); +} -void ai_readonly_context::raise_user_interact() const +int readonly_context_impl::get_recursion_count() const +{ + return recursion_counter_.get_count(); +} + + +int readwrite_context_impl::get_recursion_count() const +{ + return recursion_counter_.get_count(); +} + + +void readonly_context_impl::raise_user_interact() const { ai_manager::raise_user_interact(); } -void ai_readwrite_context::raise_unit_recruited() const + +void readwrite_context_impl::raise_unit_recruited() const { ai_manager::raise_unit_recruited(); } -void ai_readwrite_context::raise_unit_moved() const + +void readwrite_context_impl::raise_unit_moved() const { ai_manager::raise_unit_moved(); } -void ai_readwrite_context::raise_enemy_attacked() const + +void readwrite_context_impl::raise_enemy_attacked() const { ai_manager::raise_enemy_attacked(); } -std::auto_ptr ai_readwrite_context::execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){ +ai_attack_result_ptr readwrite_context_impl::execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){ return ai_actions::execute_attack_action(get_side(),true,attacker_loc,defender_loc,attacker_weapon); } -std::auto_ptr ai_readonly_context::check_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){ + +ai_attack_result_ptr readonly_context_impl::check_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){ return ai_actions::execute_attack_action(get_side(),false,attacker_loc,defender_loc,attacker_weapon); } -std::auto_ptr ai_readwrite_context::execute_move_action(const map_location& from, const map_location& to, bool remove_movement){ +ai_move_result_ptr readwrite_context_impl::execute_move_action(const map_location& from, const map_location& to, bool remove_movement){ return ai_actions::execute_move_action(get_side(),true,from,to,remove_movement); } -std::auto_ptr ai_readonly_context::check_move_action(const map_location& from, const map_location& to, bool remove_movement){ + +ai_move_result_ptr readonly_context_impl::check_move_action(const map_location& from, const map_location& to, bool remove_movement){ return ai_actions::execute_move_action(get_side(),false,from,to,remove_movement); } -std::auto_ptr ai_readwrite_context::execute_recruit_action(const std::string& unit_name, const map_location &where){ +ai_recruit_result_ptr readwrite_context_impl::execute_recruit_action(const std::string& unit_name, const map_location &where){ return ai_actions::execute_recruit_action(get_side(),true,unit_name,where); } -std::auto_ptr ai_readonly_context::check_recruit_action(const std::string& unit_name, const map_location &where){ + +ai_recruit_result_ptr readonly_context_impl::check_recruit_action(const std::string& unit_name, const map_location &where){ return ai_actions::execute_recruit_action(get_side(),false,unit_name,where); } -std::auto_ptr ai_readwrite_context::execute_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){ +ai_stopunit_result_ptr readwrite_context_impl::execute_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){ return ai_actions::execute_stopunit_action(get_side(),true,unit_location,remove_movement,remove_attacks); } -std::auto_ptr ai_readonly_context::check_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){ + +ai_stopunit_result_ptr readonly_context_impl::check_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){ return ai_actions::execute_stopunit_action(get_side(),false,unit_location,remove_movement,remove_attacks); } -bool ai_readwrite_context::recruit(const std::string& unit_name, map_location loc) + +bool readwrite_context_impl::recruit(const std::string& unit_name, map_location loc) { const std::set& recruits = current_team().recruits(); @@ -146,7 +172,7 @@ bool ai_readwrite_context::recruit(const std::string& unit_name, map_location lo if(recruit_err.empty()) { statistics::recruit_unit(new_unit); - current_team().spend_gold(u->second.cost()); + current_team_w().spend_gold(u->second.cost()); // Confirm the transaction - i.e. don't undo recruitment replay_guard.confirm_transaction(); @@ -174,27 +200,25 @@ bool ai_readwrite_context::recruit(const std::string& unit_name, map_location lo } } -const ai_game_info& ai_readonly_context::get_info() const{ + +const ai_game_info& readonly_context_impl::get_info() const{ return ai_manager::get_active_ai_info_for_side(get_side()); } -ai_game_info& ai_readwrite_context::get_info(){ +ai_game_info& readwrite_context_impl::get_info_w(){ return ai_manager::get_active_ai_info_for_side(get_side()); } -const ai_game_info& ai_readwrite_context::get_info() const{ - return ai_manager::get_active_ai_info_for_side(get_side()); -} - -void ai_readonly_context::diagnostic(const std::string& msg) +void readonly_context_impl::diagnostic(const std::string& msg) { if(game_config::debug) { get_info().disp.set_diagnostic(msg); } } -void ai_readonly_context::log_message(const std::string& msg) + +void readonly_context_impl::log_message(const std::string& msg) { if(game_config::debug) { get_info().disp.add_chat_message(time(NULL), "ai", get_side(), msg, @@ -203,7 +227,7 @@ void ai_readonly_context::log_message(const std::string& msg) } -map_location ai_readwrite_context::move_unit(map_location from, map_location to, +map_location readwrite_context_impl::move_unit(map_location from, map_location to, std::map& possible_moves) { const map_location loc = move_unit_partial(from,to,possible_moves); @@ -220,10 +244,11 @@ map_location ai_readwrite_context::move_unit(map_location from, map_location to, return loc; } -map_location ai_readwrite_context::move_unit_partial(map_location from, map_location to, + +map_location readwrite_context_impl::move_unit_partial(map_location from, map_location to, std::map& possible_moves) { - LOG_AI << "ai_readwrite_context::move_unit " << from << " -> " << to << '\n'; + LOG_AI << "readwrite_context_impl::move_unit " << from << " -> " << to << '\n'; assert(to.valid() && to.x <= MAX_MAP_AREA && to.y <= MAX_MAP_AREA); // Stop the user from issuing any commands while the unit is moving. const events::command_disabler disable_commands; @@ -384,14 +409,15 @@ map_location ai_readwrite_context::move_unit_partial(map_location from, map_loca return to; } -void ai_readonly_context::calculate_possible_moves(std::map& res, move_map& srcdst, + +void readonly_context_impl::calculate_possible_moves(std::map& res, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement, const std::set* remove_destinations) const { calculate_moves(get_info().units,res,srcdst,dstsrc,enemy,assume_full_movement,remove_destinations); } -void ai_readonly_context::calculate_moves(const unit_map& units, std::map& res, move_map& srcdst, +void readonly_context_impl::calculate_moves(const unit_map& units, std::map& res, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement, const std::set* remove_destinations, bool see_all @@ -473,7 +499,7 @@ void ai_readonly_context::calculate_moves(const unit_map& units, std::map* inputs) const -{ - using game_logic::FORMULA_READ_ONLY; - inputs->push_back(game_logic::formula_input("map", FORMULA_READ_ONLY)); -} +} //of namespace ai diff --git a/src/ai/contexts.hpp b/src/ai/contexts.hpp index 1c4c25f4c6b..1192d36784b 100644 --- a/src/ai/contexts.hpp +++ b/src/ai/contexts.hpp @@ -24,7 +24,7 @@ class game_display; class gamemap; -#include "ai_interface.hpp" +#include "ai_actions.hpp" #include "game_info.hpp" #include "../game_display.hpp" #include "../gamestatus.hpp" @@ -36,24 +36,324 @@ class ai_move_result; class ai_recruit_result; class ai_stopunit_result; -class ai_readonly_context: public game_logic::formula_callable, public ai_interface { +namespace ai { + +// recursion counter +class recursion_counter { public: - /** A convenient typedef for the often used 'location' object. */ - typedef map_location location; + recursion_counter(int counter) + : counter_(counter++) + { + if (counter > MAX_COUNTER_VALUE ) { + throw game::game_error("maximum recursion depth reached!"); + } + } - /** The standard way in which a map of possible moves is recorded. */ - typedef std::multimap move_map; + int get_count() const{ + return counter_; + } + + static const int MAX_COUNTER_VALUE = 100;//max recursion depth +private: + int counter_; +}; - /** The standard way in which a map of possible movement routes to location is recorded*/ - typedef std::map moves_map; +// side context +class side_context; + +class side_context{ +public: + virtual side_number get_side() const = 0; + virtual void set_side(side_number side) = 0; + virtual ~side_context(){} + side_context() {} + + virtual side_context& get_side_context() = 0; + virtual int get_recursion_count() const = 0; + +}; + +class readonly_context; +class readonly_context : public virtual side_context { +public: + readonly_context(){} + virtual ~readonly_context(){} + virtual readonly_context& get_readonly_context() = 0; + virtual const team& current_team() const = 0; + virtual void diagnostic(const std::string& msg) = 0; + virtual void log_message(const std::string& msg) = 0; + virtual ai_attack_result_ptr check_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon) = 0; + virtual ai_move_result_ptr check_move_action(const map_location& from, const map_location& to, bool remove_movement=true) = 0; + virtual ai_recruit_result_ptr check_recruit_action(const std::string& unit_name, const map_location &where = map_location::null_location) = 0; + virtual ai_stopunit_result_ptr check_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false) = 0; + virtual void calculate_possible_moves(std::map& possible_moves, + move_map& srcdst, move_map& dstsrc, bool enemy, + bool assume_full_movement=false, + const std::set* remove_destinations=NULL) const = 0; + virtual void calculate_moves(const unit_map& units, + std::map& possible_moves, move_map& srcdst, + move_map& dstsrc, bool enemy, bool assume_full_movement=false, + const std::set* remove_destinations=NULL, + bool see_all=false) const = 0; + const virtual ai_game_info& get_info() const = 0; + virtual void raise_user_interact() const = 0; + +}; + +class readwrite_context; +class readwrite_context : public virtual readonly_context { +public: + readwrite_context(){} + virtual ~readwrite_context(){} + virtual readwrite_context& get_readwrite_context() = 0; + virtual ai_attack_result_ptr execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon) = 0; + virtual ai_move_result_ptr execute_move_action(const map_location& from, const map_location& to, bool remove_movement=true) = 0; + virtual ai_recruit_result_ptr execute_recruit_action(const std::string& unit_name, const map_location &where = map_location::null_location) = 0; + virtual ai_stopunit_result_ptr execute_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false) = 0; + virtual team& current_team_w() = 0; + virtual void attack_enemy(const map_location u, const map_location target, int att_weapon, int def_weapon) = 0; + virtual map_location move_unit(map_location from, map_location to, std::map& possible_moves) = 0; + virtual map_location move_unit_partial(map_location from, map_location to, std::map& possible_moves) = 0; + virtual bool recruit(const std::string& unit_name, map_location loc=map_location()) = 0; + virtual void raise_unit_recruited() const = 0; + virtual void raise_unit_moved() const = 0; + virtual void raise_enemy_attacked() const = 0; + virtual ai_game_info& get_info_w() = 0; +}; + +//proxies + +class side_context_proxy : public virtual side_context { +public: + side_context_proxy(side_context& target) + :target_(target.get_side_context()) + { + } + + virtual ~side_context_proxy(){} + + + virtual side_number get_side() const + { + return target_.get_side(); + } + + virtual void set_side(side_number side) + { + return target_.set_side(side); + } + + virtual side_context& get_side_context() + { + return target_.get_side_context(); + } + + virtual int get_recursion_count(){ + return target_.get_recursion_count(); + } + +private: + side_context& target_; +}; + + +class readonly_context_proxy : public virtual readonly_context, public virtual side_context_proxy { +public: + readonly_context_proxy(readonly_context &target) + : side_context_proxy(target.get_side_context()), target_(target.get_readonly_context()) + { + } + + virtual ~readonly_context_proxy() {} + + virtual readonly_context& get_readonly_context() + { + return target_.get_readonly_context(); + } + + virtual const team& current_team() const{ + return target_.current_team(); + } + + virtual void diagnostic(const std::string& msg){ + target_.diagnostic(msg); + } + + virtual void log_message(const std::string& msg){ + target_.log_message(msg); + } + + virtual ai_attack_result_ptr check_attack_action(const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon) { + return target_.check_attack_action(attacker_loc, defender_loc, attacker_weapon); + } + + virtual ai_move_result_ptr check_move_action(const map_location &from, const map_location &to, bool remove_movement=true){ + return target_.check_move_action(from, to, remove_movement); + } + + virtual ai_recruit_result_ptr check_recruit_action(const std::string &unit_name, const map_location &where = map_location::null_location){ + return target_.check_recruit_action(unit_name, where); + } + + virtual ai_stopunit_result_ptr check_stopunit_action(const map_location &unit_location, bool remove_movement = true, bool remove_attacks = false){ + return target_.check_stopunit_action(unit_location, remove_movement, remove_attacks); + } + + virtual void calculate_possible_moves(std::map& possible_moves, + move_map& srcdst, move_map& dstsrc, bool enemy, + bool assume_full_movement=false, + const std::set* remove_destinations=NULL) const{ + target_.calculate_possible_moves(possible_moves, srcdst, dstsrc, enemy, assume_full_movement, remove_destinations); + } + + virtual void calculate_moves(const unit_map& units, + std::map& possible_moves, move_map& srcdst, + move_map& dstsrc, bool enemy, bool assume_full_movement=false, + const std::set* remove_destinations=NULL, + bool see_all=false) const{ + target_.calculate_moves(units, possible_moves, srcdst, dstsrc, enemy, assume_full_movement, remove_destinations, see_all); + } + + const virtual ai_game_info& get_info() const{ + return target_.get_info(); + } + + virtual void raise_user_interact() const{ + target_.raise_user_interact(); + } + + virtual int get_recursion_count(){ + return target_.get_recursion_count(); + } + +private: + readonly_context& target_; +}; + + +class readwrite_context_proxy : public virtual readwrite_context, public virtual readonly_context_proxy { +public: + readwrite_context_proxy(readwrite_context &target) + : side_context_proxy(target.get_side_context()), readonly_context_proxy(target.get_readonly_context()),target_(target.get_readwrite_context()) + { + } + + virtual readwrite_context& get_readwrite_context() + { + return target_.get_readwrite_context(); + } + virtual ai_attack_result_ptr execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){ + return target_.execute_attack_action(attacker_loc,defender_loc,attacker_weapon); + } + virtual ai_move_result_ptr execute_move_action(const map_location& from, const map_location& to, bool remove_movement=true) + { + return target_.execute_move_action(from, to, remove_movement); + } + virtual ai_recruit_result_ptr execute_recruit_action(const std::string& unit_name, const map_location &where = map_location::null_location){ + return target_.execute_recruit_action(unit_name,where); + } + virtual ai_stopunit_result_ptr execute_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false){ + return target_.execute_stopunit_action(unit_location,remove_movement,remove_attacks); + } + + virtual team& current_team_w(){ + return target_.current_team_w(); + } + + virtual void attack_enemy(const map_location u, const map_location target, int att_weapon, int def_weapon){ + target_.attack_enemy(u, target, att_weapon, def_weapon); + } + + virtual map_location move_unit(map_location from, map_location to, std::map& possible_moves){ + return target_.move_unit(from, to, possible_moves); + } + + virtual map_location move_unit_partial(map_location from, map_location to, std::map& possible_moves){ + return target_.move_unit_partial(from, to, possible_moves); + } + + virtual bool recruit(const std::string& unit_name, map_location loc=map_location()){ + return target_.recruit(unit_name, loc); + } + virtual void raise_unit_recruited() const{ + target_.raise_unit_recruited(); + } + + virtual void raise_unit_moved() const{ + target_.raise_unit_moved(); + } + + virtual void raise_enemy_attacked() const{ + target_.raise_enemy_attacked(); + } + virtual ai_game_info& get_info_w() { + return target_.get_info_w(); + } + + virtual int get_recursion_count(){ + return target_.get_recursion_count(); + } +private: + readwrite_context& target_; +}; + + +//implementation +class side_context_impl : public side_context { +public: + side_context_impl(side_number side) + : side_(side), recursion_counter_(0) + { + } + + virtual ~side_context_impl(){} + + virtual side_number get_side() const + { + return side_; + } + + virtual void set_side(side_number side) + { + side_ = side; + } + + + virtual side_context& get_side_context() + { + return *this; + } + + + virtual int get_recursion_count() const; + +private: + side_number side_; + recursion_counter recursion_counter_; +}; + + +//@todo: public game_logic::formula_callable +class readonly_context_impl : public virtual side_context_proxy, public readonly_context { +public: /** * The constructor. */ - ai_readonly_context(unsigned int side, bool master) : ai_interface(side,master) { - add_ref(); //this class shouldn't be reference counted. + readonly_context_impl( side_context &context ) + : side_context_proxy(context), recursion_counter_(context.get_recursion_count()) + { + //add_ref(); //this class shouldn't be reference counted. + } + virtual ~readonly_context_impl() {} + + /** + * Unwrap - this class is not a proxy, so return *this + */ + virtual readonly_context& get_readonly_context() + { + return *this; } - virtual ~ai_readonly_context() {} /** Return a reference to the 'team' object for the AI. */ const team& current_team() const { return get_info().teams[get_side()-1]; } @@ -76,7 +376,7 @@ public: * @retval possible result: attacker and/or defender are invalid * @retval possible result: attacker doesn't have the specified weapon */ - std::auto_ptr check_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon); + ai_attack_result_ptr check_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon); /** @@ -89,7 +389,7 @@ public: * @retval possible result: move is interrupted * @retval possible result: move is impossible */ - std::auto_ptr check_move_action(const map_location& from, const map_location& to, bool remove_movement=true); + ai_move_result_ptr check_move_action(const map_location& from, const map_location& to, bool remove_movement=true); /** @@ -102,7 +402,7 @@ public: * @retval possible_result: no free space on keep * @retval possible_result: not enough gold */ - std::auto_ptr check_recruit_action(const std::string& unit_name, const map_location &where = map_location::null_location); + ai_recruit_result_ptr check_recruit_action(const std::string& unit_name, const map_location &where = map_location::null_location); /** @@ -114,7 +414,7 @@ public: * @retval possible_result: something wrong * @retval possible_result: nothing to do */ - std::auto_ptr check_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false); + ai_stopunit_result_ptr check_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false); /** @@ -167,14 +467,22 @@ public: */ void raise_user_interact() const; - virtual void get_inputs(std::vector* inputs) const; + virtual int get_recursion_count() const; - virtual variant get_value(const std::string& key) const; +private: + recursion_counter recursion_counter_; }; -class ai_readwrite_context : public ai_readonly_context { +class readwrite_context_impl : public virtual side_context_proxy, public virtual readonly_context_proxy, public readwrite_context { public: + /** + * Unwrap - this class is not a proxy, so return *this + */ + virtual readwrite_context& get_readwrite_context() + { + return *this; + } /** @@ -188,7 +496,7 @@ public: * @retval possible result: attacker and/or defender are invalid * @retval possible result: attacker doesn't have the specified weapon */ - std::auto_ptr execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon); + virtual ai_attack_result_ptr execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon); /** @@ -201,7 +509,7 @@ public: * @retval possible result: move is interrupted * @retval possible result: move is impossible */ - std::auto_ptr execute_move_action(const map_location& from, const map_location& to, bool remove_movement=true); + virtual ai_move_result_ptr execute_move_action(const map_location& from, const map_location& to, bool remove_movement=true); /** @@ -214,7 +522,7 @@ public: * @retval possible_result: no free space on keep * @retval possible_result: not enough gold */ - std::auto_ptr execute_recruit_action(const std::string& unit_name, const map_location &where = map_location::null_location); + virtual ai_recruit_result_ptr execute_recruit_action(const std::string& unit_name, const map_location &where = map_location::null_location); /** @@ -226,12 +534,11 @@ public: * @retval possible_result: something wrong * @retval possible_result: nothing to do */ - std::auto_ptr execute_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false); + virtual ai_stopunit_result_ptr execute_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false); /** Return a reference to the 'team' object for the AI. */ - team& current_team() { return get_info().teams[get_side()-1]; } - const team& current_team() const { return get_info().teams[get_side()-1]; } + virtual team& current_team_w() { return get_info_w().teams[get_side()-1]; } /** * This function should be called to attack an enemy. @@ -247,7 +554,7 @@ public: * @param def_weapon The number of the weapon (0-based) which should be used * by the defender. (It must be a valid weapon of the defender.) */ - void attack_enemy(const map_location u, const map_location target, int att_weapon, int def_weapon); + virtual void attack_enemy(const map_location u, const map_location target, int att_weapon, int def_weapon); /** * This function should be called to move a unit. @@ -260,7 +567,7 @@ public: * @param possible_moves The map of possible moves, as obtained from * 'calculate_possible_moves'. */ - map_location move_unit(map_location from, map_location to, std::map& possible_moves); + virtual map_location move_unit(map_location from, map_location to, std::map& possible_moves); /** * @deprecated @@ -289,16 +596,25 @@ public: /** * The constructor. */ - ai_readwrite_context(unsigned int side, bool master) : ai_readonly_context(side,master){ + readwrite_context_impl(readonly_context &context) + : side_context_proxy(context), readonly_context_proxy(context), recursion_counter_(context.get_recursion_count()) + { } - virtual ~ai_readwrite_context() {} + virtual ~readwrite_context_impl() {} /** * Functions to retrieve the 'info' object. * Used by derived classes to discover all necessary game information. */ - const virtual ai_game_info& get_info() const; - virtual ai_game_info& get_info(); + virtual ai_game_info& get_info_w(); + + virtual int get_recursion_count() const; + +private: + recursion_counter recursion_counter_; }; +} //end of namespace ai + + #endif diff --git a/src/ai/formula_ai.cpp b/src/ai/formula_ai.cpp index 7ea07005e54..e00d0c870d1 100644 --- a/src/ai/formula_ai.cpp +++ b/src/ai/formula_ai.cpp @@ -572,7 +572,7 @@ public: private: variant execute(const formula_callable& variables) const { variant attack = args()[0]->evaluate(variables); - ai::attack_analysis* analysis = convert_variant(attack); + ai_default::attack_analysis* analysis = convert_variant(attack); unit_map units_with_moves(ai_.get_info().units); typedef std::pair mv; foreach (const mv &m, analysis->movements) { @@ -1073,8 +1073,8 @@ private: } const map_location& loc = convert_variant(res)->loc(); - const formula_ai::move_map& srcdst = ai_.srcdst(); - typedef formula_ai::move_map::const_iterator Itor; + const ai::move_map& srcdst = ai_.srcdst(); + typedef ai::move_map::const_iterator Itor; std::pair range = srcdst.equal_range(loc); for(Itor i = range.first; i != range.second; ++i) { @@ -1096,7 +1096,7 @@ private: variant execute(const formula_callable& variables) const { std::vector vars; variant dstsrc_var = args()[0]->evaluate(variables); - const ai::move_map& dstsrc = convert_variant(dstsrc_var)->dstsrc(); + const ai_default::move_map& dstsrc = convert_variant(dstsrc_var)->dstsrc(); std::pair range = dstsrc.equal_range(convert_variant(args()[1]->evaluate(variables))->loc()); while(range.first != range.second) { @@ -1517,6 +1517,10 @@ std::string formula_ai::describe_self(){ return "[formula_ai]"; } +int formula_ai::get_recursion_count() const{ + return recursion_counter_.get_count(); +} + namespace game_logic { expression_ptr ai_function_symbol_table::create_function(const std::string &fn, @@ -1596,8 +1600,12 @@ expression_ptr ai_function_symbol_table::create_function(const std::string &fn, } -formula_ai::formula_ai(int side, bool master) : - ai(side,master), +formula_ai::formula_ai(ai::readwrite_context &context) : + side_context_proxy(context), + readonly_context_proxy(context), + readwrite_context_proxy(context), + ai_default(context), + recursion_counter_(context.get_recursion_count()), recruit_formula_(), move_formula_(), outcome_positions_(), @@ -1699,7 +1707,7 @@ formula_ptr formula_ai::create_optional_formula(const std::string& formula_strin void formula_ai::new_turn() { move_maps_valid_ = false; - ai::new_turn(); + ai_default::new_turn(); } void formula_ai::play_turn() @@ -1892,9 +1900,9 @@ void formula_ai::prepare_move() const bool formula_ai::make_action(game_logic::const_formula_ptr formula_, const game_logic::formula_callable& variables) { if(!formula_) { - if(get_master()) { + if(get_recursion_count() fallback( ai_manager::create_transient_ai(ai_manager::AI_TYPE_DEFAULT, get_side(),false)); + util::scoped_ptr< ai_interface > fallback( ai_manager::create_transient_ai(ai_manager::AI_TYPE_DEFAULT, this)); if (fallback != NULL){ fallback->play_turn(); } @@ -2058,7 +2066,7 @@ bool formula_ai::execute_variant(const variant& var, bool commandline) const move_callable* move = try_convert_variant(*i); const move_partial_callable* move_partial = try_convert_variant(*i); const attack_callable* attack = try_convert_variant(*i); - const ai::attack_analysis* attack_analysis = try_convert_variant(*i); + const ai_default::attack_analysis* attack_analysis = try_convert_variant(*i); const recruit_callable* recruit_command = try_convert_variant(*i); const set_var_callable* set_var_command = try_convert_variant(*i); const set_unit_var_callable* set_unit_var_command = try_convert_variant(*i); @@ -2210,8 +2218,7 @@ bool formula_ai::execute_variant(const variant& var, bool commandline) } else if(i->is_string() && (i->as_string() == "end_turn" || i->as_string() == "end" ) ) { return false; } else if(fallback_command) { - if (get_master()) - { + if(get_recursion_count()key() == "human") { //we want give control of the side to human for the rest of this turn @@ -2219,7 +2226,7 @@ bool formula_ai::execute_variant(const variant& var, bool commandline) } else { LOG_AI << "Explicit fallback to: " << fallback_command->key() << std::endl; - util::scoped_ptr< ai_interface > fallback ( ai_manager::create_transient_ai(fallback_command->key(), get_side(),false)); + util::scoped_ptr< ai_interface > fallback ( ai_manager::create_transient_ai(fallback_command->key(), this)); if(fallback != NULL) { fallback->play_turn(); } @@ -2494,7 +2501,7 @@ variant formula_ai::get_value(const std::string& key) const return villages_from_set(get_info().map.villages(), ¤t_team().villages()); } - return ai_readonly_context::get_value(key); + return ai_default::get_value(key); } void formula_ai::get_inputs(std::vector* inputs) const @@ -2524,7 +2531,7 @@ void formula_ai::get_inputs(std::vector* inputs) const inputs->push_back(game_logic::formula_input("villages_of_side", FORMULA_READ_ONLY)); inputs->push_back(game_logic::formula_input("enemy_and_unowned_villages", FORMULA_READ_ONLY)); - ai_readonly_context::get_inputs(inputs); + ai_default::get_inputs(inputs); } variant formula_ai::get_keeps() const diff --git a/src/ai/formula_ai.hpp b/src/ai/formula_ai.hpp index 75097cebdfe..8844365550e 100644 --- a/src/ai/formula_ai.hpp +++ b/src/ai/formula_ai.hpp @@ -63,17 +63,15 @@ private: } -class formula_ai : public ai { +class formula_ai : public ai_default, public virtual ai::readwrite_context_proxy { public: - explicit formula_ai(int side, bool master); + explicit formula_ai(ai::readwrite_context &context); virtual ~formula_ai() {}; virtual void play_turn(); virtual void new_turn(); virtual std::string describe_self(); - using ai_readwrite_context::get_info; - using ai_readwrite_context::current_team; - using ai_readonly_context::move_map; + using ai_default::move_map; const move_map& srcdst() const { if(!move_maps_valid_) { prepare_move(); } return srcdst_; } @@ -101,13 +99,15 @@ public: variant get_keeps() const; + int get_recursion_count() const; + const variant& get_keeps_cache() const { return keeps_cache_; } // Check if given unit can reach another unit bool can_reach_unit(unit_map::const_unit_iterator unit_A, unit_map::const_unit_iterator unit_B) const; - const std::map& get_possible_moves() const { prepare_move(); return possible_moves_; } + const std::map& get_possible_moves() const { prepare_move(); return possible_moves_; } void handle_exception(game_logic::formula_error& e) const; void handle_exception(game_logic::formula_error& e, const std::string& failed_operation) const; @@ -130,6 +130,7 @@ public: private: + ai::recursion_counter recursion_counter_; void display_message(const std::string& msg) const; bool do_recruitment(); bool make_action(game_logic::const_formula_ptr formula_, const game_logic::formula_callable& variables); @@ -141,7 +142,7 @@ private: std::vector outcome_positions_; - mutable std::map possible_moves_; + mutable std::map possible_moves_; void prepare_move() const; @@ -155,7 +156,7 @@ private: game_logic::ai_function_symbol_table function_table; game_logic::candidate_action_manager candidate_action_manager_; - friend class ai; + friend class ai_default; }; #endif diff --git a/src/ai/game_info.hpp b/src/ai/game_info.hpp index d63c7907847..5560e9a0961 100644 --- a/src/ai/game_info.hpp +++ b/src/ai/game_info.hpp @@ -31,6 +31,17 @@ class gamemap; * that an AI might need access to, in order to make and implement its * decisions. */ +namespace ai { +typedef unsigned int side_number; + +/** The standard way in which a map of possible moves is recorded. */ +typedef std::multimap move_map; + +/** The standard way in which a map of possible movement routes to location is recorded*/ +typedef std::map moves_map; +} //of namespace ai + + class ai_game_info { public: diff --git a/src/team.hpp b/src/team.hpp index 98834522211..3be4ecc4fa2 100644 --- a/src/team.hpp +++ b/src/team.hpp @@ -161,7 +161,7 @@ public: { info_.current_player = player; } size_t average_recruit_price(); - float num_pos_recruits_to_force() + float num_pos_recruits_to_force() const { return info_.number_of_possible_recruits_to_force_recruit; } const std::set& recruits() const { return info_.can_recruit; }