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 * @file 17 * Managing the AIs lifecycle - headers 18 * @todo 1.9 Refactor history handling and internal commands. 19 * @todo 1.9 AI Interface command to clear the history. 20 */ 21 22 #pragma once 23 24 #include "ai/game_info.hpp" // for side_number, ai_ptr 25 #include "config.hpp" // for config, etc 26 #include "generic_event.hpp" // for generic_event, etc 27 28 #include <deque> // for deque 29 #include <map> // for map, map<>::value_compare 30 #include <stack> // for stack 31 #include <string> // for string 32 33 class game_launcher; 34 namespace ai { class ai_composite; } // lines 45-45 35 namespace ai { class ai_context; } // lines 42-42 36 namespace ai { class component; } // lines 43-43 37 namespace ai { class default_ai_context; } // lines 41-41 38 namespace ai { class readonly_context; } // lines 39-39 39 namespace ai { class readwrite_context; } // lines 40-40 40 namespace ai { class side_context; } // lines 38-38 41 namespace events { class generic_event; } 42 namespace events { class observer; } 43 44 45 namespace ai { 46 47 typedef std::shared_ptr<ai_composite> composite_ai_ptr; 48 49 /** 50 * Base class that holds the AI and current AI parameters. 51 * It is an implementation detail. 52 * @todo 1.9 move it out of public view 53 */ 54 class holder{ 55 public: 56 holder(side_number side, const config &cfg); 57 58 virtual ~holder(); 59 60 ai_composite& get_ai_ref(); 61 62 const std::string describe_ai(); 63 64 config to_config() const; 65 66 void modify_ai(const config& cfg); 67 68 69 void append_ai(const config& cfg); 70 71 72 const std::string get_ai_overview(); 73 74 75 const std::string get_ai_structure(); 76 77 78 const std::string get_ai_identifier() const; 79 80 component* get_component(component *root, const std::string &path); // Ai debug method 81 82 private: 83 void init( side_number side ); 84 85 86 composite_ai_ptr ai_; 87 std::unique_ptr<side_context> side_context_; 88 std::unique_ptr<readonly_context> readonly_context_; 89 std::unique_ptr<readwrite_context> readwrite_context_; 90 std::unique_ptr<default_ai_context> default_ai_context_; 91 side_number side_; 92 config cfg_; 93 }; 94 95 /** 96 * AI Command History Item. It is an implementation detail 97 */ 98 class command_history_item{ 99 public: 100 command_history_item(int number,const std::string & command)101 command_history_item(int number, const std::string &command) 102 : number_(number), command_(command) 103 {} 104 get_number() const105 int get_number() const { return number_; } 106 get_command() const107 const std::string& get_command() const { return command_; } 108 109 private: 110 int number_; 111 std::string command_; 112 113 }; 114 115 /** 116 * Class that manages AIs for all sides and manages AI redeployment. 117 * This class is responsible for managing the AI lifecycle. 118 */ 119 class manager 120 { 121 public: 122 123 // ======================================================================= 124 // CONSTANTS 125 // ======================================================================= 126 127 static const size_t MAX_HISTORY_SIZE = 200; 128 129 static const std::string AI_TYPE_COMPOSITE_AI; 130 static const std::string AI_TYPE_SAMPLE_AI; 131 static const std::string AI_TYPE_IDLE_AI; 132 static const std::string AI_TYPE_FORMULA_AI; 133 static const std::string AI_TYPE_DFOOL_AI; 134 static const std::string AI_TYPE_AI2; 135 static const std::string AI_TYPE_DEFAULT; 136 137 138 // ======================================================================= 139 // CONSTRUCTORS AND DESTRUCTORS 140 // ======================================================================= 141 142 manager(); 143 144 /* The singleton can't be set to null in the destructor because member objects 145 (which access the singleton) are destroyed *after* the destructor has been run. */ 146 ~manager() = default; 147 148 // ======================================================================= 149 // ACCESS TO MANAGER 150 // ======================================================================= 151 get_singleton()152 static manager& get_singleton() 153 { 154 assert(singleton_ != nullptr); 155 return *singleton_; 156 } 157 has_manager()158 static bool has_manager() 159 { 160 return singleton_ != nullptr; 161 } 162 163 // ======================================================================= 164 // LIFECYCLE 165 // ======================================================================= 166 167 168 /** 169 * Adds observer of game events. 170 * Should be called in playsingle_controller 's constructor. 171 */ 172 void add_observer( events::observer* event_observer); 173 174 175 /** 176 * Removes an observer of game events. 177 * Should be called in playsingle_controller 's destructor. 178 */ 179 void remove_observer( events::observer* event_observer ); 180 181 182 /** 183 * Adds observer of game events except ai_user_interact event and ai_sync_network event 184 */ 185 void add_gamestate_observer( events::observer* event_observer); 186 187 188 /** 189 * Removes an observer of game events except ai_user_interact event and ai_sync_network event 190 */ 191 void remove_gamestate_observer( events::observer* event_observer ); 192 193 194 /** 195 * Notifies all observers of 'ai_user_interact' event. 196 * Function which should be called frequently to allow the user to interact 197 * with the interface. This function will make sure that interaction 198 * doesn't occur too often, so there is no problem with calling it very 199 * regularly. 200 */ 201 void raise_user_interact(); 202 203 /** 204 * Notifies all observers of 'ai_sync_network' event. 205 * Basically a request from the AI to sync the network. 206 */ 207 void raise_sync_network(); 208 209 210 /** 211 * Notifies all observers of 'ai_gamestate_changed' event. 212 */ 213 void raise_gamestate_changed(); 214 215 216 /** 217 * Notifies all observers of 'ai_tod_changed' event. 218 */ 219 void raise_tod_changed(); 220 221 222 /** 223 * Notifies all observers of 'ai_recruit_list_changed' event. 224 */ 225 void raise_recruit_list_changed(); 226 227 228 /** 229 * Notifies all observers of 'ai_turn_started' event. 230 */ 231 void raise_turn_started(); 232 233 234 /** 235 * Notifies all observers of 'ai_map_changed' event. 236 */ 237 void raise_map_changed(); 238 239 240 /** 241 * Adds an observer of 'ai_map_changed' event. 242 */ 243 void add_map_changed_observer( events::observer* event_observer ); 244 245 246 /** 247 * Adds an observer of 'ai_recruit_list_changed' event. 248 */ 249 void add_recruit_list_changed_observer( events::observer* event_observer ); 250 251 252 /** 253 * Adds an observer of 'ai_turn_started' event. 254 */ 255 void add_turn_started_observer( events::observer* event_observer ); 256 257 258 /** 259 * Adds an observer of 'ai_tod_changed' event. 260 */ 261 void add_tod_changed_observer( events::observer* event_observer ); 262 263 264 /** 265 * Deletes an observer of 'ai_map_changed' event. 266 */ 267 void remove_map_changed_observer( events::observer* event_observer ); 268 269 270 271 /** 272 * Deletes an observer of 'ai_recruit_list_changed' event. 273 */ 274 void remove_recruit_list_changed_observer( events::observer* event_observer ); 275 276 277 /** 278 * Deletes an observer of 'ai_turn_started' event. 279 */ 280 void remove_turn_started_observer( events::observer* event_observer ); 281 282 283 /** 284 * Deletes an observer of 'ai_tod_changed' event. 285 */ 286 void remove_tod_changed_observer( events::observer* event_observer ); 287 288 289 public: 290 291 // ======================================================================= 292 // EVALUATION 293 // ======================================================================= 294 295 /** 296 * Evaluates a string command using command AI. 297 * @note Running this command may invalidate references previously returned 298 * by manager. Will intercept those commands which start with '!' 299 * and '?', and will try to evaluate them as internal commands. 300 * @param side side number (1-based). 301 * @param str string to evaluate. 302 * @return string result of evaluation. 303 */ 304 const std::string evaluate_command( side_number side, const std::string& str ); 305 306 307 // ======================================================================= 308 // ADD, CREATE AIs, OR LIST AI TYPES 309 // ======================================================================= 310 311 /** 312 * Adds active AI for specified @a side from @a file. 313 * @note Running this command may invalidate references previously returned 314 * by manager. AI is not initialized at this point. 315 * @param side side number (1-based, as in game_info). 316 * @param file file name, follows the usual WML convention. 317 * @param replace should new ai replace the current ai or 'be placed on top of it'. 318 * @return true if successful. 319 */ 320 bool add_ai_for_side_from_file( side_number side, const std::string& file, bool replace = true ); 321 322 323 /** 324 * Adds active AI for specified @a side from @a cfg. 325 * @note Running this command may invalidate references previously returned 326 * by manager. AI is not initialized at this point. 327 * @param side side number (1-based, as in game_info). 328 * @param cfg the config from which all ai parameters are to be read. 329 * @param replace should new ai replace the current ai or 'be placed on top of it'. 330 * @return true if successful. 331 */ 332 bool add_ai_for_side_from_config(side_number side, const config &cfg, bool replace = true); 333 334 335 /** 336 * Adds active AI for specified @a side from parameters. 337 * @note Running this command may invalidate references previously returned 338 * by manager. AI is not initialized at this point. 339 * @param side side number (1-based, as in game_info). 340 * @param ai_algorithm_type type of AI algorithm to create. 341 * @param replace should new ai replace the current ai or 'be placed on top of it'. 342 * @return true if successful. 343 */ 344 bool add_ai_for_side( side_number side, const std::string& ai_algorithm_type, bool replace = true); 345 346 347 // ======================================================================= 348 // REMOVE 349 // ======================================================================= 350 351 /** 352 * Removes top-level AI from @a side. 353 * @note Running this command may invalidate references previously returned 354 * by manager. 355 * @param side side number (1-based, as in game_info). 356 */ 357 void remove_ai_for_side( side_number side ); 358 359 360 /** 361 * Removes all AIs from @a side. 362 * @note Running this command may invalidate references previously returned 363 * by manager. 364 * @param side side number (1-based, as in game_info). 365 */ 366 void remove_all_ais_for_side( side_number side ); 367 368 369 /** 370 * Clears all the AIs. 371 * @note Running this command may invalidate references previously returned 372 * by manager. For example, this is called from the destructor of 373 * playsingle_controller. It is necessary to do this if any of the 374 * info structures used by the AI goes out of scope. 375 */ 376 void clear_ais(); 377 378 // ======================================================================= 379 // GET active AI parameters 380 // ======================================================================= 381 382 383 /** 384 * Gets AI info for active AI of the given @a side. 385 * @param side side number (1-based). 386 * @return a reference to active AI info. 387 */ 388 game_info& get_active_ai_info_for_side( side_number side ); 389 390 391 /** 392 * Gets AI Overview for active AI of the given @a side 393 * @param side side number (1-based) 394 * @return an ai overview 395 */ 396 std::string get_active_ai_overview_for_side( side_number side ); 397 398 399 /** 400 * Gets AI Structure for active AI of the given @a side 401 * @param side side number (1-based) 402 * @return an ai structure 403 */ 404 std::string get_active_ai_structure_for_side( side_number side ); 405 406 /** 407 * Gets AI algorithm identifier for active AI of the given @a side. 408 * @param side side number (1-based). 409 * @return ai identifier for the active AI 410 */ 411 std::string get_active_ai_identifier_for_side( side_number side ); 412 413 /** 414 * Gets the active AI holder for debug purposes. 415 * Will only work in debug mode, otherwise returns a reference to an empty holder 416 * @param side side number(1-based) 417 * @return debug ? active holder : empty holder 418 */ 419 ai::holder& get_active_ai_holder_for_side_dbg(side_number side); 420 421 /** 422 * Gets AI config for active AI of the given @a side. 423 * @param side side number (1-based). 424 * @return a config object for the active AI 425 */ 426 config to_config( side_number side ); 427 428 429 /** 430 * Gets global AI-game info 431 * @return a reference to the AI-game info. 432 */ 433 game_info& get_ai_info(); 434 435 436 // ======================================================================= 437 // SET active AI parameters 438 // ======================================================================= 439 440 /** 441 * Modifies AI parameters for active AI of the given @a side. 442 * This function is a backend for [modify_ai] tag 443 * @param side side_number (1-based, as in game_info). 444 * @param cfg - content of [modify_ai] tag 445 */ 446 447 void modify_active_ai_for_side( ai::side_number side, const config &cfg ); 448 449 /** 450 * Appends AI parameters to active AI of the given @a side. 451 * This function is a backend for [modify_side][ai] tag 452 * @param side side_number (1-based, as in game_info). 453 * @param cfg - content of [modify_side][ai] tag 454 */ 455 456 void append_active_ai_for_side( ai::side_number side, const config &cfg ); 457 458 // ======================================================================= 459 // PROXY 460 // ======================================================================= 461 462 /** 463 * Plays a turn for the specified side using its active AI. 464 * @param side side number (1-based, as in game_info). 465 */ 466 void play_turn(side_number side); 467 468 469 private: 470 471 typedef std::map< side_number, std::stack< holder >> AI_map_of_stacks; 472 473 std::deque< command_history_item > history_; 474 long history_item_counter_; 475 game_info ai_info_; 476 477 events::generic_event map_changed_; 478 events::generic_event recruit_list_changed_; 479 events::generic_event user_interact_; 480 events::generic_event sync_network_; 481 events::generic_event tod_changed_; 482 events::generic_event gamestate_changed_; 483 events::generic_event turn_started_; 484 int last_interact_; 485 int num_interact_; 486 487 AI_map_of_stacks ai_map_; 488 489 static manager* singleton_; 490 491 492 // ======================================================================= 493 // EVALUATION 494 // ======================================================================= 495 496 /** 497 * Evaluates an internal manager command. 498 * @param side side number (1-based). 499 * @param str string to evaluate. 500 * @return string result of evaluation. 501 * @todo 1.9 rewrite this function to use a fai or lua parser. 502 */ 503 const std::string internal_evaluate_command( side_number side, const std::string& str ); 504 505 /** 506 * Determines if the command should be intercepted and evaluated as internal command. 507 * @param str command string to check. 508 * @return true if the command should be intercepted and evaluated. 509 */ 510 bool should_intercept( const std::string& str ) const; 511 512 // ======================================================================= 513 // AI STACKS 514 // ======================================================================= 515 516 517 /** 518 * Gets the AI stack for the specified side, create it if it doesn't exist. 519 */ 520 std::stack< holder >& get_or_create_ai_stack_for_side(side_number side); 521 522 // ======================================================================= 523 // AI HOLDERS 524 // ======================================================================= 525 526 527 /** 528 * Gets active holder for specified @a side. 529 */ 530 holder& get_active_ai_holder_for_side( side_number side ); 531 532 // ======================================================================= 533 // AI POINTERS 534 // ======================================================================= 535 536 /** 537 * Gets active AI for specified side. 538 * @note Running this command may invalidate references previously returned 539 * by manager. 540 * @param side side number (1-based, as in game_info). 541 * @return a reference to the active AI. 542 * @note This reference may become invalid after specific manager operations. 543 */ 544 ai_composite& get_active_ai_for_side( side_number side ); 545 546 547 friend class ::game_launcher; 548 }; 549 550 } //end of namespace ai 551