AI refactoring: use a factory lookup pattern to create AIs

This commit is contained in:
Iurii Chernyi 2009-06-04 21:25:36 +00:00
parent ee886864df
commit 2fd9dddd79
10 changed files with 97 additions and 105 deletions

View file

@ -247,9 +247,6 @@ ai_default::ai_default(ai::default_ai_context &context) :
}
ai_default::~ai_default(){
if (formula_ai_!=NULL) {
delete formula_ai_;
}
}
void ai_default::switch_side(ai::side_number side){
@ -1482,11 +1479,12 @@ bool ai_default::do_recruitment()
if (get_recursion_count()<ai::recursion_counter::MAX_COUNTER_VALUE)
{
if (!current_team().ai_parameters()["recruitment"].empty()){
if (formula_ai_ == NULL){
formula_ai_ = static_cast<formula_ai*>(ai::manager::create_transient_ai(ai::manager::AI_TYPE_FORMULA_AI, this));
if (!formula_ai_){
formula_ai_ptr_ = (ai::manager::create_transient_ai(ai::manager::AI_TYPE_FORMULA_AI, this));
formula_ai_ = static_cast<formula_ai*> (formula_ai_ptr_.get());
}
assert(formula_ai_ != NULL);
assert(formula_ai_!=NULL);
if (formula_ai_->do_recruitment()) {
LOG_AI << "Recruitment done by formula_ai\n";

View file

@ -96,6 +96,7 @@ public:
int get_recursion_count() const;
private:
ai::recursion_counter recursion_counter_;
protected:
std::map<location,defensive_position> defensive_position_cache_;
@ -409,7 +410,8 @@ private:
int recruiting_preferred_;
static const int min_recruiting_value_to_force_recruit = 28;
protected:
formula_ai* formula_ai_;
formula_ai *formula_ai_;
ai::ai_ptr formula_ai_ptr_;
};
#endif

View file

@ -20,9 +20,14 @@
#ifndef AI_AI_INTERFACE_HPP_INCLUDED
#define AI_AI_INTERFACE_HPP_INCLUDED
#include "../global.hpp"
#include "../formula_callable.hpp"
#include "game_info.hpp"
#include "default/contexts.hpp"
namespace ai {
class interface {
@ -65,6 +70,50 @@ public:
};
class ai_factory;
class ai_factory{
public:
typedef boost::shared_ptr< ai_factory > factory_ptr;
typedef std::map<std::string, factory_ptr> factory_map;
typedef std::pair<const std::string, factory_ptr> factory_map_pair;
static factory_map& get_list() {
static factory_map *ai_factories;
if (ai_factories==NULL) {
ai_factories = new factory_map;
}
return *ai_factories;
}
/* cfg is commented out so far, because ai parameter handling is a mess atm */
virtual ai_ptr get_new_instance( default_ai_context &context/*, const config &cfg*/) = 0;
ai_factory( const std::string &name )
{
factory_ptr ptr_to_this(this);
get_list().insert(make_pair(name,ptr_to_this));
}
};
template<class AI>
class register_ai_factory : public ai_factory {
public:
register_ai_factory( const std::string &name )
: ai_factory( name )
{
}
virtual ai_ptr get_new_instance( default_ai_context &context/*, const config &cfg*/){
ai_ptr a(new AI(context));
a->on_create();
return a;
}
};
} //end of namespace ai
#endif

View file

@ -54,7 +54,7 @@ static lg::log_domain log_ai_manager("ai/manager");
#define ERR_AI_MANAGER LOG_STREAM(err, log_ai_manager)
holder::holder( int side, const std::string& ai_algorithm_type )
: ai_(NULL), side_context_(NULL), readonly_context_(NULL), readwrite_context_(NULL), default_ai_context_(NULL), ai_algorithm_type_(ai_algorithm_type), ai_effective_parameters_(), ai_global_parameters_(), ai_memory_(), ai_parameters_(), side_(side)
: ai_(), side_context_(NULL), readonly_context_(NULL), readwrite_context_(NULL), default_ai_context_(NULL), ai_algorithm_type_(ai_algorithm_type), ai_effective_parameters_(), ai_global_parameters_(), ai_memory_(), ai_parameters_(), side_(side)
{
DBG_AI_MANAGER << describe_ai() << "Preparing new AI holder" << std::endl;
}
@ -78,7 +78,7 @@ void holder::init( int side )
default_ai_context_ = new default_ai_context_impl(*readwrite_context_);
}
this->ai_ = create_ai(side);
if (this->ai_ == NULL) {
if (!this->ai_) {
ERR_AI_MANAGER << describe_ai()<<"AI lazy initialization error!" << std::endl;
}
@ -87,10 +87,9 @@ void holder::init( int side )
holder::~holder()
{
if (this->ai_ != NULL) {
if (this->ai_) {
LOG_AI_MANAGER << describe_ai() << "Managed AI will be deleted" << std::endl;
}
delete this->ai_;
delete this->default_ai_context_;
delete this->readwrite_context_;
delete this->readonly_context_;
@ -100,10 +99,10 @@ holder::~holder()
interface& holder::get_ai_ref( int side )
{
if (this->ai_ == NULL) {
if (!this->ai_) {
this->init(side);
}
assert(this->ai_ != NULL);
assert(this->ai_);
return *this->ai_;
}
@ -202,7 +201,7 @@ bool holder::is_mandate_ok()
return true;
}
interface* holder::create_ai( int side )
ai_ptr holder::create_ai( int side )
{
assert (side > 0);
assert (default_ai_context_!=NULL);
@ -549,76 +548,21 @@ bool manager::add_ai_for_side( int side, const std::string& ai_algorithm_type, b
}
interface* manager::create_transient_ai( const std::string &ai_algorithm_type, default_ai_context *ai_context )
ai_ptr manager::create_transient_ai( const std::string &ai_algorithm_type, default_ai_context *ai_context )
{
assert(ai_context!=NULL);
//@todo 1.7 modify this code to use a 'factory lookup' pattern -
//a singleton which holds a map<string,ai_factory_ptr> of all functors which can create AIs.
//this will allow individual AI implementations to 'register' themselves.
//: To add an AI of your own, put
// if(ai_algorithm_type == "my_ai") {
// LOG_AI_MANAGER << "Creating new AI of type [" << "my_ai" << "]"<< std::endl;
// interface *a = new my_ai(*ai_context);
// a->on_create();
// return a;
// }
// at the top of this function
//if(ai_algorithm_type == manager::AI_TYPE_SAMPLE_AI) {
// LOG_AI_MANAGER << "Creating new AI of type [" << manager::AI_TYPE_IDLE_AI << "]"<< std::endl;
// interface *a = new sample_ai(*ai_context);
// a->on_create();
// return a;
//}
if(ai_algorithm_type == manager::AI_TYPE_IDLE_AI) {
LOG_AI_MANAGER << "Creating new AI of type [" << manager::AI_TYPE_IDLE_AI << "]"<< std::endl;
interface *a = new idle_ai(*ai_context);
a->on_create();
return a;
//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!");
}
}
if(ai_algorithm_type == manager::AI_TYPE_FORMULA_AI) {
LOG_AI_MANAGER << "Creating new AI of type [" << manager::AI_TYPE_FORMULA_AI << "]"<< std::endl;
interface *a = new formula_ai(*ai_context);
a->on_create();
return a;
}
if(ai_algorithm_type == manager::AI_TYPE_DFOOL_AI) {
LOG_AI_MANAGER << "Creating new AI of type [" << manager::AI_TYPE_DFOOL_AI << "]"<< std::endl;
interface *a = new dfool::dfool_ai(*ai_context);
a->on_create();
return a;
}
if(ai_algorithm_type == manager::AI_TYPE_AI2) {
LOG_AI_MANAGER << "Creating new AI of type [" << manager::AI_TYPE_AI2 << "]"<< std::endl;
interface *a = new ai2(*ai_context);
a->on_create();
return a;
}
if(ai_algorithm_type == manager::AI_TYPE_COMPOSITE_AI) {
LOG_AI_MANAGER << "Creating new AI of type [" << manager::AI_TYPE_COMPOSITE_AI << "]"<< std::endl;
interface *a = new composite_ai::ai_composite(*ai_context);
a->on_create();
return a;
}
if (!ai_algorithm_type.empty() && ai_algorithm_type != manager::AI_TYPE_DEFAULT) {
ERR_AI_MANAGER << "AI not found: [" << ai_algorithm_type << "]. Using default instead.\n";
}
LOG_AI_MANAGER << "Creating new AI of type [" << manager::AI_TYPE_DEFAULT << "]"<< std::endl;
interface *a = new ai_default(*ai_context);
a->on_create();
return a;
LOG_AI_MANAGER << "Creating new AI of type [" << ai_algorithm_type << "]"<< std::endl;
ai_ptr new_ai = aii->second->get_new_instance(*ai_context);
return new_ai;
}
std::vector<std::string> manager::get_available_ais()

View file

@ -79,7 +79,7 @@ public:
bool is_mandate_ok();
private:
interface *ai_;
ai_ptr ai_;
side_context *side_context_;
readonly_context *readonly_context_;
readwrite_context *readwrite_context_;
@ -91,7 +91,7 @@ private:
std::vector<config> ai_parameters_;
int side_;
interface* create_ai( int side );
ai_ptr create_ai( int side );
};
/**
@ -270,13 +270,12 @@ public:
/**
* Returns a pointer to a new AI. It is the sole responsibility of the caller
* to manage its lifetime.
* Returns a smart pointer to a new AI.
* @param ai_algorithm_type type of AI algorithm to create
* @param context context in which this ai is created
* @return the reference to the created AI
*/
static interface* create_transient_ai( const std::string& ai_algorithm_type, default_ai_context *ai_context);
static ai_ptr create_transient_ai( const std::string& ai_algorithm_type, default_ai_context *ai_context);
/**

View file

@ -33,11 +33,15 @@ class gamemap;
namespace ai {
class interface;
typedef boost::shared_ptr< interface > ai_ptr;
// recursion counter
class recursion_counter {
public:
recursion_counter(int counter)
: counter_(counter++)
: counter_(++counter)
{
if (counter > MAX_COUNTER_VALUE ) {
throw game::game_error("maximum recursion depth reached!");

View file

@ -1857,8 +1857,8 @@ bool formula_ai::make_action(game_logic::const_formula_ptr formula_, const game_
if(!formula_) {
if(get_recursion_count()<ai::recursion_counter::MAX_COUNTER_VALUE) {
LOG_AI << "Falling back to default AI.\n";
util::scoped_ptr< ai::interface > fallback( ai::manager::create_transient_ai(ai::manager::AI_TYPE_DEFAULT, this));
if (fallback != NULL){
ai::ai_ptr fallback( ai::manager::create_transient_ai(ai::manager::AI_TYPE_DEFAULT, this));
if (fallback){
fallback->play_turn();
}
}
@ -2182,8 +2182,8 @@ bool formula_ai::execute_variant(const variant& var, bool commandline)
} else
{
LOG_AI << "Explicit fallback to: " << fallback_command->key() << std::endl;
util::scoped_ptr< ai::interface > fallback ( ai::manager::create_transient_ai(fallback_command->key(), this));
if(fallback != NULL) {
ai::ai_ptr fallback( ai::manager::create_transient_ai(fallback_command->key(), this));
if(fallback) {
fallback->play_turn();
}
}

View file

@ -32,14 +32,14 @@ namespace ai {
// =======================================================================
// AIs
// =======================================================================
/*
static ai_factory<ai> ai_factory("");
static ai_factory<ai> default_ai_ai_factory("default_ai");
static ai_factory<ai2> ai2_ai_factory("ai2");
static ai_factory<dfool_ai> dfool_ai_ai_factory("dfool_ai");
static ai_factory<formula_ai> formula_ai_ai_factory("formula_ai");
static ai_factory<composite_ai> composite_ai_ai_factory("composite_ai");
*/
static register_ai_factory<ai_default> ai_factory_default("");
static register_ai_factory<ai_default> ai_default_ai_factory("default_ai");
static register_ai_factory<ai2> ai2_ai_factory("ai2");
static register_ai_factory<dfool::dfool_ai> ai_dfool_ai_factory("dfool_ai");
static register_ai_factory<formula_ai> ai_formula_ai_factory("formula_ai");
static register_ai_factory<composite_ai::ai_composite> ai_composite_ai_factory("composite_ai");
// =======================================================================
// Engines

View file

@ -32,7 +32,7 @@ static lg::log_domain log_ai_testing_stage_fallback("ai/testing/stage_fallback")
#define ERR_AI_TESTING_STAGE_FALLBACK LOG_STREAM(err, log_ai_testing_stage_fallback)
fallback_to_other_ai::fallback_to_other_ai( ai::composite_ai::composite_ai_context &context, const config &cfg )
: stage(context,cfg), cfg_(cfg), fallback_ai_(NULL)
: stage(context,cfg), cfg_(cfg), fallback_ai_()
{
}
@ -53,9 +53,6 @@ void fallback_to_other_ai::do_play_stage()
fallback_to_other_ai::~fallback_to_other_ai()
{
if (fallback_ai_!=NULL){
delete fallback_ai_;
}
}
} // of namespace testing_ai_default

View file

@ -43,10 +43,9 @@ public:
private:
const config &cfg_;
ai::interface *fallback_ai_;
ai::ai_ptr fallback_ai_;
};
} // of namespace testing_ai_default
} // end of namespace testing_ai_default
#endif