53 #include <SDL2/SDL_timer.h>
64 #define DBG_AI_MANAGER LOG_STREAM(debug, log_ai_manager)
65 #define LOG_AI_MANAGER LOG_STREAM(info, log_ai_manager)
66 #define ERR_AI_MANAGER LOG_STREAM(err, log_ai_manager)
69 #define DBG_AI_MOD LOG_STREAM(debug, log_ai_mod)
70 #define LOG_AI_MOD LOG_STREAM(info, log_ai_mod)
71 #define WRN_AI_MOD LOG_STREAM(warn, log_ai_mod)
72 #define ERR_AI_MOD LOG_STREAM(err, log_ai_mod)
75 :
ai_(), side_context_(nullptr), readonly_context_(nullptr), readwrite_context_(nullptr), default_ai_context_(nullptr), side_(side), cfg_(cfg)
104 if (!mod_ai.has_attribute(
"side")) {
105 mod_ai[
"side"] = side;
110 micro[
"side"] = side;
111 micro[
"action"] =
"add";
116 std::vector<engine_ptr> engines =
ai_->get_engines();
119 (*it)->set_ai_context(&(
ai_->get_ai_context()));
155 if(
auto lua = std::dynamic_pointer_cast<engine_lua>(
engine)) {
156 lua->apply_micro_ai(cfg);
166 const std::string &act = cfg[
"action"];
167 LOG_AI_MOD <<
"side "<<
side_ <<
" "<<act<<
"_ai_component \""<<cfg[
"path"]<<
"\"";
173 }
else if (act ==
"change") {
175 }
else if (act ==
"delete") {
178 ERR_AI_MOD <<
"modify_ai tag has invalid 'action' attribute " << act;
195 const std::string&
id =
aspect[
"id"];
196 for(
const config& facet :
aspect.child_range(
"facet")) {
197 ai_->add_facet(
id, facet);
204 if(
stage[
"name"] !=
"empty") {
209 if (!mod.has_attribute(
"side")) {
216 micro[
"action"] =
"add";
249 return formatter() <<
"not initialized ai with id=[" <<
cfg_[
"id"] <<
"] for side " <<
side_ <<
" : ";
259 auto lik =
ai_->get_leader_ignores_keep();
260 auto pl =
ai_->get_passive_leader();
261 auto plsk =
ai_->get_passive_leader_shares_keep();
264 cfg[
"allow_ally_villages"] =
ai_->get_allow_ally_villages();
265 cfg[
"simple_targeting"] =
ai_->get_simple_targeting();
266 cfg[
"support_villages"] =
ai_->get_support_villages();
268 s <<
"advancements: " <<
ai_->get_advancements().get_value() << std::endl;
269 s <<
"aggression: " <<
ai_->get_aggression() << std::endl;
270 s <<
"allow_ally_villages: " << cfg[
"allow_ally_villages"] << std::endl;
271 s <<
"caution: " <<
ai_->get_caution() << std::endl;
272 s <<
"grouping: " <<
ai_->get_grouping() << std::endl;
273 s <<
"leader_aggression: " <<
ai_->get_leader_aggression() << std::endl;
275 s <<
"leader_value: " <<
ai_->get_leader_value() << std::endl;
278 s <<
"recruitment_diversity: " <<
ai_->get_recruitment_diversity() << std::endl;
279 s <<
"recruitment_instructions: " << std::endl <<
"----config begin----" << std::endl;
280 s <<
ai_->get_recruitment_instructions() <<
"-----config end-----" << std::endl;
281 s <<
"recruitment_more: " <<
utils::join(
ai_->get_recruitment_more()) << std::endl;
282 s <<
"recruitment_pattern: " <<
utils::join(
ai_->get_recruitment_pattern()) << std::endl;
283 s <<
"recruitment_randomness: " <<
ai_->get_recruitment_randomness() << std::endl;
284 s <<
"recruitment_save_gold: " << std::endl <<
"----config begin----" << std::endl;
285 s <<
ai_->get_recruitment_save_gold() <<
"-----config end-----" << std::endl;
286 s <<
"retreat_enemy_weight: " <<
ai_->get_retreat_enemy_weight() << std::endl;
287 s <<
"retreat_factor: " <<
ai_->get_retreat_factor() << std::endl;
288 s <<
"scout_village_targeting: " <<
ai_->get_scout_village_targeting() << std::endl;
289 s <<
"simple_targeting: " << cfg[
"simple_targeting"] << std::endl;
290 s <<
"support_villages: " << cfg[
"support_villages"] << std::endl;
291 s <<
"village_value: " <<
ai_->get_village_value() << std::endl;
292 s <<
"villages_per_scout: " <<
ai_->get_villages_per_scout() << std::endl;
335 , history_item_counter_(0)
337 , map_changed_(
"ai_map_changed")
338 , recruit_list_changed_(
"ai_recruit_list_changed")
339 , user_interact_(
"ai_user_interact")
340 , sync_network_(
"ai_sync_network")
341 , tod_changed_(
"ai_tod_changed")
342 , gamestate_changed_(
"ai_gamestate_changed")
343 , turn_started_(
"ai_turn_started")
429 using namespace std::chrono_literals;
430 constexpr
auto interact_time = 30ms;
432 const auto now = std::chrono::steady_clock::now();
434 if(time_since_interact < interact_time) {
486 return ai.evaluate(str);
494 if (str.length()<1) {
512 const int MAX_HISTORY_VISIBLE = 30;
523 return "AI MANAGER: empty history";
536 return "AI MANAGER: History is empty";
539 int n = std::min<int>( MAX_HISTORY_VISIBLE,
history_.size() );
540 std::stringstream strstream;
541 strstream <<
"AI MANAGER: History - last "<<
n <<
" commands:\n";
542 std::deque< command_history_item >::reverse_iterator j =
history_.rbegin();
544 for (
int cmd_id=
n; cmd_id>0; --cmd_id){
545 strstream << j->get_number() <<
" :" << j->get_command() <<
'\n';
549 return strstream.str();
556 if (cmd.at(0)==
"!add_ai"){
558 std::string file = cmd.at(2);
562 return std::string(
"AI MANAGER: failed attempt to add AI for side ")+std::to_string(side)+std::string(
" from file ")+file;
566 if (cmd.at(0)==
"!replace_ai"){
568 std::string file = cmd.at(2);
572 return std::string(
"AI MANAGER: failed attempt to add AI for side ")+std::to_string(side)+std::string(
" from file ")+file;
576 }
else if (cmd.size()==2){
578 if (cmd.at(0)==
"!remove_ai"){
581 return std::string(
"AI MANAGER: made an attempt to remove AI for side ")+std::to_string(side);
591 std::deque< command_history_item >::reverse_iterator j =
history_.rbegin();
593 while ( (j!=
history_.rend()) && (j->get_number()!=command) ){
599 return "AI MANAGER: no command with requested number found";
601 }
else if (cmd.size()==1){
602 if (cmd.at(0)==
"!help") {
605 "! - repeat last command (? and ! do not count)\n"
606 "! NUMBER - repeat numbered command\n"
607 "? - show a history list\n"
608 "!add_ai TEAM FILE - add a AI to side (0 - command AI, N - AI for side #N) from file\n"
609 "!remove_ai TEAM - remove AI from side (0 - command AI, N - AI for side #N)\n"
610 "!replace_ai TEAM FILE - replace AI of side (0 - command AI, N - AI for side #N) from file\n"
611 "!help - show this help message";
615 return "AI MANAGER: nothing to do";
626 ERR_AI_MANAGER <<
" unable to read [SIDE] config for side "<< side <<
"from file [" << file <<
"]";
641 ai_stack_for_specific_side.emplace(side, parsed_cfg);
652 if (!ai_stack_for_specific_side.empty()){
653 ai_stack_for_specific_side.pop();
662 while (!ai_stack_for_specific_side.empty()){
663 ai_stack_for_specific_side.pop();
734 const auto turn_start_time = std::chrono::steady_clock::now();
744 const auto turn_end_time = std::chrono::steady_clock::now();
746 DBG_AI_MANAGER <<
"side " << side <<
": total turn time: " << (turn_end_time - turn_start_time).count() <<
" ms ";
761 return ai_map_.emplace(side, std::stack<holder>()).first->second;
771 if (!ai_stack_for_specific_side.empty()){
772 return ai_stack_for_specific_side.top();
775 ai_stack_for_specific_side.emplace(side, cfg);
776 return ai_stack_for_specific_side.top();
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
virtual void new_turn()
On new turn.
void play_turn()
Play the turn.
static bool delete_component(component *root, const std::string &path)
static component * get_component(component *root, const std::string &path)
static bool change_component(component *root, const std::string &path, const config &cfg)
static bool add_component(component *root, const std::string &path, const config &cfg)
static std::string print_component_tree(component *root, const std::string &path)
static bool get_side_config_from_file(const std::string &file, config &cfg)
get side config from file
static bool parse_side_config(side_number side, const config &original_cfg, config &cfg)
static const config & get_default_ai_parameters()
get default AI parameters
std::set< map_location > recent_attacks
Base class that holds the AI and current AI parameters.
std::string describe_ai() const
std::string get_ai_structure()
std::string get_ai_identifier() const
void micro_ai(const config &cfg)
void modify_ai(const config &cfg)
ai_composite & get_ai_ref()
component * get_component(component *root, const std::string &path)
std::unique_ptr< side_context > side_context_
std::unique_ptr< readwrite_context > readwrite_context_
std::string get_ai_overview()
std::unique_ptr< readonly_context > readonly_context_
void init(side_number side)
void append_ai(const config &cfg)
std::unique_ptr< default_ai_context > default_ai_context_
holder(side_number side, const config &cfg)
Class that manages AIs for all sides and manages AI redeployment.
static const std::string AI_TYPE_SAMPLE_AI
events::generic_event map_changed_
std::string get_active_ai_identifier_for_side(side_number side)
Gets AI algorithm identifier for active AI of the given side.
void clear_ais()
Clears all the AIs.
std::string get_active_ai_overview_for_side(side_number side)
Gets AI Overview for active AI of the given side.
ai_composite & get_active_ai_for_side(side_number side)
Gets active AI for specified side.
void raise_tod_changed()
Notifies all observers of 'ai_tod_changed' event.
bool add_ai_for_side_from_config(side_number side, const config &cfg, bool replace=true)
Adds active AI for specified side from cfg.
config to_config(side_number side)
Gets AI config for active AI of the given side.
events::generic_event user_interact_
void remove_all_ais_for_side(side_number side)
Removes all AIs from side.
void remove_gamestate_observer(events::observer *event_observer)
Removes an observer of game events except ai_user_interact event and ai_sync_network event.
void remove_observer(events::observer *event_observer)
Removes an observer of game events.
void raise_turn_started()
Notifies all observers of 'ai_turn_started' event.
static manager * singleton_
static const std::string AI_TYPE_IDLE_AI
static const std::string AI_TYPE_COMPOSITE_AI
static const std::size_t MAX_HISTORY_SIZE
void raise_gamestate_changed()
Notifies all observers of 'ai_gamestate_changed' event.
void add_gamestate_observer(events::observer *event_observer)
Adds observer of game events except ai_user_interact event and ai_sync_network event.
void append_active_ai_for_side(ai::side_number side, const config &cfg)
Appends AI parameters to active AI of the given side.
std::string get_active_ai_structure_for_side(side_number side)
Gets AI Structure for active AI of the given side.
events::generic_event sync_network_
void raise_user_interact()
Notifies all observers of 'ai_user_interact' event.
static const std::string AI_TYPE_FORMULA_AI
events::generic_event recruit_list_changed_
const std::string evaluate_command(side_number side, const std::string &str)
Evaluates a string command using command AI.
std::stack< holder > & get_or_create_ai_stack_for_side(side_number side)
Gets the AI stack for the specified side, create it if it doesn't exist.
holder & get_active_ai_holder_for_side(side_number side)
Gets active holder for specified side.
void add_recruit_list_changed_observer(events::observer *event_observer)
Adds an observer of 'ai_recruit_list_changed' event.
game_info & get_active_ai_info_for_side(side_number side)
Gets AI info for active AI of the given side.
void add_map_changed_observer(events::observer *event_observer)
Adds an observer of 'ai_map_changed' event.
void add_turn_started_observer(events::observer *event_observer)
Adds an observer of 'ai_turn_started' event.
static const std::string AI_TYPE_DEFAULT
std::deque< command_history_item > history_
game_info & get_ai_info()
Gets global AI-game info.
events::generic_event gamestate_changed_
const ai::unit_advancements_aspect & get_advancement_aspect_for_side(side_number side)
bool add_ai_for_side_from_file(side_number side, const std::string &file, bool replace=true)
Adds active AI for specified side from file.
events::generic_event turn_started_
void remove_ai_for_side(side_number side)
Removes top-level AI from side.
void modify_active_ai_for_side(ai::side_number side, const config &cfg)
Modifies AI parameters for active AI of the given side.
void play_turn(side_number side)
Plays a turn for the specified side using its active AI.
std::chrono::steady_clock::time_point last_interact_
const std::string internal_evaluate_command(side_number side, const std::string &str)
Evaluates an internal manager command.
long history_item_counter_
void raise_map_changed()
Notifies all observers of 'ai_map_changed' event.
bool should_intercept(const std::string &str) const
Determines if the command should be intercepted and evaluated as internal command.
void raise_recruit_list_changed()
Notifies all observers of 'ai_recruit_list_changed' event.
void remove_turn_started_observer(events::observer *event_observer)
Deletes an observer of 'ai_turn_started' event.
ai::holder & get_active_ai_holder_for_side_dbg(side_number side)
Gets the active AI holder for debug purposes.
void raise_sync_network()
Notifies all observers of 'ai_sync_network' event.
events::generic_event tod_changed_
void remove_map_changed_observer(events::observer *event_observer)
Deletes an observer of 'ai_map_changed' event.
void add_tod_changed_observer(events::observer *event_observer)
Adds an observer of 'ai_tod_changed' event.
void remove_tod_changed_observer(events::observer *event_observer)
Deletes an observer of 'ai_tod_changed' event.
void add_observer(events::observer *event_observer)
Adds observer of game events.
void remove_recruit_list_changed_observer(events::observer *event_observer)
Deletes an observer of 'ai_recruit_list_changed' event.
virtual const unit_advancements_aspect & get_advancements() const override
A config object defines a single node in a WML file, with access to child nodes.
void clear_children(T... keys)
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
child_itors child_range(config_key_type key)
virtual bool attach_handler(observer *obs)
virtual bool detach_handler(observer *obs)
virtual void notify_observers()
game_events::wml_event_pump & pump()
pump_result_t fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
A component of the AI framework.
Composite AI with turn sequence which is a vector of stages.
Definitions for the interface to Wesnoth Markup Language (WML).
Managing the AIs configuration - headers.
Helper functions for the object which operates in the context of AI for specific side this is part of...
AI Support engine - creating specific ai components from config.
LUA AI Support engine - creating specific ai components from config.
Game information for the AI.
Standard logging facilities (interface).
A small explanation about what's going on here: Each action has access to two game_info objects First...
static lg::log_domain log_ai_mod("ai/mod")
static lg::log_domain log_ai_manager("ai/manager")
::tod_manager * tod_manager
game_events::manager * game_events
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
std::vector< std::string > parenthetical_split(std::string_view val, const char separator, std::string_view left, std::string_view right, const int flags)
Splits a string based either on a separator, except then the text appears within specified parenthesi...
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::string::const_iterator iterator
Define the game's event mechanism.
static map_location::direction n
static map_location::direction s