mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-07 20:54:52 +00:00
Rewrote the AI village assigning algorithm,
...instead of using brute force it now tries to optimize before resorting to brute force (bug #7215). It's rather late in the dev cycle for this commit but I tested it pretty intensively and it can be easily reverted if it does cause problems. NOTE this commits adds a new file.
This commit is contained in:
parent
ef9f8e3a31
commit
a89fcd9bf6
@ -55,6 +55,8 @@ Version 1.3.13+svn:
|
||||
maximum number of auto-saves to keep, default value is 10, so the 11th
|
||||
oldest and all older auto-saves will be deleted
|
||||
* the MP server now always sends gzipped data
|
||||
* Rewrote the AI village assigning algorithm, instead of using brute force
|
||||
it now tries to optimize before resorting to brute force (bug #7215).
|
||||
|
||||
Version 1.3.13:
|
||||
* campaigns:
|
||||
|
@ -56,6 +56,7 @@ wesnoth_SOURCES = \
|
||||
ai_attack.cpp \
|
||||
ai_move.cpp \
|
||||
ai_python.cpp \
|
||||
ai_village.cpp \
|
||||
animated_game.cpp \
|
||||
attack_prediction.cpp \
|
||||
config_adapter.cpp \
|
||||
|
206
src/ai.cpp
206
src/ai.cpp
@ -561,8 +561,9 @@ gamemap::location ai_interface::move_unit_partial(location from, location to,
|
||||
return to;
|
||||
}
|
||||
|
||||
bool ai::multistep_move_possible(location from, location to, location via,
|
||||
std::map<location,paths>& possible_moves)
|
||||
bool ai::multistep_move_possible(const location& from,
|
||||
const location& to, const location& via,
|
||||
const std::map<location,paths>& possible_moves) const
|
||||
{
|
||||
const unit_map::const_iterator i = units_.find(from);
|
||||
if(i != units_.end()) {
|
||||
@ -946,14 +947,11 @@ void ai::do_move()
|
||||
unit_map::iterator leader = find_leader(units_,team_num_);
|
||||
|
||||
LOG_AI << "villages...\n";
|
||||
const bool got_village = get_villages(possible_moves,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,leader);
|
||||
if(got_village) {
|
||||
if(get_villages(possible_moves, dstsrc, enemy_dstsrc, leader)) {
|
||||
do_move();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_AI << "healing phase\n";
|
||||
|
||||
LOG_AI << "healing...\n";
|
||||
const bool healed_unit = get_healing(possible_moves,srcdst,enemy_dstsrc);
|
||||
if(healed_unit) {
|
||||
@ -1136,202 +1134,6 @@ void ai_interface::attack_enemy(const location u,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<gamemap::location,gamemap::location> > ai::get_village_combinations(
|
||||
std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst,
|
||||
const move_map& dstsrc, const move_map& enemy_srcdst,
|
||||
const move_map& enemy_dstsrc, unit_map::const_iterator leader,
|
||||
std::set<gamemap::location>& taken_villages,
|
||||
std::set<gamemap::location>& moved_units,
|
||||
const std::vector<std::pair<gamemap::location,gamemap::location> >& village_moves,
|
||||
std::vector<std::pair<gamemap::location,
|
||||
gamemap::location> >::const_iterator start_at)
|
||||
{
|
||||
int leader_distance_from_keep = -1;
|
||||
|
||||
std::vector<std::pair<location,location> > result;
|
||||
|
||||
for(std::vector<std::pair<location,location> >::const_iterator i = start_at;
|
||||
i != village_moves.end(); ++i) {
|
||||
|
||||
if(taken_villages.count(i->first) || moved_units.count(i->second)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int distance = -1;
|
||||
|
||||
if(leader != units_.end() && leader->first == i->second) {
|
||||
const location& start_pos = nearest_keep(leader->first);;
|
||||
distance = distance_between(start_pos,i->first);
|
||||
}
|
||||
|
||||
taken_villages.insert(i->first);
|
||||
moved_units.insert(i->second);
|
||||
|
||||
std::vector<std::pair<location,location> > res = get_village_combinations(
|
||||
possible_moves, srcdst, dstsrc, enemy_srcdst, enemy_dstsrc, leader,
|
||||
taken_villages, moved_units, village_moves, i+1);
|
||||
|
||||
// The result is better if it results in getting more villages,
|
||||
// or if it results in the same number of villages,
|
||||
// but the leader ends closer to their keep.
|
||||
const bool result_better = res.size() >= result.size() ||
|
||||
res.size()+1 == result.size() &&
|
||||
distance != -1 &&
|
||||
distance < leader_distance_from_keep;
|
||||
|
||||
if(result_better) {
|
||||
result.swap(res);
|
||||
result.push_back(*i);
|
||||
|
||||
if(distance != -1) {
|
||||
leader_distance_from_keep = distance;
|
||||
}
|
||||
}
|
||||
|
||||
taken_villages.erase(i->first);
|
||||
moved_units.erase(i->second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool ai::get_villages(std::map<gamemap::location,paths>& possible_moves,
|
||||
const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst,
|
||||
const move_map& enemy_dstsrc, unit_map::iterator &leader)
|
||||
{
|
||||
LOG_AI << "deciding which villages we want...\n";
|
||||
|
||||
location start_pos;
|
||||
|
||||
if(leader != units_.end()) {
|
||||
start_pos = nearest_keep(leader->first);
|
||||
}
|
||||
|
||||
std::map<location,double> vulnerability;
|
||||
|
||||
// We want to build up a list of possible moves we can make
|
||||
// that will capture villages.
|
||||
// Limit the moves to 'max_village_moves' to make sure
|
||||
// things don't get out of hand.
|
||||
const size_t max_village_moves = 50;
|
||||
std::vector<std::pair<location,location> > village_moves;
|
||||
for(move_map::const_iterator j = dstsrc.begin();
|
||||
j != dstsrc.end() && village_moves.size() < max_village_moves; ++j) {
|
||||
|
||||
if(map_.is_village(j->first) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool want_village = true, owned = false;
|
||||
for(size_t n = 0; n != teams_.size(); ++n) {
|
||||
owned = teams_[n].owns_village(j->first);
|
||||
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 == units_.end()) {
|
||||
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 &&
|
||||
leader != units_.end() &&
|
||||
current_team().gold() > 20 &&
|
||||
leader->first == j->second &&
|
||||
leader->first != start_pos &&
|
||||
multistep_move_possible(j->second,j->first,start_pos,possible_moves) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double threat = 0.0;
|
||||
const std::map<location,double>::const_iterator vuln = vulnerability.find(j->first);
|
||||
if(vuln != vulnerability.end()) {
|
||||
threat = vuln->second;
|
||||
} else {
|
||||
threat = power_projection(j->first,enemy_dstsrc);
|
||||
vulnerability.insert(std::pair<location,double>(j->first,threat));
|
||||
}
|
||||
|
||||
const unit_map::const_iterator u = units_.find(j->second);
|
||||
if(u == units_.end() || utils::string_bool(u->second.get_state("guardian"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const unit& un = u->second;
|
||||
if(un.hitpoints() < (threat*2*un.defense_modifier(map_.get_terrain(j->first)))/100) {
|
||||
continue;
|
||||
}
|
||||
|
||||
village_moves.push_back(*j);
|
||||
}
|
||||
|
||||
std::set<location> taken_villages, moved_units;
|
||||
const int ticks = SDL_GetTicks();
|
||||
LOG_AI << "get_villages()..." << village_moves.size() << "\n";
|
||||
const std::vector<std::pair<location,location> >& moves = get_village_combinations(
|
||||
possible_moves, srcdst, dstsrc, enemy_srcdst, enemy_dstsrc, leader,
|
||||
taken_villages, moved_units, village_moves, village_moves.begin());
|
||||
|
||||
LOG_AI << "get_villages() done: " << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
// 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<location,location> leader_move;
|
||||
|
||||
int moves_made = 0;
|
||||
for(std::vector<std::pair<location,location> >::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) {
|
||||
const location loc = move_unit(i->second,i->first,possible_moves);
|
||||
++moves_made;
|
||||
leader = find_leader(units_, team_num_);
|
||||
|
||||
// If we didn't make it to the destination, it means we were ambushed.
|
||||
if(loc != i->first) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const unit_map::const_iterator new_unit = units_.find(loc);
|
||||
|
||||
if(new_unit != units_.end() &&
|
||||
power_projection(i->first,enemy_dstsrc) >= new_unit->second.hitpoints()/4) {
|
||||
add_target(target(new_unit->first,1.0,target::SUPPORT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(leader_move.second.valid()) {
|
||||
if(units_.count(leader_move.first) == 0) {
|
||||
gamemap::location loc = move_unit(leader_move.second,leader_move.first,possible_moves);
|
||||
++moves_made;
|
||||
// Update leader iterator, since we moved it.
|
||||
leader = units_.find(loc);
|
||||
}
|
||||
}
|
||||
|
||||
return moves_made > 0 && village_moves.size() == max_village_moves;
|
||||
}
|
||||
|
||||
bool ai::get_healing(std::map<gamemap::location,paths>& possible_moves,
|
||||
const move_map& srcdst, const move_map& enemy_dstsrc)
|
||||
{
|
||||
|
27
src/ai.hpp
27
src/ai.hpp
@ -75,8 +75,7 @@ protected:
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc);
|
||||
virtual bool get_villages(std::map<gamemap::location,paths>& possible_moves,
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
|
||||
const move_map& dstsrc, const move_map& enemy_dstsrc,
|
||||
unit_map::iterator &leader);
|
||||
virtual bool get_healing(std::map<gamemap::location,paths>& possible_moves,
|
||||
const move_map& srcdst, const move_map& enemy_dstsrc);
|
||||
@ -111,16 +110,6 @@ protected:
|
||||
|
||||
bool threats_found_;
|
||||
|
||||
//! Calculate which movements should be made to get an optimal number of villages.
|
||||
std::vector<std::pair<gamemap::location,gamemap::location> > get_village_combinations(
|
||||
std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst,
|
||||
const move_map& dstsrc, const move_map& enemy_srcdst,
|
||||
const move_map& enemy_dstsrc, unit_map::const_iterator leader,
|
||||
std::set<location>& taken_villages, std::set<location>& moved_units,
|
||||
const std::vector<std::pair<gamemap::location,gamemap::location> >& village_moves,
|
||||
std::vector<std::pair<gamemap::location,gamemap::location> >::const_iterator start_at);
|
||||
|
||||
|
||||
//! Our own version of 'move_unit'. It is like the version in ai_interface,
|
||||
//! however if it is the leader moving, it will first attempt recruitment.
|
||||
location move_unit(location from, location to, std::map<location,paths>& possible_moves);
|
||||
@ -132,8 +121,9 @@ protected:
|
||||
std::set<location> attacks_;
|
||||
|
||||
//! Sees if it's possible for a unit to move 'from' -> 'via' -> 'to' all in one turn.
|
||||
bool multistep_move_possible(location from, location to, location via,
|
||||
std::map<location,paths>& possible_moves);
|
||||
bool multistep_move_possible(const location& from,
|
||||
const location& to, const location& via,
|
||||
const std::map<location,paths>& possible_moves) const;
|
||||
|
||||
struct attack_analysis
|
||||
{
|
||||
@ -319,6 +309,15 @@ protected:
|
||||
int attack_depth();
|
||||
int attack_depth_;
|
||||
friend struct attack_analysis;
|
||||
|
||||
private:
|
||||
void find_villages(/*std::vector<unit_map::const_iterator>& our_units,
|
||||
std::vector<std::vector<gamemap::location> >& reachable_villages, */
|
||||
std::map<gamemap::location /*unit location*/, std::vector<gamemap::location /* villages we can reach*/> >& reachmap,
|
||||
std::vector<std::pair<gamemap::location,gamemap::location> >& moves,
|
||||
const std::multimap<gamemap::location,gamemap::location>& dstsrc,
|
||||
const std::map<gamemap::location,paths>& possible_moves,
|
||||
const std::multimap<gamemap::location,gamemap::location>& enemy_dstsrc) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
1013
src/ai_village.cpp
Normal file
1013
src/ai_village.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user