AI refactoring: split ai_interface into three parts:

1) ai_interface - interface between the ai and the game

2) ai_game_info - information about the game for the AI (former
ai_interface::info)

3) ai_readwrite_context - interface between the ai
and ai components. 

Removed ai_interface code from ai/ai.cpp (this removed ~20% from
ai/ai.cpp, and, accidentally, left ai/ai.cpp exactly 2000 lines long)
This commit is contained in:
Iurii Chernyi 2009-04-30 23:33:56 +00:00
parent 74e1e20612
commit 75f6779ee8
19 changed files with 1040 additions and 797 deletions

View file

@ -2757,6 +2757,10 @@
RelativePath="..\..\src\ai\ai_village.cpp"
>
</File>
<File
RelativePath="..\..\src\ai\contexts.cpp"
>
</File>
<File
RelativePath="..\..\src\ai\formula_ai.cpp"
>
@ -2765,6 +2769,10 @@
RelativePath="..\..\src\ai\formula_candidates.cpp"
>
</File>
<File
RelativePath="..\..\src\ai\game_info.cpp"
>
</File>
<File
RelativePath="..\..\src\ai\testing.cpp"
>
@ -3560,6 +3568,10 @@
RelativePath="..\..\src\ai\ai_manager.hpp"
>
</File>
<File
RelativePath="..\..\src\ai\contexts.hpp"
>
</File>
<File
RelativePath="..\..\src\ai\formula_ai.hpp"
>
@ -3568,6 +3580,10 @@
RelativePath="..\..\src\ai\formula_candidates.hpp"
>
</File>
<File
RelativePath="..\..\src\ai\game_info.hpp"
>
</File>
<File
RelativePath="..\..\src\ai\testing.hpp"
>

View file

