Author: suokko
Date: Sun Sep 14 18:11:43 2008
New Revision: 29463
URL: http://svn.gna.org/viewcvs/wesnoth?rev=29463&view=rev
Log:
* Made AI to prefer recruiting over attacking/village grapping
* Added heurestics to select which keep leader should try to move
* other minor tweaks
Modified:
trunk/src/ai.cpp
trunk/src/ai.hpp
trunk/src/ai_move.cpp
trunk/src/ai_village.cpp
Modified: trunk/src/ai.cpp
URL:
http://svn.gna.org/viewcvs/wesnoth/trunk/src/ai.cpp?rev=29463&r1=29462&r2=29463&view=diff
==============================================================================
--- trunk/src/ai.cpp (original)
+++ trunk/src/ai.cpp Sun Sep 14 18:11:43 2008
@@ -47,6 +47,7 @@
#include <cassert>
#include <fstream>
+
#define DBG_AI LOG_STREAM(debug, ai)
#define LOG_AI LOG_STREAM(info, ai)
#define WRN_AI LOG_STREAM(warn, ai)
@@ -293,7 +294,8 @@
keeps_(),
avoid_(),
unit_stats_cache_(),
- attack_depth_(0)
+ attack_depth_(0),
+ recruiting_prefered_(false)
{}
void ai::new_turn()
@@ -335,10 +337,10 @@
{
const std::string& name = i->second.id();
// If usage is empty consider any unit.
- DBG_AI << name << " considered\n";
+// DBG_AI << name << " considered\n";
if (i->second.usage() == usage || usage == "") {
if (!recruits.count(name)) {
- DBG_AI << name << " rejected, not in
recruitment list\n";
+// DBG_AI << name << " rejected, not in
recruitment list\n";
continue;
}
LOG_AI << name << " considered for " << usage << "
recruitment\n";
@@ -489,7 +491,7 @@
if(u->second.movement_left()==u->second.total_movement()) {
u->second.set_movement(0);
u->second.set_state("not_moved","yes");
- } else {
+ } else if (from == loc) {
u->second.set_movement(0);
}
}
@@ -532,25 +534,26 @@
}
if(rt != p.routes.end()) {
+
assert(static_cast<size_t>(u_it->second.movement_left()) >=
rt->second.steps.size()-1 && "Trying to move unit without enough move points
left\n");
u_it->second.set_movement(rt->second.move_left);
std::vector<location> steps = rt->second.steps;
while(steps.empty() == false && (!(info_.units.find(to)
== info_.units.end() || from == to))){
- LOG_AI << "AI attempting illegal move.
Attempting to move onto existing unit\n";
- LOG_AI << "\t" <<
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';
+ LOG_AI << "AI attempting illegal move.
Attempting to move onto existing unit\n";
+ LOG_AI << "\t" <<
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=info_.units.find(*(steps.begin()));
- if(utest != info_.units.end() &&
current_team().is_enemy(utest->second.side())){
- LOG_STREAM(err, ai) << "AI tried to move onto
existing enemy unit at"<<*(steps.begin())<<"\n";
- // return(from);
- }
+ unit_map::const_iterator
utest=info_.units.find(*(steps.begin()));
+ if(utest != info_.units.end() &&
current_team().is_enemy(utest->second.side())){
+ LOG_STREAM(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) {
@@ -568,27 +571,27 @@
const unit_map::const_iterator
u = info_.units.find(adj[n]);
// If level 0 is invisible it
ambush us too
if (u != info_.units.end() &&
(u->second.emits_zoc() || u->second.invisible(adj[n], info_.units, info_.teams))
- &&
current_team().is_enemy(u->second.side())) {
+ &&
current_team().is_enemy(u->second.side())) {
if
(u->second.invisible(adj[n], info_.units, info_.teams)) {
to = *i;
u->second.ambush();
steps.erase(i,steps.end());
break;
} else {
- if
(!u_it->second.get_ability_bool("skirmisher",*i)){
- LOG_STREAM(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 && (!(info_.units.find(to) == info_.units.end() || from == to))){
- to =
*(steps.end()-1);
-
steps.pop_back();
- LOG_AI <<
"\tresetting to " << from << " -> " << to << '\n';
- }
-
- break;
- }
+ if
(!u_it->second.get_ability_bool("skirmisher",*i)){
+
LOG_STREAM(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 && (!(info_.units.find(to) == info_.units.end() ||
from == to))){
+
to = *(steps.end()-1);
+
steps.pop_back();
+
LOG_AI << "\tresetting to " << from << " -> " << to << '\n';
+ }
+
+ break;
+ }
}
}
}
@@ -726,7 +729,8 @@
possible_moves_ptr = &temp_possible_moves;
}
- do_recruitment();
+ if (map_.is_keep(from))
+ do_recruitment();
}
if(units_.count(to) == 0 || from == to) {
@@ -795,7 +799,8 @@
continue;
}
// Discount incapacitated units
- if(un_it->second.incapacitated()) {
+ if(un_it->second.incapacitated()
+ || un_it->second.movement_left() == 0) {
continue;
}
@@ -960,6 +965,14 @@
}
}
+void ai::evaluate_recruiting_value(unit_map::iterator leader)
+{
+ const int gold = current_team().gold();
+// const int unit_price = current_team().average_recruit_price();
+// recruiting_prefered_ = (gold/unit_price) >
min_recruiting_value_to_force_recruit && !map_.is_keep();
+ recruiting_prefered_ = gold > min_recruiting_value_to_force_recruit &&
!map_.is_keep(leader->first);
+}
+
void ai::do_move()
{
log_scope2(ai, "doing ai move");
@@ -980,9 +993,13 @@
const bool passive_leader =
utils::string_bool(current_team().ai_parameters()["passive_leader"]);
- if (passive_leader) {
- unit_map::iterator leader = find_leader(units_,team_num_);
- if(leader != units_.end()) {
+
+ unit_map::iterator leader = find_leader(units_,team_num_);
+ if (leader != units_.end())
+ {
+ evaluate_recruiting_value(leader);
+ if (passive_leader)
+ {
remove_unit_from_moves(leader->first,srcdst,dstsrc);
}
}
@@ -1037,7 +1054,7 @@
LOG_AI << "get villages phase\n";
// Iterator could be invalidated by combat analysis or
move_leader_to_goals.
- unit_map::iterator leader = find_leader(units_,team_num_);
+ leader = find_leader(units_,team_num_);
LOG_AI << "villages...\n";
if(get_villages(possible_moves, dstsrc, enemy_dstsrc, leader)) {
@@ -1089,7 +1106,8 @@
move_leader_to_keep(enemy_dstsrc);
}
- do_recruitment();
+ if (map_.is_keep(leader->first))
+ do_recruitment();
if(!passive_leader) {
move_leader_after_recruit(srcdst,dstsrc,enemy_dstsrc);
@@ -1135,6 +1153,16 @@
LOG_AI << "attack option rated at " << rating << " ("
<< current_team().aggression() << ")\n";
+ if (recruiting_prefered_)
+ {
+ unit_map::unit_iterator u =
units_.find(it->movements[0].first);
+ if (u != units_.end()
+ && u->second.can_recruit())
+ {
+ LOG_AI << "Not fighting with leader because
recruiting is more preferable\n";
+ continue;
+ }
+ }
if(rating > choice_rating) {
choice_it = it;
choice_rating = rating;
@@ -1144,7 +1172,7 @@
time_taken = SDL_GetTicks() - ticks;
LOG_AI << "analysis took " << time_taken << " ticks\n";
- if(choice_rating > 0.0) {
+ if(choice_rating > current_team().caution()) {
location from = choice_it->movements[0].first;
location to = choice_it->movements[0].second;
location target_loc = choice_it->target;
@@ -1916,64 +1944,12 @@
}
}
-void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
-{
- const unit_map::iterator leader = find_leader(units_,team_num_);
- if(leader == units_.end() || leader->second.incapacitated()) {
- return;
- }
-
- // Find where the leader can move
- const paths leader_paths(map_, units_, leader->first,
- teams_, false, false, current_team());
- const gamemap::location& start_pos = nearest_keep(leader->first);
-
- std::map<gamemap::location,paths> possible_moves;
-
possible_moves.insert(std::pair<gamemap::location,paths>(leader->first,leader_paths));
-
- // If the leader is not on his starting location, move him there.
- if(leader->first != start_pos) {
- const paths::routes_map::const_iterator itor =
leader_paths.routes.find(start_pos);
- if(itor != leader_paths.routes.end() && units_.count(start_pos)
== 0) {
- move_unit(leader->first,start_pos,possible_moves);
- } else {
- // Make a map of the possible locations the leader can
move to,
- // ordered by the distance from the keep.
- std::multimap<int,gamemap::location> moves_toward_keep;
-
- // The leader can't move to his keep, try to move to
the closest location
- // to the keep where there are no enemies in range.
- const int current_distance =
distance_between(leader->first,start_pos);
- for(paths::routes_map::const_iterator i =
leader_paths.routes.begin();
- i != leader_paths.routes.end(); ++i) {
-
- const int new_distance =
distance_between(i->first,start_pos);
- if(new_distance < current_distance) {
-
moves_toward_keep.insert(std::pair<int,gamemap::location>(new_distance,i->first));
- }
- }
-
- // Find the first location which we can move to,
- // without the threat of enemies.
-
for(std::multimap<int,gamemap::location>::const_iterator j =
moves_toward_keep.begin();
- j != moves_toward_keep.end(); ++j) {
-
- if(enemy_dstsrc.count(j->second) == 0) {
-
move_unit(leader->first,j->second,possible_moves);
- break;
- }
- }
- }
- }
-}
-
void ai::move_leader_after_recruit(const move_map& /*srcdst*/,
const move_map& /*dstsrc*/, const move_map& enemy_dstsrc)
{
- LOG_AI << "moving leader after recruit...\n";
unit_map::iterator leader = find_leader(units_,team_num_);
- if(leader == units_.end() || leader->second.incapacitated()) {
+ if(leader == units_.end() || leader->second.incapacitated() ||
leader->second.movement_left() == 0) {
return;
}
Modified: trunk/src/ai.hpp
URL:
http://svn.gna.org/viewcvs/wesnoth/trunk/src/ai.hpp?rev=29463&r1=29462&r2=29463&view=diff
==============================================================================
--- trunk/src/ai.hpp (original)
+++ trunk/src/ai.hpp Sun Sep 14 18:11:43 2008
@@ -338,6 +338,9 @@
/** Functions to deal with keeps. */
const std::set<location>& keeps();
const location& nearest_keep(const location& loc);
+ int count_free_hexes_in_castle(const gamemap::location& loc,
std::set<gamemap::location>&);
+
+ void evaluate_recruiting_value(unit_map::iterator leader);
std::set<location> keeps_;
@@ -373,6 +376,9 @@
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;
+
+ bool recruiting_prefered_;
+ static const int min_recruiting_value_to_force_recruit = 28;
};
class ai_manager {
Modified: trunk/src/ai_move.cpp
URL:
http://svn.gna.org/viewcvs/wesnoth/trunk/src/ai_move.cpp?rev=29463&r1=29462&r2=29463&view=diff
==============================================================================
--- trunk/src/ai_move.cpp (original)
+++ trunk/src/ai_move.cpp Sun Sep 14 18:11:43 2008
@@ -25,7 +25,10 @@
#include <cassert>
#include <iostream>
+#include <queue>
+
#define LOG_AI LOG_STREAM(info, ai)
+#define DBG_AI LOG_STREAM(debug, ai)
struct move_cost_calculator : cost_calculator
{
@@ -433,8 +436,11 @@
assert(map_.on_board(tg->loc));
- const double locStopValue = std::min(tg->value / best_rating,
500.0);
+ const double locStopValue = 500.0;
paths::route cur_route = a_star_search(u->first, tg->loc,
locStopValue, &cost_calc, map_.w(), map_.h());
+
+ if (cur_route.move_left == cost_calc.getNoPathValue())
+ continue;
if (cur_route.move_left < locStopValue)
{
@@ -790,3 +796,114 @@
}
}
}
+
+struct keep_value {
+ size_t value;
+ gamemap::location loc;
+
+ keep_value(size_t v, const gamemap::location& l) : value(v), loc(l)
+ {}
+ bool operator<(const keep_value& val) const
+ {
+ return value > val.value;
+ }
+};
+
+
+void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
+{
+ const unit_map::iterator leader = find_leader(units_,team_num_);
+ if(leader == units_.end()
+ || leader->second.incapacitated()
+ || leader->second.movement_left() == 0
+ || !recruiting_prefered_) {
+ return;
+ }
+
+ std::map<gamemap::location,paths> possible_moves;
+ std::map<gamemap::location,paths>::iterator path_itor =
possible_moves.insert(std::make_pair(leader->first, paths())).first;
+
+ // Guess how many units we want to recruit
+ const int number_of_recruit = current_team().gold() / 15;
+
+ // If the leader is not on his starting location, move him there.
+ {
+ {
+ // Make a map of the possible locations the leader can
move to,
+ // ordered by the distance from the keep.
+ std::priority_queue<keep_value> moves_toward_keep;
+
+ // The leader can't move to his keep, try to move to
the closest location
+ // to the keep where there are no enemies in range.
+ for(std::set<location>::iterator i = keeps().begin();
+ i != keeps().end(); ++i) {
+
+ const shortest_path_calculator
cost_calc(leader->second, current_team(), units_,
+ teams_,map_);
+
+ paths::routes_map::iterator route =
path_itor->second.routes.insert(
+ std::make_pair(*i,
+
a_star_search(leader->first, *i, 10000.0, &cost_calc,map_.w(),
map_.h()))).first;
+
+ std::set<gamemap::location> checked_hexes;
+ const int distance =
route->second.steps.size()-1;
+ checked_hexes.insert(*i);
+ const int free_slots =
count_free_hexes_in_castle(*i, checked_hexes);
+ const int tactical_value =
leader->second.total_movement() * 0;
+ const int empty_slots =
leader->second.total_movement() * std::max(number_of_recruit - free_slots,0);
+ unit_map::const_iterator u = units_.find(*i);
+ const int reserved_penalty =
leader->second.total_movement() *
+ (u != units_.end()?
+
(current_team().is_enemy(u->second.side())?4:2)
+ :0);
+ const int enemy =
leader->second.total_movement() * enemy_dstsrc.count(*i);
+ gamemap::location target;
+ if (distance > leader->second.movement_left())
+ {
+ target =
route->second.steps[leader->second.movement_left()+1];
+
route->second.steps.erase(route->second.steps.begin() +
leader->second.movement_left(),route->second.steps.end());
+ route->second.move_left = 0;
+ } else {
+ target = *i;
+ route->second.move_left =
leader->second.movement_left() - distance;
+ }
+ DBG_AI << "Considiring keep: " << *i <<
+ " empty slots: " << empty_slots <<
+ " distance: " << distance <<
+ " enemy: " << enemy <<
+ " target: " << target <<
+ " route: " << route->second.steps.size() <<
" " << route->second.move_left <<
+ "\n";
+ moves_toward_keep.push(keep_value(distance +
empty_slots + enemy + tactical_value + reserved_penalty ,target));
+ }
+
+ // Find the location with the best value
+ if (leader->first != moves_toward_keep.top().loc)
+
move_unit(leader->first,moves_toward_keep.top().loc,possible_moves);
+ }
+ }
+}
+
+int ai::count_free_hexes_in_castle(const gamemap::location& loc,
std::set<gamemap::location>& checked_hexes)
+{
+ int ret = 0;
+ location adj[6];
+ get_adjacent_tiles(loc,adj);
+ for(size_t n = 0; n != 6; ++n) {
+ if (checked_hexes.find(adj[n]) != checked_hexes.end())
+ continue;
+ checked_hexes.insert(adj[n]);
+ if (map_.is_castle(adj[n])) {
+ const unit_map::const_iterator u = units_.find(adj[n]);
+ ret += count_free_hexes_in_castle(adj[n],
checked_hexes);
+ if (u == units_.end()
+ || (current_team().is_enemy(u->second.side())
+ && u->second.invisible(adj[n], units_,
teams_))) {
+ ret += 1;
+ }
+ }
+ }
+ return ret;
+}
+
+
Modified: trunk/src/ai_village.cpp
URL:
http://svn.gna.org/viewcvs/wesnoth/trunk/src/ai_village.cpp?rev=29463&r1=29462&r2=29463&view=diff
==============================================================================
--- trunk/src/ai_village.cpp (original)
+++ trunk/src/ai_village.cpp Sun Sep 14 18:11:43 2008
@@ -174,7 +174,6 @@
if(u_itor->second.side() == team_num_
&& u_itor->second.movement_left()) {
-
reachmap.insert(std::make_pair(u_itor->first,
std::vector<gamemap::location>()));
}
}
@@ -261,7 +260,7 @@
{
std::map<location, double> vulnerability;
- const bool passive_leader =
+ const bool passive_leader = recruiting_prefered_ ||
utils::string_bool(current_team().ai_parameters()["passive_leader"]);
size_t min_distance = 100000;
_______________________________________________
Wesnoth-commits mailing list
[email protected]
https://mail.gna.org/listinfo/wesnoth-commits