AI Refactoring: reworked event system...

...to allow cheap invalidation and on-demand initialization of move
maps after gamestate changes
This commit is contained in:
Iurii Chernyi 2009-07-01 00:13:28 +00:00
parent b40f504bbc
commit 4df9f65d02
7 changed files with 291 additions and 69 deletions

View File

@ -289,8 +289,8 @@ void attack_result::do_execute()
}
check_victory(get_info().units,get_info().teams, get_info().disp);
manager::raise_enemy_attacked();
set_gamestate_changed();
manager::raise_enemy_attacked();
}
@ -751,10 +751,12 @@ void stopunit_result::do_execute()
if (remove_movement_){
un->second.set_movement(0);
set_gamestate_changed();
manager::raise_unit_moved();
}
if (remove_attacks_){
un->second.set_attacks(0);
set_gamestate_changed();
manager::raise_unit_moved();//to be on the safe side
}
}

View File

@ -89,10 +89,7 @@ void readwrite_context_impl::raise_enemy_attacked() const
attack_result_ptr readwrite_context_impl::execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){
attack_result_ptr r = actions::execute_attack_action(get_side(),true,attacker_loc,defender_loc,attacker_weapon);
recalculate_move_maps();//@todo 1.7 replace with event system
return r;
return actions::execute_attack_action(get_side(),true,attacker_loc,defender_loc,attacker_weapon);
}
@ -102,9 +99,7 @@ attack_result_ptr readonly_context_impl::check_attack_action(const map_location&
move_result_ptr readwrite_context_impl::execute_move_action(const map_location& from, const map_location& to, bool remove_movement){
move_result_ptr r = actions::execute_move_action(get_side(),true,from,to,remove_movement);
recalculate_move_maps();//@todo 1.7 replace with event system
return r;
return actions::execute_move_action(get_side(),true,from,to,remove_movement);
}
@ -114,9 +109,7 @@ move_result_ptr readonly_context_impl::check_move_action(const map_location& fro
recruit_result_ptr readwrite_context_impl::execute_recruit_action(const std::string& unit_name, const map_location &where){
recruit_result_ptr r = actions::execute_recruit_action(get_side(),true,unit_name,where);
recalculate_move_maps();//@todo 1.7 replace with event system
return r;
return actions::execute_recruit_action(get_side(),true,unit_name,where);
}
@ -126,9 +119,7 @@ recruit_result_ptr readonly_context_impl::check_recruit_action(const std::string
stopunit_result_ptr readwrite_context_impl::execute_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){
stopunit_result_ptr r = actions::execute_stopunit_action(get_side(),true,unit_location,remove_movement,remove_attacks);
recalculate_move_maps();//@todo 1.7 replace with event system
return r;
return actions::execute_stopunit_action(get_side(),true,unit_location,remove_movement,remove_attacks);
}
@ -136,6 +127,24 @@ stopunit_result_ptr readonly_context_impl::check_stopunit_action(const map_locat
return actions::execute_stopunit_action(get_side(),false,unit_location,remove_movement,remove_attacks);
}
readonly_context_impl::readonly_context_impl(side_context &context)
: recursion_counter_(context.get_recursion_count()),dstsrc_(),enemy_dstsrc_(),enemy_possible_moves_(),enemy_srcdst_(),possible_moves_(),srcdst_(),avoided_locations_(),move_maps_enemy_valid_(false),move_maps_valid_(false)
{
init_side_context_proxy(context);
manager::add_gamestate_observer(this);
}
readonly_context_impl::~readonly_context_impl()
{
manager::remove_gamestate_observer(this);
}
void readonly_context_impl::handle_generic_event(const std::string& event_name)
{
invalidate_move_maps();
}
bool readwrite_context_impl::recruit(const std::string& unit_name, map_location loc)
{
@ -194,7 +203,6 @@ bool readwrite_context_impl::recruit(const std::string& unit_name, map_location
((data.net_income < 0) ? "" : "+") <<
data.net_income << "\n";
recorder.add_checksum_check(loc);
recalculate_move_maps();
return true;
} else {
const team_data data = calculate_team_data(current_team(),get_side(),get_info().units);
@ -205,7 +213,6 @@ bool readwrite_context_impl::recruit(const std::string& unit_name, map_location
" gold=" << data.gold <<
((data.net_income < 0) ? "" : "+") <<
data.net_income << "\n";
recalculate_move_maps();
return false;
}
}
@ -246,11 +253,12 @@ map_location readwrite_context_impl::move_unit(map_location from, map_location t
if(u->second.movement_left()==u->second.total_movement()) {
u->second.set_movement(0);
u->second.set_state(unit::STATE_NOT_MOVED,true);
raise_unit_moved();
} else if (from == loc) {
u->second.set_movement(0);
raise_unit_moved();
}
}
recalculate_move_maps();
return loc;
}
@ -268,13 +276,11 @@ map_location readwrite_context_impl::move_unit_partial(map_location from, map_lo
if(u_it == get_info().units.end()) {
ERR_AI << "Could not find unit at " << from << '\n';
assert(false);
recalculate_move_maps();
return map_location();
}
if(from == to) {
LOG_AI << "moving unit at " << from << " on spot. resetting moves\n";
recalculate_move_maps();
return to;
}
@ -417,7 +423,6 @@ map_location readwrite_context_impl::move_unit_partial(map_location from, map_lo
// would have to go via mousehandler to make this work:
//get_info().disp.unhighlight_reach();
raise_unit_moved();
recalculate_move_maps();
return to;
}
@ -572,11 +577,10 @@ void readwrite_context_impl::attack_enemy(const map_location u,
check_victory(get_info().units,get_info().teams, get_info().disp);
raise_enemy_attacked();
recalculate_move_maps();
}
const std::set<map_location>& readonly_context_impl::avoided_locations()
const std::set<map_location>& readonly_context_impl::avoided_locations() const
{
if(avoided_locations_.empty()) {
foreach (const config &av, current_team().ai_parameters().child_range("avoid"))
@ -596,57 +600,89 @@ const std::set<map_location>& readonly_context_impl::avoided_locations()
const move_map& readonly_context_impl::get_dstsrc() const
{
if (!move_maps_valid_) {
recalculate_move_maps();
}
return dstsrc_;
}
const move_map& readonly_context_impl::get_enemy_dstsrc() const
{
if (!move_maps_enemy_valid_) {
recalculate_move_maps_enemy();
}
return enemy_dstsrc_;
}
const moves_map& readonly_context_impl::get_enemy_possible_moves() const
{
if (!move_maps_enemy_valid_) {
recalculate_move_maps_enemy();
}
return enemy_possible_moves_;
}
const move_map& readonly_context_impl::get_enemy_srcdst() const
{
if (!move_maps_enemy_valid_) {
recalculate_move_maps_enemy();
}
return enemy_srcdst_;
}
const moves_map& readonly_context_impl::get_possible_moves() const
{
if (!move_maps_valid_) {
recalculate_move_maps();
}
return possible_moves_;
}
const move_map& readonly_context_impl::get_srcdst() const
{
if (!move_maps_valid_) {
recalculate_move_maps();
}
return srcdst_;
}
void readonly_context_impl::invalidate_avoided_locations_cache(){
void readonly_context_impl::invalidate_avoided_locations_cache() const
{
avoided_locations_.clear();
}
void readonly_context_impl::recalculate_move_maps()
void readonly_context_impl::invalidate_move_maps() const
{
move_maps_valid_ = false;
move_maps_enemy_valid_ = false;
}
void readonly_context_impl::recalculate_move_maps() const
{
dstsrc_ = move_map();
possible_moves_ = moves_map();
srcdst_ = move_map();
calculate_possible_moves(possible_moves_,srcdst_,dstsrc_,false,false,&avoided_locations());
move_maps_valid_ = true;
}
void readonly_context_impl::recalculate_move_maps_enemy() const
{
enemy_dstsrc_ = move_map();
enemy_srcdst_ = move_map();
enemy_possible_moves_ = moves_map();
possible_moves_ = moves_map();
srcdst_ = move_map();
calculate_possible_moves(possible_moves_,srcdst_,dstsrc_,false,false,&avoided_locations());
calculate_possible_moves(enemy_possible_moves_,enemy_srcdst_,enemy_dstsrc_,true);
move_maps_enemy_valid_ = true;
}
} //of namespace ai

View File

@ -28,6 +28,7 @@ class gamemap;
#include "game_info.hpp"
#include "../game_display.hpp"
#include "../gamestatus.hpp"
#include "../generic_event.hpp"
#include "../pathfind.hpp"
#include "../playturn.hpp"
@ -155,7 +156,7 @@ public:
virtual void raise_user_interact() const = 0;
virtual const std::set<map_location>& avoided_locations() = 0;
virtual const std::set<map_location>& avoided_locations() const = 0;
virtual const move_map& get_dstsrc() const = 0;
@ -176,10 +177,16 @@ public:
virtual const move_map& get_srcdst() const = 0;
virtual void invalidate_avoided_locations_cache() = 0;
virtual void invalidate_avoided_locations_cache() const = 0;
virtual void recalculate_move_maps() = 0;
virtual void invalidate_move_maps() const = 0;
virtual void recalculate_move_maps() const = 0;
virtual void recalculate_move_maps_enemy() const = 0;
};
@ -204,15 +211,6 @@ public:
virtual game_info& get_info_w() = 0;
};
class ai_default_context;
class ai_default_context : public virtual readwrite_context {
public:
ai_default_context(){}
virtual ~ai_default_context(){}
virtual ai_default_context& get_ai_default_context() = 0;
};
//proxies
class side_context_proxy : public virtual side_context {
@ -337,7 +335,7 @@ public:
}
virtual const std::set<map_location>& avoided_locations()
virtual const std::set<map_location>& avoided_locations() const
{
return target_->avoided_locations();
}
@ -385,17 +383,29 @@ public:
}
virtual void invalidate_avoided_locations_cache()
virtual void invalidate_avoided_locations_cache() const
{
target_->invalidate_avoided_locations_cache();
}
virtual void recalculate_move_maps()
virtual void invalidate_move_maps() const
{
target_->invalidate_move_maps();
}
virtual void recalculate_move_maps() const
{
target_->recalculate_move_maps();
}
virtual void recalculate_move_maps_enemy() const
{
target_->recalculate_move_maps_enemy();
}
private:
readonly_context *target_;
};
@ -545,23 +555,19 @@ private:
//@todo: public game_logic::formula_callable
class readonly_context_impl : public virtual side_context_proxy, public readonly_context {
class readonly_context_impl : public virtual side_context_proxy, public readonly_context, public events::observer {
public:
/**
* Constructor
*/
readonly_context_impl(side_context &context)
: recursion_counter_(context.get_recursion_count()),dstsrc_(),enemy_dstsrc_(),enemy_possible_moves_(),enemy_srcdst_(),possible_moves_(),srcdst_(),avoided_locations_()
{
init_side_context_proxy(context);
}
readonly_context_impl(side_context &context);
/**
* Destructor
*/
virtual ~readonly_context_impl() {}
virtual ~readonly_context_impl();
/**
@ -574,6 +580,10 @@ public:
}
/** Handle generic event */
virtual void handle_generic_event(const std::string& event_name);
/** Return a reference to the 'team' object for the AI. */
const team& current_team() const { return get_info().teams[get_side()-1]; }
@ -689,7 +699,7 @@ public:
void raise_user_interact() const;
virtual const std::set<map_location>& avoided_locations();
virtual const std::set<map_location>& avoided_locations() const;
virtual int get_recursion_count() const;
@ -713,21 +723,28 @@ public:
virtual const move_map& get_srcdst() const;
virtual void invalidate_avoided_locations_cache();
virtual void invalidate_avoided_locations_cache() const;
virtual void recalculate_move_maps();
virtual void invalidate_move_maps() const;
virtual void recalculate_move_maps() const;
virtual void recalculate_move_maps_enemy() const;
private:
recursion_counter recursion_counter_;
move_map dstsrc_;
move_map enemy_dstsrc_;
moves_map enemy_possible_moves_;
move_map enemy_srcdst_;
moves_map possible_moves_;
move_map srcdst_;
std::set<map_location> avoided_locations_;
mutable move_map dstsrc_;
mutable move_map enemy_dstsrc_;
mutable moves_map enemy_possible_moves_;
mutable move_map enemy_srcdst_;
mutable moves_map possible_moves_;
mutable move_map srcdst_;
mutable std::set<map_location> avoided_locations_;
mutable bool move_maps_enemy_valid_;
mutable bool move_maps_valid_;
};
@ -863,7 +880,9 @@ public:
}
virtual ~readwrite_context_impl() {}
virtual ~readwrite_context_impl()
{
}
/**
* Functions to retrieve the 'info' object.

View File

@ -279,6 +279,7 @@ events::generic_event manager::user_interact_("ai_user_interact");
events::generic_event manager::unit_recruited_("ai_unit_recruited");
events::generic_event manager::unit_moved_("ai_unit_moved");
events::generic_event manager::enemy_attacked_("ai_enemy_attacked");
events::generic_event manager::turn_started_("ai_turn_started");
int manager::last_interact_ = 0;
int manager::num_interact_ = 0;
@ -300,18 +301,98 @@ void manager::clear_ai_info(){
}
}
void manager::add_observer( events::observer* event_observer){
user_interact_.attach_handler(event_observer);
unit_recruited_.attach_handler(event_observer);
unit_moved_.attach_handler(event_observer);
enemy_attacked_.attach_handler(event_observer);
turn_started_.attach_handler(event_observer);
}
void manager::remove_observer(events::observer* event_observer){
user_interact_.detach_handler(event_observer);
unit_recruited_.detach_handler(event_observer);
unit_moved_.detach_handler(event_observer);
enemy_attacked_.detach_handler(event_observer);
turn_started_.detach_handler(event_observer);
}
void manager::add_gamestate_observer( events::observer* event_observer){
unit_recruited_.attach_handler(event_observer);
unit_moved_.attach_handler(event_observer);
enemy_attacked_.attach_handler(event_observer);
turn_started_.attach_handler(event_observer);
}
void manager::remove_gamestate_observer(events::observer* event_observer){
unit_recruited_.detach_handler(event_observer);
unit_moved_.detach_handler(event_observer);
enemy_attacked_.detach_handler(event_observer);
turn_started_.detach_handler(event_observer);
}
void manager::add_user_interact_observer( events::observer* event_observer )
{
user_interact_.attach_handler(event_observer);
}
void manager::add_unit_recruited_observer( events::observer* event_observer )
{
unit_recruited_.attach_handler(event_observer);
}
void manager::add_unit_moved_observer( events::observer* event_observer )
{
unit_moved_.attach_handler(event_observer);
}
void manager::add_enemy_attacked_observer( events::observer* event_observer )
{
enemy_attacked_.attach_handler(event_observer);
}
void manager::add_turn_started_observer( events::observer* event_observer )
{
turn_started_.attach_handler(event_observer);
}
void manager::delete_user_interact_observer( events::observer* event_observer )
{
user_interact_.detach_handler(event_observer);
}
void manager::delete_unit_recruited_observer( events::observer* event_observer )
{
unit_recruited_.detach_handler(event_observer);
}
void manager::delete_unit_moved_observer( events::observer* event_observer )
{
unit_moved_.detach_handler(event_observer);
}
void manager::delete_enemy_attacked_observer( events::observer* event_observer )
{
enemy_attacked_.detach_handler(event_observer);
}
void manager::delete_turn_started_observer( events::observer* event_observer )
{
turn_started_.detach_handler(event_observer);
}
void manager::raise_user_interact() {
@ -341,6 +422,11 @@ void manager::raise_enemy_attacked() {
}
void manager::raise_turn_started() {
turn_started_.notify_observers();
}
// =======================================================================
// EVALUATION
// =======================================================================
@ -548,6 +634,7 @@ bool manager::add_ai_for_side( int side, const std::string& ai_algorithm_type, b
}
//@todo 1.7 refactor away from ai::manager
ai_ptr manager::create_transient_ai( const std::string &ai_algorithm_type, default_ai_context *ai_context )
{
assert(ai_context!=NULL);
@ -678,11 +765,12 @@ void manager::set_active_ai_algorithm_type_for_side( int side, const std::string
// PROXY
// =======================================================================
void manager::play_turn( int side, events::observer* /*event_observer*/ ){
void manager::play_turn( side_number side ){
last_interact_ = 0;
num_interact_ = 0;
const int turn_start_time = SDL_GetTicks();
interface& ai_obj = get_active_ai_for_side(side);
raise_turn_started();
ai_obj.new_turn();
ai_obj.play_turn();
const int turn_end_time= SDL_GetTicks();

View File

@ -178,6 +178,18 @@ public:
static void remove_observer( events::observer* event_observer );
/**
* Adds observer of game events except user interact event
*/
static void add_gamestate_observer( events::observer* event_observer);
/**
* Removes an observer of game events except user interact event
*/
static void remove_gamestate_observer( events::observer* event_observer );
/**
* Notifies all observers of 'user interact' event.
* Function which should be called frequently to allow the user to interact
@ -201,10 +213,77 @@ public:
/**
* Notifies all observers of 'enemy attack' event.
* Notifies all observers of 'enemy attacked' event.
*/
static void raise_enemy_attacked();
/**
* Notifies all observers of 'turn started' event.
*/
static void raise_turn_started();
/**
* Adds an observer of 'user interact' event.
*/
static void add_user_interact_observer( events::observer* event_observer );
/**
* Adds an observer of 'unit recruited' event.
*/
static void add_unit_recruited_observer( events::observer* event_observer );
/**
* Adds an observer of 'unit moved' event.
*/
static void add_unit_moved_observer( events::observer* event_observer );
/**
* Adds an observers of 'enemy attacked' event.
*/
static void add_enemy_attacked_observer( events::observer* event_observer );
/**
* Adds an observer of 'turn started' event.
*/
static void add_turn_started_observer( events::observer* event_observer );
/**
* Deletes an observer of 'user interact' event.
*/
static void delete_user_interact_observer( events::observer* event_observer );
/**
* Deletes an observer of 'unit recruited' event.
*/
static void delete_unit_recruited_observer( events::observer* event_observer );
/**
* Deletes an observer of 'unit moved' event.
*/
static void delete_unit_moved_observer( events::observer* event_observer );
/**
* Deletes an observer of 'enemy attacked' event.
*/
static void delete_enemy_attacked_observer( events::observer* event_observer );
/**
* Deletes an observer of 'turn started' event.
*/
static void delete_turn_started_observer( events::observer* event_observer );
protected:
manager();
@ -452,9 +531,8 @@ public:
/**
* Plays a turn for the specified side using its active AI.
* @param side side number (1-based, as in game_info).
* @param event_observer controller which will observe events produced by the AI.
*/
static void play_turn(int side, events::observer* event_observer);
static void play_turn(side_number side);
private:
@ -469,6 +547,7 @@ private:
static events::generic_event unit_recruited_;
static events::generic_event unit_moved_;
static events::generic_event enemy_attacked_;
static events::generic_event turn_started_;
static int last_interact_;
static int num_interact_;

View File

@ -732,7 +732,6 @@ bool move_leader_to_keep_phase::execute()
{
bool gamestate_changed = false;
move_->execute();
recalculate_move_maps();//@todo 1.7: replace with event observers
if (!move_->is_ok()){
LOG_AI_TESTING_AI_DEFAULT << get_name() <<"::execute not ok" << std::endl;
}
@ -1718,7 +1717,6 @@ bool simple_move_and_targeting_phase::execute()
{
bool gamestate_changed = false;
move_->execute();
recalculate_move_maps();//@todo 1.7: replace with event observers
if (!move_->is_ok()){
LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
}

View File

@ -856,7 +856,7 @@ void playsingle_controller::play_ai_turn(){
map_, teams_, player_number_, units_, replay_sender_, undo_stack_, *this);
try {
ai::manager::play_turn(player_number_, this);
ai::manager::play_turn(player_number_);
} catch (end_turn_exception) {
}
recorder.end_turn();