@ -221,6 +221,10 @@ SET(wesnoth-main_SRC
ai/ai_manager.cpp
ai/ai_move.cpp
ai/ai_village.cpp
ai/contexts.cpp
ai/formula_ai.cpp
ai/formula_candidates.cpp
ai/game_info.cpp
ai/testing.cpp
animated_game.cpp
attack_prediction.cpp
@ -232,8 +236,6 @@ SET(wesnoth-main_SRC
dialogs.cpp
floating_textbox.cpp
formula.cpp
ai/formula_ai.cpp
ai/formula_candidates.cpp
formula_function.cpp
formula_tokenizer.cpp
formula_string_utils.cpp

View file

@ -51,6 +51,10 @@ wesnoth_source = \
ai/ai_manager.cpp \
ai/ai_move.cpp \
ai/ai_village.cpp \
ai/contexts.cpp \
ai/formula_ai.cpp \
ai/formula_candidates.cpp \
ai/game_info.cpp \
ai/testing.cpp \
animated_game.cpp \
attack_prediction.cpp \
@ -62,8 +66,6 @@ wesnoth_source = \
dialogs.cpp \
floating_textbox.cpp \
formula.cpp \
ai/formula_ai.cpp \
ai/formula_candidates.cpp \
formula_function.cpp \
formula_tokenizer.cpp \
formula_string_utils.cpp \

View file

@ -156,6 +156,10 @@ wesnoth_sources = Split("""
ai/ai_manager.cpp
ai/ai_move.cpp
ai/ai_village.cpp
ai/contexts.cpp
ai/formula_ai.cpp
ai/formula_candidates.cpp
ai/game_info.cpp
ai/testing.cpp
animated_game.cpp
attack_prediction.cpp
@ -167,8 +171,6 @@ wesnoth_sources = Split("""
dialogs.cpp
floating_textbox.cpp
formula.cpp
ai/formula_ai.cpp
ai/formula_candidates.cpp
formula_function.cpp
formula_tokenizer.cpp
formula_string_utils.cpp

View file

@ -46,8 +46,8 @@ static lg::log_domain log_ai("ai/general");
typedef util::array<map_location,6> adjacent_tiles_array;
idle_ai::idle_ai(int side, bool master) : ai_interface(side,master) {
idle_ai::idle_ai(int side, bool master) : ai_interface(side,master)
{
}
std::string idle_ai::describe_self(){
@ -321,288 +321,6 @@ bool ai::recruit_usage(const std::string& usage)
return false;
}
bool ai_interface::recruit(const std::string& unit_name, location loc)
{
const std::set<std::string>& recruits = current_team().recruits();
const std::set<std::string>::const_iterator i = recruits.find(unit_name);
if(i == recruits.end()) {
return false;
}
const int num = std::distance(recruits.begin(),i);
// We have to add the recruit command now, because when the unit
// is created it has to have the recruit command in the recorder
// to be able to put random numbers into to generate unit traits.
// However, we're not sure if the transaction will be successful,
// so use a replay_undo object to cancel it if we don't get
// a confirmation for the transaction.
recorder.add_recruit(num,loc);
replay_undo replay_guard(recorder);
unit_type_data::unit_type_map::const_iterator u = unit_type_data::types().find_unit_type(unit_name);
if(u == unit_type_data::types().end() || u->first == "dummy_unit") {
return false;
}
// Check if we have enough money
if(current_team().gold() < u->second.cost()) {
return false;
}
LOG_AI << "trying recruit: team=" << (get_side()) <<
" type=" << unit_name <<
" cost=" << u->second.cost() <<
" loc=(" << loc << ')' <<
" gold=" << (current_team().gold()) <<
" (-> " << (current_team().gold()-u->second.cost()) << ")\n";
unit new_unit(&get_info().units,&get_info().map,&get_info().state,&get_info().teams,&u->second,get_side(),true);
// See if we can actually recruit (i.e. have enough room etc.)
std::string recruit_err = recruit_unit(get_info().map,get_side(),get_info().units,new_unit,loc,false,preferences::show_ai_moves());
if(recruit_err.empty()) {
statistics::recruit_unit(new_unit);
current_team().spend_gold(u->second.cost());
// Confirm the transaction - i.e. don't undo recruitment
replay_guard.confirm_transaction();
raise_unit_recruited();
const team_data data = calculate_team_data(current_team(),get_side(),get_info().units);
LOG_AI <<
"recruit confirmed: team=" << get_side() <<
" units=" << data.units <<
" gold=" << data.gold <<
((data.net_income < 0) ? "" : "+") <<
data.net_income << "\n";
recorder.add_checksum_check(loc);
return true;
} else {
const team_data data = calculate_team_data(current_team(),get_side(),get_info().units);
LOG_AI << recruit_err << "\n";
LOG_AI <<
"recruit UNconfirmed: team=" << (get_side()) <<
" units=" << data.units <<
" gold=" << data.gold <<
((data.net_income < 0) ? "" : "+") <<
data.net_income << "\n";
return false;
}
}
ai_interface::info& ai_interface::get_info(){
return ai_manager::get_active_ai_info_for_side(get_side());
}
const ai_interface::info& ai_interface::get_info() const{
return ai_manager::get_active_ai_info_for_side(get_side());
}
void ai_interface::diagnostic(const std::string& msg)
{
if(game_config::debug) {
get_info().disp.set_diagnostic(msg);
}
}
void ai_interface::log_message(const std::string& msg)
{
if(game_config::debug) {
get_info().disp.add_chat_message(time(NULL), "ai", get_side(), msg,
game_display::MESSAGE_PUBLIC, false);
}
}
map_location ai_interface::move_unit(location from, location to,
std::map<location,paths>& possible_moves)
{
const location loc = move_unit_partial(from,to,possible_moves);
const unit_map::iterator u = get_info().units.find(loc);
if(u != get_info().units.end()) {
if(u->second.movement_left()==u->second.total_movement()) {
u->second.set_movement(0);
u->second.set_state("not_moved","yes");
} else if (from == loc) {
u->second.set_movement(0);
}
}
return loc;
}
map_location ai_interface::move_unit_partial(location from, location to,
std::map<location,paths>& possible_moves)
{
LOG_AI << "ai_interface::move_unit " << from << " -> " << to << '\n';
assert(to.valid() && to.x <= MAX_MAP_AREA && to.y <= MAX_MAP_AREA);
// Stop the user from issuing any commands while the unit is moving.
const events::command_disabler disable_commands;
log_scope2(log_ai, "move_unit");
unit_map::iterator u_it = get_info().units.find(from);
if(u_it == get_info().units.end()) {
ERR_AI << "Could not find unit at " << from << '\n';
assert(false);
return location();
}
if(from == to) {
LOG_AI << "moving unit at " << from << " on spot. resetting moves\n";
return to;
}
const bool show_move = preferences::show_ai_moves();
const std::map<location,paths>::iterator p_it = possible_moves.find(from);
std::vector<location> steps;
if(p_it != possible_moves.end()) {
paths& p = p_it->second;
std::map<location,paths::route>::iterator rt = p.routes.begin();
for(; rt != p.routes.end(); ++rt) {
if(rt->first == to) {
break;
}
}
if(rt != p.routes.end()) {
if (static_cast<size_t>(u_it->second.movement_left()) >= rt->second.steps.size()) {
LOG_AI<<"Trying to move unit without enough move points left\n";
}
u_it->second.set_movement(rt->second.move_left);
steps = rt->second.steps;
while(steps.empty() == false && get_info().units.find(to) != get_info().units.end() && from != to){
LOG_AI << "AI attempting illegal move. Attempting to move onto existing unit\n";
LOG_AI << "\t" << get_info().units.find(to)->second.underlying_id() <<" already on " << to << "\n";
LOG_AI <<"\tremoving "<<*(steps.end()-1)<<"\n";
to = *(steps.end()-1);
steps.pop_back();
LOG_AI << "\tresetting to " << from << " -> " << to << '\n';
}
if(steps.size()) { // First step is starting hex
unit_map::const_iterator utest=get_info().units.find(*(steps.begin()));
if(utest != get_info().units.end() && current_team().is_enemy(utest->second.side())){
ERR_AI << "AI tried to move onto existing enemy unit at" << *steps.begin() << '\n';
// return(from);
}
// Check if there are any invisible units that we uncover
for(std::vector<location>::iterator i = steps.begin()+1; i != steps.end(); ++i) {
location adj[6];
get_adjacent_tiles(*i,adj);
size_t n;
for(n = 0; n != 6; ++n) {
// See if there is an enemy unit next to this tile.
// If it's invisible, we need to stop: we're ambushed.
// If it's not, we must be a skirmisher, otherwise AI wouldn't try.
// Or would it? If it doesn't cheat, it might...
const unit_map::const_iterator u = get_info().units.find(adj[n]);
// If level 0 is invisible it ambush us too
if (u != get_info().units.end() && (u->second.emits_zoc() || u->second.invisible(adj[n], get_info().units, get_info().teams))
&& current_team().is_enemy(u->second.side())) {
if (u->second.invisible(adj[n], get_info().units, get_info().teams)) {
to = *i;
u->second.ambush();
steps.erase(i,steps.end());
break;
} else {
if (!u_it->second.get_ability_bool("skirmisher",*i)){
ERR_AI << "AI tried to skirmish with non-skirmisher\n";
LOG_AI << "\tresetting destination from " <<to;
to = *i;
LOG_AI << " to " << to;
steps.erase(i,steps.end());
while(steps.empty() == false && (!(get_info().units.find(to) == get_info().units.end() || from == to))){
to = *(steps.end()-1);
steps.pop_back();
LOG_AI << "\tresetting to " << from << " -> " << to << '\n';
}
break;
}
}
}
}
if(n != 6) {
u_it->second.set_movement(0); // Enter enemy ZoC, no movement left
break;
}
}
}
if(steps.empty() || steps.back() != to) {
//Add the destination to the end of the steps if it's not
//already there.
steps.push_back(to);
}
if(show_move && unit_display::unit_visible_on_path(steps,
u_it->second, get_info().units,get_info().teams)) {
get_info().disp.display_unit_hex(from);
unit_map::iterator up = get_info().units.find(u_it->first);
unit_display::move_unit(steps,up->second,get_info().teams);
} else if(steps.size()>1) {
unit_map::iterator up = get_info().units.find(u_it->first);
std::vector<map_location>::const_reverse_iterator last_step = steps.rbegin();
std::vector<map_location>::const_reverse_iterator before_last = last_step +1;
up->second.set_facing(before_last->get_relative_dir(*last_step));
}
}
}
//FIXME: probably missing some "else" here
// It looks like if the AI doesn't find a route in possible_move,
// she will just teleport her unit between 'from' and 'to'
// I suppose this never happen, but in the meantime, add code for replay
if (steps.empty()) {
steps.push_back(from);
steps.push_back(to);
}
std::pair<map_location,unit> *p = get_info().units.extract(u_it->first);
p->first = to;
get_info().units.insert(p);
p->second.set_standing(p->first);
if(get_info().map.is_village(to)) {
// If a new village is captured, disallow any future movement.
if (!current_team().owns_village(to))
get_info().units.find(to)->second.set_movement(-1);
get_village(to,get_info().disp,get_info().teams,get_side()-1,get_info().units);
}
if(show_move) {
get_info().disp.invalidate(to);
get_info().disp.draw();
}
recorder.add_movement(steps);
game_events::fire("moveto",to,from);
if((get_info().teams.front().uses_fog() || get_info().teams.front().uses_shroud()) &&
!get_info().teams.front().fogged(to)) {
game_events::fire("sighted",to);
}
// would have to go via mousehandler to make this work:
//get_info().disp.unhighlight_reach();
raise_unit_moved();
return to;
}
bool ai::multistep_move_possible(const location& from,
const location& to, const location& via,
const std::map<location,paths>& possible_moves) const
@ -660,7 +378,7 @@ map_location ai::move_unit(location from, location to, std::map<location,paths>&
// If we can make it back to the keep and then to our original destination, do so.
if(multistep_move_possible(from,to,start_pos,possible_moves)) {
from = ai_interface::move_unit(from,start_pos,possible_moves);
from = ai_readwrite_context::move_unit(from,start_pos,possible_moves);
if(from != start_pos) {
return from;
}
@ -682,7 +400,7 @@ map_location ai::move_unit(location from, location to, std::map<location,paths>&
}
if(units_.count(to) == 0 || from == to) {
const location res = ai_interface::move_unit(from,to,*possible_moves_ptr);
const location res = ai_readwrite_context::move_unit(from,to,*possible_moves_ptr);
if(res != to) {
// We've been ambushed; find the ambushing unit and attack them.
adjacent_tiles_array locs;
@ -720,95 +438,9 @@ void ai::attack_enemy(const location& attacking_unit, const location& target,
int att_weapon, int def_weapon)
{
attacks_.insert(attacking_unit);
ai_interface::attack_enemy(attacking_unit,target,att_weapon,def_weapon);
ai_readwrite_context::attack_enemy(attacking_unit,target,att_weapon,def_weapon);
}
void ai_interface::calculate_possible_moves(std::map<location,paths>& res, move_map& srcdst,
move_map& dstsrc, bool enemy, bool assume_full_movement,
const std::set<map_location>* remove_destinations) const
{
calculate_moves(get_info().units,res,srcdst,dstsrc,enemy,assume_full_movement,remove_destinations);
}
void ai_interface::calculate_moves(const unit_map& units, std::map<location,paths>& res, move_map& srcdst,
move_map& dstsrc, bool enemy, bool assume_full_movement,
const std::set<map_location>* remove_destinations,
bool see_all
) const
{
for(unit_map::const_iterator un_it = units.begin(); un_it != units.end(); ++un_it) {
// If we are looking for the movement of enemies, then this unit must be an enemy unit.
// If we are looking for movement of our own units, it must be on our side.
// If we are assuming full movement, then it may be a unit on our side, or allied.
if((enemy && current_team().is_enemy(un_it->second.side()) == false) ||
(!enemy && !assume_full_movement && un_it->second.side() != get_side()) ||
(!enemy && assume_full_movement && current_team().is_enemy(un_it->second.side()))) {
continue;
}
// Discount incapacitated units
if(un_it->second.incapacitated()
|| (!assume_full_movement && un_it->second.movement_left() == 0)) {
continue;
}
// We can't see where invisible enemy units might move.
if(enemy && un_it->second.invisible(un_it->first,units,get_info().teams) && !see_all) {
continue;
}
// If it's an enemy unit, reset its moves while we do the calculations.
unit* held_unit = const_cast<unit*>(&(un_it->second));
const unit_movement_resetter move_resetter(*held_unit,enemy || assume_full_movement);
// Insert the trivial moves of staying on the same location.
if(un_it->second.movement_left() > 0 ) {
std::pair<location,location> trivial_mv(un_it->first,un_it->first);
srcdst.insert(trivial_mv);
dstsrc.insert(trivial_mv);
}
const bool teleports = un_it->second.get_ability_bool("teleport",un_it->first);
res.insert(std::pair<map_location,paths>(
un_it->first,paths(get_info().map,units,
un_it->first,get_info().teams,false,teleports,
current_team(),0,see_all)));
}
for(std::map<location,paths>::iterator m = res.begin(); m != res.end(); ++m) {
for(paths::routes_map::iterator rtit =
m->second.routes.begin(); rtit != m->second.routes.end(); ++rtit) {
const location& src = m->first;
const location& dst = rtit->first;
if(remove_destinations != NULL && remove_destinations->count(dst) != 0) {
continue;
}
bool friend_owns = false;
// Don't take friendly villages
if(!enemy && get_info().map.is_village(dst)) {
for(size_t n = 0; n != get_info().teams.size(); ++n) {
if(get_info().teams[n].owns_village(dst)) {
if(n+1 != get_side() && current_team().is_enemy(n+1) == false) {
friend_owns = true;
}
break;
}
}
}
if(friend_owns) {
continue;
}
if(src != dst && units.find(dst) == units.end()) {
srcdst.insert(std::pair<location,location>(src,dst));
dstsrc.insert(std::pair<location,location>(dst,src));
}
}
}
}
void ai::remove_unit_from_moves(const map_location& loc, move_map& srcdst, move_map& dstsrc)
{
@ -1213,68 +845,6 @@ bool ai::do_combat(std::map<map_location,paths>& possible_moves, const move_map&
}
}
void ai_interface::attack_enemy(const location u,
const location target, int weapon, int def_weapon)
{
// Stop the user from issuing any commands while the unit is attacking
const events::command_disabler disable_commands;
if(!get_info().units.count(u))
{
ERR_AI << "attempt to attack without attacker\n";
return;
}
if (!get_info().units.count(target))
{
ERR_AI << "attempt to attack without defender\n";
return;
}
if(get_info().units.find(target)->second.incapacitated()) {
ERR_AI << "attempt to attack unit that is petrified\n";
return;
}
if(!get_info().units.find(u)->second.attacks_left()) {
ERR_AI << "attempt to attack twice with the same unit\n";
return;
}
if(weapon >= 0) {
recorder.add_attack(u,target,weapon,def_weapon);
}
try {
attack(get_info().disp, get_info().map, get_info().teams, u, target, weapon, def_weapon,
get_info().units, get_info().state);
}
catch (end_level_exception&)
{
dialogs::advance_unit(get_info().map,get_info().units,u,get_info().disp,true);
const unit_map::const_iterator defender = get_info().units.find(target);
if(defender != get_info().units.end()) {
const size_t defender_team = size_t(defender->second.side()) - 1;
if(defender_team < get_info().teams.size()) {
dialogs::advance_unit(get_info().map, get_info().units,
target, get_info().disp, !get_info().teams[defender_team].is_human());
}
}
throw;
}
dialogs::advance_unit(get_info().map,get_info().units,u,get_info().disp,true);
const unit_map::const_iterator defender = get_info().units.find(target);
if(defender != get_info().units.end()) {
const size_t defender_team = size_t(defender->second.side()) - 1;
if(defender_team < get_info().teams.size()) {
dialogs::advance_unit(get_info().map, get_info().units,
target, get_info().disp, !get_info().teams[defender_team].is_human());
}
}
check_victory(get_info().state,get_info().units,get_info().teams, get_info().disp);
raise_enemy_attacked();
}
bool ai::get_healing(std::map<map_location,paths>& possible_moves,
const move_map& srcdst, const move_map& enemy_dstsrc)

View file

@ -19,7 +19,7 @@
/**
* A small explanation about what's going on here:
* Each action has access to two ai_interface::info objects
* Each action has access to two ai_game_info objects
* First is 'info' - real information
* Second is 'subjective info' - AIs perception of what's going on
* So, when we check_before action, we use 'subjective info' and don't
@ -134,13 +134,13 @@ unsigned int ai_action_result::get_side() const
}
ai_interface::info& ai_action_result::get_info() const
ai_game_info& ai_action_result::get_info() const
{
return ai_manager::get_active_ai_info_for_side(get_side());
}
ai_interface::info& ai_action_result::get_subjective_info() const
ai_game_info& ai_action_result::get_subjective_info() const
{
return get_info();
}
@ -152,7 +152,12 @@ bool ai_action_result::using_subjective_info() const
}
team& ai_action_result::get_my_team(ai_interface::info info) const
team& ai_action_result::get_my_team(ai_game_info& info) const
{
return info.teams[side_-1];
}
const team& ai_action_result::get_my_team(const ai_game_info& info) const
{
return info.teams[side_-1];
}
@ -248,8 +253,8 @@ bool ai_move_result::test_route(const unit_map::const_iterator& un, const team&
void ai_move_result::do_check_before()
{
DBG_AI_ACTIONS << " check_before " << *this << std::endl;
const ai_interface::info& s_info = get_subjective_info();
const ai_interface::info& info = get_info();
const ai_game_info& s_info = get_subjective_info();
const ai_game_info& info = get_info();
const unit_map& s_units = s_info.units;
const unit_map& units = info.units;
@ -303,7 +308,7 @@ void ai_move_result::do_execute()
DBG_AI_ACTIONS << " execute "<< *this << std::endl;
assert(is_success());
ai_interface::info& info = get_info();
ai_game_info& info = get_info();
move_unit(
/*game_display* disp*/ NULL,
@ -414,8 +419,8 @@ bool ai_recruit_result::test_suitable_recruit_location( const gamemap& map, cons
void ai_recruit_result::do_check_before()
{
DBG_AI_ACTIONS << " check_before " << *this << std::endl;
const ai_interface::info& s_info = get_subjective_info();
const ai_interface::info& info = get_info();
const ai_game_info& s_info = get_subjective_info();
const ai_game_info& info = get_info();
const unit_map& s_units = s_info.units;
const unit_map& units = info.units;
@ -482,7 +487,7 @@ void ai_recruit_result::do_check_before()
void ai_recruit_result::do_check_after()
{
const ai_interface::info& info = get_info();
const ai_game_info& info = get_info();
const gamemap& map = info.map;
if (!map.on_board(recruit_location_)){
set_error(AI_ACTION_FAILURE);
@ -519,7 +524,7 @@ void ai_recruit_result::do_execute()
{
DBG_AI_ACTIONS << " execute: " << *this << std::endl;
assert(is_success());
const ai_interface::info& info = get_info();
ai_game_info& info = get_info();
// We have to add the recruit command now, because when the unit
// is created it has to have the recruit command in the recorder
// to be able to put random numbers into to generate unit traits.
@ -585,8 +590,8 @@ bool ai_stopunit_result::test_unit(unit_map::const_iterator& un, const unit_map&
void ai_stopunit_result::do_check_before()
{
DBG_AI_ACTIONS << " check_before " << *this << std::endl;
const ai_interface::info& s_info = get_subjective_info();
const ai_interface::info& info = get_info();
const ai_game_info& s_info = get_subjective_info();
const ai_game_info& info = get_info();
const unit_map& s_units = s_info.units;
const unit_map& units = info.units;
@ -604,7 +609,7 @@ void ai_stopunit_result::do_check_before()
void ai_stopunit_result::do_check_after()
{
const ai_interface::info& info = get_info();
const ai_game_info& info = get_info();
unit_map::const_iterator un = info.units.find(unit_location_);
if (un==info.units.end()){
set_error(AI_ACTION_FAILURE);
@ -640,7 +645,7 @@ void ai_stopunit_result::do_execute()
{
DBG_AI_ACTIONS << " execute: " << *this << std::endl;
assert(is_success());
const ai_interface::info& info = get_info();
const ai_game_info& info = get_info();
unit_map::iterator un = info.units.find(unit_location_);
if (remove_movement_){
un->second.set_movement(0);

View file

@ -22,7 +22,7 @@
#include "../global.hpp"
#include "ai_interface.hpp"
#include "game_info.hpp"
#include "../map.hpp"
#include "../map_location.hpp"
#include "../team.hpp"
@ -73,16 +73,19 @@ protected:
unsigned int get_side() const;
/* return real information about the game state */
ai_interface::info& get_info() const;
ai_game_info& get_info() const;
/* return subjective information about the game state */
ai_interface::info& get_subjective_info() const;
ai_game_info& get_subjective_info() const;
/* are we using the subjective info ? */
bool using_subjective_info() const;
/* get the team object corresponding to current side */
team& get_my_team(ai_interface::info info) const;
team& get_my_team(ai_game_info& info) const;
/* get the team object corresponding to current side */
const team& get_my_team(const ai_game_info& info) const;
/* set error code */
void set_error(int error_code);

View file

@ -100,7 +100,7 @@ namespace dfool {
*/
class dfool_ai : public ai_interface {
public:
dfool_ai(int side, bool master) : ai_interface(side, master),unit_memory_(current_team().ai_memory()){}
dfool_ai(int side, bool master) : ai_interface(side, master), unit_memory_(current_team().ai_memory()){}
void play_turn();
virtual std::string describe_self();
private:

View file

@ -24,59 +24,4 @@
// =======================================================================
//
// =======================================================================
void ai_interface::raise_user_interact() const
{
ai_manager::raise_user_interact();
}
void ai_interface::raise_unit_recruited() const
{
ai_manager::raise_unit_recruited();
}
void ai_interface::raise_unit_moved() const
{
ai_manager::raise_unit_moved();
}
void ai_interface::raise_enemy_attacked() const
{
ai_manager::raise_enemy_attacked();
}
std::auto_ptr<ai_attack_result> ai_interface::execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){
return ai_actions::execute_attack_action(get_side(),true,attacker_loc,defender_loc,attacker_weapon);
}
std::auto_ptr<ai_attack_result> ai_interface::check_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){
return ai_actions::execute_attack_action(get_side(),false,attacker_loc,defender_loc,attacker_weapon);
}
std::auto_ptr<ai_move_result> ai_interface::execute_move_action(const map_location& from, const location& to, bool remove_movement){
return ai_actions::execute_move_action(get_side(),true,from,to,remove_movement);
}
std::auto_ptr<ai_move_result> ai_interface::check_move_action(const map_location& from, const location& to, bool remove_movement){
return ai_actions::execute_move_action(get_side(),false,from,to,remove_movement);
}
std::auto_ptr<ai_recruit_result> ai_interface::execute_recruit_action(const std::string& unit_name, const location &where){
return ai_actions::execute_recruit_action(get_side(),true,unit_name,where);
}
std::auto_ptr<ai_recruit_result> ai_interface::check_recruit_action(const std::string& unit_name, const location &where){
return ai_actions::execute_recruit_action(get_side(),false,unit_name,where);
}
std::auto_ptr<ai_stopunit_result> ai_interface::execute_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){
return ai_actions::execute_stopunit_action(get_side(),true,unit_location,remove_movement,remove_attacks);
}
std::auto_ptr<ai_stopunit_result> ai_interface::check_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){
return ai_actions::execute_stopunit_action(get_side(),false,unit_location,remove_movement,remove_attacks);
}

View file

@ -20,75 +20,15 @@
#ifndef AI_AI_INTERFACE_HPP_INCLUDED
#define AI_AI_INTERFACE_HPP_INCLUDED
class game_display;
class gamemap;
#include "contexts.hpp"
#include "../formula_callable.hpp"
#include "../pathfind.hpp"
#include "../gamestatus.hpp"
#include "../playturn.hpp"
class ai_attack_result;
class ai_move_result;
class ai_recruit_result;
class ai_stopunit_result;
class ai_interface : public game_logic::formula_callable {
class ai_interface : public game_logic::formula_callable, public ai_readwrite_context {
public:
/** get the 1-based side number which is controlled by this AI */
unsigned int get_side() const { return side_;}
/** get the 'master' flag of the AI. 'master' AI is the top-level-AI. */
bool get_master() const { return master_;}
/** A convenient typedef for the often used 'location' object. */
typedef map_location location;
/** The standard way in which a map of possible moves is recorded. */
typedef std::multimap<location,location> move_map;
/** The standard way in which a map of possible movement routes to location is recorded*/
typedef std::map<location,paths> moves_map;
/**
* info is structure which holds references to all the important objects
* that an AI might need access to, in order to make and implement its
* decisions.
*/
struct info {
info(game_display& disp, gamemap& map, unit_map& units,
std::vector<team>& teams, gamestatus& state, class game_state& game_state)
: disp(disp), map(map), units(units), teams(teams),
state(state), game_state_(game_state)
{}
/** The display object, used to draw the moves the AI makes. */
game_display& disp;
/** The map of the game -- use this object to find the terrain at any location. */
gamemap& map;
/** The map of units. It maps locations -> units. */
unit_map& units;
/** A list of the teams in the game. */
std::vector<team>& teams;
/** Information about what turn it is, and what time of day. */
gamestatus& state;
/** The global game state, because we may set the completion field. */
class game_state& game_state_;
};
/**
* The constructor.
*
* All derived classes should take an argument of type info& which they
* should pass to this constructor.
*/
ai_interface(int side, bool master) : side_(side), master_(master) {
ai_interface(int side, bool master) : ai_readwrite_context(side,master) {
add_ref(); //this class shouldn't be reference counted.
}
virtual ~ai_interface() {}
@ -101,199 +41,14 @@ public:
/**
* Function called when a a new turn is played
* Derived persistant AIs should call this function each turn (expect first)
* Derived AIs should call this function each turn (expect first)
*/
virtual void new_turn() {
}
/** Return a reference to the 'team' object for the AI. */
team& current_team() { return get_info().teams[get_side()-1]; }
const team& current_team() const { return get_info().teams[get_side()-1]; }
/** Show a diagnostic message on the screen. */
void diagnostic(const std::string& msg);
/** Display a debug message as a chat message. */
void log_message(const std::string& msg);
/** Set the side */
virtual void set_side(unsigned int side) { side_ = side; }
/** Evaluate */
virtual std::string evaluate(const std::string& /*str*/)
{ return "evaluate command not implemented by this AI"; }
/** Return a message with information about the ai. Useful for making debugging ai-independent. */
virtual std::string describe_self() { return "? ai"; }
protected:
/**
* Ask the game to attack an enemy defender using our unit attacker from attackers current location,
* @param attacker_loc location of attacker
* @param defender_loc location of defender
* @param attacker_weapon weapon of attacker
* @retval possible result: ok
* @retval possible result: something wrong
* @retval possible result: attacker and/or defender are invalid
* @retval possible result: attacker and/or defender are invalid
* @retval possible result: attacker doesn't have the specified weapon
*/
std::auto_ptr<ai_attack_result> execute_attack_action(const location& attacker_loc, const location& defender_loc, int attacker_weapon);
std::auto_ptr<ai_attack_result> check_attack_action(const location& attacker_loc, const location& defender_loc, int attacker_weapon);
/**
* Ask the game to move our unit from location 'from' to location 'to', optionally - doing a partial move
* @param from location of our unit
* @param to where to move
* @param remove_movement set unit movement to 0 in case of successful move
* @retval possible result: ok
* @retval possible result: something wrong
* @retval possible result: move is interrupted
* @retval possible result: move is impossible
*/
std::auto_ptr<ai_move_result> execute_move_action(const location& from, const location& to, bool remove_movement=true);
std::auto_ptr<ai_move_result> check_move_action(const location& from, const location& to, bool remove_movement=true);
/**
* Ask the game to recruit a unit for us on specified location
* @param unit_name the name of the unit to be recruited.
* @param where location where the unit is to be recruited.
* @retval possible result: ok
* @retval possible_result: something wrong
* @retval possible_result: leader not on keep
* @retval possible_result: no free space on keep
* @retval possible_result: not enough gold
*/
std::auto_ptr<ai_recruit_result> execute_recruit_action(const std::string& unit_name, const location &where = map_location::null_location);
std::auto_ptr<ai_recruit_result> check_recruit_action(const std::string& unit_name, const location &where = map_location::null_location);
/**
* Ask the game to remove unit movements and/or attack
* @param unit_location the location of our unit
* @param remove_movement set remaining movements to 0
* @param remove_attacks set remaining attacks to 0
* @retval possible result: ok
* @retval possible_result: something wrong
* @retval possible_result: nothing to do
*/
std::auto_ptr<ai_stopunit_result> execute_stopunit_action(const location& unit_location, bool remove_movement = true, bool remove_attacks = false);
std::auto_ptr<ai_stopunit_result> check_stopunit_action(const location& unit_location, bool remove_movement = true, bool remove_attacks = false);
/**
* This function should be called to attack an enemy.
*
* @deprecated
* @param u The location of the attacking unit. (Note this shouldn't
* be a reference since attack::attack() can invalidate the
* unit_map and references to the map are also invalid then.)
* @param target The location of the target unit. This unit must be in
* range of the attacking unit's weapon. (See note at param u.)
* @param weapon The number of the weapon (0-based) which should be used
* by the attacker. (It must be a valid weapon of the attacker.)
* @param def_weapon The number of the weapon (0-based) which should be used
* by the defender. (It must be a valid weapon of the defender.)
*/
void attack_enemy(const location u, const location target, int att_weapon, int def_weapon);
/**
* This function should be called to move a unit.
*
* @deprecated
* Once the unit has been moved, its movement allowance is set to 0.
* @param from The location of the unit being moved.
* @param to The location to be moved to. This must be a
* valid move for the unit.
* @param possible_moves The map of possible moves, as obtained from
* 'calculate_possible_moves'.
*/
location move_unit(location from, location to, std::map<location,paths>& possible_moves);
/**
* @deprecated
* Identical to 'move_unit', except that the unit's movement
* isn't set to 0 after the move is complete.
*/
location move_unit_partial(location from, location t, std::map<location,paths>& possible_moves);
/**
* Calculate the moves units may possibly make.
*
* @param possible_moves A map which will be filled with the paths
* each unit can take to get to every possible
* destination. You probably don't want to use
* this object at all, except to pass to
* 'move_unit'.
* @param srcdst A map of units to all their possible
* destinations.
* @param dstsrc A map of destinations to all the units that
* can move to that destination.
* @param enemy if true, a map of possible moves for enemies
* will be calculated. If false, a map of
* possible moves for units on the AI's side
* will be calculated. The AI's own leader will
* not be included in this map.
* @param assume_full_movement
* If true, the function will operate on the
* assumption that all units can move their full
* movement allotment.
* @param remove_destinations a pointer to a set of possible destinations
* to omit.
*/
void calculate_possible_moves(std::map<location,paths>& possible_moves,
move_map& srcdst, move_map& dstsrc, bool enemy,
bool assume_full_movement=false,
const std::set<location>* remove_destinations=NULL) const;
/**
* A more fundamental version of calculate_possible_moves which allows the
* use of a speculative unit map.
*/
void calculate_moves(const unit_map& units,
std::map<location,paths>& possible_moves, move_map& srcdst,
move_map& dstsrc, bool enemy, bool assume_full_movement=false,
const std::set<location>* remove_destinations=NULL,
bool see_all=false) const;
/**
* Recruit a unit. It will recruit the unit with the given name,
* at the given location, or at an available location to recruit units
* if 'loc' is not a valid recruiting location.
*
* @retval false If recruitment cannot be performed, because
* there are no available tiles, or not enough
* money.
*/
bool recruit(const std::string& unit_name, location loc=location());
/**
* Functions to retrieve the 'info' object.
* Used by derived classes to discover all necessary game information.
*/
info& get_info();
const info& get_info() const;
/**
* Function which should be called frequently to allow the user to interact
* with the interface. This function will make sure that interaction
* doesn't occur too often, so there is no problem with calling it very
* regularly.
*/
void raise_user_interact() const;
/** Notifies all interested observers of the event respectively. */
void raise_unit_recruited() const;
void raise_unit_moved() const;
void raise_enemy_attacked() const;
protected:
virtual void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
virtual variant get_value(const std::string& key) const;
private:
unsigned int side_;
bool master_;
};
#endif

View file

@ -250,7 +250,7 @@ ai_manager::~ai_manager()
ai_manager::AI_map_of_stacks ai_manager::ai_map_;
ai_interface::info *ai_manager::ai_info_;
ai_game_info *ai_manager::ai_info_;
events::generic_event ai_manager::user_interact_("ai_user_interact");
events::generic_event ai_manager::unit_recruited_("ai_unit_recruited");
events::generic_event ai_manager::unit_moved_("ai_unit_moved");
@ -258,12 +258,12 @@ events::generic_event ai_manager::enemy_attacked_("ai_enemy_attacked");
int ai_manager::last_interact_ = 0;
void ai_manager::set_ai_info(const ai_interface::info& i)
void ai_manager::set_ai_info(const ai_game_info& i)
{
if (ai_info_!=NULL){
clear_ai_info();
}
ai_info_ = new ai_interface::info(i);
ai_info_ = new ai_game_info(i);
}
@ -640,13 +640,13 @@ const std::string& ai_manager::get_active_ai_algorithm_type_for_side( int side )
}
ai_interface::info& ai_manager::get_active_ai_info_for_side( int /*side*/ )
ai_game_info& ai_manager::get_active_ai_info_for_side( int /*side*/ )
{
return *ai_info_;
}
ai_interface::info& ai_manager::get_ai_info()
ai_game_info& ai_manager::get_ai_info()
{
return *ai_info_;
}

View file

@ -141,7 +141,7 @@ public:
* Sets AI information.
* @param info ai_information to be set.
*/
static void set_ai_info(const ai_interface::info& info);
static void set_ai_info(const ai_game_info& info);
/**
@ -225,7 +225,7 @@ public:
* Adds active AI for specified @a side from @a file.
* @note Running this command may invalidate references previously returned
* by ai_manager. AI is not initialized at this point.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @param file file name, follows the usual WML convention.
* @param replace should new ai replace the current ai or 'be placed on top of it'.
* @return true if successful.
@ -237,7 +237,7 @@ public:
* Adds active AI for specified @a side from @a cfg.
* @note Running this command may invalidate references previously returned
* by ai_manager. AI is not initialized at this point.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @param cfg the config from which all ai parameters are to be read.
* @param replace should new ai replace the current ai or 'be placed on top of it'.
* @return true if successful.
@ -249,7 +249,7 @@ public:
* Adds active AI for specified @a side from parameters.
* @note Running this command may invalidate references previously returned
* by ai_manager. AI is not initialized at this point.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @param ai_algorithm_type type of AI algorithm to create.
* @param replace should new ai replace the current ai or 'be placed on top of it'.
* @return true if successful.
@ -285,7 +285,7 @@ public:
* Removes top-level AI from @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
*/
static void remove_ai_for_side( int side );
@ -294,7 +294,7 @@ public:
* Removes all AIs from @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
*/
static void remove_all_ais_for_side( int side );
@ -317,7 +317,7 @@ public:
* Gets AI parameters for active AI of the given @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @return a reference to active AI parameters.
* @note This reference may become invalid after specific ai_manager operations.
*/
@ -328,7 +328,7 @@ public:
* Gets effective AI parameters for active AI of the given @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @return a reference to active AI effective parameters.
* @note this reference may become invalid after specific ai_manager operations.
*/
@ -339,7 +339,7 @@ public:
* Get global AI parameters for active AI of the @a given side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @return a reference to active ai global parameters.
* @note This reference may become invalid after specific ai_manager operations.
*/
@ -351,21 +351,21 @@ public:
* @param side side number (1-based).
* @return a reference to active AI info.
*/
static ai_interface::info& get_active_ai_info_for_side( int side );
static ai_game_info& get_active_ai_info_for_side( int side );
/**
* Gets global AI-game info
* @return a reference to the AI-game info.
*/
static ai_interface::info& get_ai_info();
static ai_game_info& get_ai_info();
/**
* Gets AI memory for active AI of the given @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @return a reference to active AI memory.
* @note This reference may become invalid after specific ai_manager operations.
*/
@ -376,7 +376,7 @@ public:
* Gets AI algorithm type for active AI of the given @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @return a reference to active AI algorithm_type.
* @note This reference may become invalid after specific ai_manager operations.
*/
@ -391,7 +391,7 @@ public:
* Sets AI parameters for active AI of the given @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @param ai_parameters AI parameters to be set.
*/
static void set_active_ai_parameters_for_side( int side, const std::vector<config>& ai_parameters );
@ -401,7 +401,7 @@ public:
* Sets effective AI parameters for active AI of the given @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @param ai_effective_parameters AI effective parameters to be set.
* @deprecated Added only for bug-for-bug compatibility with side.cpp.
* Will be refactored away.
@ -413,7 +413,7 @@ public:
* Sets global AI parameters for active AI of the given @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @param ai_global_parameters AI global parameters to be set.
* @deprecated Added only for bug-for-bug compatibility with side.cpp.
* Will be refactored away.
@ -425,7 +425,7 @@ public:
* Sets AI memory for active AI of the given @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @param ai_memory AI memory to be set.
* @deprecated Added only for bug-for-bug compatibility with side.cpp.
* Will be refactored away.
@ -437,7 +437,7 @@ public:
* Sets AI algorithm type for active AI of the given @a side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @param ai_algorithm_type AI algorithm type to be set.
*/
static void set_active_ai_algorithm_type_for_side( int side, const std::string& ai_algorithm_type );
@ -449,7 +449,7 @@ public:
/**
* Plays a turn for the specified side using its active AI.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @param event_observer controller which will observe events produced by the AI.
*/
static void play_turn(int side, events::observer* event_observer);
@ -461,7 +461,7 @@ private:
static AI_map_of_stacks ai_map_;
static std::deque< ai_command_history_item > history_;
static long history_item_counter_;
static ai_interface::info *ai_info_;
static ai_game_info *ai_info_;
static events::generic_event user_interact_;
static events::generic_event unit_recruited_;
@ -534,7 +534,7 @@ private:
* Gets active AI for specified side.
* @note Running this command may invalidate references previously returned
* by ai_manager.
* @param side side number (1-based, as in ai_interface::info).
* @param side side number (1-based, as in ai_game_info).
* @return a reference to the active AI.
* @note This reference may become invalid after specific ai_manager operations.
*/

541
src/ai/contexts.cpp Normal file
View file

@ -0,0 +1,541 @@
/* $Id$ */
/*
Copyright (C) 2009 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.
*/
/**
* Helper functions for the object which operates in the context of AI for specific side
* This is part of AI interface
* @file ai/contexts.cpp
*/
#include "contexts.hpp"
#include "ai_actions.hpp"
#include "ai_manager.hpp"
#include "../dialogs.hpp"
#include "../game_end_exceptions.hpp"
#include "../game_events.hpp"
#include "../game_preferences.hpp"
#include "../log.hpp"
#include "../mouse_handler_base.hpp"
#include "../replay.hpp"
#include "../statistics.hpp"
#include "../unit_display.hpp"
static lg::log_domain log_ai("ai/general");
#define DBG_AI LOG_STREAM(debug, log_ai)
#define LOG_AI LOG_STREAM(info, log_ai)
#define WRN_AI LOG_STREAM(warn, log_ai)
#define ERR_AI LOG_STREAM(err, log_ai)
// =======================================================================
//
// =======================================================================
std::string ai_readonly_context::describe_self() const
{
return "? [ai]";
}
void ai_readonly_context::raise_user_interact() const
{
ai_manager::raise_user_interact();
}
void ai_readwrite_context::raise_unit_recruited() const
{
ai_manager::raise_unit_recruited();
}
void ai_readwrite_context::raise_unit_moved() const
{
ai_manager::raise_unit_moved();
}
void ai_readwrite_context::raise_enemy_attacked() const
{
ai_manager::raise_enemy_attacked();
}
std::auto_ptr<ai_attack_result> ai_readwrite_context::execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){
return ai_actions::execute_attack_action(get_side(),true,attacker_loc,defender_loc,attacker_weapon);
}
std::auto_ptr<ai_attack_result> ai_readonly_context::check_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon){
return ai_actions::execute_attack_action(get_side(),false,attacker_loc,defender_loc,attacker_weapon);
}
std::auto_ptr<ai_move_result> ai_readwrite_context::execute_move_action(const map_location& from, const map_location& to, bool remove_movement){
return ai_actions::execute_move_action(get_side(),true,from,to,remove_movement);
}
std::auto_ptr<ai_move_result> ai_readonly_context::check_move_action(const map_location& from, const map_location& to, bool remove_movement){
return ai_actions::execute_move_action(get_side(),false,from,to,remove_movement);
}
std::auto_ptr<ai_recruit_result> ai_readwrite_context::execute_recruit_action(const std::string& unit_name, const map_location &where){
return ai_actions::execute_recruit_action(get_side(),true,unit_name,where);
}
std::auto_ptr<ai_recruit_result> ai_readonly_context::check_recruit_action(const std::string& unit_name, const map_location &where){
return ai_actions::execute_recruit_action(get_side(),false,unit_name,where);
}
std::auto_ptr<ai_stopunit_result> ai_readwrite_context::execute_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){
return ai_actions::execute_stopunit_action(get_side(),true,unit_location,remove_movement,remove_attacks);
}
std::auto_ptr<ai_stopunit_result> ai_readonly_context::check_stopunit_action(const map_location& unit_location, bool remove_movement, bool remove_attacks){
return ai_actions::execute_stopunit_action(get_side(),false,unit_location,remove_movement,remove_attacks);
}
bool ai_readwrite_context::recruit(const std::string& unit_name, map_location loc)
{
const std::set<std::string>& recruits = current_team().recruits();
const std::set<std::string>::const_iterator i = recruits.find(unit_name);
if(i == recruits.end()) {
return false;
}
const int num = std::distance(recruits.begin(),i);
// We have to add the recruit command now, because when the unit
// is created it has to have the recruit command in the recorder
// to be able to put random numbers into to generate unit traits.
// However, we're not sure if the transaction will be successful,
// so use a replay_undo object to cancel it if we don't get
// a confirmation for the transaction.
recorder.add_recruit(num,loc);
replay_undo replay_guard(recorder);
unit_type_data::unit_type_map::const_iterator u = unit_type_data::types().find_unit_type(unit_name);
if(u == unit_type_data::types().end() || u->first == "dummy_unit") {
return false;
}
// Check if we have enough money
if(current_team().gold() < u->second.cost()) {
return false;
}
LOG_AI << "trying recruit: team=" << (get_side()) <<
" type=" << unit_name <<
" cost=" << u->second.cost() <<
" loc=(" << loc << ')' <<
" gold=" << (current_team().gold()) <<
" (-> " << (current_team().gold()-u->second.cost()) << ")\n";
unit new_unit(&get_info().units,&get_info().map,&get_info().state,&get_info().teams,&u->second,get_side(),true);
// See if we can actually recruit (i.e. have enough room etc.)
std::string recruit_err = recruit_unit(get_info().map,get_side(),get_info().units,new_unit,loc,false,preferences::show_ai_moves());
if(recruit_err.empty()) {
statistics::recruit_unit(new_unit);
current_team().spend_gold(u->second.cost());
// Confirm the transaction - i.e. don't undo recruitment
replay_guard.confirm_transaction();
raise_unit_recruited();
const team_data data = calculate_team_data(current_team(),get_side(),get_info().units);
LOG_AI <<
"recruit confirmed: team=" << get_side() <<
" units=" << data.units <<
" gold=" << data.gold <<
((data.net_income < 0) ? "" : "+") <<
data.net_income << "\n";
recorder.add_checksum_check(loc);
return true;
} else {
const team_data data = calculate_team_data(current_team(),get_side(),get_info().units);
LOG_AI << recruit_err << "\n";
LOG_AI <<
"recruit UNconfirmed: team=" << (get_side()) <<
" units=" << data.units <<
" gold=" << data.gold <<
((data.net_income < 0) ? "" : "+") <<
data.net_income << "\n";
return false;
}
}
const ai_game_info& ai_readonly_context::get_info() const{
return ai_manager::get_active_ai_info_for_side(get_side());
}
ai_game_info& ai_readwrite_context::get_info(){
return ai_manager::get_active_ai_info_for_side(get_side());
}
const ai_game_info& ai_readwrite_context::get_info() const{
return ai_manager::get_active_ai_info_for_side(get_side());
}
void ai_readonly_context::diagnostic(const std::string& msg)
{
if(game_config::debug) {
get_info().disp.set_diagnostic(msg);
}
}
void ai_readonly_context::log_message(const std::string& msg)
{
if(game_config::debug) {
get_info().disp.add_chat_message(time(NULL), "ai", get_side(), msg,
game_display::MESSAGE_PUBLIC, false);
}
}
map_location ai_readwrite_context::move_unit(map_location from, map_location to,
std::map<map_location,paths>& possible_moves)
{
const map_location loc = move_unit_partial(from,to,possible_moves);
const unit_map::iterator u = get_info().units.find(loc);
if(u != get_info().units.end()) {
if(u->second.movement_left()==u->second.total_movement()) {
u->second.set_movement(0);
u->second.set_state("not_moved","yes");
} else if (from == loc) {
u->second.set_movement(0);
}
}
return loc;
}
map_location ai_readwrite_context::move_unit_partial(map_location from, map_location to,
std::map<map_location,paths>& possible_moves)
{
LOG_AI << "ai_readwrite_context::move_unit " << from << " -> " << to << '\n';
assert(to.valid() && to.x <= MAX_MAP_AREA && to.y <= MAX_MAP_AREA);
// Stop the user from issuing any commands while the unit is moving.
const events::command_disabler disable_commands;
log_scope2(log_ai, "move_unit");
unit_map::iterator u_it = get_info().units.find(from);
if(u_it == get_info().units.end()) {
ERR_AI << "Could not find unit at " << from << '\n';
assert(false);
return map_location();
}
if(from == to) {
LOG_AI << "moving unit at " << from << " on spot. resetting moves\n";
return to;
}
const bool show_move = preferences::show_ai_moves();
const std::map<map_location,paths>::iterator p_it = possible_moves.find(from);
std::vector<map_location> steps;
if(p_it != possible_moves.end()) {
paths& p = p_it->second;
std::map<map_location,paths::route>::iterator rt = p.routes.begin();
for(; rt != p.routes.end(); ++rt) {
if(rt->first == to) {
break;
}
}
if(rt != p.routes.end()) {
if (static_cast<size_t>(u_it->second.movement_left()) >= rt->second.steps.size()) {
LOG_AI<<"Trying to move unit without enough move points left\n";
}
u_it->second.set_movement(rt->second.move_left);
steps = rt->second.steps;
while(steps.empty() == false && get_info().units.find(to) != get_info().units.end() && from != to){
LOG_AI << "AI attempting illegal move. Attempting to move onto existing unit\n";
LOG_AI << "\t" << get_info().units.find(to)->second.underlying_id() <<" already on " << to << "\n";
LOG_AI <<"\tremoving "<<*(steps.end()-1)<<"\n";
to = *(steps.end()-1);
steps.pop_back();
LOG_AI << "\tresetting to " << from << " -> " << to << '\n';
}
if(steps.size()) { // First step is starting hex
unit_map::const_iterator utest=get_info().units.find(*(steps.begin()));
if(utest != get_info().units.end() && current_team().is_enemy(utest->second.side())){
ERR_AI << "AI tried to move onto existing enemy unit at" << *steps.begin() << '\n';
// return(from);
}
// Check if there are any invisible units that we uncover
for(std::vector<map_location>::iterator i = steps.begin()+1; i != steps.end(); ++i) {
map_location adj[6];
get_adjacent_tiles(*i,adj);
size_t n;
for(n = 0; n != 6; ++n) {
// See if there is an enemy unit next to this tile.
// If it's invisible, we need to stop: we're ambushed.
// If it's not, we must be a skirmisher, otherwise AI wouldn't try.
// Or would it? If it doesn't cheat, it might...
const unit_map::const_iterator u = get_info().units.find(adj[n]);
// If level 0 is invisible it ambush us too
if (u != get_info().units.end() && (u->second.emits_zoc() || u->second.invisible(adj[n], get_info().units, get_info().teams))
&& current_team().is_enemy(u->second.side())) {
if (u->second.invisible(adj[n], get_info().units, get_info().teams)) {
to = *i;
u->second.ambush();
steps.erase(i,steps.end());
break;
} else {
if (!u_it->second.get_ability_bool("skirmisher",*i)){
ERR_AI << "AI tried to skirmish with non-skirmisher\n";
LOG_AI << "\tresetting destination from " <<to;
to = *i;
LOG_AI << " to " << to;
steps.erase(i,steps.end());
while(steps.empty() == false && (!(get_info().units.find(to) == get_info().units.end() || from == to))){
to = *(steps.end()-1);
steps.pop_back();
LOG_AI << "\tresetting to " << from << " -> " << to << '\n';
}
break;
}
}
}
}
if(n != 6) {
u_it->second.set_movement(0); // Enter enemy ZoC, no movement left
break;
}
}
}
if(steps.empty() || steps.back() != to) {
//Add the destination to the end of the steps if it's not
//already there.
steps.push_back(to);
}
if(show_move && unit_display::unit_visible_on_path(steps,
u_it->second, get_info().units,get_info().teams)) {
get_info().disp.display_unit_hex(from);
unit_map::iterator up = get_info().units.find(u_it->first);
unit_display::move_unit(steps,up->second,get_info().teams);
} else if(steps.size()>1) {
unit_map::iterator up = get_info().units.find(u_it->first);
std::vector<map_location>::const_reverse_iterator last_step = steps.rbegin();
std::vector<map_location>::const_reverse_iterator before_last = last_step +1;
up->second.set_facing(before_last->get_relative_dir(*last_step));
}
}
}
//FIXME: probably missing some "else" here
// It looks like if the AI doesn't find a route in possible_move,
// she will just teleport her unit between 'from' and 'to'
// I suppose this never happen, but in the meantime, add code for replay
if (steps.empty()) {
steps.push_back(from);
steps.push_back(to);
}
std::pair<map_location,unit> *p = get_info().units.extract(u_it->first);
p->first = to;
get_info().units.insert(p);
p->second.set_standing(p->first);
if(get_info().map.is_village(to)) {
// If a new village is captured, disallow any future movement.
if (!current_team().owns_village(to))
get_info().units.find(to)->second.set_movement(-1);
get_village(to,get_info().disp,get_info().teams,get_side()-1,get_info().units);
}
if(show_move) {
get_info().disp.invalidate(to);
get_info().disp.draw();
}
recorder.add_movement(steps);
game_events::fire("moveto",to,from);
if((get_info().teams.front().uses_fog() || get_info().teams.front().uses_shroud()) &&
!get_info().teams.front().fogged(to)) {
game_events::fire("sighted",to);
}
// would have to go via mousehandler to make this work:
//get_info().disp.unhighlight_reach();
raise_unit_moved();
return to;
}
void ai_readonly_context::calculate_possible_moves(std::map<map_location,paths>& res, move_map& srcdst,
move_map& dstsrc, bool enemy, bool assume_full_movement,
const std::set<map_location>* remove_destinations) const
{
calculate_moves(get_info().units,res,srcdst,dstsrc,enemy,assume_full_movement,remove_destinations);
}
void ai_readonly_context::calculate_moves(const unit_map& units, std::map<map_location,paths>& res, move_map& srcdst,
move_map& dstsrc, bool enemy, bool assume_full_movement,
const std::set<map_location>* remove_destinations,
bool see_all
) const
{
for(unit_map::const_iterator un_it = units.begin(); un_it != units.end(); ++un_it) {
// If we are looking for the movement of enemies, then this unit must be an enemy unit.
// If we are looking for movement of our own units, it must be on our side.
// If we are assuming full movement, then it may be a unit on our side, or allied.
if((enemy && current_team().is_enemy(un_it->second.side()) == false) ||
(!enemy && !assume_full_movement && un_it->second.side() != get_side()) ||
(!enemy && assume_full_movement && current_team().is_enemy(un_it->second.side()))) {
continue;
}
// Discount incapacitated units
if(un_it->second.incapacitated()
|| (!assume_full_movement && un_it->second.movement_left() == 0)) {
continue;
}
// We can't see where invisible enemy units might move.
if(enemy && un_it->second.invisible(un_it->first,units,get_info().teams) && !see_all) {
continue;
}
// If it's an enemy unit, reset its moves while we do the calculations.
unit* held_unit = const_cast<unit*>(&(un_it->second));
const unit_movement_resetter move_resetter(*held_unit,enemy || assume_full_movement);
// Insert the trivial moves of staying on the same map location.
if(un_it->second.movement_left() > 0 ) {
std::pair<map_location,map_location> trivial_mv(un_it->first,un_it->first);
srcdst.insert(trivial_mv);
dstsrc.insert(trivial_mv);
}
const bool teleports = un_it->second.get_ability_bool("teleport",un_it->first);
res.insert(std::pair<map_location,paths>(
un_it->first,paths(get_info().map,units,
un_it->first,get_info().teams,false,teleports,
current_team(),0,see_all)));
}
for(std::map<map_location,paths>::iterator m = res.begin(); m != res.end(); ++m) {
for(paths::routes_map::iterator rtit =
m->second.routes.begin(); rtit != m->second.routes.end(); ++rtit) {
const map_location& src = m->first;
const map_location& dst = rtit->first;
if(remove_destinations != NULL && remove_destinations->count(dst) != 0) {
continue;
}
bool friend_owns = false;
// Don't take friendly villages
if(!enemy && get_info().map.is_village(dst)) {
for(size_t n = 0; n != get_info().teams.size(); ++n) {
if(get_info().teams[n].owns_village(dst)) {
if(n+1 != get_side() && current_team().is_enemy(n+1) == false) {
friend_owns = true;
}
break;
}
}
}
if(friend_owns) {
continue;
}
if(src != dst && units.find(dst) == units.end()) {
srcdst.insert(std::pair<map_location,map_location>(src,dst));
dstsrc.insert(std::pair<map_location,map_location>(dst,src));
}
}
}
}
void ai_readwrite_context::attack_enemy(const map_location u,
const map_location target, int weapon, int def_weapon)
{
// Stop the user from issuing any commands while the unit is attacking
const events::command_disabler disable_commands;
if(!get_info().units.count(u))
{
ERR_AI << "attempt to attack without attacker\n";
return;
}
if (!get_info().units.count(target))
{
ERR_AI << "attempt to attack without defender\n";
return;
}
if(get_info().units.find(target)->second.incapacitated()) {
ERR_AI << "attempt to attack unit that is petrified\n";
return;
}
if(!get_info().units.find(u)->second.attacks_left()) {
ERR_AI << "attempt to attack twice with the same unit\n";
return;
}
if(weapon >= 0) {
recorder.add_attack(u,target,weapon,def_weapon);
}
try {
attack(get_info().disp, get_info().map, get_info().teams, u, target, weapon, def_weapon,
get_info().units, get_info().state);
}
catch (end_level_exception&)
{
dialogs::advance_unit(get_info().map,get_info().units,u,get_info().disp,true);
const unit_map::const_iterator defender = get_info().units.find(target);
if(defender != get_info().units.end()) {
const size_t defender_team = size_t(defender->second.side()) - 1;
if(defender_team < get_info().teams.size()) {
dialogs::advance_unit(get_info().map, get_info().units,
target, get_info().disp, !get_info().teams[defender_team].is_human());
}
}
throw;
}
dialogs::advance_unit(get_info().map,get_info().units,u,get_info().disp,true);
const unit_map::const_iterator defender = get_info().units.find(target);
if(defender != get_info().units.end()) {
const size_t defender_team = size_t(defender->second.side()) - 1;
if(defender_team < get_info().teams.size()) {
dialogs::advance_unit(get_info().map, get_info().units,
target, get_info().disp, !get_info().teams[defender_team].is_human());
}
}
check_victory(get_info().state,get_info().units,get_info().teams, get_info().disp);
raise_enemy_attacked();
}

314
src/ai/contexts.hpp Normal file
View file

@ -0,0 +1,314 @@
/* $Id$ */
/*
Copyright (C) 2009 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.
*/
/**
* @file ai/contexts.hpp
* Helper functions for the object which operates in the context of AI for specific side
* this is part of AI interface
*/
#ifndef AI_CONTEXTS_HPP_INCLUDED
#define AI_CONTEXTS_HPP_INCLUDED
class game_display;
class gamemap;
#include "game_info.hpp"
#include "../pathfind.hpp"
#include "../gamestatus.hpp"
#include "../playturn.hpp"
class ai_attack_result;
class ai_move_result;
class ai_recruit_result;
class ai_stopunit_result;
class ai_readonly_context {
public:
/** A convenient typedef for the often used 'location' object. */
typedef map_location location;
/** The standard way in which a map of possible moves is recorded. */
typedef std::multimap<location,location> move_map;
/** The standard way in which a map of possible movement routes to location is recorded*/
typedef std::map<location,paths> moves_map;
/** get the 'master' flag of the AI. 'master' AI is the top-level-AI. */
bool get_master() const { return master_;}
/** get the 1-based side number which is controlled by this AI */
unsigned int get_side() const { return side_;}
/** Set the side */
virtual void set_side(unsigned int side) { side_ = side; }
/**
* The constructor.
*/
ai_readonly_context(unsigned int side, bool master) : side_(side), master_(master){
}
virtual ~ai_readonly_context() {}
/** Return a reference to the 'team' object for the AI. */
const team& current_team() const { return get_info().teams[get_side()-1]; }
/** Show a diagnostic message on the screen. */
void diagnostic(const std::string& msg);
/** Display a debug message as a chat message. */
void log_message(const std::string& msg);
/** Describe self*/
virtual std::string describe_self() const;
/**
* Check if it is possible to attack enemy defender using our unit attacker from attackers current location,
* @param attacker_loc location of attacker
* @param defender_loc location of defender
* @param attacker_weapon weapon of attacker
* @retval possible result: ok
* @retval possible result: something wrong
* @retval possible result: attacker and/or defender are invalid
* @retval possible result: attacker and/or defender are invalid
* @retval possible result: attacker doesn't have the specified weapon
*/
std::auto_ptr<ai_attack_result> check_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon);
/**
* Check if it is possible to move our unit from location 'from' to location 'to'
* @param from location of our unit
* @param to where to move
* @param remove_movement set unit movement to 0 in case of successful move
* @retval possible result: ok
* @retval possible result: something wrong
* @retval possible result: move is interrupted
* @retval possible result: move is impossible
*/
std::auto_ptr<ai_move_result> check_move_action(const map_location& from, const map_location& to, bool remove_movement=true);
/**
* Check if it is possible to recruit a unit for us on specified location
* @param unit_name the name of the unit to be recruited.
* @param where location where the unit is to be recruited.
* @retval possible result: ok
* @retval possible_result: something wrong
* @retval possible_result: leader not on keep
* @retval possible_result: no free space on keep
* @retval possible_result: not enough gold
*/
std::auto_ptr<ai_recruit_result> check_recruit_action(const std::string& unit_name, const map_location &where = map_location::null_location);
/**
* Check if it is possible to remove unit movements and/or attack
* @param unit_location the location of our unit
* @param remove_movement set remaining movements to 0
* @param remove_attacks set remaining attacks to 0
* @retval possible result: ok
* @retval possible_result: something wrong
* @retval possible_result: nothing to do
*/
std::auto_ptr<ai_stopunit_result> check_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false);
/**
* Calculate the moves units may possibly make.
*
* @param possible_moves A map which will be filled with the paths
* each unit can take to get to every possible
* destination. You probably don't want to use
* this object at all, except to pass to
* 'move_unit'.
* @param srcdst A map of units to all their possible
* destinations.
* @param dstsrc A map of destinations to all the units that
* can move to that destination.
* @param enemy if true, a map of possible moves for enemies
* will be calculated. If false, a map of
* possible moves for units on the AI's side
* will be calculated. The AI's own leader will
* not be included in this map.
* @param assume_full_movement
* edonly_ai_context If true, the function will operate on the
* assumption thareadonly_ai_contextt all units can move their full
* movement allotment.
* @param remove_destinations a pointer to a set of possible destinations
* to omit.
*/
void calculate_possible_moves(std::map<map_location,paths>& possible_moves,
move_map& srcdst, move_map& dstsrc, bool enemy,
bool assume_full_movement=false,
const std::set<map_location>* remove_destinations=NULL) const;
/**
* A more fundamental version of calculate_possible_moves which allows the
* use of a speculative unit map.
*/
void calculate_moves(const unit_map& units,
std::map<map_location,paths>& possible_moves, move_map& srcdst,
move_map& dstsrc, bool enemy, bool assume_full_movement=false,
const std::set<map_location>* remove_destinations=NULL,
bool see_all=false) const;
const virtual ai_game_info& get_info() const;
/**
* Function which should be called frequently to allow the user to interact
* with the interface. This function will make sure that interaction
* doesn't occur too often, so there is no problem with calling it very
* regularly.
*/
void raise_user_interact() const;
private:
unsigned int side_;
bool master_;
};
class ai_readwrite_context : public ai_readonly_context {
public:
/**
* Ask the game to attack an enemy defender using our unit attacker from attackers current location,
* @param attacker_loc location of attacker
* @param defender_loc location of defender
* @param attacker_weapon weapon of attacker
* @retval possible result: ok
* @retval possible result: something wrong
* @retval possible result: attacker and/or defender are invalid
* @retval possible result: attacker and/or defender are invalid
* @retval possible result: attacker doesn't have the specified weapon
*/
std::auto_ptr<ai_attack_result> execute_attack_action(const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon);
/**
* Ask the game to move our unit from location 'from' to location 'to', optionally - doing a partial move
* @param from location of our unit
* @param to where to move
* @param remove_movement set unit movement to 0 in case of successful move
* @retval possible result: ok
* @retval possible result: something wrong
* @retval possible result: move is interrupted
* @retval possible result: move is impossible
*/
std::auto_ptr<ai_move_result> execute_move_action(const map_location& from, const map_location& to, bool remove_movement=true);
/**
* Ask the game to recruit a unit for us on specified location
* @param unit_name the name of the unit to be recruited.
* @param where location where the unit is to be recruited.
* @retval possible result: ok
* @retval possible_result: something wrong
* @retval possible_result: leader not on keep
* @retval possible_result: no free space on keep
* @retval possible_result: not enough gold
*/
std::auto_ptr<ai_recruit_result> execute_recruit_action(const std::string& unit_name, const map_location &where = map_location::null_location);
/**
* Ask the game to remove unit movements and/or attack
* @param unit_location the location of our unit
* @param remove_movement set remaining movements to 0
* @param remove_attacks set remaining attacks to 0
* @retval possible result: ok
* @retval possible_result: something wrong
* @retval possible_result: nothing to do
*/
std::auto_ptr<ai_stopunit_result> execute_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false);
/** Return a reference to the 'team' object for the AI. */
team& current_team() { return get_info().teams[get_side()-1]; }
const team& current_team() const { return get_info().teams[get_side()-1]; }
/**
* This function should be called to attack an enemy.
*
* @deprecated
* @param u The location of the attacking unit. (Note this shouldn't
* be a reference since attack::attack() can invalidate the
* unit_map and references to the map are also invalid then.)
* @param target The location of the target unit. This unit must be in
* range of the attacking unit's weapon. (See note at param u.)
* @param weapon The number of the weapon (0-based) which should be used
* by the attacker. (It must be a valid weapon of the attacker.)
* @param def_weapon The number of the weapon (0-based) which should be used
* by the defender. (It must be a valid weapon of the defender.)
*/
void attack_enemy(const map_location u, const map_location target, int att_weapon, int def_weapon);
/**
* This function should be called to move a unit.
*
* @deprecated
* Once the unit has been moved, its movement allowance is set to 0.
* @param from The location of the unit being moved.
* @param to The location to be moved to. This must be a
* valid move for the unit.
* @param possible_moves The map of possible moves, as obtained from
* 'calculate_possible_moves'.
*/
map_location move_unit(map_location from, map_location to, std::map<map_location,paths>& possible_moves);
/**
* @deprecated
* Identical to 'move_unit', except that the unit's movement
* isn't set to 0 after the move is complete.
*/
map_location move_unit_partial(map_location from, map_location to, std::map<map_location,paths>& possible_moves);
/** Evaluate */
virtual std::string evaluate(const std::string& /*str*/)
{ return "evaluate command not implemented by this AI"; }
/**
* Recruit a unit. It will recruit the unit with the given name,
* at the given location, or at an available location to recruit units
* if 'loc' is not a valid recruiting location.
*
* @retval false If recruitment cannot be performed, because
* there are no available tiles, or not enough
* money.
*/
bool recruit(const std::string& unit_name, map_location loc=map_location());
/** Notifies all interested observers of the event respectively. */
void raise_unit_recruited() const;
void raise_unit_moved() const;
void raise_enemy_attacked() const;
/**
* The constructor.
*/
ai_readwrite_context(unsigned int side, bool master) : ai_readonly_context(side,master){
}
virtual ~ai_readwrite_context() {}
/**
* Functions to retrieve the 'info' object.
* Used by derived classes to discover all necessary game information.
*/
const virtual ai_game_info& get_info() const;
virtual ai_game_info& get_info();
};
#endif

View file

@ -72,9 +72,9 @@ public:
virtual void new_turn();
virtual std::string describe_self();
using ai_interface::get_info;
using ai_interface::current_team;
using ai_interface::move_map;
using ai_readwrite_context::get_info;
using ai_readwrite_context::current_team;
using ai_readonly_context::move_map;
const move_map& srcdst() const { if(!move_maps_valid_) { prepare_move(); } return srcdst_; }

25
src/ai/game_info.cpp Normal file
View file

@ -0,0 +1,25 @@
/* $Id$ */
/*
Copyright (C) 2009 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.
*/
/**
* Base class for the AI and AI-ai_manager contract.
* @file ai/game_info.cpp
*/
#include "game_info.hpp"
// =======================================================================
//
// =======================================================================

62
src/ai/game_info.hpp Normal file
View file

@ -0,0 +1,62 @@
/* $Id$ */
/*
Copyright (C) 2003 - 2009 by David White <dave@whitevine.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.
*/
/**
* @file ai/game_info.hpp
* Game information for the AI
*/
#ifndef AI_GAME_INFO_HPP_INCLUDED
#define AI_GAME_INFO_HPP_INCLUDED
class game_display;
class gamemap;
#include "../gamestatus.hpp"
#include "../playturn.hpp"
/**
* info is structure which holds references to all the important objects
* that an AI might need access to, in order to make and implement its
* decisions.
*/
class ai_game_info {
public:
ai_game_info(game_display& disp, gamemap& map, unit_map& units,
std::vector<team>& teams, gamestatus& state, class game_state& game_state)
: disp(disp), map(map), units(units), teams(teams),
state(state), game_state_(game_state)
{}
/** The display object, used to draw the moves the AI makes. */
game_display& disp;
/** The map of the game -- use this object to find the terrain at any location. */
gamemap& map;
/** The map of units. It maps locations -> units. */
unit_map& units;
/** A list of the teams in the game. */
std::vector<team>& teams;
/** Information about what turn it is, and what time of day. */
gamestatus& state;
/** The global game state, because we may set the completion field. */
class game_state& game_state_;
};
#endif

View file

@ -38,7 +38,7 @@ void ai_testing::log_turn_end(unsigned int side)
void ai_testing::log_turn(const char* msg, unsigned int side)
{
ai_interface::info& i = ai_manager::get_ai_info();
ai_game_info& i = ai_manager::get_ai_info();
assert(side>=1);
team& current_team = i.teams[side-1];
@ -71,7 +71,7 @@ void ai_testing::log_victory(std::vector<unsigned int> winners)
void ai_testing::log_game_start()
{
ai_interface::info& i = ai_manager::get_ai_info();
ai_game_info& i = ai_manager::get_ai_info();
for (std::vector<team>::const_iterator tm = i.teams.begin(); tm != i.teams.end(); ++tm) {
int side = tm-i.teams.begin()+1;
LOG_AI_TESTING << "AI_IDENTIFIER"<<side<<": " << tm->ai_algorithm_identifier() <<std::endl;

View file

@ -22,6 +22,7 @@
#include "playsingle_controller.hpp"
#include "ai/ai_manager.hpp"
#include "ai/game_info.hpp"
#include "ai/testing.hpp"
#include "foreach.hpp"
#include "game_end_exceptions.hpp"
@ -64,7 +65,7 @@ playsingle_controller::playsingle_controller(const config& level,
browse_ = linger_ = true;
}
ai_interface::info ai_info(*gui_,map_,units_,teams_,status_, gamestate_);
ai_game_info ai_info(*gui_,map_,units_,teams_,status_, gamestate_);
ai_manager::set_ai_info(ai_info);
ai_manager::add_observer(this) ;
}