The Battle for Wesnoth  1.19.18+dev
contexts.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2025
3  by Yurii Chernyi <terraninfo@terraninfo.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * Helper functions for the object which operates in the context of AI for specific side
18  * This is part of AI interface
19  * @file
20  */
21 
22 #include "ai/default/contexts.hpp"
23 
24 #include "game_board.hpp"
25 #include "log.hpp"
26 #include "map/map.hpp"
27 #include "resources.hpp"
28 #include "team.hpp"
29 #include "units/unit.hpp"
30 #include "ai/composite/goal.hpp"
31 #include "pathfind/pathfind.hpp"
32 
33 static lg::log_domain log_ai("ai/general");
34 #define DBG_AI LOG_STREAM(debug, log_ai)
35 #define LOG_AI LOG_STREAM(info, log_ai)
36 #define WRN_AI LOG_STREAM(warn, log_ai)
37 #define ERR_AI LOG_STREAM(err, log_ai)
38 
39 // =======================================================================
40 namespace ai {
41 
42 // default ai context
44 {
45 }
46 
48 {
49 }
50 
51 // default ai context proxy
52 
54 {
55 }
56 
58 {
60  target_= &target.get_default_ai_context();
61 }
62 
64 {
65 }
66 
67 int default_ai_context_impl::count_free_hexes_in_castle(const map_location &loc, std::set<map_location> &checked_hexes)
68 {
69  int ret = 0;
70  unit_map &units_ = resources::gameboard->units();
71  for(const map_location& adj : get_adjacent_tiles(loc)) {
72  if (checked_hexes.find(adj) != checked_hexes.end())
73  continue;
74  checked_hexes.insert(adj);
75  if (resources::gameboard->map().is_castle(adj)) {
76  const unit_map::const_iterator u = units_.find(adj);
77  ret += count_free_hexes_in_castle(adj, checked_hexes);
78  if (u == units_.end()
79  || (current_team().is_enemy(u->side())
80  && u->invisible(adj))
81  || ((&resources::gameboard->get_team(u->side()) == &current_team())
82  && u->movement_left() > 0)) {
83  ret += 1;
84  }
85  }
86  }
87  return ret;
88 }
89 
91  return *this;
92 }
93 
95 {
96  const gamemap &map_ = resources::gameboard->map();
97  const int defense = u.defense_modifier(map_.get_terrain(loc));
98  int rating = 100 - defense;
99 
100  const int healing_value = 10;
101  const int friendly_village_value = 5;
102  const int neutral_village_value = 10;
103  const int enemy_village_value = 15;
104 
105  if(map_.gives_healing(loc) && u.get_ability_bool("regenerate", loc) == false) {
106  rating += healing_value;
107  }
108 
109  if(map_.is_village(loc)) {
110  int owner = resources::gameboard->village_owner(loc);
111 
112  if(owner == get_side()) {
113  rating += friendly_village_value;
114  } else if(owner == 0) {
115  rating += neutral_village_value;
116  } else {
117  rating += enemy_village_value;
118  }
119  }
120 
121  return rating;
122 }
123 
124 std::vector<target> default_ai_context_impl::find_targets(const move_map& enemy_dstsrc)
125 {
126 
127  log_scope2(log_ai, "finding targets...");
128  unit_map &units_ = resources::gameboard->units();
129  unit_map::iterator leader = units_.find_leader(get_side());
130  const gamemap &map_ = resources::gameboard->map();
131  const bool has_leader = leader != units_.end();
132 
133  std::vector<target> targets;
134 
135  //=== start getting targets
136 
137  //if enemy units are in range of the leader, then we target the enemies who are in range.
138  if(has_leader) {
139  double threat = power_projection(leader->get_location(), enemy_dstsrc);
140  if(threat > 0.0) {
141  //find the location of enemy threats
142  std::set<map_location> threats;
143 
144  for(const map_location& adj : get_adjacent_tiles(leader->get_location())) {
145  auto itors = enemy_dstsrc.equal_range(adj);
146  while(itors.first != itors.second) {
147  if(units_.count(itors.first->second)) {
148  threats.insert(itors.first->second);
149  }
150 
151  ++itors.first;
152  }
153  }
154 
155  assert(threats.empty() == false);
156 
157  const double value = threat/static_cast<double>(threats.size());
158  for(std::set<map_location>::const_iterator i = threats.begin(); i != threats.end(); ++i) {
159  LOG_AI << "found threat target... " << *i << " with value: " << value;
160  targets.emplace_back(*i,value,ai_target::type::threat);
161  }
162  }
163  }
164 
165  double corner_distance = distance_between(map_location::ZERO(), map_location(map_.w(),map_.h()));
166  double village_value = get_village_value();
167  if(has_leader && village_value > 0.0) {
168  std::map<map_location,pathfind::paths> friends_possible_moves;
169  move_map friends_srcdst, friends_dstsrc;
170  calculate_possible_moves(friends_possible_moves, friends_srcdst, friends_dstsrc, false, true);
171 
172  for(const map_location& village_loc : map_.villages()) {
173  assert(map_.on_board(village_loc));
174 
175  bool ally_village = false;
176  for(const team& t : resources::gameboard->teams()) {
177  if(!current_team().is_enemy(t.side()) && t.owns_village(village_loc)) {
178  ally_village = true;
179  break;
180  }
181  }
182 
183  if (ally_village)
184  {
185  //Support seems to cause the AI to just 'sit around' a lot, so
186  //only turn it on if it's explicitly enabled.
187  if(get_support_villages()) {
188  double enemy = power_projection(village_loc, enemy_dstsrc);
189  if (enemy > 0)
190  {
191  enemy *= 1.7;
192  double our = power_projection(village_loc, friends_dstsrc);
193  double value = village_value * our / enemy;
194  add_target(target(village_loc, value, ai_target::type::support));
195  }
196  }
197  }
198  else
199  {
200  double leader_distance = distance_between(village_loc, leader->get_location());
201  double value = village_value * (1.0 - leader_distance / corner_distance);
202  LOG_AI << "found village target... " << village_loc
203  << " with value: " << value
204  << " distance: " << leader_distance;
205  targets.emplace_back(village_loc,value,ai_target::type::village);
206  }
207  }
208  }
209 
210  std::vector<goal_ptr>& goals = get_goals();
211 
212  //find the enemy leaders and explicit targets
214  if (get_leader_value()>0.0) {
215  for(u = units_.begin(); u != units_.end(); ++u) {
216  //is a visible enemy leader
217  if (u->can_recruit() && current_team().is_enemy(u->side())
218  && !u->invisible(u->get_location())) {
219  assert(map_.on_board(u->get_location()));
220  LOG_AI << "found enemy leader (side: " << u->side() << ") target... " << u->get_location() << " with value: " << get_leader_value();
221  targets.emplace_back(u->get_location(), get_leader_value(), ai_target::type::leader);
222  }
223  }
224 
225  }
226 
227  //explicit targets for this team
228  for(std::vector<goal_ptr>::iterator j = goals.begin();
229  j != goals.end(); ++j) {
230 
231  if (!(*j)->active()) {
232  continue;
233  }
234  (*j)->add_targets(std::back_inserter(targets));
235 
236  }
237 
238  //=== end getting targets
239 
240  std::vector<double> new_values;
241 
242  for(std::vector<target>::iterator i = targets.begin();
243  i != targets.end(); ++i) {
244 
245  new_values.push_back(i->value);
246 
247  for(std::vector<target>::const_iterator j = targets.begin(); j != targets.end(); ++j) {
248  if(i->loc == j->loc) {
249  continue;
250  }
251 
252  const double distance = std::abs(j->loc.x - i->loc.x) +
253  std::abs(j->loc.y - i->loc.y);
254  new_values.back() += j->value/(distance*distance);
255  }
256  }
257 
258  assert(new_values.size() == targets.size());
259  for(std::size_t n = 0; n != new_values.size(); ++n) {
260  LOG_AI << "target value: " << targets[n].value << " -> " << new_values[n];
261  targets[n].value = new_values[n];
262  }
263 
264  return targets;
265 }
266 
267 const std::vector<target>& default_ai_context_impl::additional_targets() const
268 {
269  return additional_targets_;
270 }
271 
273 {
274  additional_targets_.push_back(t);
275 }
276 
278 {
279  additional_targets_.clear();
280 }
281 
283 {
284  return config();
285 }
286 
287 } //of namespace ai
map_location loc
Definition: move.cpp:172
double t
Definition: astarsearch.cpp:63
virtual default_ai_context & get_default_ai_context()
Definition: contexts.cpp:90
virtual void clear_additional_targets() const
Definition: contexts.cpp:277
virtual const std::vector< target > & additional_targets() const
Definition: contexts.cpp:267
virtual int rate_terrain(const unit &u, const map_location &loc) const
Definition: contexts.cpp:94
virtual std::vector< target > find_targets(const move_map &enemy_dstsrc)
Definition: contexts.cpp:124
virtual ~default_ai_context_impl()
Definition: contexts.cpp:63
virtual config to_default_ai_context_config() const
Definition: contexts.cpp:282
int count_free_hexes_in_castle(const map_location &loc, std::set< map_location > &checked_hexes)
Definition: contexts.cpp:67
virtual void add_target(const target &t) const
Definition: contexts.cpp:272
std::vector< target > additional_targets_
Definition: contexts.hpp:236
virtual ~default_ai_context_proxy()
Definition: contexts.cpp:53
default_ai_context * target_
Definition: contexts.hpp:199
void init_default_ai_context_proxy(default_ai_context &target)
Definition: contexts.cpp:57
default_ai_context()
Constructor.
Definition: contexts.cpp:43
virtual ~default_ai_context()
Destructor.
Definition: contexts.cpp:47
virtual const team & current_team() const override
Definition: contexts.hpp:447
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
Definition: contexts.hpp:494
virtual const std::vector< goal_ptr > & get_goals() const override
Definition: contexts.hpp:628
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.
Definition: contexts.hpp:673
virtual double get_village_value() const override
Definition: contexts.hpp:738
virtual double get_leader_value() const override
Definition: contexts.hpp:653
virtual bool get_support_villages() const override
Definition: contexts.hpp:733
void init_readwrite_context_proxy(readwrite_context &target)
Definition: contexts.hpp:874
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:393
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number of the team that currently owns it,...
team & get_team(int i)
Definition: game_board.hpp:92
virtual const unit_map & units() const override
Definition: game_board.hpp:107
virtual const gamemap & map() const override
Definition: game_board.hpp:97
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:273
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:356
Encapsulates the map of the game.
Definition: map.hpp:173
bool is_village(const map_location &loc) const
Definition: map.cpp:66
const std::vector< map_location > & villages() const
Return a list of the locations of villages on the map.
Definition: map.hpp:234
int gives_healing(const map_location &loc) const
Definition: map.cpp:68
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
std::size_t count(const map_location &loc) const
Definition: map.hpp:413
unit_iterator find(std::size_t id)
Definition: map.cpp:302
unit_iterator begin()
Definition: map.hpp:418
unit_iterator find_leader(int side)
Definition: map.cpp:320
umap_retval_pair_t insert(const unit_ptr &p)
Inserts the unit pointed to by p into the map.
Definition: map.cpp:135
This class represents a single unit of a specific type.
Definition: unit.hpp:39
static lg::log_domain log_ai("ai/general")
#define LOG_AI
Definition: contexts.cpp:35
Default AI contexts.
std::size_t i
Definition: function.cpp:1032
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses or is affected by a given ability.
Definition: abilities.cpp:521
int defense_modifier(const t_translation::terrain_code &terrain, const map_location &loc) const
The unit's defense on a given terrain.
Definition: unit.cpp:1756
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.
Definition: location.cpp:584
void get_adjacent_tiles(const map_location &a, utils::span< map_location, 6 > res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:513
Standard logging facilities (interface).
#define log_scope2(domain, description)
Definition: log.hpp:276
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
Definition: game_info.hpp:43
game_board * gameboard
Definition: resources.cpp:20
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
@ enemy
Belongs to a non-friendly side; normally visualised by not displaying an orb.
This module contains various pathfinding functions and utilities.
Encapsulates the map of the game.
Definition: location.hpp:46
static const map_location & ZERO()
Definition: location.hpp:97
static map_location::direction n