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:
parent
74e1e20612
commit
75f6779ee8
19 changed files with 1040 additions and 797 deletions
|
@ -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"
|
||||
>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
440
src/ai/ai.cpp
440
src/ai/ai.cpp
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
}
|
||||
|
|
|
@ -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
541
src/ai/contexts.cpp
Normal 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
314
src/ai/contexts.hpp
Normal 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
|
|
@ -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
25
src/ai/game_info.cpp
Normal 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
62
src/ai/game_info.hpp
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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) ;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue