37 namespace ai_default_rca {
40 #define DBG_AI LOG_STREAM(debug, log_ai_testing_ca_move_to_targets)
41 #define LOG_AI LOG_STREAM(info, log_ai_testing_ca_move_to_targets)
42 #define WRN_AI LOG_STREAM(warn, log_ai_testing_ca_move_to_targets)
43 #define ERR_AI LOG_STREAM(err, log_ai_testing_ca_move_to_targets)
64 double res = move_cost;
95 DBG_AI <<
"removing target "<<
t.loc <<
" due to it not on_board";
100 DBG_AI <<
"removing target "<<
t.loc <<
" due to value<=0";
105 DBG_AI <<
"removing target "<<
t.loc <<
" due to 'avoid' match";
134 LOG_AI <<
"finding targets...";
135 std::vector<target> targets;
137 if(targets.empty()) {
141 LOG_AI <<
"Found " << targets.size() <<
" targets";
142 if(targets.empty()) {
149 if(targets.empty()) {
153 LOG_AI <<
"choosing move with " << targets.size() <<
" targets";
154 std::pair<map_location,map_location> move =
choose_move(targets);
155 LOG_AI <<
"choose_move ends with " << targets.size() <<
" targets";
157 for(std::vector<target>::const_iterator ittg = targets.begin();
158 ittg != targets.end(); ++ittg) {
162 if(move.first.valid() ==
false || move.second.valid() ==
false) {
169 LOG_AI <<
"move: " << move.first <<
" -> " << move.second;
173 WRN_AI <<
"unexpected outcome of move";
202 typedef std::multimap<map_location,map_location>::const_iterator multimapItor;
203 std::pair<multimapItor,multimapItor> locRange = dstsrc.equal_range(tg.
loc);
204 while (locRange.first != locRange.second) {
205 if (locRange.first->second == u->get_location()) {
213 double rating = tg.
value;
225 if(tg.
type == ai_target::type::support) {
226 if (move_cost <= u->movement_left() * 2) {
235 if (u->usage() ==
"scout") {
237 if(tg.
type == ai_target::type::village) {
241 std::set<map_location> enemies_guarding;
245 if(enemies_guarding.size() > 1) {
246 rating /= enemies_guarding.size();
269 for(u = units_.
begin(); u != units_.
end(); ++u) {
271 if (u->get_state(
"guardian")) {
272 LOG_AI << u->type_id() <<
" is guardian, staying still";
273 return std::pair(u->get_location(), u->get_location());
279 for(u = units_.
begin(); u != units_.
end(); ++u) {
285 if(u == units_.
end()) {
286 LOG_AI <<
"no eligible units found";
287 return std::pair<map_location,map_location>();
300 std::vector<rated_target> rated_targets;
303 double max_rating =
rate_target(*tg, u, dstsrc, enemy_dstsrc, dummy_route);
304 rated_targets.emplace_back(tg, max_rating);
314 double best_rating = -1.0;
320 for(; rated_tg != rated_targets.end(); ++rated_tg) {
321 const target& tg = *(rated_tg->tg);
323 LOG_AI <<
"Considering target at: " << tg.
loc;
331 const double locStopValue = 500.0;
335 if(real_route.
steps.empty()) {
336 LOG_AI <<
"Can't reach target: " << locStopValue <<
" = " << tg.
value <<
"/" << best_rating;
340 double real_rating =
rate_target(tg, u, dstsrc, enemy_dstsrc, real_route);
344 if(real_rating > best_rating){
345 best_rating = real_rating;
346 best_rated_target = rated_tg;
347 best_route = real_route;
352 best_rating = 0.000000001;
357 if(rated_tg+1 != rated_targets.end() && best_rating >= (rated_tg+1)->max_rating)
362 LOG_AI <<
"choose target...";
364 if(best_rated_target == rated_targets.end()) {
365 LOG_AI <<
"no eligible targets found for unit at " << u->get_location();
366 return std::pair(u->get_location(), u->get_location());
369 assert(best_rating >= 0);
377 if(simple_targeting ==
false) {
378 LOG_AI <<
"complex targeting...";
380 for(++u; u != units_.
end(); ++u) {
382 u->movement_left() <= 0 || u->get_state(
"guardian") ||
395 const double locStopValue = 500.0;
399 if(cur_route.
steps.empty()) {
403 double rating =
rate_target(*best_target, u, dstsrc, enemy_dstsrc, cur_route);
405 if(best == units_.
end() || rating > best_rating) {
406 best_rating = rating;
408 best_route = cur_route;
412 LOG_AI <<
"done complex targeting...";
417 LOG_AI <<
"best unit: " << best->get_location();
419 assert(best_target != targets.end());
424 if(best_target->type == ai_target::type::support) {
427 std::vector<map_location> locs;
428 access_points(srcdst, best->get_location(), best_target->loc, locs);
430 if(locs.empty() ==
false) {
431 LOG_AI <<
"supporting unit at " << best_target->loc.wml_x() <<
"," << best_target->loc.wml_y();
433 int best_defense = 0;
434 double best_vulnerability = 0.0;
436 for(std::vector<map_location>::const_iterator
i = locs.begin();
i != locs.end(); ++
i) {
437 const int defense = best->defense_modifier(map_.
get_terrain(*
i));
441 if(best_loc.
valid() ==
false || defense < best_defense || (defense == best_defense && vulnerability < best_vulnerability)) {
443 best_defense = defense;
444 best_vulnerability = vulnerability;
448 LOG_AI <<
"returning support...";
449 return std::pair(best->get_location(), best_loc);
453 std::map<map_location,pathfind::paths> dummy_possible_moves;
458 bool dangerous =
false;
463 int movement = best->movement_left();
465 const bool defensive_grouping =
get_grouping() ==
"defensive";
470 for(std::vector<map_location>::const_iterator
i = best_route.
steps.begin();
i != best_route.
steps.end() && movement > 0; ++
i) {
475 if ((threat >= best->hitpoints() && threat >
power_projection(*
i,fullmove_dstsrc)) ||
476 (
i+1 >= best_route.
steps.end()-1 && unit_at_target != units_.
end() &&
current_team().is_enemy(unit_at_target->side()))) {
481 if(!defensive_grouping) {
486 LOG_AI <<
"done grouping...";
490 LOG_AI <<
"dangerous path";
491 std::set<map_location> group, enemies;
503 LOG_AI <<
"group didn't move " << group.size();
506 return std::pair(best->get_location(), best->get_location());
510 LOG_AI <<
"massing to attack " << best_target->loc.wml_x() <<
"," << best_target->loc.wml_y()
511 <<
" " << our_strength;
513 const double value = best_target->value;
516 const unit& un = *best;
518 targets.erase(best_target);
522 double best_threat = 0.0;
523 int best_distance = 0;
525 const double max_acceptable_threat = un.
hitpoints() / 4.0;
527 std::set<map_location> mass_locations;
529 const std::pair<move_map::const_iterator,move_map::const_iterator> itors = srcdst.equal_range(
loc);
530 for(move_map::const_iterator
i = itors.first;
i != itors.second; ++
i) {
536 if(best_loc.
valid() ==
false || (threat < std::max<double>(best_threat,max_acceptable_threat) && distance < best_distance)) {
537 best_loc =
i->second;
538 best_threat = threat;
539 best_distance = distance;
542 if(threat < max_acceptable_threat) {
543 mass_locations.insert(
i->second);
547 for(std::set<map_location>::const_iterator j = mass_locations.begin(); j != mass_locations.end(); ++j) {
549 LOG_AI <<
"found mass-to-attack target... " << *j <<
" with value: " << value*4.0;
550 targets.emplace_back(*j,value*4.0,ai_target::type::mass);
551 best_target = targets.end() - 1;
555 return std::pair<map_location,map_location>(
loc,best_loc);
559 for(std::vector<map_location>::reverse_iterator ri =
560 best_route.
steps.rbegin(); ri != best_route.
steps.rend(); ++ri) {
564 bool is_dangerous =
false;
566 typedef std::multimap<map_location,map_location>::const_iterator Itor;
567 std::pair<Itor,Itor> its = dstsrc.equal_range(*ri);
568 while(its.first != its.second) {
569 if (its.first->second == best->get_location()) {
570 if(!
should_retreat(its.first->first,best,fullmove_srcdst,fullmove_dstsrc,enemy_dstsrc,
572 double value = best_target->value - best->cost() / 20.0;
574 if(value > 0.0 && best_target->type != ai_target::type::mass) {
578 LOG_AI <<
"found reinforcement target... " << its.first->first <<
" with value: " << value*2.0;
579 targets.emplace_back(its.first->first,value*2.0,ai_target::type::battle_aid);
582 best_target->value = value;
584 targets.erase(best_target);
587 LOG_AI <<
"Moving to " << its.first->first.wml_x() <<
"," << its.first->first.wml_y();
589 return std::pair<map_location,map_location>(its.first->second,its.first->first);
600 if(best != units_.
end()) {
601 LOG_AI <<
"Could not make good move, staying still";
605 targets.emplace_back(best->get_location(), best_target->value);
606 best_target = targets.end() - 1;
607 return std::pair(best->get_location(), best->get_location());
610 LOG_AI <<
"Could not find anywhere to move!";
611 return std::pair<map_location,map_location>();
619 if(u_it == units_.
end()) {
625 const std::pair<move_map::const_iterator,move_map::const_iterator> locs = srcdst.equal_range(u);
626 for(move_map::const_iterator
i = locs.first;
i != locs.second; ++
i) {
632 if(rt.
steps.empty() ==
false) {
641 const double a =
rate_group(our_group,battlefield);
642 const double b = std::max<double>(
rate_group(their_group,battlefield),0.01);
648 for(std::vector<map_location>::const_iterator
i = route.begin();
i != route.end(); ++
i) {
650 const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(adj);
651 for(move_map::const_iterator j = itors.first; j != itors.second; ++j) {
652 res.insert(j->second);
665 std::vector<map_location>::const_iterator
i;
666 for(
i = route.begin();
i != route.end(); ++
i) {
667 if(units_.
count(*
i) > 0) {
671 std::size_t
n = 0, nunits = res.size();
673 const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(*
i);
674 for(move_map::const_iterator j = itors.first; j != itors.second; ++j) {
675 if(res.count(j->second) != 0) {
679 if(un == units_.
end() || (un->can_recruit() && !
is_keep_ignoring_leader(un->id())) || un->movement_left() < un->total_movement()) {
683 res.insert(j->second);
693 if(
i != route.begin()) {
705 const std::vector<map_location>::const_iterator itor =
std::find(route.begin(),route.end(),
dst);
706 if(itor == route.end()) {
710 LOG_AI <<
"group has " << units.size() <<
" members";
714 std::size_t direction = 0;
717 if(itor+1 != route.end()) {
719 }
else if(itor != route.begin()) {
725 direction = std::distance(adj.begin(),
std::find(adj.begin(), adj.end(), next));
728 std::deque<map_location> preferred_moves;
729 preferred_moves.push_back(
dst);
731 std::map<map_location,pathfind::paths> possible_moves;
735 bool gamestate_changed =
false;
737 for(std::set<map_location>::const_iterator
i = units.begin();
i != units.end(); ++
i) {
739 if(un == units_.
end()) {
744 int best_defense = -1;
745 for(std::deque<map_location>::const_iterator j = preferred_moves.begin(); j != preferred_moves.end(); ++j) {
746 if(units_.
count(*j)) {
750 const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(*j);
751 move_map::const_iterator m;
752 for(m = itors.first; m != itors.second; ++m) {
753 if(m->second == *
i) {
758 if(m == itors.second) {
762 int defense = un->defense_modifier(map_.
get_terrain(*j));
763 if(best_loc.
valid() ==
false || defense < best_defense) {
765 best_defense = defense;
769 if(best_loc.
valid()) {
771 gamestate_changed |= move_res->is_gamestate_changed();
774 if (!move_res->is_ok()) {
775 return gamestate_changed;
778 preferred_moves.erase(
std::find(preferred_moves.begin(),preferred_moves.end(),best_loc));
782 for(std::size_t
n = 0;
n < adj.size(); ++
n) {
783 if(
n != direction && ((
n+3)%6) != direction && map_.
on_board(adj[
n]) &&
784 units_.
count(adj[
n]) == 0 && std::count(preferred_moves.begin(),preferred_moves.end(),adj[
n]) == 0) {
785 preferred_moves.push_front(adj[
n]);
786 LOG_AI <<
"added moves: " << adj[
n].wml_x() <<
"," << adj[
n].wml_y();
790 LOG_AI <<
"Could not move group member to any of " << preferred_moves.size() <<
" locations";
794 return gamestate_changed;
802 double strength = 0.0;
803 for(std::set<map_location>::const_iterator
i = group.begin();
i != group.end(); ++
i) {
805 if(u == units_.
end()) {
812 for(std::vector<map_location>::const_iterator j = battlefield.begin(); j != battlefield.end(); ++j) {
816 defense /= battlefield.size();
820 const int attack_strength = a.num_attacks() * a.damage();
821 best_attack = std::max<int>(attack_strength, best_attack);
825 strength +=
static_cast<double>(rating);
841 const double proposed_terrain =
846 const double exposure = proposed_terrain - optimal_terrain;
850 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.
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)
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
std::shared_ptr< move > move_ptr
std::string::const_iterator iterator
This module contains various pathfinding functions and utilities.
rect dst
Location on the final composed sheet.
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