1 /*
2 Copyright (C) 2009 - 2018 by Yurii Chernyi <terraninfo@terraninfo.net>
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13 */
14
15 /**
16 * Managing the AI lifecycle and interface for the rest of Wesnoth
17 * @file
18 */
19
20 #include "ai/manager.hpp"
21
22 #include "config.hpp" // for config, etc
23 #include "game_events/pump.hpp"
24 #include "log.hpp"
25 #include "map/location.hpp" // for map_location
26 #include "resources.hpp"
27 #include "serialization/string_utils.hpp"
28 #include "tod_manager.hpp"
29
30 #include "ai/composite/ai.hpp" // for ai_composite
31 #include "ai/composite/component.hpp" // for component_manager
32 #include "ai/composite/engine.hpp" // for engine
33 #include "ai/configuration.hpp" // for configuration
34 #include "ai/contexts.hpp" // for readonly_context, etc
35 #include "ai/default/contexts.hpp" // for default_ai_context, etc
36 #include "game_end_exceptions.hpp" // for ai_end_turn_exception
37 #include "ai/game_info.hpp" // for side_number, engine_ptr, etc
38 #include "game_config.hpp" // for debug
39 #include "ai/lua/aspect_advancements.hpp"
40 #include "ai/registry.hpp" // for init
41
42 #include <algorithm> // for min
43 #include <cassert> // for assert
44 #include <iterator> // for reverse_iterator, etc
45 #include <map> // for _Rb_tree_iterator, etc
46 #include <ostream> // for operator<<, basic_ostream, etc
47 #include <set> // for set
48 #include <stack> // for stack
49 #include <utility> // for pair, make_pair
50 #include <vector> // for vector, allocator, etc
51
52 #include <SDL2/SDL_timer.h>
53
54 namespace ai {
55
56 const std::string manager::AI_TYPE_COMPOSITE_AI = "composite_ai";
57 const std::string manager::AI_TYPE_SAMPLE_AI = "sample_ai";
58 const std::string manager::AI_TYPE_IDLE_AI = "idle_ai";
59 const std::string manager::AI_TYPE_FORMULA_AI = "formula_ai";
60 const std::string manager::AI_TYPE_DEFAULT = "default";
61
62
63 static lg::log_domain log_ai_manager("ai/manager");
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)
67
68 static lg::log_domain log_ai_mod("ai/mod");
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)
73
holder(side_number side,const config & cfg)74 holder::holder( side_number side, const config &cfg )
75 : ai_(), side_context_(nullptr), readonly_context_(nullptr), readwrite_context_(nullptr), default_ai_context_(nullptr), side_(side), cfg_(cfg)
76 {
77 DBG_AI_MANAGER << describe_ai() << "Preparing new AI holder" << std::endl;
78 }
79
80
init(side_number side)81 void holder::init( side_number side )
82 {
83 if (side_context_ == nullptr) {
84 side_context_.reset(new side_context_impl(side,cfg_));
85 } else {
86 side_context_->set_side(side);
87 }
88 if (readonly_context_ == nullptr){
89 readonly_context_.reset(new readonly_context_impl(*side_context_,cfg_));
90 readonly_context_->on_readonly_context_create();
91 }
92 if (readwrite_context_ == nullptr){
93 readwrite_context_.reset(new readwrite_context_impl(*readonly_context_,cfg_));
94 }
95 if (default_ai_context_ == nullptr){
96 default_ai_context_.reset(new default_ai_context_impl(*readwrite_context_,cfg_));
97 }
98 if (!this->ai_){
99 ai_.reset(new ai_composite(*default_ai_context_,cfg_));
100 }
101
102 if (this->ai_) {
103 ai_->on_create();
104 for (config &mod_ai : cfg_.child_range("modify_ai")) {
105 if (!mod_ai.has_attribute("side")) {
106 mod_ai["side"] = side;
107 }
108 modify_ai(mod_ai);
109 }
110 cfg_.clear_children("modify_ai");
111
112 std::vector<engine_ptr> engines = ai_->get_engines();
113 for (std::vector<engine_ptr>::iterator it = engines.begin(); it != engines.end(); ++it)
114 {
115 (*it)->set_ai_context(&(ai_->get_ai_context()));
116 }
117
118 } else {
119 ERR_AI_MANAGER << describe_ai()<<"AI lazy initialization error!" << std::endl;
120 }
121
122 }
123
~holder()124 holder::~holder()
125 {
126 try {
127 if (this->ai_) {
128 LOG_AI_MANAGER << describe_ai() << "Managed AI will be deleted" << std::endl;
129 }
130 } catch (...) {}
131 }
132
133
get_ai_ref()134 ai_composite& holder::get_ai_ref()
135 {
136 if (!this->ai_) {
137 this->init(this->side_);
138 }
139 assert(this->ai_);
140
141 return *this->ai_;
142 }
143
144
modify_ai(const config & cfg)145 void holder::modify_ai(const config &cfg)
146 {
147 if (!this->ai_) {
148 // if not initialized, initialize now.
149 get_ai_ref();
150 }
151 const std::string &act = cfg["action"];
152 LOG_AI_MOD << "side "<< side_ << " "<<act<<"_ai_component \""<<cfg["path"]<<"\""<<std::endl;
153 DBG_AI_MOD << std::endl << cfg << std::endl;
154 DBG_AI_MOD << "side "<< side_ << " before "<<act<<"_ai_component"<<std::endl << to_config() << std::endl;
155 bool res = false;
156 if (act == "add") {
157 res = component_manager::add_component(&*this->ai_,cfg["path"],cfg);
158 } else if (act == "change") {
159 res = component_manager::change_component(&*this->ai_,cfg["path"],cfg);
160 } else if (act == "delete") {
161 res = component_manager::delete_component(&*this->ai_,cfg["path"]);
162 } else {
163 ERR_AI_MOD << "modify_ai tag has invalid 'action' attribute " << act << std::endl;
164 }
165 DBG_AI_MOD << "side "<< side_ << " after [modify_ai]"<<act<<std::endl << to_config() << std::endl;
166 if (!res) {
167 LOG_AI_MOD << act << "_ai_component failed"<< std::endl;
168 } else {
169 LOG_AI_MOD << act << "_ai_component success"<< std::endl;
170 }
171
172 }
173
append_ai(const config & cfg)174 void holder::append_ai(const config& cfg)
175 {
176 if(!this->ai_) {
177 get_ai_ref();
178 }
179 for(const config& aspect : cfg.child_range("aspect")) {
180 const std::string& id = aspect["id"];
181 for(const config& facet : aspect.child_range("facet")) {
182 ai_->add_facet(id, facet);
183 }
184 }
185 for(const config& goal : cfg.child_range("goal")) {
186 ai_->add_goal(goal);
187 }
188 for(const config& stage : cfg.child_range("stage")) {
189 if(stage["name"] != "empty") {
190 ai_->add_stage(stage);
191 }
192 }
193 }
194
to_config() const195 config holder::to_config() const
196 {
197 if (!this->ai_) {
198 return cfg_;
199 } else {
200 config cfg = ai_->to_config();
201 if (this->side_context_!=nullptr) {
202 cfg.merge_with(this->side_context_->to_side_context_config());
203 }
204 if (this->readonly_context_!=nullptr) {
205 cfg.merge_with(this->readonly_context_->to_readonly_context_config());
206 }
207 if (this->readwrite_context_!=nullptr) {
208 cfg.merge_with(this->readwrite_context_->to_readwrite_context_config());
209 }
210 if (this->default_ai_context_!=nullptr) {
211 cfg.merge_with(this->default_ai_context_->to_default_ai_context_config());
212 }
213
214 return cfg;
215 }
216 }
217
218
219
describe_ai()220 const std::string holder::describe_ai()
221 {
222 std::string sidestr = std::to_string(this->side_);
223
224 if (this->ai_!=nullptr) {
225 return this->ai_->describe_self()+std::string(" for side ")+sidestr+std::string(" : ");
226 } else {
227 return std::string("not initialized ai with id=[")+cfg_["id"]+std::string("] for side ")+sidestr+std::string(" : ");
228 }
229 }
230
231
get_ai_overview()232 const std::string holder::get_ai_overview()
233 {
234 if (!this->ai_) {
235 get_ai_ref();
236 }
237 std::stringstream s;
238 s << "advancements: " << this->ai_->get_advancements().get_value() << std::endl;
239 s << "aggression: " << this->ai_->get_aggression() << std::endl;
240 s << "attack_depth: " << this->ai_->get_attack_depth() << std::endl;
241 s << "caution: " << this->ai_->get_caution() << std::endl;
242 s << "grouping: " << this->ai_->get_grouping() << std::endl;
243 s << "leader_aggression: " << this->ai_->get_leader_aggression() << std::endl;
244 s << "leader_ignores_keep: " << this->ai_->get_leader_ignores_keep() << std::endl;
245 s << "leader_value: " << this->ai_->get_leader_value() << std::endl;
246 s << "passive_leader: " << this->ai_->get_passive_leader() << std::endl;
247 s << "passive_leader_shares_keep: " << this->ai_->get_passive_leader_shares_keep() << std::endl;
248 s << "recruitment_diversity: " << this->ai_->get_recruitment_diversity() << std::endl;
249 s << "recruitment_instructions: " << std::endl << "----config begin----" << std::endl;
250 s << this->ai_->get_recruitment_instructions() << "-----config end-----" << std::endl;
251 s << "recruitment_more: " << utils::join(this->ai_->get_recruitment_more()) << std::endl;
252 s << "recruitment_pattern: " << utils::join(this->ai_->get_recruitment_pattern()) << std::endl;
253 s << "recruitment_randomness: " << this->ai_->get_recruitment_randomness() << std::endl;
254 s << "recruitment_save_gold: " << std::endl << "----config begin----" << std::endl;
255 s << this->ai_->get_recruitment_save_gold() << "-----config end-----" << std::endl;
256 s << "scout_village_targeting: " << this->ai_->get_scout_village_targeting() << std::endl;
257 s << "simple_targeting: " << this->ai_->get_simple_targeting() << std::endl;
258 s << "support_villages: " << this->ai_->get_support_villages() << std::endl;
259 s << "village_value: " << this->ai_->get_village_value() << std::endl;
260 s << "villages_per_scout: " << this->ai_->get_villages_per_scout() << std::endl;
261
262 return s.str();
263 }
264
265
266
get_ai_structure()267 const std::string holder::get_ai_structure()
268 {
269 if (!this->ai_) {
270 get_ai_ref();
271 }
272 return component_manager::print_component_tree(&*this->ai_,"");
273 }
274
275
get_ai_identifier() const276 const std::string holder::get_ai_identifier() const
277 {
278 return cfg_["id"];
279 }
280
get_component(component * root,const std::string & path)281 component* holder::get_component(component *root, const std::string &path) {
282 if (!game_config::debug) // Debug guard
283 {
284 return nullptr;
285 }
286
287 if (root == nullptr) // Return root component(ai_)
288 {
289 if (!this->ai_) {
290 this->init(this->side_);
291 }
292 assert(this->ai_);
293
294 return &*this->ai_;
295 }
296
297 return component_manager::get_component(root, path);
298 }
299
300 // =======================================================================
301 // LIFECYCLE
302 // =======================================================================
303
304
manager()305 manager::manager()
306 : history_()
307 , history_item_counter_(0)
308 , ai_info_()
309 , map_changed_("ai_map_changed")
310 , recruit_list_changed_("ai_recruit_list_changed")
311 , user_interact_("ai_user_interact")
312 , sync_network_("ai_sync_network")
313 , tod_changed_("ai_tod_changed")
314 , gamestate_changed_("ai_gamestate_changed")
315 , turn_started_("ai_turn_started")
316 , last_interact_(0)
317 , num_interact_(0)
318 {
319 registry::init();
320 singleton_ = this;
321 }
322
323
324 manager* manager::singleton_ = nullptr;
325
326
add_observer(events::observer * event_observer)327 void manager::add_observer( events::observer* event_observer){
328 user_interact_.attach_handler(event_observer);
329 sync_network_.attach_handler(event_observer);
330 turn_started_.attach_handler(event_observer);
331 gamestate_changed_.attach_handler(event_observer);
332 }
333
334
remove_observer(events::observer * event_observer)335 void manager::remove_observer(events::observer* event_observer){
336 user_interact_.detach_handler(event_observer);
337 sync_network_.detach_handler(event_observer);
338 turn_started_.detach_handler(event_observer);
339 gamestate_changed_.detach_handler(event_observer);
340 }
341
342
add_gamestate_observer(events::observer * event_observer)343 void manager::add_gamestate_observer( events::observer* event_observer){
344 gamestate_changed_.attach_handler(event_observer);
345 turn_started_.attach_handler(event_observer);
346 map_changed_.attach_handler(event_observer);
347 }
348
349
remove_gamestate_observer(events::observer * event_observer)350 void manager::remove_gamestate_observer(events::observer* event_observer){
351 gamestate_changed_.detach_handler(event_observer);
352 turn_started_.detach_handler(event_observer);
353 map_changed_.detach_handler(event_observer);
354 }
355
356
add_tod_changed_observer(events::observer * event_observer)357 void manager::add_tod_changed_observer( events::observer* event_observer){
358 tod_changed_.attach_handler(event_observer);
359 }
360
361
remove_tod_changed_observer(events::observer * event_observer)362 void manager::remove_tod_changed_observer(events::observer* event_observer){
363 tod_changed_.detach_handler(event_observer);
364 }
365
366
367
add_map_changed_observer(events::observer * event_observer)368 void manager::add_map_changed_observer( events::observer* event_observer )
369 {
370 map_changed_.attach_handler(event_observer);
371 }
372
373
add_recruit_list_changed_observer(events::observer * event_observer)374 void manager::add_recruit_list_changed_observer( events::observer* event_observer )
375 {
376 recruit_list_changed_.attach_handler(event_observer);
377 }
378
379
add_turn_started_observer(events::observer * event_observer)380 void manager::add_turn_started_observer( events::observer* event_observer )
381 {
382 turn_started_.attach_handler(event_observer);
383 }
384
385
remove_recruit_list_changed_observer(events::observer * event_observer)386 void manager::remove_recruit_list_changed_observer( events::observer* event_observer )
387 {
388 recruit_list_changed_.detach_handler(event_observer);
389 }
390
391
remove_map_changed_observer(events::observer * event_observer)392 void manager::remove_map_changed_observer( events::observer* event_observer )
393 {
394 map_changed_.detach_handler(event_observer);
395 }
396
397
remove_turn_started_observer(events::observer * event_observer)398 void manager::remove_turn_started_observer( events::observer* event_observer )
399 {
400 turn_started_.detach_handler(event_observer);
401 }
402
raise_user_interact()403 void manager::raise_user_interact() {
404 if(resources::simulation_){
405 return;
406 }
407
408 const int interact_time = 30;
409 const int time_since_interact = SDL_GetTicks() - last_interact_;
410 if(time_since_interact < interact_time) {
411 return;
412 }
413
414 ++num_interact_;
415 user_interact_.notify_observers();
416
417 last_interact_ = SDL_GetTicks();
418
419 }
420
raise_sync_network()421 void manager::raise_sync_network() {
422 sync_network_.notify_observers();
423 }
424
425
raise_gamestate_changed()426 void manager::raise_gamestate_changed() {
427 gamestate_changed_.notify_observers();
428 }
429
430
raise_tod_changed()431 void manager::raise_tod_changed() {
432 tod_changed_.notify_observers();
433 }
434
435
raise_turn_started()436 void manager::raise_turn_started() {
437 turn_started_.notify_observers();
438 }
439
440
raise_recruit_list_changed()441 void manager::raise_recruit_list_changed() {
442 recruit_list_changed_.notify_observers();
443 }
444
445
raise_map_changed()446 void manager::raise_map_changed() {
447 map_changed_.notify_observers();
448 }
449
450
451 // =======================================================================
452 // EVALUATION
453 // =======================================================================
454
evaluate_command(side_number side,const std::string & str)455 const std::string manager::evaluate_command( side_number side, const std::string& str )
456 {
457 //insert new command into history
458 history_.emplace_back(history_item_counter_++,str);
459
460 //prune history - erase 1/2 of it if it grows too large
461 if (history_.size()>MAX_HISTORY_SIZE){
462 history_.erase(history_.begin(),history_.begin()+MAX_HISTORY_SIZE/2);
463 LOG_AI_MANAGER << "AI MANAGER: pruned history" << std::endl;
464 }
465
466 if (!should_intercept(str)){
467 ai_composite& ai = get_active_ai_for_side(side);
468 raise_gamestate_changed();
469 return ai.evaluate(str);
470 }
471
472 return internal_evaluate_command(side,str);
473 }
474
475
should_intercept(const std::string & str) const476 bool manager::should_intercept( const std::string& str ) const
477 {
478 if (str.length()<1) {
479 return false;
480 }
481 if (str.at(0)=='!'){
482 return true;
483 }
484 if (str.at(0)=='?'){
485 return true;
486 }
487 return false;
488
489 }
490
491 //this is stub code to allow testing of basic 'history', 'repeat-last-command', 'add/remove/replace ai' capabilities.
492 //yes, it doesn't look nice. but it is usable.
493 //to be refactored at earliest opportunity
494 ///@todo 1.9 extract to separate class which will use fai or lua parser
internal_evaluate_command(side_number side,const std::string & str)495 const std::string manager::internal_evaluate_command( side_number side, const std::string& str ){
496 const int MAX_HISTORY_VISIBLE = 30;
497
498 //repeat last command
499 if (str=="!") {
500 //this command should not be recorded in history
501 if (!history_.empty()){
502 history_.pop_back();
503 history_item_counter_--;
504 }
505
506 if (history_.empty()){
507 return "AI MANAGER: empty history";
508 }
509 return evaluate_command(side, history_.back().get_command());//no infinite loop since '!' commands are not present in history
510 };
511 //show last command
512 if (str=="?") {
513 //this command should not be recorded in history
514 if (!history_.empty()){
515 history_.pop_back();
516 history_item_counter_--;
517 }
518
519 if (history_.empty()){
520 return "AI MANAGER: History is empty";
521 }
522
523 int n = std::min<int>( MAX_HISTORY_VISIBLE, history_.size() );
524 std::stringstream strstream;
525 strstream << "AI MANAGER: History - last "<< n <<" commands:\n";
526 std::deque< command_history_item >::reverse_iterator j = history_.rbegin();
527
528 for (int cmd_id=n; cmd_id>0; --cmd_id){
529 strstream << j->get_number() << " :" << j->get_command() << '\n';
530 ++j;//this is *reverse* iterator
531 }
532
533 return strstream.str();
534 };
535
536
537 std::vector< std::string > cmd = utils::parenthetical_split(str, ' ',"'","'");
538
539 if (cmd.size()==3){
540 //!add_ai side file
541 if (cmd.at(0)=="!add_ai"){
542 side = std::stoi(cmd.at(1));
543 std::string file = cmd.at(2);
544 if (add_ai_for_side_from_file(side,file,false)){
545 return std::string("AI MANAGER: added [")+manager::get_active_ai_identifier_for_side(side)+std::string("] AI for side ")+std::to_string(side)+std::string(" from file ")+file;
546 } else {
547 return std::string("AI MANAGER: failed attempt to add AI for side ")+std::to_string(side)+std::string(" from file ")+file;
548 }
549 }
550 //!replace_ai side file
551 if (cmd.at(0)=="!replace_ai"){
552 side = std::stoi(cmd.at(1));
553 std::string file = cmd.at(2);
554 if (add_ai_for_side_from_file(side,file,true)){
555 return std::string("AI MANAGER: added [")+manager::get_active_ai_identifier_for_side(side)+std::string("] AI for side ")+std::to_string(side)+std::string(" from file ")+file;
556 } else {
557 return std::string("AI MANAGER: failed attempt to add AI for side ")+std::to_string(side)+std::string(" from file ")+file;
558 }
559 }
560
561 } else if (cmd.size()==2){
562 //!remove_ai side
563 if (cmd.at(0)=="!remove_ai"){
564 side = std::stoi(cmd.at(1));
565 remove_ai_for_side(side);
566 return std::string("AI MANAGER: made an attempt to remove AI for side ")+std::to_string(side);
567 }
568 if (cmd.at(0)=="!"){
569 //this command should not be recorded in history
570 if (!history_.empty()){
571 history_.pop_back();
572 history_item_counter_--;
573 }
574
575 int command = std::stoi(cmd.at(1));
576 std::deque< command_history_item >::reverse_iterator j = history_.rbegin();
577 //yes, the iterator could be precisely positioned (since command numbers go 1,2,3,4,..). will do it later.
578 while ( (j!=history_.rend()) && (j->get_number()!=command) ){
579 ++j;// this is *reverse* iterator
580 }
581 if (j!=history_.rend()){
582 return evaluate_command(side,j->get_command());//no infinite loop since '!' commands are not present in history
583 }
584 return "AI MANAGER: no command with requested number found";
585 }
586 } else if (cmd.size()==1){
587 if (cmd.at(0)=="!help") {
588 return
589 "known commands:\n"
590 "! - repeat last command (? and ! do not count)\n"
591 "! NUMBER - repeat numbered command\n"
592 "? - show a history list\n"
593 "!add_ai TEAM FILE - add a AI to side (0 - command AI, N - AI for side #N) from file\n"
594 "!remove_ai TEAM - remove AI from side (0 - command AI, N - AI for side #N)\n"
595 "!replace_ai TEAM FILE - replace AI of side (0 - command AI, N - AI for side #N) from file\n"
596 "!help - show this help message";
597 }
598 }
599
600
601 return "AI MANAGER: nothing to do";
602 }
603
604 // =======================================================================
605 // ADD, CREATE AIs, OR LIST AI TYPES
606 // =======================================================================
607
608 ///@todo 1.9 add error reporting
add_ai_for_side_from_file(side_number side,const std::string & file,bool replace)609 bool manager::add_ai_for_side_from_file( side_number side, const std::string& file, bool replace )
610 {
611 config cfg;
612 if (!configuration::get_side_config_from_file(file,cfg)){
613 ERR_AI_MANAGER << " unable to read [SIDE] config for side "<< side << "from file [" << file <<"]"<< std::endl;
614 return false;
615 }
616 return add_ai_for_side_from_config(side,cfg,replace);
617 }
618
619
add_ai_for_side_from_config(side_number side,const config & cfg,bool replace)620 bool manager::add_ai_for_side_from_config( side_number side, const config& cfg, bool replace ){
621 config parsed_cfg;
622 configuration::parse_side_config(side, cfg, parsed_cfg);
623
624 if (replace) {
625 remove_ai_for_side(side);
626 }
627
628 std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
629 ai_stack_for_specific_side.emplace(side, parsed_cfg);
630 return true;
631 }
632
633
634 ///@todo 1.9 add error reporting
add_ai_for_side(side_number side,const std::string & ai_algorithm_type,bool replace)635 bool manager::add_ai_for_side( side_number side, const std::string& ai_algorithm_type, bool replace )
636 {
637 if (replace) {
638 remove_ai_for_side (side);
639 }
640 config cfg;
641 cfg["ai_algorithm"] = ai_algorithm_type;
642 std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
643 ai_stack_for_specific_side.emplace(side, cfg);
644 return true;
645 }
646
647
648 // =======================================================================
649 // REMOVE
650 // =======================================================================
651
remove_ai_for_side(side_number side)652 void manager::remove_ai_for_side( side_number side )
653 {
654 std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
655 if (!ai_stack_for_specific_side.empty()){
656 ai_stack_for_specific_side.pop();
657 }
658 }
659
660
remove_all_ais_for_side(side_number side)661 void manager::remove_all_ais_for_side( side_number side )
662 {
663 std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
664
665 //clear the stack. std::stack doesn't have a '.clear()' method to do it
666 while (!ai_stack_for_specific_side.empty()){
667 ai_stack_for_specific_side.pop();
668 }
669 }
670
671
clear_ais()672 void manager::clear_ais()
673 {
674 ai_map_.clear();
675 }
676
677
modify_active_ai_for_side(side_number side,const config & cfg)678 void manager::modify_active_ai_for_side ( side_number side, const config &cfg )
679 {
680 get_active_ai_holder_for_side(side).modify_ai(cfg);
681 }
682
683
append_active_ai_for_side(side_number side,const config & cfg)684 void manager::append_active_ai_for_side(side_number side, const config& cfg)
685 {
686 get_active_ai_holder_for_side(side).append_ai(cfg);
687 }
688
get_active_ai_overview_for_side(side_number side)689 std::string manager::get_active_ai_overview_for_side( side_number side )
690 {
691 return get_active_ai_holder_for_side(side).get_ai_overview();
692 }
693
694
get_active_ai_structure_for_side(side_number side)695 std::string manager::get_active_ai_structure_for_side( side_number side )
696 {
697 return get_active_ai_holder_for_side(side).get_ai_structure();
698 }
699
700
get_active_ai_identifier_for_side(side_number side)701 std::string manager::get_active_ai_identifier_for_side( side_number side )
702 {
703 return get_active_ai_holder_for_side(side).get_ai_identifier();
704 }
705
get_active_ai_holder_for_side_dbg(side_number side)706 ai::holder& manager::get_active_ai_holder_for_side_dbg( side_number side )
707 {
708 if (!game_config::debug)
709 {
710 static ai::holder empty_holder(side, config());
711 return empty_holder;
712 }
713 return get_active_ai_holder_for_side(side);
714 }
715
716
to_config(side_number side)717 config manager::to_config( side_number side )
718 {
719 return get_active_ai_holder_for_side(side).to_config();
720 }
721
722
get_active_ai_info_for_side(side_number)723 game_info& manager::get_active_ai_info_for_side( side_number /*side*/ )
724 {
725 return ai_info_;
726 }
727
728
get_ai_info()729 game_info& manager::get_ai_info()
730 {
731 return ai_info_;
732 }
733
734
735 // =======================================================================
736 // PROXY
737 // =======================================================================
738
play_turn(side_number side)739 void manager::play_turn( side_number side ){
740 last_interact_ = 0;
741 num_interact_ = 0;
742 const int turn_start_time = SDL_GetTicks();
743 /*hack. @todo 1.9 rework via extended event system*/
744 get_ai_info().recent_attacks.clear();
745 ai_composite& ai_obj = get_active_ai_for_side(side);
746 resources::game_events->pump().fire("ai_turn");
747 raise_turn_started();
748 if (resources::tod_manager->has_tod_bonus_changed()) {
749 raise_tod_changed();
750 }
751 ai_obj.new_turn();
752 ai_obj.play_turn();
753 const int turn_end_time= SDL_GetTicks();
754 DBG_AI_MANAGER << "side " << side << ": number of user interactions: "<<num_interact_<<std::endl;
755 DBG_AI_MANAGER << "side " << side << ": total turn time: "<<turn_end_time - turn_start_time << " ms "<< std::endl;
756 }
757
758
759 // =======================================================================
760 // PRIVATE
761 // =======================================================================
762 // =======================================================================
763 // AI STACKS
764 // =======================================================================
get_or_create_ai_stack_for_side(side_number side)765 std::stack<holder>& manager::get_or_create_ai_stack_for_side( side_number side )
766 {
767 AI_map_of_stacks::iterator iter = ai_map_.find(side);
768 if (iter!=ai_map_.end()){
769 return iter->second;
770 }
771 return ai_map_.emplace(side, std::stack<holder>()).first->second;
772 }
773
774 // =======================================================================
775 // AI HOLDERS
776 // =======================================================================
get_active_ai_holder_for_side(side_number side)777 holder& manager::get_active_ai_holder_for_side( side_number side )
778 {
779 std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
780
781 if (!ai_stack_for_specific_side.empty()){
782 return ai_stack_for_specific_side.top();
783 } else {
784 config cfg = configuration::get_default_ai_parameters();
785 ai_stack_for_specific_side.emplace(side, cfg);
786 return ai_stack_for_specific_side.top();
787 }
788 }
789
790 // =======================================================================
791 // AI POINTERS
792 // =======================================================================
793
get_active_ai_for_side(side_number side)794 ai_composite& manager::get_active_ai_for_side( side_number side )
795 {
796 return get_active_ai_holder_for_side(side).get_ai_ref();
797 }
798
799
800 // =======================================================================
801 // MISC
802 // =======================================================================
803
804 } //end of namespace ai
805