Some modifications to ai::recall_result and ai::recruit_result:

1) Use check_recall/recruit_location() instead of the AI-specific
checks in do_check_before().

2) Get rid of the redundant checks in do_execute().

An advantage of #1 is that the AI better supports sides with multiple
leaders, along with leader-specific recruit lists and recall filters
(not full support, but better support).
This commit is contained in:
J. Tyne 2013-02-08 21:31:59 +00:00
parent 0214c1b22f
commit ae5c69417f
2 changed files with 89 additions and 170 deletions

View file

@ -528,17 +528,18 @@ recall_result::recall_result(side_number side,
, where_(where)
, recall_location_(where)
, recall_from_(from)
, location_checked_(false)
{
}
bool recall_result::test_available_for_recalling(const team &my_team)
const unit * recall_result::get_recall_unit(const team &my_team)
{
const std::vector<unit>::const_iterator rec = find_if_matches_id(my_team.recall_list(), unit_id_);
if (rec == my_team.recall_list().end()) {
set_error(E_NOT_AVAILABLE_FOR_RECALLING);
return false;
return NULL;
}
return true;
return &*rec;
}
@ -551,74 +552,49 @@ bool recall_result::test_enough_gold(const team &my_team)
return true;
}
const unit *recall_result::get_leader()
{
unit_map::const_iterator my_leader = resources::units->find_leader(get_side());
if (my_leader == resources::units->end()){
set_error(E_NO_LEADER);
return NULL;
}
return &*my_leader;
}
bool recall_result::test_leader_on_keep(const unit &my_leader)
{
if (!resources::game_map->is_keep(my_leader.get_location())) {
set_error(E_LEADER_NOT_ON_KEEP);
return false;
}
return true;
}
bool recall_result::test_suitable_recall_location(const unit &my_leader)
{
recall_location_ = where_;
//If we have an off-board location, such as null_location, then the caller wants us to recall on 'any' possible tile.
if (!resources::game_map->on_board(recall_location_)) {
recall_location_ = pathfind::find_vacant_castle(my_leader);
}
if ( !can_recruit_on(my_leader, recall_location_) ) {
set_error(E_BAD_RECALL_LOCATION);
return false;
}
return true;
}
void recall_result::do_check_before()
{
LOG_AI_ACTIONS << " check_before " << *this << std::endl;
const team& my_team = get_my_team();
//Unit available for recalling?
if ( !test_available_for_recalling(my_team)) {
return;
}
const bool location_specified = recall_location_.valid();
//Enough gold?
if (!test_enough_gold(my_team)) {
return;
}
//Leader present?
const unit *my_leader = get_leader();
if (!my_leader) {
//Unit available for recalling?
const unit * to_recall = get_recall_unit(my_team);
if ( !to_recall ) {
return;
}
//Leader on keep?
if (!test_leader_on_keep(*my_leader)) {
// Leader available for recalling?
switch ( ::actions::check_recall_location(get_side(), recall_location_,
recall_from_, *to_recall) )
{
case ::actions::RECRUIT_NO_LEADER:
case ::actions::RECRUIT_NO_ABLE_LEADER:
set_error(E_NO_LEADER);
return;
}
//Try to get suitable recall location. Is suitable location available ?
if (!test_suitable_recall_location(*my_leader)) {
case ::actions::RECRUIT_NO_KEEP_LEADER:
set_error(E_LEADER_NOT_ON_KEEP);
return;
}
case ::actions::RECRUIT_NO_VACANCY:
set_error(E_BAD_RECALL_LOCATION);
return;
case ::actions::RECRUIT_ALTERNATE_LOCATION:
if ( location_specified ) {
set_error(E_BAD_RECALL_LOCATION);
return;
}
// No break. If the location was not specified, this counts as "OK".
case ::actions::RECRUIT_OK:
location_checked_ = true;
}
}
@ -662,29 +638,24 @@ void recall_result::do_execute()
LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
assert(is_success());
team& my_team = get_my_team();
const events::command_disabler disable_commands;
std::vector<unit>::iterator rec = find_if_matches_id(my_team.recall_list(), unit_id_);
assert(rec != my_team.recall_list().end());
// Assert that recall_location_ has been validated.
// This should be implied by is_success() once check_before() has been
// called, so this is a guard against future breakage.
assert(location_checked_);
const std::string &err = ::actions::find_recall_location(get_side(), recall_location_, recall_from_, *rec);
if(!err.empty()) {
set_error(AI_ACTION_FAILURE);
return;
} else {
::actions::recall_unit(unit_id_, my_team, recall_location_, recall_from_, preferences::show_ai_moves(), false);
// Do the actual recalling.
::actions::recall_unit(unit_id_, get_my_team(), recall_location_,
recall_from_, preferences::show_ai_moves(), false);
set_gamestate_changed();
try {
manager::raise_gamestate_changed();
} catch (...) {
is_ok(); //Silences "unchecked result" warning
throw;
}
set_gamestate_changed();
try {
manager::raise_gamestate_changed();
} catch (...) {
is_ok(); //Silences "unchecked result" warning
throw;
}
}
@ -703,23 +674,10 @@ recruit_result::recruit_result(side_number side,
, where_(where)
, recruit_location_(where)
, recruit_from_(from)
, num_(0)
, location_checked_(false)
{
}
const std::string &recruit_result::get_available_for_recruiting(const team &my_team)
{
const std::set<std::string> &recruit_set = my_team.recruits();
std::set<std::string>::const_iterator recruit = recruit_set.find(unit_name_);
if (recruit == recruit_set.end()) {
set_error(E_NOT_AVAILABLE_FOR_RECRUITING);
static std::string dummy;
return dummy;
}
num_ = std::distance(recruit_set.begin(),recruit);
return *recruit;
}
const unit_type *recruit_result::get_unit_type_known(const std::string &recruit)
{
const unit_type *type = unit_types.find(recruit);
@ -739,57 +697,14 @@ bool recruit_result::test_enough_gold(const team &my_team, const unit_type &type
return true;
}
const unit *recruit_result::get_leader()
{
unit_map::const_iterator my_leader = resources::units->find_leader(get_side());
if (my_leader == resources::units->end()){
set_error(E_NO_LEADER);
return NULL;
}
return &*my_leader;
}
bool recruit_result::test_leader_on_keep(const unit &my_leader)
{
if (!resources::game_map->is_keep(my_leader.get_location())) {
set_error(E_LEADER_NOT_ON_KEEP);
return false;
}
return true;
}
bool recruit_result::test_suitable_recruit_location(const unit &my_leader)
{
recruit_location_ = where_;
//If we have an off-board location, such as null_location, then the caller wants us to recruit on 'any' possible tile.
if (!resources::game_map->on_board(recruit_location_)) {
recruit_location_ = pathfind::find_vacant_castle(my_leader);
}
if ( !can_recruit_on(my_leader, recruit_location_) ) {
set_error(E_BAD_RECRUIT_LOCATION);
return false;
}
return true;
}
void recruit_result::do_check_before()
{
LOG_AI_ACTIONS << " check_before " << *this << std::endl;
const team& my_team = get_my_team();
//Unit available for recruiting?
const std::string &s_recruit = get_available_for_recruiting(my_team);
if (s_recruit.empty()) {
return;
}
const bool location_specified = recruit_location_.valid();
//Unit type known ?
const unit_type *s_type = get_unit_type_known(s_recruit);
const unit_type *s_type = get_unit_type_known(unit_name_);
if (!s_type) {
return;
}
@ -799,24 +714,32 @@ void recruit_result::do_check_before()
return;
}
//Leader present?
const unit *my_leader = get_leader();
if (!my_leader ) {
// Leader available for recruiting?
switch ( ::actions::check_recruit_location(get_side(), recruit_location_,
recruit_from_, unit_name_) )
{
case ::actions::RECRUIT_NO_LEADER:
case ::actions::RECRUIT_NO_ABLE_LEADER:
set_error(E_NO_LEADER);
return;
}
//Leader on keep?
if (!test_leader_on_keep(*my_leader)) {
case ::actions::RECRUIT_NO_KEEP_LEADER:
set_error(E_LEADER_NOT_ON_KEEP);
return;
}
//Try to get suitable recruit location. Is suitable location available ?
if (!test_suitable_recruit_location(*my_leader)) {
case ::actions::RECRUIT_NO_VACANCY:
set_error(E_BAD_RECRUIT_LOCATION);
return;
}
case ::actions::RECRUIT_ALTERNATE_LOCATION:
if ( location_specified ) {
set_error(E_BAD_RECRUIT_LOCATION);
return;
}
// No break. If the location was not specified, this counts as "OK".
case ::actions::RECRUIT_OK:
location_checked_ = true;
}
}
@ -863,19 +786,25 @@ void recruit_result::do_execute()
const unit_type *u = unit_types.find(unit_name_);
const events::command_disabler disable_commands;
const std::string recruit_err = ::actions::find_recruit_location(get_side(), recruit_location_, recruit_from_, u->id());
if(recruit_err.empty()) {
recorder.add_recruit(num_,recruit_location_,recruit_from_);
::actions::recruit_unit(*u, get_side(), recruit_location_, recruit_from_, preferences::show_ai_moves(), true);
set_gamestate_changed();
try {
manager::raise_gamestate_changed();
} catch (...) {
is_ok(); //Silences "unchecked result" warning
throw;
}
} else {
set_error(AI_ACTION_FAILURE);
// Assert that recruit_location_ has been validated.
// This should be implied by is_success() once check_before() has been
// called, so this is a guard against future breakage.
assert(location_checked_ && u != NULL);
// Calculate the index to be fed to the recorder.
const std::set<std::string> recruit_set = get_my_team().recruits();
int num = std::distance(recruit_set.begin(), recruit_set.find(unit_name_));
recorder.add_recruit(num, recruit_location_, recruit_from_);
::actions::recruit_unit(*u, get_side(), recruit_location_, recruit_from_,
preferences::show_ai_moves(), true);
set_gamestate_changed();
try {
manager::raise_gamestate_changed();
} catch (...) {
is_ok(); //Silences "unchecked result" warning
throw;
}
}

View file

@ -217,20 +217,16 @@ protected:
virtual void do_execute();
virtual void do_init_for_execution();
private:
bool test_available_for_recalling(
const unit * get_recall_unit(
const team& my_team);
bool test_enough_gold(
const team& my_team);
const unit *get_leader();
bool test_leader_on_keep(
const unit &my_leader);
bool test_suitable_recall_location(
const unit &my_leader);
const std::string& unit_id_;
const map_location where_;
map_location recall_location_;
map_location recall_from_;
bool location_checked_;
};
class recruit_result : public action_result {
@ -253,23 +249,17 @@ protected:
virtual void do_execute();
virtual void do_init_for_execution();
private:
const std::string &get_available_for_recruiting(
const team& my_team);
const unit_type *get_unit_type_known(
const std::string &recruit);
bool test_enough_gold(
const team& my_team,
const unit_type &type );
const unit *get_leader();
bool test_leader_on_keep(
const unit &my_leader);
bool test_suitable_recruit_location (
const unit &my_leader);
const std::string& unit_name_;
const map_location& where_;
map_location recruit_location_;
map_location recruit_from_;
int num_;
bool location_checked_;
};
class stopunit_result : public action_result {