ai_composite: new candidate action:
...testing_ai_default::get_villages_phase
This commit is contained in:
parent
91e4767d74
commit
3c7a993eda
6 changed files with 946 additions and 15 deletions
|
@ -238,7 +238,7 @@ void readonly_context_impl::log_message(const std::string& msg)
|
|||
|
||||
|
||||
map_location readwrite_context_impl::move_unit(map_location from, map_location to,
|
||||
std::map<map_location,paths>& possible_moves)
|
||||
const moves_map &possible_moves)
|
||||
{
|
||||
const map_location loc = move_unit_partial(from,to,possible_moves);
|
||||
const unit_map::iterator u = get_info().units.find(loc);
|
||||
|
@ -256,7 +256,7 @@ map_location readwrite_context_impl::move_unit(map_location from, map_location t
|
|||
|
||||
|
||||
map_location readwrite_context_impl::move_unit_partial(map_location from, map_location to,
|
||||
std::map<map_location,paths>& possible_moves)
|
||||
const moves_map &possible_moves)
|
||||
{
|
||||
LOG_AI << "readwrite_context_impl::move_unit " << from << " -> " << to << '\n';
|
||||
assert(to.valid() && to.x <= MAX_MAP_AREA && to.y <= MAX_MAP_AREA);
|
||||
|
@ -280,12 +280,12 @@ map_location readwrite_context_impl::move_unit_partial(map_location from, map_lo
|
|||
|
||||
const bool show_move = preferences::show_ai_moves();
|
||||
|
||||
const std::map<map_location,paths>::iterator p_it = possible_moves.find(from);
|
||||
const std::map<map_location,paths>::const_iterator p_it = possible_moves.find(from);
|
||||
|
||||
std::vector<map_location> steps;
|
||||
|
||||
if(p_it != possible_moves.end()) {
|
||||
paths& p = p_it->second;
|
||||
const paths& p = p_it->second;
|
||||
paths::dest_vect::const_iterator rt = p.destinations.find(to);
|
||||
if (rt != p.destinations.end())
|
||||
{
|
||||
|
|
|
@ -195,8 +195,8 @@ public:
|
|||
virtual stopunit_result_ptr execute_stopunit_action(const map_location& unit_location, bool remove_movement = true, bool remove_attacks = false) = 0;
|
||||
virtual team& current_team_w() = 0;
|
||||
virtual void attack_enemy(const map_location u, const map_location target, int att_weapon, int def_weapon) = 0;
|
||||
virtual map_location move_unit(map_location from, map_location to, std::map<map_location,paths>& possible_moves) = 0;
|
||||
virtual map_location move_unit_partial(map_location from, map_location to, std::map<map_location,paths>& possible_moves) = 0;
|
||||
virtual map_location move_unit(map_location from, map_location to, const moves_map &possible_moves) = 0;
|
||||
virtual map_location move_unit_partial(map_location from, map_location to, const moves_map &possible_moves) = 0;
|
||||
virtual bool recruit(const std::string& unit_name, map_location loc=map_location()) = 0;
|
||||
virtual void raise_unit_recruited() const = 0;
|
||||
virtual void raise_unit_moved() const = 0;
|
||||
|
@ -458,13 +458,13 @@ public:
|
|||
}
|
||||
|
||||
|
||||
virtual map_location move_unit(map_location from, map_location to, std::map<map_location,paths>& possible_moves)
|
||||
virtual map_location move_unit(map_location from, map_location to, const moves_map &possible_moves)
|
||||
{
|
||||
return target_->move_unit(from, to, possible_moves);
|
||||
}
|
||||
|
||||
|
||||
virtual map_location move_unit_partial(map_location from, map_location to, std::map<map_location,paths>& possible_moves)
|
||||
virtual map_location move_unit_partial(map_location from, map_location to, const moves_map &possible_moves)
|
||||
{
|
||||
return target_->move_unit_partial(from, to, possible_moves);
|
||||
}
|
||||
|
@ -826,14 +826,14 @@ public:
|
|||
* @param possible_moves The map of possible moves, as obtained from
|
||||
* 'calculate_possible_moves'.
|
||||
*/
|
||||
virtual map_location move_unit(map_location from, map_location to, std::map<map_location,paths>& possible_moves);
|
||||
virtual map_location move_unit(map_location from, map_location to, const moves_map &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);
|
||||
map_location move_unit_partial(map_location from, map_location to, const moves_map &possible_moves);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -550,6 +550,51 @@ bool default_ai_context_impl::leader_can_reach_keep()
|
|||
}
|
||||
|
||||
|
||||
bool default_ai_context_impl::multistep_move_possible(const map_location& from,
|
||||
const map_location& to, const map_location& via,
|
||||
const moves_map& possible_moves) const
|
||||
{
|
||||
unit_map &units_ = get_info().units;
|
||||
const unit_map::const_iterator i = units_.find(from);
|
||||
if(i != units_.end()) {
|
||||
if(from != via && to != via && units_.count(via) == 0) {
|
||||
LOG_AI << "when seeing if leader can move from "
|
||||
<< from << " -> " << to
|
||||
<< " seeing if can detour to keep at " << via << '\n';
|
||||
const std::map<map_location,paths>::const_iterator moves = possible_moves.find(from);
|
||||
if(moves != possible_moves.end()) {
|
||||
|
||||
LOG_AI << "found leader moves..\n";
|
||||
|
||||
// See if the unit can make it to 'via', and if it can,
|
||||
// how much movement it will have left when it gets there.
|
||||
paths::dest_vect::const_iterator itor =
|
||||
moves->second.destinations.find(via);
|
||||
if (itor != moves->second.destinations.end())
|
||||
{
|
||||
LOG_AI << "Can make it to keep with " << itor->move_left << " movement left.\n";
|
||||
unit temp_unit(i->second);
|
||||
temp_unit.set_movement(itor->move_left);
|
||||
const temporary_unit_placer unit_placer(units_,via,temp_unit);
|
||||
const paths unit_paths(get_info().map,units_,via,get_info().teams,false,false,current_team());
|
||||
|
||||
LOG_AI << "Found " << unit_paths.destinations.size() << " moves for temp leader.\n";
|
||||
|
||||
// See if this leader could make it back to the keep.
|
||||
if (unit_paths.destinations.contains(to)) {
|
||||
LOG_AI << "can make it back to the keep\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const map_location& default_ai_context_impl::nearest_keep(const map_location& loc)
|
||||
{
|
||||
const std::set<map_location>& keeps = this->keeps();
|
||||
|
|
|
@ -193,6 +193,11 @@ public:
|
|||
virtual bool leader_can_reach_keep() = 0;
|
||||
|
||||
|
||||
virtual bool multistep_move_possible(const map_location& from,
|
||||
const map_location& to, const map_location& via,
|
||||
const moves_map& possible_moves) const = 0;
|
||||
|
||||
|
||||
virtual const map_location& nearest_keep(const map_location& loc) = 0;
|
||||
|
||||
|
||||
|
@ -326,6 +331,14 @@ public:
|
|||
}
|
||||
|
||||
|
||||
virtual bool multistep_move_possible(const map_location& from,
|
||||
const map_location& to, const map_location& via,
|
||||
const moves_map& possible_moves) const
|
||||
{
|
||||
return target_->multistep_move_possible(from,to,via,possible_moves);
|
||||
}
|
||||
|
||||
|
||||
virtual const map_location& nearest_keep( const map_location& loc )
|
||||
{
|
||||
return target_->nearest_keep(loc);
|
||||
|
@ -433,6 +446,11 @@ public:
|
|||
virtual const std::set<map_location>& keeps();
|
||||
|
||||
|
||||
virtual bool multistep_move_possible(const map_location& from,
|
||||
const map_location& to, const map_location& via,
|
||||
const moves_map& possible_moves) const;
|
||||
|
||||
|
||||
virtual const map_location& nearest_keep( const map_location& loc );
|
||||
|
||||
|
||||
|
|
|
@ -16,13 +16,14 @@
|
|||
* Default AI (Testing)
|
||||
* @file ai/testing/ca.cpp
|
||||
*/
|
||||
#include "../../global.hpp"
|
||||
|
||||
#include "ca.hpp"
|
||||
#include "../composite/engine.hpp"
|
||||
#include "../composite/rca.hpp"
|
||||
#include "../../log.hpp"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
static lg::log_domain log_ai_testing_ai_default("ai/testing/ai_default");
|
||||
#define DBG_AI_TESTING_AI_DEFAULT LOG_STREAM(debug, log_ai_testing_ai_default)
|
||||
#define LOG_AI_TESTING_AI_DEFAULT LOG_STREAM(info, log_ai_testing_ai_default)
|
||||
|
@ -202,7 +203,8 @@ bool move_leader_to_goals_phase::execute()
|
|||
//==============================================================
|
||||
|
||||
get_villages_phase::get_villages_phase( rca_context &context, const config &cfg )
|
||||
: candidate_action(context,"testing_ai_default::get_villages_phase",cfg["type"])
|
||||
: candidate_action(context,"testing_ai_default::get_villages_phase",cfg["type"]), keep_loc_(),
|
||||
leader_loc_(),best_leader_loc_(),debug_(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -212,14 +214,803 @@ get_villages_phase::~get_villages_phase()
|
|||
|
||||
double get_villages_phase::evaluate()
|
||||
{
|
||||
ERR_AI_TESTING_AI_DEFAULT << get_name() << ": evaluate - not yet implemented!" << std::endl;
|
||||
moves_.clear();
|
||||
unit_map::const_iterator leader = get_info().units.find_leader(get_side());
|
||||
get_villages(get_possible_moves(),get_dstsrc(),get_enemy_dstsrc(),leader);
|
||||
if (moves_.size()>0) {
|
||||
return 25;
|
||||
}
|
||||
return BAD_SCORE;
|
||||
}
|
||||
|
||||
bool get_villages_phase::execute()
|
||||
{
|
||||
ERR_AI_TESTING_AI_DEFAULT << get_name() << ": execute - not yet implemented!" << std::endl;
|
||||
return true;
|
||||
bool gamestate_changed = false;
|
||||
unit_map &units_ = get_info().units;
|
||||
unit_map::const_iterator leader = units_.find_leader(get_side());
|
||||
// Move all the units to get villages, however move the leader last,
|
||||
// so that the castle will be cleared if it wants to stop to recruit along the way.
|
||||
std::pair<map_location,map_location> leader_move;
|
||||
|
||||
for(tmoves::const_iterator i = moves_.begin(); i != moves_.end(); ++i) {
|
||||
|
||||
if(leader != units_.end() && leader->first == i->second) {
|
||||
leader_move = *i;
|
||||
} else {
|
||||
if(units_.count(i->first) == 0) {
|
||||
move_result_ptr move_res = execute_move_action(i->second,i->first,true);
|
||||
gamestate_changed |= move_res->is_gamestate_changed();
|
||||
if (!move_res->is_ok()) {
|
||||
return gamestate_changed;
|
||||
}
|
||||
|
||||
const map_location loc = move_res->get_unit_location();
|
||||
leader = units_.find_leader(get_side());
|
||||
const unit_map::const_iterator new_unit = units_.find(loc);
|
||||
|
||||
if(new_unit != units_.end() &&
|
||||
power_projection(i->first,get_enemy_dstsrc()) >= new_unit->second.hitpoints()/4) {
|
||||
LOG_AI_TESTING_AI_DEFAULT << "found support target... " << new_unit->first << "\n";
|
||||
//FIXME: suokko tweaked the constant 1.0 to the formula:
|
||||
//25.0* current_team().caution() * power_projection(loc,enemy_dstsrc) / new_unit->second.hitpoints()
|
||||
//Is this an improvement?
|
||||
|
||||
//@todo 1.7 fix this to account for RCA semantics
|
||||
//add_target(target(new_unit->first,1.0,target::SUPPORT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(leader_move.second.valid()) {
|
||||
if(units_.count(leader_move.first) == 0 && get_info().map.is_village(leader_move.first)) {
|
||||
move_result_ptr move_res = execute_move_action(leader_move.second,leader_move.first,true);
|
||||
gamestate_changed |= move_res->is_gamestate_changed();
|
||||
if (!move_res->is_ok()) {
|
||||
return gamestate_changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return gamestate_changed;
|
||||
}
|
||||
|
||||
bool get_villages_phase::get_villages(const moves_map& possible_moves,
|
||||
const move_map& dstsrc, const move_map& enemy_dstsrc,
|
||||
unit_map::const_iterator &leader)
|
||||
{
|
||||
DBG_AI_TESTING_AI_DEFAULT << "deciding which villages we want...\n";
|
||||
unit_map &units_ = get_info().units;
|
||||
const int ticks = SDL_GetTicks();
|
||||
best_leader_loc_ = map_location::null_location;
|
||||
if(leader != units_.end()) {
|
||||
keep_loc_ = nearest_keep(leader->first);
|
||||
leader_loc_ = leader->first;
|
||||
} else {
|
||||
keep_loc_ = map_location::null_location;
|
||||
leader_loc_ = map_location::null_location;
|
||||
}
|
||||
|
||||
debug_ = !lg::debug.dont_log(log_ai_testing_ai_default);
|
||||
|
||||
// Find our units who can move.
|
||||
treachmap reachmap;
|
||||
for(unit_map::const_iterator u_itor = units_.begin();
|
||||
u_itor != units_.end(); ++u_itor) {
|
||||
|
||||
if(u_itor->second.side() == get_side()
|
||||
&& u_itor->second.movement_left()) {
|
||||
reachmap.insert(std::make_pair(u_itor->first, std::vector<map_location>()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units found who can try to capture a village.\n";
|
||||
|
||||
find_villages(reachmap, moves_, dstsrc, possible_moves, enemy_dstsrc);
|
||||
|
||||
treachmap::iterator itor = reachmap.begin();
|
||||
while(itor != reachmap.end()) {
|
||||
if(itor->second.size() == 0) {
|
||||
itor = remove_unit(reachmap, moves_, itor);
|
||||
} else {
|
||||
++itor;
|
||||
}
|
||||
}
|
||||
|
||||
if(reachmap.size()) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units left after removing the ones who "
|
||||
"can't reach a village, send the to the dispatcher.\n";
|
||||
|
||||
dump_reachmap(reachmap);
|
||||
|
||||
dispatch(reachmap, moves_);
|
||||
} else {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "No more units left after removing the ones who can't reach a village.\n";
|
||||
}
|
||||
|
||||
LOG_AI_TESTING_AI_DEFAULT << "Village assignment done: " << (SDL_GetTicks() - ticks)
|
||||
<< " ms, resulted in " << moves_.size() << " units being dispatched.\n";
|
||||
|
||||
}
|
||||
|
||||
void get_villages_phase::find_villages(
|
||||
treachmap& reachmap,
|
||||
tmoves& moves,
|
||||
const std::multimap<map_location,map_location>& dstsrc,
|
||||
const std::map<map_location,paths>& possible_moves,
|
||||
const std::multimap<map_location,map_location>& enemy_dstsrc)
|
||||
|
||||
{
|
||||
std::map<map_location, double> vulnerability;
|
||||
|
||||
const bool passive_leader = utils::string_bool(current_team().ai_parameters()["passive_leader"]);//@todo: make an aspect
|
||||
|
||||
size_t min_distance = 100000;
|
||||
gamemap &map_ = get_info().map;
|
||||
std::vector<team> &teams_ = get_info().teams;
|
||||
|
||||
// When a unit is dispatched we need to make sure we don't
|
||||
// dispatch this unit a second time, so store them here.
|
||||
std::vector<map_location> dispatched_units;
|
||||
for(std::multimap<map_location, map_location>::const_iterator
|
||||
j = dstsrc.begin();
|
||||
j != dstsrc.end(); ++j) {
|
||||
|
||||
const map_location ¤t_loc = j->first;
|
||||
|
||||
if(j->second == leader_loc_) {
|
||||
if(passive_leader) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t distance = distance_between(keep_loc_, current_loc);
|
||||
if(distance < min_distance) {
|
||||
min_distance = distance;
|
||||
best_leader_loc_ = current_loc;
|
||||
}
|
||||
}
|
||||
|
||||
if(std::find(dispatched_units.begin(), dispatched_units.end(),
|
||||
j->second) != dispatched_units.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(map_.is_village(current_loc) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool want_village = true, owned = false;
|
||||
for(size_t n = 0; n != teams_.size(); ++n) {
|
||||
owned = teams_[n].owns_village(current_loc);
|
||||
if(owned && !current_team().is_enemy(n+1)) {
|
||||
want_village = false;
|
||||
}
|
||||
|
||||
if(owned) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(want_village == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it is a neutral village, and we have no leader,
|
||||
// then the village is of no use to us, and we don't want it.
|
||||
if(!owned && leader_loc_ == map_location::null_location) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have a decent amount of gold, and the leader can't access
|
||||
// the keep this turn if they get this village,
|
||||
// then don't get this village with them.
|
||||
if(want_village &&
|
||||
current_team().gold() > 20 &&
|
||||
leader_loc_ == current_loc &&
|
||||
leader_loc_ != keep_loc_ &&
|
||||
multistep_move_possible(j->second, current_loc, keep_loc_, possible_moves) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double threat = 0.0;
|
||||
const std::map<map_location,double>::const_iterator vuln = vulnerability.find(current_loc);
|
||||
if(vuln != vulnerability.end()) {
|
||||
threat = vuln->second;
|
||||
} else {
|
||||
threat = power_projection(current_loc,enemy_dstsrc);
|
||||
vulnerability.insert(std::pair<map_location,double>(current_loc,threat));
|
||||
}
|
||||
|
||||
const unit_map::const_iterator u = get_info().units.find(j->second);
|
||||
if(u == get_info().units.end() || utils::string_bool(u->second.get_state("guardian"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const unit& un = u->second;
|
||||
//FIXME: suokko turned this 2:1 to 1.5:1.0.
|
||||
//and dropped the second term of the multiplication. Is that better?
|
||||
//const double threat_multipler = (current_loc == leader_loc?2:1) * current_team().caution() * 10;
|
||||
if(un.hitpoints() < (threat*2*un.defense_modifier(map_.get_terrain(current_loc)))/100) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the next and previous destination differs from our current destination,
|
||||
// we're the only one who can reach the village -> dispatch.
|
||||
std::multimap<map_location, map_location>::const_iterator next = j;
|
||||
++next; // j + 1 fails
|
||||
const bool at_begin = (j == dstsrc.begin());
|
||||
std::multimap<map_location, map_location>::const_iterator prev = j; //FIXME seems not to work
|
||||
if(!at_begin) {
|
||||
--prev;
|
||||
}
|
||||
#if 1
|
||||
if((next == dstsrc.end() || next->first != current_loc)
|
||||
&& (at_begin || prev->first != current_loc)) {
|
||||
|
||||
move_result_ptr move_check_res = check_move_action(j->second,j->first,true);
|
||||
if (move_check_res->is_ok()) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << j->second << " to village " << j->first << '\n';
|
||||
moves.push_back(std::make_pair(j->first, j->second));
|
||||
}
|
||||
reachmap.erase(j->second);
|
||||
dispatched_units.push_back(j->second);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
reachmap[j->second].push_back(current_loc);
|
||||
}
|
||||
|
||||
DBG_AI_TESTING_AI_DEFAULT << moves.size() << " units already dispatched, "
|
||||
<< reachmap.size() << " left to evaluate.\n";
|
||||
}
|
||||
|
||||
void get_villages_phase::dispatch(treachmap& reachmap, tmoves& moves)
|
||||
{
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Starting simple dispatch.\n";
|
||||
|
||||
// we now have a list with units with the villages they can reach.
|
||||
// keep trying the following steps as long as one of them changes
|
||||
// the state.
|
||||
// 1. Dispatch units who can reach 1 village (if more units can reach that
|
||||
// village only one can capture it, so use the first in the list.)
|
||||
// 2. Villages which can only be reached by one unit get that unit dispatched
|
||||
// to them.
|
||||
size_t village_count = 0;
|
||||
bool dispatched = true;
|
||||
while(dispatched) {
|
||||
dispatched = false;
|
||||
|
||||
if(dispatch_unit_simple(reachmap, moves)) {
|
||||
dispatched = true;
|
||||
} else {
|
||||
if(reachmap.empty()) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "dispatch_unit_simple() found a final solution.\n";
|
||||
break;
|
||||
} else {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "dispatch_unit_simple() couldn't dispatch more units.\n";
|
||||
}
|
||||
}
|
||||
|
||||
if(dispatch_village_simple(reachmap, moves, village_count)) {
|
||||
dispatched = true;
|
||||
} else {
|
||||
if(reachmap.empty()) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "dispatch_village_simple() found a final solution.\n";
|
||||
break;
|
||||
} else {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "dispatch_village_simple() couldn't dispatch more units.\n";
|
||||
}
|
||||
}
|
||||
|
||||
if(reachmap.size() != 0 && dispatched) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " unit(s) left restarting simple dispatching.\n";
|
||||
|
||||
dump_reachmap(reachmap);
|
||||
}
|
||||
}
|
||||
|
||||
if(reachmap.size() == 0) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "No units left after simple dispatcher.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units left for complex dispatch with "
|
||||
<< village_count << " villages left.\n";
|
||||
|
||||
dump_reachmap(reachmap);
|
||||
|
||||
dispatch_complex(reachmap, moves, village_count);
|
||||
}
|
||||
|
||||
// Returns need further processing
|
||||
// false Nothing has been modified or no units left
|
||||
bool get_villages_phase::dispatch_unit_simple(treachmap& reachmap, tmoves& moves)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
treachmap::iterator itor = reachmap.begin();
|
||||
while(itor != reachmap.end()) {
|
||||
if(itor->second.size() == 1) {
|
||||
const map_location village = itor->second[0];
|
||||
result = true;
|
||||
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << itor->first << " to village " << village << '\n';
|
||||
moves.push_back(std::make_pair(village, itor->first));
|
||||
reachmap.erase(itor++);
|
||||
|
||||
if(remove_village(reachmap, moves, village)) {
|
||||
itor = reachmap.begin();
|
||||
}
|
||||
|
||||
} else {
|
||||
++itor;
|
||||
}
|
||||
}
|
||||
|
||||
// Test special cases.
|
||||
if(reachmap.empty()) {
|
||||
// We're done.
|
||||
return false;
|
||||
}
|
||||
|
||||
if(reachmap.size() == 1) {
|
||||
// One unit left.
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Dispatched _last_ unit at " << reachmap.begin()->first
|
||||
<< " to village " << reachmap.begin()->second[0] << '\n';
|
||||
|
||||
moves.push_back(std::make_pair(
|
||||
reachmap.begin()->second[0], reachmap.begin()->first));
|
||||
|
||||
reachmap.clear();
|
||||
// We're done.
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool get_villages_phase::dispatch_village_simple(
|
||||
treachmap& reachmap, tmoves& moves, size_t& village_count)
|
||||
{
|
||||
|
||||
bool result = false;
|
||||
bool dispatched = true;
|
||||
while(dispatched) {
|
||||
dispatched = false;
|
||||
|
||||
// build the reverse map
|
||||
std::map<map_location /*village location*/,
|
||||
std::vector<map_location /* units that can reach it*/> >reversemap;
|
||||
|
||||
treachmap::const_iterator itor = reachmap.begin();
|
||||
for(;itor != reachmap.end(); ++itor) {
|
||||
|
||||
for(std::vector<map_location>::const_iterator
|
||||
v_itor = itor->second.begin();
|
||||
v_itor != itor->second.end(); ++v_itor) {
|
||||
|
||||
reversemap[*v_itor].push_back(itor->first);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
village_count = reversemap.size();
|
||||
|
||||
itor = reversemap.begin();
|
||||
while(itor != reversemap.end()) {
|
||||
if(itor->second.size() == 1) {
|
||||
// One unit can reach this village.
|
||||
const map_location village = itor->first;
|
||||
dispatched = true;
|
||||
result = true;
|
||||
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << itor->second[0] << " to village " << itor->first << '\n';
|
||||
moves.push_back(std::make_pair(itor->first, itor->second[0]));
|
||||
|
||||
reachmap.erase(itor->second[0]);
|
||||
remove_village(reachmap, moves, village);
|
||||
// Get can go to some trouble to remove the unit from the other villages
|
||||
// instead we abort this loop end do a full rebuild on the map.
|
||||
break;
|
||||
} else {
|
||||
++itor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool get_villages_phase::remove_village(
|
||||
treachmap& reachmap, tmoves& moves, const map_location& village)
|
||||
{
|
||||
bool result = false;
|
||||
treachmap::iterator itor = reachmap.begin();
|
||||
while(itor != reachmap.end()) {
|
||||
itor->second.erase(std::remove(itor->second.begin(), itor->second.end(), village), itor->second.end());
|
||||
if(itor->second.empty()) {
|
||||
result = true;
|
||||
itor = remove_unit(reachmap, moves, itor);
|
||||
} else {
|
||||
++itor;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
get_villages_phase::treachmap::iterator get_villages_phase::remove_unit(
|
||||
treachmap& reachmap, tmoves& moves, treachmap::iterator unit)
|
||||
{
|
||||
assert(unit->second.empty());
|
||||
|
||||
if(unit->first == leader_loc_ && best_leader_loc_ != map_location::null_location) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Dispatch leader at " << leader_loc_ << " closer to the keep at "
|
||||
<< best_leader_loc_ << '\n';
|
||||
|
||||
moves.push_back(std::make_pair(best_leader_loc_, leader_loc_));
|
||||
}
|
||||
|
||||
reachmap.erase(unit++);
|
||||
return unit;
|
||||
}
|
||||
|
||||
void get_villages_phase::dispatch_complex(
|
||||
treachmap& reachmap, tmoves& moves, const size_t village_count)
|
||||
{
|
||||
// ***** ***** Init and dispatch if every unit can reach every village.
|
||||
|
||||
const size_t unit_count = reachmap.size();
|
||||
// The maximum number of villages we can capture with the available units.
|
||||
const size_t max_result = unit_count < village_count ? unit_count : village_count;
|
||||
|
||||
assert(unit_count >= 2 && village_count >= 2);
|
||||
|
||||
// Every unit can reach every village.
|
||||
if(unit_count == 2 && village_count == 2) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Every unit can reach every village for 2 units, dispatch them.\n";
|
||||
full_dispatch(reachmap, moves);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<map_location> units(unit_count);
|
||||
std::vector<size_t> villages_per_unit(unit_count);
|
||||
std::vector<map_location> villages;
|
||||
std::vector<size_t> units_per_village(village_count);
|
||||
|
||||
// We want to test the units, the ones who can reach the least
|
||||
// villages first so this is our lookup map.
|
||||
std::multimap<size_t /* villages_per_unit value*/,
|
||||
size_t /*villages_per_unit index*/> unit_lookup;
|
||||
|
||||
std::vector</*unit*/std::vector</*village*/bool> >
|
||||
matrix(reachmap.size(), std::vector<bool>(village_count, false));
|
||||
|
||||
treachmap::const_iterator itor = reachmap.begin();
|
||||
for(size_t u = 0; u < unit_count; ++u, ++itor) {
|
||||
units[u] = itor->first;
|
||||
villages_per_unit[u] = itor->second.size();
|
||||
unit_lookup.insert(std::make_pair(villages_per_unit[u], u));
|
||||
|
||||
assert(itor->second.size() >= 2);
|
||||
|
||||
for(size_t v = 0; v < itor->second.size(); ++v) {
|
||||
|
||||
size_t v_index;
|
||||
// find the index of the v in the villages
|
||||
std::vector<map_location>::const_iterator v_itor =
|
||||
std::find(villages.begin(), villages.end(), itor->second[v]);
|
||||
if(v_itor == villages.end()) {
|
||||
v_index = villages.size(); // will be the last element after push_back.
|
||||
villages.push_back(itor->second[v]);
|
||||
} else {
|
||||
v_index = v_itor - villages.begin();
|
||||
}
|
||||
|
||||
units_per_village[v_index]++;
|
||||
|
||||
matrix[u][v_index] = true;
|
||||
}
|
||||
}
|
||||
for(std::vector<size_t>::const_iterator upv_it = units_per_village.begin();
|
||||
upv_it != units_per_village.end(); ++upv_it) {
|
||||
|
||||
assert(*upv_it >=2);
|
||||
}
|
||||
|
||||
if(debug_) {
|
||||
// Print header
|
||||
std::cerr << "Reach matrix:\n\nvillage";
|
||||
size_t u, v;
|
||||
for(v = 0; v < village_count; ++v) {
|
||||
std::cerr << '\t' << villages[v];
|
||||
}
|
||||
std::cerr << "\ttotal\nunit\n";
|
||||
|
||||
// Print data
|
||||
for(u = 0; u < unit_count; ++u) {
|
||||
std::cerr << units[u];
|
||||
|
||||
for(size_t v = 0; v < village_count; ++v) {
|
||||
std::cerr << '\t' << matrix[u][v];
|
||||
}
|
||||
std::cerr << "\t" << villages_per_unit[u] << '\n';
|
||||
}
|
||||
|
||||
// Print footer
|
||||
std::cerr << "total";
|
||||
for(v = 0; v < village_count; ++v) {
|
||||
std::cerr << '\t' << units_per_village[v];
|
||||
}
|
||||
std::cerr << '\n';
|
||||
}
|
||||
|
||||
// Test the special case, everybody can reach all villages
|
||||
const bool reach_all = ((village_count == unit_count)
|
||||
&& (std::accumulate(villages_per_unit.begin(), villages_per_unit.end(), size_t())
|
||||
== (village_count * unit_count)));
|
||||
|
||||
if(reach_all) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Every unit can reach every village, dispatch them\n";
|
||||
full_dispatch(reachmap, moves);
|
||||
reachmap.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// ***** ***** Find a square
|
||||
std::multimap<size_t /* villages_per_unit value*/, size_t /*villages_per_unit index*/>
|
||||
::const_iterator src_itor = unit_lookup.begin();
|
||||
|
||||
while(src_itor != unit_lookup.end() && src_itor->first == 2) {
|
||||
|
||||
for(std::multimap<size_t, size_t>::const_iterator
|
||||
dst_itor = unit_lookup.begin();
|
||||
dst_itor != unit_lookup.end(); ++ dst_itor) {
|
||||
|
||||
// avoid comparing us with ourselves.
|
||||
if(src_itor == dst_itor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<bool> result;
|
||||
std::transform(matrix[src_itor->second].begin(), matrix[src_itor->second].end(),
|
||||
matrix[dst_itor->second].begin(),
|
||||
std::back_inserter(result),
|
||||
std::logical_and<bool>()
|
||||
);
|
||||
|
||||
size_t matched = std::count(result.begin(), result.end(), true);
|
||||
|
||||
// we found a solution, dispatch
|
||||
if(matched == 2) {
|
||||
// Collect data
|
||||
std::vector<bool>::iterator first = std::find(result.begin(), result.end(), true);
|
||||
std::vector<bool>::iterator second = std::find(first + 1, result.end(), true);
|
||||
|
||||
const map_location village1 = villages[first - result.begin()];
|
||||
const map_location village2 = villages[second - result.begin()];
|
||||
|
||||
const bool perfect = (src_itor->first == 2 &&
|
||||
dst_itor->first == 2 &&
|
||||
units_per_village[first - result.begin()] == 2 &&
|
||||
units_per_village[second - result.begin()] == 2);
|
||||
|
||||
// Dispatch
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Found a square.\nDispatched unit at " << units[src_itor->second]
|
||||
<< " to village " << village1 << '\n';
|
||||
moves.push_back(std::make_pair(village1, units[src_itor->second]));
|
||||
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << units[dst_itor->second]
|
||||
<< " to village " << village2 << '\n';
|
||||
moves.push_back(std::make_pair(village2, units[dst_itor->second]));
|
||||
|
||||
// Remove the units
|
||||
reachmap.erase(units[src_itor->second]);
|
||||
reachmap.erase(units[dst_itor->second]);
|
||||
|
||||
// Evaluate and start correct function.
|
||||
if(perfect) {
|
||||
// We did a perfect dispatch 2 units who could visit 2 villages.
|
||||
// This means we didn't change the assertion for this funtions
|
||||
// so call ourselves recursively, and finish afterwards.
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Perfect dispatch, do complex again.\n";
|
||||
dispatch_complex(reachmap, moves, village_count - 2);
|
||||
return;
|
||||
} else {
|
||||
// We did a not perfect dispatch but we did modify things
|
||||
// so restart dispatching.
|
||||
DBG_AI_TESTING_AI_DEFAULT << "NON Perfect dispatch, do dispatch again.\n";
|
||||
remove_village(reachmap, moves, village1);
|
||||
remove_village(reachmap, moves, village2);
|
||||
dispatch(reachmap, moves);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++src_itor;
|
||||
}
|
||||
|
||||
// ***** ***** Do all permutations.
|
||||
// Now walk through all possible permutations
|
||||
// - test whether the suggestion is possible
|
||||
// - does it result in max_villages
|
||||
// - dispatch and ready
|
||||
// - is it's result better as the last best
|
||||
// - store
|
||||
std::vector<std::pair<map_location, map_location> > best_result;
|
||||
|
||||
// Bruteforcing all possible permutations can result in a slow game.
|
||||
// So there needs to be a balance between the best possible result and
|
||||
// not too slow. From the test (at the end of the file) a good number is
|
||||
// picked. In general we shouldn't reach this point too often if we do
|
||||
// there are a lot of villages which are unclaimed and a lot of units
|
||||
// to claim them.
|
||||
const size_t max_options = 8;
|
||||
if(unit_count >= max_options && village_count >= max_options) {
|
||||
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Too many units " << unit_count << " and villages "
|
||||
<< village_count<<" found, evaluate only the first "
|
||||
<< max_options << " options;\n";
|
||||
|
||||
std::vector<size_t> perm (max_options, 0);
|
||||
for(size_t i =0; i < max_options; ++i) {
|
||||
perm[i] = i;
|
||||
}
|
||||
while(std::next_permutation(perm.begin(), perm.end())) {
|
||||
|
||||
// Get result for current permutation.
|
||||
std::vector<std::pair<map_location,map_location> > result;
|
||||
for(size_t u = 0; u < max_options; ++u) {
|
||||
if(matrix[u][perm[u]]) {
|
||||
result.push_back(std::make_pair(villages[perm[u]], units[u]));
|
||||
|
||||
}
|
||||
}
|
||||
if(result.size() == max_result) {
|
||||
best_result.swap(result);
|
||||
break;
|
||||
}
|
||||
|
||||
if(result.size() > best_result.size()) {
|
||||
best_result.swap(result);
|
||||
}
|
||||
}
|
||||
// End of loop no optimal found, assign the best
|
||||
std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
|
||||
|
||||
// Clean up the reachmap for dispatched units.
|
||||
for(std::vector<std::pair<map_location, map_location> >::const_iterator
|
||||
itor = best_result.begin(); itor != best_result.end(); ++itor) {
|
||||
reachmap.erase(itor->second);
|
||||
}
|
||||
|
||||
// Try to dispatch whatever is left
|
||||
dispatch(reachmap, moves);
|
||||
return;
|
||||
|
||||
} else if(unit_count <= village_count) {
|
||||
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Unit major\n";
|
||||
|
||||
std::vector<size_t> perm (unit_count, 0);
|
||||
for(size_t i =0; i < unit_count; ++i) {
|
||||
perm[i] = i;
|
||||
}
|
||||
while(std::next_permutation(perm.begin(), perm.end())) {
|
||||
// Get result for current permutation.
|
||||
std::vector<std::pair<map_location,map_location> > result;
|
||||
for(size_t u = 0; u < unit_count; ++u) {
|
||||
if(matrix[u][perm[u]]) {
|
||||
result.push_back(std::make_pair(villages[perm[u]], units[u]));
|
||||
|
||||
}
|
||||
}
|
||||
if(result.size() == max_result) {
|
||||
std::copy(result.begin(), result.end(), std::back_inserter(moves));
|
||||
reachmap.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if(result.size() > best_result.size()) {
|
||||
best_result.swap(result);
|
||||
}
|
||||
}
|
||||
// End of loop no optimal found, assign the best
|
||||
std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
|
||||
|
||||
// clean up the reachmap we need to test whether the leader is still there
|
||||
// and if so remove him manually to get him dispatched.
|
||||
for(std::vector<std::pair<map_location, map_location> >::const_iterator
|
||||
itor = best_result.begin(); itor != best_result.end(); ++itor) {
|
||||
reachmap.erase(itor->second);
|
||||
}
|
||||
treachmap::iterator unit = reachmap.find(leader_loc_);
|
||||
if(unit != reachmap.end()) {
|
||||
unit->second.clear();
|
||||
remove_unit(reachmap, moves, unit);
|
||||
}
|
||||
reachmap.clear();
|
||||
|
||||
} else {
|
||||
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Village major\n";
|
||||
|
||||
std::vector<size_t> perm (village_count, 0);
|
||||
for(size_t i =0; i < village_count; ++i) {
|
||||
perm[i] = i;
|
||||
}
|
||||
while(std::next_permutation(perm.begin(), perm.end())) {
|
||||
// Get result for current permutation.
|
||||
std::vector<std::pair<map_location,map_location> > result;
|
||||
for(size_t v = 0; v < village_count; ++v) {
|
||||
if(matrix[perm[v]][v]) {
|
||||
result.push_back(std::make_pair(villages[v], units[perm[v]]));
|
||||
|
||||
}
|
||||
}
|
||||
if(result.size() == max_result) {
|
||||
std::copy(result.begin(), result.end(), std::back_inserter(moves));
|
||||
reachmap.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if(result.size() > best_result.size()) {
|
||||
best_result.swap(result);
|
||||
}
|
||||
}
|
||||
// End of loop no optimal found, assigne the best
|
||||
std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
|
||||
|
||||
// clean up the reachmap we need to test whether the leader is still there
|
||||
// and if so remove him manually to get him dispatched.
|
||||
for(std::vector<std::pair<map_location, map_location> >::const_iterator
|
||||
itor = best_result.begin(); itor != best_result.end(); ++itor) {
|
||||
reachmap.erase(itor->second);
|
||||
}
|
||||
treachmap::iterator unit = reachmap.find(leader_loc_);
|
||||
if(unit != reachmap.end()) {
|
||||
unit->second.clear();
|
||||
remove_unit(reachmap, moves, unit);
|
||||
}
|
||||
reachmap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void get_villages_phase::full_dispatch(treachmap& reachmap, tmoves& moves)
|
||||
{
|
||||
treachmap::const_iterator itor = reachmap.begin();
|
||||
for(size_t i = 0; i < reachmap.size(); ++i, ++itor) {
|
||||
DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << itor->first
|
||||
<< " to village " << itor->second[i] << '\n';
|
||||
moves.push_back(std::make_pair(itor->second[i], itor->first));
|
||||
}
|
||||
}
|
||||
|
||||
void get_villages_phase::dump_reachmap(treachmap& reachmap)
|
||||
{
|
||||
if(!debug_) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(treachmap::const_iterator itor =
|
||||
reachmap.begin(); itor != reachmap.end(); ++itor) {
|
||||
|
||||
std::cerr << "Reachlist for unit at " << itor->first;
|
||||
|
||||
if(itor->second.empty()) {
|
||||
std::cerr << "\tNone";
|
||||
}
|
||||
|
||||
for(std::vector<map_location>::const_iterator
|
||||
v_itor = itor->second.begin();
|
||||
v_itor != itor->second.end(); ++v_itor) {
|
||||
|
||||
std::cerr << '\t' << *v_itor;
|
||||
}
|
||||
std::cerr << '\n';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================
|
||||
|
|
|
@ -114,6 +114,83 @@ public:
|
|||
virtual double evaluate();
|
||||
|
||||
virtual bool execute();
|
||||
private:
|
||||
/** Location of the keep the closest to our leader. */
|
||||
map_location keep_loc_;
|
||||
|
||||
/** Locaton of our leader. */
|
||||
map_location leader_loc_;
|
||||
|
||||
/** The best possible location for our leader if it can't reach a village. */
|
||||
map_location best_leader_loc_;
|
||||
|
||||
/** debug log level for AI enabled? */
|
||||
bool debug_;
|
||||
|
||||
typedef std::map<map_location /* unit location */,
|
||||
std::vector<map_location /* villages we can reach */> > treachmap;
|
||||
|
||||
typedef std::vector<std::pair<map_location /* destination */,
|
||||
map_location /* start */ > > tmoves;
|
||||
|
||||
|
||||
// The list of moves we want to make
|
||||
tmoves moves_;
|
||||
|
||||
|
||||
/** Dispatches all units to their best location. */
|
||||
void dispatch(treachmap& reachmap, tmoves& moves);
|
||||
|
||||
|
||||
/**
|
||||
* Dispatches all units who can reach one village.
|
||||
* Returns true if it modified reachmap isn't empty.
|
||||
*/
|
||||
bool dispatch_unit_simple(treachmap& reachmap, tmoves& moves);
|
||||
|
||||
|
||||
/*
|
||||
* Dispatches units to villages which can only be reached by one unit.
|
||||
* Returns true if modified reachmap and reachmap isn't empty.
|
||||
*/
|
||||
bool dispatch_village_simple(
|
||||
treachmap& reachmap, tmoves& moves, size_t& village_count);
|
||||
|
||||
|
||||
/** Removes a village for all units, returns true if anything is deleted. */
|
||||
bool remove_village(
|
||||
treachmap& reachmap, tmoves& moves, const map_location& village);
|
||||
|
||||
|
||||
/** Removes a unit which can't reach any village anymore. */
|
||||
treachmap::iterator remove_unit(
|
||||
treachmap& reachmap, tmoves& moves, treachmap::iterator unit);
|
||||
|
||||
|
||||
/** Dispatches the units to a village after the simple dispatching failed. */
|
||||
void dispatch_complex(
|
||||
treachmap& reachmap, tmoves& moves, const size_t village_count);
|
||||
|
||||
|
||||
/** Dispatches all units to a village, every unit can reach every village. */
|
||||
void full_dispatch(treachmap& reachmap, tmoves& moves);
|
||||
|
||||
|
||||
/** Shows which villages every unit can reach (debug function). */
|
||||
void dump_reachmap(treachmap& reachmap);
|
||||
|
||||
|
||||
bool get_villages(const moves_map &possible_moves,
|
||||
const move_map &dstsrc, const move_map &enemy_dstsrc,
|
||||
unit_map::const_iterator &leader);
|
||||
|
||||
|
||||
void find_villages(
|
||||
treachmap& reachmap,
|
||||
tmoves& moves,
|
||||
const std::multimap<map_location,map_location>& dstsrc,
|
||||
const std::map<map_location,paths>& possible_moves,
|
||||
const std::multimap<map_location,map_location>& enemy_dstsrc);
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue