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