38 namespace ai_default_rca {
41 #define DBG_AI LOG_STREAM(debug, log_ai_testing_ca_move_to_targets)
42 #define LOG_AI LOG_STREAM(info, log_ai_testing_ca_move_to_targets)
43 #define WRN_AI LOG_STREAM(warn, log_ai_testing_ca_move_to_targets)
44 #define ERR_AI LOG_STREAM(err, log_ai_testing_ca_move_to_targets)
65 double res = move_cost;
96 DBG_AI <<
"removing target "<<
t.loc <<
" due to it not on_board";
101 DBG_AI <<
"removing target "<<
t.loc <<
" due to value<=0";
106 DBG_AI <<
"removing target "<<
t.loc <<
" due to 'avoid' match";
135 LOG_AI <<
"finding targets...";
136 std::vector<target> targets;
138 if(targets.empty()) {
142 LOG_AI <<
"Found " << targets.size() <<
" targets";
143 if(targets.empty()) {
148 targets.erase( std::remove_if(targets.begin(),targets.end(),
remove_wrong_targets(*
this)), targets.end() );
150 if(targets.empty()) {
154 LOG_AI <<
"choosing move with " << targets.size() <<
" targets";
155 std::pair<map_location,map_location> move =
choose_move(targets);
156 LOG_AI <<
"choose_move ends with " << targets.size() <<
" targets";
158 for(std::vector<target>::const_iterator ittg = targets.begin();
159 ittg != targets.end(); ++ittg) {
163 if(move.first.valid() ==
false || move.second.valid() ==
false) {
170 LOG_AI <<
"move: " << move.first <<
" -> " << move.second;
174 WRN_AI <<
"unexpected outcome of move";
191 return a.max_rating >
b.max_rating;
203 typedef std::multimap<map_location,map_location>::const_iterator multimapItor;
204 std::pair<multimapItor,multimapItor> locRange = dstsrc.equal_range(tg.
loc);
205 while (locRange.first != locRange.second) {
206 if (locRange.first->second == u->get_location()) {
214 double rating = tg.
value;
226 if(tg.
type == ai_target::type::support) {
227 if (move_cost <= u->movement_left() * 2) {
236 if (u->usage() ==
"scout") {
238 if(tg.
type == ai_target::type::village) {
242 std::set<map_location> enemies_guarding;
246 if(enemies_guarding.size() > 1) {
247 rating /= enemies_guarding.size();
270 for(u = units_.
begin(); u != units_.
end(); ++u) {
272 if (u->get_state(
"guardian")) {
273 LOG_AI << u->type_id() <<
" is guardian, staying still";
274 return std::pair(u->get_location(), u->get_location());
280 for(u = units_.
begin(); u != units_.
end(); ++u) {
286 if(u == units_.
end()) {
287 LOG_AI <<
"no eligible units found";
288 return std::pair<map_location,map_location>();
301 std::vector<rated_target> rated_targets;
304 double max_rating =
rate_target(*tg, u, dstsrc, enemy_dstsrc, dummy_route);
305 rated_targets.emplace_back(tg, max_rating);
315 double best_rating = -1.0;
321 for(; rated_tg != rated_targets.end(); ++rated_tg) {
322 const target& tg = *(rated_tg->tg);
324 LOG_AI <<
"Considering target at: " << tg.
loc;
332 const double locStopValue = 500.0;
336 if(real_route.
steps.empty()) {
337 LOG_AI <<
"Can't reach target: " << locStopValue <<
" = " << tg.
value <<
"/" << best_rating;
341 double real_rating =
rate_target(tg, u, dstsrc, enemy_dstsrc, real_route);
345 if(real_rating > best_rating){
346 best_rating = real_rating;
347 best_rated_target = rated_tg;
348 best_route = real_route;
353 best_rating = 0.000000001;
358 if(rated_tg+1 != rated_targets.end() && best_rating >= (rated_tg+1)->max_rating)
363 LOG_AI <<
"choose target...";
365 if(best_rated_target == rated_targets.end()) {
366 LOG_AI <<
"no eligible targets found for unit at " << u->get_location();
367 return std::pair(u->get_location(), u->get_location());
370 assert(best_rating >= 0);
378 if(simple_targeting ==
false) {
379 LOG_AI <<
"complex targeting...";
381 for(++u; u != units_.
end(); ++u) {
383 u->movement_left() <= 0 || u->get_state(
"guardian") ||
396 const double locStopValue = 500.0;
400 if(cur_route.
steps.empty()) {
404 double rating =
rate_target(*best_target, u, dstsrc, enemy_dstsrc, cur_route);
406 if(best == units_.
end() || rating > best_rating) {
407 best_rating = rating;
409 best_route = cur_route;
413 LOG_AI <<
"done complex targeting...";
418 LOG_AI <<
"best unit: " << best->get_location();
420 assert(best_target != targets.end());
425 if(best_target->type == ai_target::type::support) {
428 std::vector<map_location> locs;
429 access_points(srcdst, best->get_location(), best_target->loc, locs);
431 if(locs.empty() ==
false) {
432 LOG_AI <<
"supporting unit at " << best_target->loc.wml_x() <<
"," << best_target->loc.wml_y();
434 int best_defense = 0;
435 double best_vulnerability = 0.0;
437 for(std::vector<map_location>::const_iterator
i = locs.begin();
i != locs.end(); ++
i) {
438 const int defense = best->defense_modifier(map_.
get_terrain(*
i));
442 if(best_loc.
valid() ==
false || defense < best_defense || (defense == best_defense && vulnerability < best_vulnerability)) {
444 best_defense = defense;
445 best_vulnerability = vulnerability;
449 LOG_AI <<
"returning support...";
450 return std::pair(best->get_location(), best_loc);
454 std::map<map_location,pathfind::paths> dummy_possible_moves;
459 bool dangerous =
false;
464 int movement = best->movement_left();
466 const bool defensive_grouping =
get_grouping() ==
"defensive";
471 for(std::vector<map_location>::const_iterator
i = best_route.
steps.begin();
i != best_route.
steps.end() && movement > 0; ++
i) {
476 if ((threat >= best->hitpoints() && threat >
power_projection(*
i,fullmove_dstsrc)) ||
477 (
i+1 >= best_route.
steps.end()-1 && unit_at_target != units_.
end() &&
current_team().is_enemy(unit_at_target->side()))) {
482 if(!defensive_grouping) {
487 LOG_AI <<
"done grouping...";
491 LOG_AI <<
"dangerous path";
492 std::set<map_location> group, enemies;
504 LOG_AI <<
"group didn't move " << group.size();
507 return std::pair(best->get_location(), best->get_location());
511 LOG_AI <<
"massing to attack " << best_target->loc.wml_x() <<
"," << best_target->loc.wml_y()
512 <<
" " << our_strength;
514 const double value = best_target->value;
517 const unit& un = *best;
519 targets.erase(best_target);
523 double best_threat = 0.0;
524 int best_distance = 0;
526 const double max_acceptable_threat = un.
hitpoints() / 4.0;
528 std::set<map_location> mass_locations;
530 const std::pair<move_map::const_iterator,move_map::const_iterator> itors = srcdst.equal_range(loc);
531 for(move_map::const_iterator
i = itors.first;
i != itors.second; ++
i) {
537 if(best_loc.
valid() ==
false || (threat < std::max<double>(best_threat,max_acceptable_threat) && distance < best_distance)) {
538 best_loc =
i->second;
539 best_threat = threat;
540 best_distance = distance;
543 if(threat < max_acceptable_threat) {
544 mass_locations.insert(
i->second);
548 for(std::set<map_location>::const_iterator j = mass_locations.begin(); j != mass_locations.end(); ++j) {
550 LOG_AI <<
"found mass-to-attack target... " << *j <<
" with value: " << value*4.0;
551 targets.emplace_back(*j,value*4.0,ai_target::type::mass);
552 best_target = targets.end() - 1;
556 return std::pair<map_location,map_location>(loc,best_loc);
560 for(std::vector<map_location>::reverse_iterator ri =
561 best_route.
steps.rbegin(); ri != best_route.
steps.rend(); ++ri) {
565 bool is_dangerous =
false;
567 typedef std::multimap<map_location,map_location>::const_iterator Itor;
568 std::pair<Itor,Itor> its = dstsrc.equal_range(*ri);
569 while(its.first != its.second) {
570 if (its.first->second == best->get_location()) {
571 if(!
should_retreat(its.first->first,best,fullmove_srcdst,fullmove_dstsrc,enemy_dstsrc,
573 double value = best_target->value - best->cost() / 20.0;
575 if(value > 0.0 && best_target->type != ai_target::type::mass) {
579 LOG_AI <<
"found reinforcement target... " << its.first->first <<
" with value: " << value*2.0;
580 targets.emplace_back(its.first->first,value*2.0,ai_target::type::battle_aid);
583 best_target->value = value;
585 targets.erase(best_target);
588 LOG_AI <<
"Moving to " << its.first->first.wml_x() <<
"," << its.first->first.wml_y();
590 return std::pair<map_location,map_location>(its.first->second,its.first->first);
601 if(best != units_.
end()) {
602 LOG_AI <<
"Could not make good move, staying still";
606 targets.emplace_back(best->get_location(), best_target->value);
607 best_target = targets.end() - 1;
608 return std::pair(best->get_location(), best->get_location());
611 LOG_AI <<
"Could not find anywhere to move!";
612 return std::pair<map_location,map_location>();
620 if(u_it == units_.
end()) {
626 const std::pair<move_map::const_iterator,move_map::const_iterator> locs = srcdst.equal_range(u);
627 for(move_map::const_iterator
i = locs.first;
i != locs.second; ++
i) {
629 if (
static_cast<int>(
distance_between(loc,dst)) <= u_it->total_movement()) {
633 if(rt.
steps.empty() ==
false) {
642 const double a =
rate_group(our_group,battlefield);
643 const double b = std::max<double>(
rate_group(their_group,battlefield),0.01);
649 for(std::vector<map_location>::const_iterator
i = route.begin();
i != route.end(); ++
i) {
651 const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(adj);
652 for(move_map::const_iterator j = itors.first; j != itors.second; ++j) {
653 res.insert(j->second);
666 std::vector<map_location>::const_iterator
i;
667 for(
i = route.begin();
i != route.end(); ++
i) {
668 if(units_.
count(*
i) > 0) {
672 std::size_t
n = 0, nunits = res.size();
674 const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(*
i);
675 for(move_map::const_iterator j = itors.first; j != itors.second; ++j) {
676 if(res.count(j->second) != 0) {
680 if(un == units_.
end() || (un->can_recruit() && !
is_keep_ignoring_leader(un->id())) || un->movement_left() < un->total_movement()) {
684 res.insert(j->second);
694 if(
i != route.begin()) {
706 const std::vector<map_location>::const_iterator itor = std::find(route.begin(),route.end(),dst);
707 if(itor == route.end()) {
711 LOG_AI <<
"group has " << units.size() <<
" members";
715 std::size_t direction = 0;
718 if(itor+1 != route.end()) {
720 }
else if(itor != route.begin()) {
726 direction = std::distance(adj.begin(), std::find(adj.begin(), adj.end(), next));
729 std::deque<map_location> preferred_moves;
730 preferred_moves.push_back(dst);
732 std::map<map_location,pathfind::paths> possible_moves;
736 bool gamestate_changed =
false;
738 for(std::set<map_location>::const_iterator
i = units.begin();
i != units.end(); ++
i) {
740 if(un == units_.
end()) {
745 int best_defense = -1;
746 for(std::deque<map_location>::const_iterator j = preferred_moves.begin(); j != preferred_moves.end(); ++j) {
747 if(units_.
count(*j)) {
751 const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(*j);
752 move_map::const_iterator m;
753 for(m = itors.first; m != itors.second; ++m) {
754 if(m->second == *
i) {
759 if(m == itors.second) {
763 int defense = un->defense_modifier(map_.
get_terrain(*j));
764 if(best_loc.
valid() ==
false || defense < best_defense) {
766 best_defense = defense;
770 if(best_loc.
valid()) {
772 gamestate_changed |= move_res->is_gamestate_changed();
775 if (!move_res->is_ok()) {
776 return gamestate_changed;
779 preferred_moves.erase(std::find(preferred_moves.begin(),preferred_moves.end(),best_loc));
783 for(std::size_t
n = 0;
n < adj.size(); ++
n) {
784 if(
n != direction && ((
n+3)%6) != direction && map_.
on_board(adj[
n]) &&
785 units_.
count(adj[
n]) == 0 && std::count(preferred_moves.begin(),preferred_moves.end(),adj[
n]) == 0) {
786 preferred_moves.push_front(adj[
n]);
787 LOG_AI <<
"added moves: " << adj[
n].wml_x() <<
"," << adj[
n].wml_y();
791 LOG_AI <<
"Could not move group member to any of " << preferred_moves.size() <<
" locations";
795 return gamestate_changed;
803 double strength = 0.0;
804 for(std::set<map_location>::const_iterator
i = group.begin();
i != group.end(); ++
i) {
806 if(u == units_.
end()) {
813 for(std::vector<map_location>::const_iterator j = battlefield.begin(); j != battlefield.end(); ++j) {
817 defense /= battlefield.size();
821 const int attack_strength =
a.num_attacks() *
a.damage();
822 best_attack = std::max<int>(attack_strength, best_attack);
826 strength +=
static_cast<double>(rating);
842 const double proposed_terrain =
847 const double exposure = proposed_terrain - optimal_terrain;
851 return caution*their_power*(1.0+exposure) > our_power;
Managing the AI-Game interaction - AI actions and their results.
Strategic movement routine, for experimentation.
void access_points(const move_map &srcdst, const map_location &u, const map_location &dst, std::vector< map_location > &out)
virtual ~move_to_targets_phase()
map_location form_group(const std::vector< map_location > &route, const move_map &dstsrc, std::set< map_location > &res)
void enemies_along_path(const std::vector< map_location > &route, const move_map &dstsrc, std::set< map_location > &res)
bool move_group(const map_location &dst, const std::vector< map_location > &route, const std::set< map_location > &units)
bool should_retreat(const map_location &loc, const unit_map::const_iterator &un, const move_map &srcdst, const move_map &dstsrc, const move_map &enemy_dstsrc, double caution)
double rate_group(const std::set< map_location > &group, const std::vector< map_location > &battlefield) const
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
double compare_groups(const std::set< map_location > &our_group, const std::set< map_location > &their_group, const std::vector< map_location > &battlefield) const
move_to_targets_phase(rca_context &context, const config &cfg)
double rate_target(const target &tg, const unit_map::iterator &u, const move_map &dstsrc, const move_map &enemy_dstsrc, const pathfind::plain_route &rt)
rate a target, but can also return the maximal possible rating by passing a dummy route
virtual void execute()
Execute the candidate action.
std::pair< map_location, map_location > choose_move(std::vector< target > &targets)
const terrain_filter & avoid_
remove_wrong_targets(const readonly_context &context)
bool operator()(const target &t)
bool is_allowed_unit(const unit &u) const
Flag indicating whether unit may be used by this candidate action.
double get_score() const
Get the usual score of the candidate action without re-evaluation.
virtual std::vector< target > find_targets(const move_map &enemy_dstsrc)
virtual const std::vector< target > & additional_targets() const
virtual double get_caution() const override
virtual const team & current_team() const override
virtual bool is_keep_ignoring_leader(const std::string &id) const override
virtual const move_map & get_dstsrc() const override
virtual std::string get_grouping() const override
virtual void raise_user_interact() const override
virtual bool get_simple_targeting() const override
virtual const move_map & get_srcdst() const override
const defensive_position & best_defensive_position(const map_location &unit, const move_map &dstsrc, const move_map &srcdst, const move_map &enemy_dstsrc) const override
virtual void calculate_possible_moves(std::map< map_location, pathfind::paths > &possible_moves, move_map &srcdst, move_map &dstsrc, bool enemy, bool assume_full_movement=false, const terrain_filter *remove_destinations=nullptr) const override
virtual double power_projection(const map_location &loc, const move_map &dstsrc) const override
Function which finds how much 'power' a side can attack a certain location with.
virtual double get_scout_village_targeting() const override
virtual const move_map & get_enemy_dstsrc() const override
virtual move_result_ptr execute_move_action(const map_location &from, const map_location &to, bool remove_movement=true, bool unreach_is_ok=false) override
virtual side_number get_side() const override
Get the side number.
A config object defines a single node in a WML file, with access to child nodes.
virtual const unit_map & units() const override
virtual const gamemap & map() const override
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
int w() const
Effective map width.
int h() const
Effective map height.
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Encapsulates the map of the game.
Container associating units to locations.
std::size_t count(const map_location &loc) const
unit_iterator find(std::size_t id)
unit_iterator find_leader(int side)
This class represents a single unit of a specific type.
Composite AI with turn sequence which is a vector of stages.
int max_hitpoints() const
The max number of hitpoints this unit can have.
int hitpoints() const
The current number of hitpoints this unit has.
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit's defense on a given terrain.
attack_itors attacks()
Gets an iterator over this unit's attacks.
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Standard logging facilities (interface).
#define log_scope2(domain, description)
static lg::log_domain log_ai_testing_ca_move_to_targets("ai/ca/move_to_targets")
A small explanation about what's going on here: Each action has access to two game_info objects First...
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
std::shared_ptr< move_result > move_result_ptr
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const std::size_t width, const std::size_t height, const teleport_map *teleports, bool border)
const teleport_map get_teleport_locations(const unit &u, const team &viewing_team, bool see_all, bool ignore_units, bool check_vision)
std::shared_ptr< move > move_ptr
std::string::const_iterator iterator
This module contains various pathfinding functions and utilities.
const move_map & enemy_dstsrc_
double cost(const map_location &loc, const double) const
const bool avoid_enemies_
move_cost_calculator(const unit &u, const gamemap &map, const unit_map &units, const move_map &enemy_dstsrc)
bool operator()(const rated_target &a, const rated_target &b) const
rated_target(const std::vector< target >::iterator &t, double r)
std::vector< target >::iterator tg
Encapsulates the map of the game.
static double getNoPathValue()
Structure which holds a single route between one location and another.
std::vector< map_location > steps
int move_cost
Movement cost for reaching the end of the route.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
static map_location::DIRECTION n