mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-20 16:05:19 +00:00
788 lines
24 KiB
C++
788 lines
24 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2009 - 2010 by Yurii Chernyi <terraninfo@terraninfo.net>
|
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2
|
|
or at your option any later version.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
/**
|
|
* Managing the AI lifecycle and interface for the rest of Wesnoth
|
|
* @file ai/manager.cpp
|
|
*/
|
|
|
|
#include "composite/ai.hpp"
|
|
#include "configuration.hpp"
|
|
#include "contexts.hpp"
|
|
#include "default/ai.hpp"
|
|
#include "manager.hpp"
|
|
#include "formula/ai.hpp"
|
|
#include "registry.hpp"
|
|
#include "../game_events.hpp"
|
|
#include "../game_preferences.hpp"
|
|
#include "../foreach.hpp"
|
|
#include "../log.hpp"
|
|
#include "../replay.hpp"
|
|
#include "../serialization/string_utils.hpp"
|
|
#include "../statistics.hpp"
|
|
#include "composite/component.hpp"
|
|
|
|
#include <map>
|
|
#include <stack>
|
|
#include <vector>
|
|
|
|
namespace ai {
|
|
|
|
const std::string manager::AI_TYPE_COMPOSITE_AI = "composite_ai";
|
|
const std::string manager::AI_TYPE_SAMPLE_AI = "sample_ai";
|
|
const std::string manager::AI_TYPE_IDLE_AI = "idle_ai";
|
|
const std::string manager::AI_TYPE_FORMULA_AI = "formula_ai";
|
|
const std::string manager::AI_TYPE_DEFAULT = "default";
|
|
|
|
|
|
static lg::log_domain log_ai_manager("ai/manager");
|
|
#define DBG_AI_MANAGER LOG_STREAM(debug, log_ai_manager)
|
|
#define LOG_AI_MANAGER LOG_STREAM(info, log_ai_manager)
|
|
#define ERR_AI_MANAGER LOG_STREAM(err, log_ai_manager)
|
|
|
|
static lg::log_domain log_ai_mod("ai/mod");
|
|
#define DBG_AI_MOD LOG_STREAM(debug, log_ai_mod)
|
|
#define LOG_AI_MOD LOG_STREAM(info, log_ai_mod)
|
|
#define WRN_AI_MOD LOG_STREAM(warn, log_ai_mod)
|
|
#define ERR_AI_MOD LOG_STREAM(err, log_ai_mod)
|
|
|
|
holder::holder( side_number side, const config &cfg )
|
|
: ai_(), side_context_(NULL), readonly_context_(NULL), readwrite_context_(NULL), default_ai_context_(NULL), side_(side), cfg_(cfg)
|
|
{
|
|
DBG_AI_MANAGER << describe_ai() << "Preparing new AI holder" << std::endl;
|
|
}
|
|
|
|
|
|
void holder::init( side_number side )
|
|
{
|
|
if (side_context_ == NULL) {
|
|
side_context_ = new side_context_impl(side,cfg_);
|
|
} else {
|
|
side_context_->set_side(side);
|
|
}
|
|
if (readonly_context_ == NULL){
|
|
readonly_context_ = new readonly_context_impl(*side_context_,cfg_);
|
|
readonly_context_->on_readonly_context_create();
|
|
}
|
|
if (readwrite_context_ == NULL){
|
|
readwrite_context_ = new readwrite_context_impl(*readonly_context_,cfg_);
|
|
}
|
|
if (default_ai_context_ == NULL){
|
|
default_ai_context_ = new default_ai_context_impl(*readwrite_context_,cfg_);
|
|
}
|
|
if (!this->ai_){
|
|
ai_ = boost::shared_ptr<ai_composite>(new ai_composite(*default_ai_context_,cfg_));
|
|
}
|
|
|
|
if (this->ai_) {
|
|
ai_->on_create();
|
|
foreach (config &mod_ai, cfg_.child_range("modify_ai")) {
|
|
if (!mod_ai.has_attribute("side")) {
|
|
mod_ai["side"] = side;
|
|
}
|
|
modify_ai(mod_ai);
|
|
}
|
|
cfg_.clear_children("modify_ai");
|
|
|
|
} else {
|
|
ERR_AI_MANAGER << describe_ai()<<"AI lazy initialization error!" << std::endl;
|
|
}
|
|
|
|
}
|
|
|
|
holder::~holder()
|
|
{
|
|
if (this->ai_) {
|
|
LOG_AI_MANAGER << describe_ai() << "Managed AI will be deleted" << std::endl;
|
|
}
|
|
delete this->default_ai_context_;
|
|
delete this->readwrite_context_;
|
|
delete this->readonly_context_;
|
|
delete this->side_context_;
|
|
}
|
|
|
|
|
|
interface& holder::get_ai_ref()
|
|
{
|
|
if (!this->ai_) {
|
|
this->init(this->side_);
|
|
}
|
|
assert(this->ai_);
|
|
|
|
return *this->ai_;
|
|
}
|
|
|
|
|
|
void holder::modify_ai_config_old( const config::const_child_itors &ai_parameters)
|
|
{
|
|
// only handle aspects
|
|
// transform ai_parameters to new-style config
|
|
|
|
config cfg;
|
|
configuration::upgrade_aspect_configs_from_1_07_02_to_1_07_03(this->side_,ai_parameters,cfg);
|
|
//at this point we have a single config which contains [aspect][facet] tags
|
|
DBG_AI_MANAGER << "after transforming [modify_side][ai] into new syntax, config contains:"<< std::endl << cfg << std::endl;
|
|
|
|
if (this->readonly_context_ == NULL) {
|
|
// if not initialized, append that config to the bottom of base cfg
|
|
// then, merge aspects with the same id
|
|
cfg_.merge_with(cfg);
|
|
cfg_.merge_children_by_attribute("aspect","id");
|
|
} else {
|
|
// else run 'add_facet' command on each [aspect][facet]
|
|
foreach (const config &cfg_a, cfg.child_range("aspect")) {
|
|
foreach (const config &cfg_f, cfg_a.child_range("facet")) {
|
|
readonly_context_->add_facet(cfg_a["id"],cfg_f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void holder::modify_ai(const config &cfg)
|
|
{
|
|
if (!this->ai_) {
|
|
// if not initialized, initialize now.
|
|
get_ai_ref();
|
|
}
|
|
const std::string &act = cfg["action"];
|
|
LOG_AI_MOD << "side "<< side_ << " [modify_ai] "<<act<<" \""<<cfg["path"]<<"\""<<std::endl;
|
|
DBG_AI_MOD << std::endl << cfg << std::endl;
|
|
DBG_AI_MOD << "side "<< side_ << " before [modify_ai]"<<std::endl << to_config() << std::endl;
|
|
bool res = false;
|
|
if (act == "add") {
|
|
res = component_manager::add_component(&*this->ai_,cfg["path"],cfg);
|
|
} else if (act == "change") {
|
|
res = component_manager::change_component(&*this->ai_,cfg["path"],cfg);
|
|
} else if (act == "delete") {
|
|
res = component_manager::delete_component(&*this->ai_,cfg["path"]);
|
|
} else if (act == "try_delete") {
|
|
res = component_manager::delete_component(&*this->ai_,cfg["path"]);
|
|
if (!res) {
|
|
LOG_AI_MOD << "[modify_ai] "<<act<<" failed, ignoring because it's a try_delete"<< std::endl;
|
|
res = true;
|
|
}
|
|
} else {
|
|
ERR_AI_MOD << "modify_ai tag has invalid 'action' attribute " << act << std::endl;
|
|
}
|
|
DBG_AI_MOD << "side "<< side_ << " after [modify_ai]"<<act<<std::endl << to_config() << std::endl;
|
|
if (!res) {
|
|
LOG_AI_MOD << "[modify_ai] "<<act<<" failed"<< std::endl;
|
|
} else {
|
|
LOG_AI_MOD << "[modify_ai] "<<act<<" success"<< std::endl;
|
|
}
|
|
|
|
}
|
|
|
|
config holder::to_config() const
|
|
{
|
|
if (!this->ai_) {
|
|
return cfg_;
|
|
} else {
|
|
config cfg = ai_->to_config();
|
|
cfg["version"] = "10703";
|
|
if (this->side_context_!=NULL) {
|
|
cfg.merge_with(this->side_context_->to_side_context_config());
|
|
}
|
|
if (this->readonly_context_!=NULL) {
|
|
cfg.merge_with(this->readonly_context_->to_readonly_context_config());
|
|
}
|
|
if (this->readwrite_context_!=NULL) {
|
|
cfg.merge_with(this->readwrite_context_->to_readwrite_context_config());
|
|
}
|
|
if (this->default_ai_context_!=NULL) {
|
|
cfg.merge_with(this->default_ai_context_->to_default_ai_context_config());
|
|
}
|
|
|
|
return cfg;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const std::string holder::describe_ai()
|
|
{
|
|
std::string sidestr = lexical_cast<std::string>(this->side_);
|
|
|
|
if (this->ai_!=NULL) {
|
|
return this->ai_->describe_self()+std::string(" for side ")+sidestr+std::string(" : ");
|
|
} else {
|
|
return std::string("not initialized ai with id=[")+cfg_["id"]+std::string("] for side ")+sidestr+std::string(" : ");
|
|
}
|
|
}
|
|
|
|
|
|
const std::string holder::get_ai_overview()
|
|
{
|
|
if (!this->ai_) {
|
|
get_ai_ref();
|
|
}
|
|
std::stringstream s;
|
|
s << "aggression: " << this->ai_->get_aggression() << std::endl;
|
|
s << "attack_depth: " << this->ai_->get_attack_depth() << std::endl;
|
|
s << "caution: " << this->ai_->get_caution() << std::endl;
|
|
s << "grouping: " << this->ai_->get_grouping() << std::endl;
|
|
s << "leader_aggression: " << this->ai_->get_leader_aggression() << std::endl;
|
|
s << "leader_value: " << this->ai_->get_leader_value() << std::endl;
|
|
s << "number_of_possible_recruits_to_force_recruit: " << this->ai_->get_number_of_possible_recruits_to_force_recruit() << std::endl;
|
|
s << "passive_leader: " << this->ai_->get_passive_leader() << std::endl;
|
|
s << "passive_leader_shares_keep: " << this->ai_->get_passive_leader_shares_keep() << std::endl;
|
|
s << "recruitment_ignore_bad_combat: " << this->ai_->get_recruitment_ignore_bad_movement() << std::endl;
|
|
s << "recruitment_ignore_bad_movement: " << this->ai_->get_recruitment_ignore_bad_combat() << std::endl;
|
|
// s << "recruitment_pattern: ";
|
|
// for(std::vector<std::string>::const_iterator i = this->ai_->get_recruitment_pattern().begin(); i != this->ai_->get_recruitment_pattern().end(); ++i) {
|
|
// if(i != this->ai_->get_recruitment_pattern().begin())
|
|
// s << ",";
|
|
//
|
|
// s << *i;
|
|
// }
|
|
// s << std::endl;
|
|
s << "scout_village_targeting: " << this->ai_->get_scout_village_targeting() << std::endl;
|
|
s << "simple_targeting: " << this->ai_->get_simple_targeting() << std::endl;
|
|
s << "support_villages: " << this->ai_->get_support_villages() << std::endl;
|
|
s << "village_value: " << this->ai_->get_village_value() << std::endl;
|
|
s << "villages_per_scout: " << this->ai_->get_villages_per_scout() << std::endl;
|
|
return s.str();
|
|
}
|
|
|
|
|
|
|
|
const std::string holder::get_ai_structure()
|
|
{
|
|
if (!this->ai_) {
|
|
get_ai_ref();
|
|
}
|
|
return component_manager::print_component_tree(&*this->ai_,"");
|
|
}
|
|
|
|
|
|
const std::string holder::get_ai_identifier() const
|
|
{
|
|
return cfg_["id"];
|
|
}
|
|
|
|
// =======================================================================
|
|
// LIFECYCLE
|
|
// =======================================================================
|
|
|
|
|
|
manager::AI_map_of_stacks manager::ai_map_;
|
|
game_info *manager::ai_info_;
|
|
events::generic_event manager::user_interact_("ai_user_interact");
|
|
events::generic_event manager::sync_network_("ai_sync_network");
|
|
events::generic_event manager::gamestate_changed_("ai_gamestate_changed");
|
|
events::generic_event manager::turn_started_("ai_turn_started");
|
|
events::generic_event manager::recruit_list_changed_("ai_recruit_list_changed");
|
|
events::generic_event manager::map_changed_("ai_map_changed");
|
|
int manager::last_interact_ = 0;
|
|
int manager::num_interact_ = 0;
|
|
|
|
|
|
void manager::set_ai_info(const game_info& i)
|
|
{
|
|
if (ai_info_!=NULL){
|
|
clear_ai_info();
|
|
}
|
|
ai_info_ = new game_info(i);
|
|
registry::init();
|
|
}
|
|
|
|
|
|
void manager::clear_ai_info(){
|
|
if (ai_info_ != NULL){
|
|
delete ai_info_;
|
|
ai_info_ = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void manager::add_observer( events::observer* event_observer){
|
|
user_interact_.attach_handler(event_observer);
|
|
sync_network_.attach_handler(event_observer);
|
|
turn_started_.attach_handler(event_observer);
|
|
gamestate_changed_.attach_handler(event_observer);
|
|
}
|
|
|
|
|
|
void manager::remove_observer(events::observer* event_observer){
|
|
user_interact_.detach_handler(event_observer);
|
|
sync_network_.detach_handler(event_observer);
|
|
turn_started_.detach_handler(event_observer);
|
|
gamestate_changed_.detach_handler(event_observer);
|
|
}
|
|
|
|
|
|
void manager::add_gamestate_observer( events::observer* event_observer){
|
|
gamestate_changed_.attach_handler(event_observer);
|
|
turn_started_.attach_handler(event_observer);
|
|
map_changed_.attach_handler(event_observer);
|
|
}
|
|
|
|
|
|
void manager::remove_gamestate_observer(events::observer* event_observer){
|
|
gamestate_changed_.detach_handler(event_observer);
|
|
turn_started_.detach_handler(event_observer);
|
|
map_changed_.detach_handler(event_observer);
|
|
}
|
|
|
|
|
|
|
|
void manager::add_map_changed_observer( events::observer* event_observer )
|
|
{
|
|
map_changed_.attach_handler(event_observer);
|
|
}
|
|
|
|
|
|
void manager::add_recruit_list_changed_observer( events::observer* event_observer )
|
|
{
|
|
recruit_list_changed_.attach_handler(event_observer);
|
|
}
|
|
|
|
|
|
void manager::add_turn_started_observer( events::observer* event_observer )
|
|
{
|
|
turn_started_.attach_handler(event_observer);
|
|
}
|
|
|
|
|
|
void manager::remove_recruit_list_changed_observer( events::observer* event_observer )
|
|
{
|
|
recruit_list_changed_.detach_handler(event_observer);
|
|
}
|
|
|
|
|
|
void manager::remove_map_changed_observer( events::observer* event_observer )
|
|
{
|
|
map_changed_.detach_handler(event_observer);
|
|
}
|
|
|
|
|
|
void manager::remove_turn_started_observer( events::observer* event_observer )
|
|
{
|
|
turn_started_.detach_handler(event_observer);
|
|
}
|
|
|
|
void manager::raise_user_interact() {
|
|
const int interact_time = 30;
|
|
const int time_since_interact = SDL_GetTicks() - last_interact_;
|
|
if(time_since_interact < interact_time) {
|
|
return;
|
|
}
|
|
|
|
++num_interact_;
|
|
user_interact_.notify_observers();
|
|
|
|
last_interact_ = SDL_GetTicks();
|
|
|
|
}
|
|
|
|
void manager::raise_sync_network() {
|
|
sync_network_.notify_observers();
|
|
}
|
|
|
|
|
|
void manager::raise_gamestate_changed() {
|
|
gamestate_changed_.notify_observers();
|
|
}
|
|
|
|
|
|
void manager::raise_turn_started() {
|
|
turn_started_.notify_observers();
|
|
}
|
|
|
|
|
|
void manager::raise_recruit_list_changed() {
|
|
recruit_list_changed_.notify_observers();
|
|
}
|
|
|
|
|
|
void manager::raise_map_changed() {
|
|
map_changed_.notify_observers();
|
|
}
|
|
|
|
|
|
// =======================================================================
|
|
// EVALUATION
|
|
// =======================================================================
|
|
|
|
const std::string manager::evaluate_command( side_number side, const std::string& str )
|
|
{
|
|
//insert new command into history
|
|
history_.push_back(command_history_item(history_item_counter_++,str));
|
|
|
|
//prune history - erase 1/2 of it if it grows too large
|
|
if (history_.size()>MAX_HISTORY_SIZE){
|
|
history_.erase(history_.begin(),history_.begin()+MAX_HISTORY_SIZE/2);
|
|
LOG_AI_MANAGER << "AI MANAGER: pruned history" << std::endl;
|
|
}
|
|
|
|
if (!should_intercept(str)){
|
|
interface& ai = get_active_ai_for_side(side);
|
|
raise_gamestate_changed();
|
|
return ai.evaluate(str);
|
|
}
|
|
|
|
return internal_evaluate_command(side,str);
|
|
}
|
|
|
|
|
|
bool manager::should_intercept( const std::string& str )
|
|
{
|
|
if (str.length()<1) {
|
|
return false;
|
|
}
|
|
if (str.at(0)=='!'){
|
|
return true;
|
|
}
|
|
if (str.at(0)=='?'){
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
std::deque< command_history_item > manager::history_;
|
|
long manager::history_item_counter_ = 1;
|
|
|
|
//this is stub code to allow testing of basic 'history', 'repeat-last-command', 'add/remove/replace ai' capabilities.
|
|
//yes, it doesn't look nice. but it is usable.
|
|
//to be refactored at earliest opportunity
|
|
//@todo 1.9 extract to separate class which will use fai or lua parser
|
|
const std::string manager::internal_evaluate_command( side_number side, const std::string& str ){
|
|
const int MAX_HISTORY_VISIBLE = 30;
|
|
|
|
//repeat last command
|
|
if (str=="!") {
|
|
//this command should not be recorded in history
|
|
if (!history_.empty()){
|
|
history_.pop_back();
|
|
history_item_counter_--;
|
|
}
|
|
|
|
if (history_.empty()){
|
|
return "AI MANAGER: empty history";
|
|
}
|
|
return evaluate_command(side, history_.back().get_command());//no infinite loop since '!' commands are not present in history
|
|
};
|
|
//show last command
|
|
if (str=="?") {
|
|
//this command should not be recorded in history
|
|
if (!history_.empty()){
|
|
history_.pop_back();
|
|
history_item_counter_--;
|
|
}
|
|
|
|
if (history_.empty()){
|
|
return "AI MANAGER: History is empty";
|
|
}
|
|
|
|
int n = std::min<int>( MAX_HISTORY_VISIBLE, history_.size() );
|
|
std::stringstream strstream;
|
|
strstream << "AI MANAGER: History - last "<< n <<" commands:\n";
|
|
std::deque< command_history_item >::reverse_iterator j = history_.rbegin();
|
|
|
|
for (int cmd_id=n; cmd_id>0; --cmd_id){
|
|
strstream << j->get_number() << " :" << j->get_command() << '\n';
|
|
j++;//this is *reverse* iterator
|
|
}
|
|
|
|
return strstream.str();
|
|
};
|
|
|
|
|
|
std::vector< std::string > cmd = utils::parenthetical_split(str, ' ',"'","'");
|
|
|
|
if (cmd.size()==3){
|
|
//!add_ai side file
|
|
if (cmd.at(0)=="!add_ai"){
|
|
side_number side = lexical_cast<side_number>(cmd.at(1));
|
|
std::string file = cmd.at(2);
|
|
if (add_ai_for_side_from_file(side,file,false)){
|
|
return std::string("AI MANAGER: added [")+manager::get_active_ai_identifier_for_side(side)+std::string("] AI for side ")+lexical_cast<std::string>(side)+std::string(" from file ")+file;
|
|
} else {
|
|
return std::string("AI MANAGER: failed attempt to add AI for side ")+lexical_cast<std::string>(side)+std::string(" from file ")+file;
|
|
}
|
|
}
|
|
//!replace_ai side file
|
|
if (cmd.at(0)=="!replace_ai"){
|
|
side_number side = lexical_cast<side_number>(cmd.at(1));
|
|
std::string file = cmd.at(2);
|
|
if (add_ai_for_side_from_file(side,file,true)){
|
|
return std::string("AI MANAGER: added [")+manager::get_active_ai_identifier_for_side(side)+std::string("] AI for side ")+lexical_cast<std::string>(side)+std::string(" from file ")+file;
|
|
} else {
|
|
return std::string("AI MANAGER: failed attempt to add AI for side ")+lexical_cast<std::string>(side)+std::string(" from file ")+file;
|
|
}
|
|
}
|
|
|
|
} else if (cmd.size()==2){
|
|
//!remove_ai side
|
|
if (cmd.at(0)=="!remove_ai"){
|
|
side_number side = lexical_cast<side_number>(cmd.at(1));
|
|
remove_ai_for_side(side);
|
|
return std::string("AI MANAGER: made an attempt to remove AI for side ")+lexical_cast<std::string>(side);
|
|
}
|
|
if (cmd.at(0)=="!"){
|
|
//this command should not be recorded in history
|
|
if (!history_.empty()){
|
|
history_.pop_back();
|
|
history_item_counter_--;
|
|
}
|
|
|
|
int command = lexical_cast<int>(cmd.at(1));
|
|
std::deque< command_history_item >::reverse_iterator j = history_.rbegin();
|
|
//yes, the iterator could be precisely positioned (since command numbers go 1,2,3,4,..). will do it later.
|
|
while ( (j!=history_.rend()) && (j->get_number()!=command) ){
|
|
j++;// this is *reverse* iterator
|
|
}
|
|
if (j!=history_.rend()){
|
|
return evaluate_command(side,j->get_command());//no infinite loop since '!' commands are not present in history
|
|
}
|
|
return "AI MANAGER: no command with requested number found";
|
|
}
|
|
} else if (cmd.size()==1){
|
|
if (cmd.at(0)=="!help") {
|
|
return
|
|
"known commands:\n"
|
|
"! - repeat last command (? and ! do not count)\n"
|
|
"! NUMBER - repeat numbered command\n"
|
|
"? - show a history list\n"
|
|
"!add_ai TEAM FILE - add a AI to side (0 - command AI, N - AI for side #N) from file\n"
|
|
"!remove_ai TEAM - remove AI from side (0 - command AI, N - AI for side #N)\n"
|
|
"!replace_ai TEAM FILE - replace AI of side (0 - command AI, N - AI for side #N) from file\n"
|
|
"!help - show this help message";
|
|
}
|
|
}
|
|
|
|
|
|
return "AI MANAGER: nothing to do";
|
|
}
|
|
|
|
// =======================================================================
|
|
// ADD, CREATE AIs, OR LIST AI TYPES
|
|
// =======================================================================
|
|
|
|
//@todo 1.9 add error reporting
|
|
bool manager::add_ai_for_side_from_file( side_number side, const std::string& file, bool replace )
|
|
{
|
|
config cfg;
|
|
if (!configuration::get_side_config_from_file(file,cfg)){
|
|
ERR_AI_MANAGER << " unable to read [SIDE] config for side "<< side << "from file [" << file <<"]"<< std::endl;
|
|
return false;
|
|
}
|
|
return add_ai_for_side_from_config(side,cfg,replace);
|
|
}
|
|
|
|
|
|
bool manager::add_ai_for_side_from_config( side_number side, const config& cfg, bool replace ){
|
|
config parsed_cfg;
|
|
configuration::parse_side_config(side, cfg, parsed_cfg);
|
|
|
|
if (replace) {
|
|
remove_ai_for_side(side);
|
|
}
|
|
|
|
holder new_holder(side,parsed_cfg);
|
|
std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
|
|
ai_stack_for_specific_side.push(new_holder);
|
|
return true;
|
|
}
|
|
|
|
|
|
//@todo 1.9 add error reporting
|
|
bool manager::add_ai_for_side( side_number side, const std::string& ai_algorithm_type, bool replace )
|
|
{
|
|
if (replace) {
|
|
remove_ai_for_side (side);
|
|
}
|
|
config cfg;
|
|
cfg["ai_algorithm"] = ai_algorithm_type;
|
|
holder new_holder(side,cfg);
|
|
std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
|
|
ai_stack_for_specific_side.push(new_holder);
|
|
return true;
|
|
}
|
|
|
|
|
|
ai_ptr manager::create_transient_ai(const std::string &ai_algorithm_type, const config &cfg, ai_context *ai_context )
|
|
{
|
|
assert(ai_context!=NULL);
|
|
|
|
//to add your own ai, register it in registry,cpp
|
|
ai_factory::factory_map::iterator aii = ai_factory::get_list().find(ai_algorithm_type);
|
|
if (aii == ai_factory::get_list().end()){
|
|
aii = ai_factory::get_list().find("");
|
|
if (aii == ai_factory::get_list().end()){
|
|
throw game::game_error("no default ai set!");
|
|
}
|
|
}
|
|
LOG_AI_MANAGER << "Creating new AI of type [" << ai_algorithm_type << "]"<< std::endl;
|
|
ai_ptr new_ai = aii->second->get_new_instance(*ai_context,cfg);
|
|
return new_ai;
|
|
}
|
|
|
|
|
|
// =======================================================================
|
|
// REMOVE
|
|
// =======================================================================
|
|
|
|
void manager::remove_ai_for_side( side_number side )
|
|
{
|
|
std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
|
|
if (!ai_stack_for_specific_side.empty()){
|
|
ai_stack_for_specific_side.pop();
|
|
}
|
|
}
|
|
|
|
|
|
void manager::remove_all_ais_for_side( side_number side )
|
|
{
|
|
std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
|
|
|
|
//clear the stack. std::stack doesn't have a '.clear()' method to do it
|
|
while (!ai_stack_for_specific_side.empty()){
|
|
ai_stack_for_specific_side.pop();
|
|
}
|
|
}
|
|
|
|
|
|
void manager::clear_ais()
|
|
{
|
|
ai_map_.clear();
|
|
}
|
|
|
|
// =======================================================================
|
|
// Work with active AI parameters
|
|
// =======================================================================
|
|
|
|
void manager::modify_active_ai_config_old_for_side ( side_number side, const config::const_child_itors &ai_parameters )
|
|
{
|
|
get_active_ai_holder_for_side(side).modify_ai_config_old(ai_parameters);
|
|
}
|
|
|
|
|
|
void manager::modify_active_ai_for_side ( side_number side, const config &cfg )
|
|
{
|
|
if (ai_info_==NULL) {
|
|
//replay ?
|
|
return;
|
|
}
|
|
get_active_ai_holder_for_side(side).modify_ai(cfg);
|
|
}
|
|
|
|
|
|
std::string manager::get_active_ai_overview_for_side( side_number side)
|
|
{
|
|
return get_active_ai_holder_for_side(side).get_ai_overview();
|
|
}
|
|
|
|
|
|
std::string manager::get_active_ai_structure_for_side( side_number side)
|
|
{
|
|
return get_active_ai_holder_for_side(side).get_ai_structure();
|
|
}
|
|
|
|
|
|
std::string manager::get_active_ai_identifier_for_side( side_number side )
|
|
{
|
|
return get_active_ai_holder_for_side(side).get_ai_identifier();
|
|
}
|
|
|
|
|
|
config manager::to_config( side_number side )
|
|
{
|
|
return get_active_ai_holder_for_side(side).to_config();
|
|
}
|
|
|
|
|
|
game_info& manager::get_active_ai_info_for_side( side_number /*side*/ )
|
|
{
|
|
return *ai_info_;
|
|
}
|
|
|
|
|
|
game_info& manager::get_ai_info()
|
|
{
|
|
return *ai_info_;
|
|
}
|
|
|
|
|
|
// =======================================================================
|
|
// PROXY
|
|
// =======================================================================
|
|
|
|
void manager::play_turn( side_number side ){
|
|
last_interact_ = 0;
|
|
num_interact_ = 0;
|
|
const int turn_start_time = SDL_GetTicks();
|
|
/*hack. @todo 1.9 rework via extended event system*/
|
|
get_ai_info().recent_attacks.clear();
|
|
interface& ai_obj = get_active_ai_for_side(side);
|
|
game_events::fire("ai turn");
|
|
raise_turn_started();
|
|
ai_obj.new_turn();
|
|
ai_obj.play_turn();
|
|
const int turn_end_time= SDL_GetTicks();
|
|
DBG_AI_MANAGER << "side " << side << ": number of user interactions: "<<num_interact_<<std::endl;
|
|
DBG_AI_MANAGER << "side " << side << ": total turn time: "<<turn_end_time - turn_start_time << " ms "<< std::endl;
|
|
}
|
|
|
|
|
|
// =======================================================================
|
|
// PRIVATE
|
|
// =======================================================================
|
|
// =======================================================================
|
|
// AI STACKS
|
|
// =======================================================================
|
|
std::stack<holder>& manager::get_or_create_ai_stack_for_side( side_number side )
|
|
{
|
|
AI_map_of_stacks::iterator iter = ai_map_.find(side);
|
|
if (iter!=ai_map_.end()){
|
|
return iter->second;
|
|
}
|
|
return ai_map_.insert(std::make_pair(side, std::stack<holder>())).first->second;
|
|
}
|
|
|
|
// =======================================================================
|
|
// AI HOLDERS
|
|
// =======================================================================
|
|
holder& manager::get_active_ai_holder_for_side( side_number side )
|
|
{
|
|
std::stack<holder>& ai_stack_for_specific_side = get_or_create_ai_stack_for_side(side);
|
|
|
|
if (!ai_stack_for_specific_side.empty()){
|
|
return ai_stack_for_specific_side.top();
|
|
} else {
|
|
config cfg = configuration::get_default_ai_parameters();
|
|
holder new_holder(side, cfg);
|
|
ai_stack_for_specific_side.push(new_holder);
|
|
return ai_stack_for_specific_side.top();
|
|
}
|
|
}
|
|
|
|
// =======================================================================
|
|
// AI POINTERS
|
|
// =======================================================================
|
|
|
|
interface& manager::get_active_ai_for_side( side_number side )
|
|
{
|
|
return get_active_ai_holder_for_side(side).get_ai_ref();
|
|
}
|
|
|
|
|
|
// =======================================================================
|
|
// MISC
|
|
// =======================================================================
|
|
|
|
} //end of namespace ai
|