The Battle for Wesnoth  1.19.5+dev
contexts.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
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 t_translation::terrain_code terrain = map_.get_terrain(loc);
98  const int defense = u.defense_modifier(terrain);
99  int rating = 100 - defense;
100 
101  const int healing_value = 10;
102  const int friendly_village_value = 5;
103  const int neutral_village_value = 10;
104  const int enemy_village_value = 15;
105 
106  if(map_.gives_healing(terrain) && u.get_ability_bool("regenerate", loc) == false) {
107  rating += healing_value;
108  }
109 
110  if(map_.is_village(terrain)) {
111  int owner = resources::gameboard->village_owner(loc);
112 
113  if(owner == get_side()) {
114  rating += friendly_village_value;
115  } else if(owner == 0) {
116  rating += neutral_village_value;
117  } else {
118  rating += enemy_village_value;
119  }
120  }
121 
122  return rating;
123 }
124 
125 std::vector<target> default_ai_context_impl::find_targets(const move_map& enemy_dstsrc)
126 {
127 
128  log_scope2(log_ai, "finding targets...");
129  unit_map &units_ = resources::gameboard->units();
130  unit_map::iterator leader = units_.find_leader(get_side());
131  const gamemap &map_ = resources::gameboard->map();
132  const bool has_leader = leader != units_.end();
133 
134  std::vector<target> targets;
135 
136  //=== start getting targets
137 
138  //if enemy units are in range of the leader, then we target the enemies who are in range.
139  if(has_leader) {
140  double threat = power_projection(leader->get_location(), enemy_dstsrc);
141  if(threat > 0.0) {
142  //find the location of enemy threats
143  std::set<map_location> threats;
144 
145  for(const map_location& adj : get_adjacent_tiles(leader->get_location())) {
146  std::pair<move_map::const_iterator,move_map::const_iterator> itors = enemy_dstsrc.equal_range(adj);
147  while(itors.first != itors.second) {
148  if(units_.count(itors.first->second)) {
149  threats.insert(itors.first->second);
150  }
151 
152  ++itors.first;
153  }
154  }
155 
156  assert(threats.empty() == false);
157 
158  const double value = threat/static_cast<double>(threats.size());
159  for(std::set<map_location>::const_iterator i = threats.begin(); i != threats.end(); ++i) {
160  LOG_AI << "found threat target... " << *i << " with value: " << value;
161  targets.emplace_back(*i,value,ai_target::type::threat);
162  }
163  }
164  }
165 
166  double corner_distance = distance_between(map_location::ZERO(), map_location(map_.w(),map_.h()));
167  double village_value = get_village_value();
168  if(has_leader && village_value > 0.0) {
169  std::map<map_location,pathfind::paths> friends_possible_moves;
170  move_map friends_srcdst, friends_dstsrc;
171  calculate_possible_moves(friends_possible_moves, friends_srcdst, friends_dstsrc, false, true);
172 
173  for(const map_location& village_loc : map_.villages()) {
174  assert(map_.on_board(village_loc));
175 
176  bool ally_village = false;
177  for(const team& t : resources::gameboard->teams()) {
178  if(!current_team().is_enemy(t.side()) && t.owns_village(village_loc)) {
179  ally_village = true;
180  break;
181  }
182  }
183 
184  if (ally_village)
185  {
186  //Support seems to cause the AI to just 'sit around' a lot, so
187  //only turn it on if it's explicitly enabled.
188  if(get_support_villages()) {
189  double enemy = power_projection(village_loc, enemy_dstsrc);
190  if (enemy > 0)
191  {
192  enemy *= 1.7;
193  double our = power_projection(village_loc, friends_dstsrc);
194  double value = village_value * our / enemy;
195  add_target(target(village_loc, value, ai_target::type::support));
196  }
197  }
198  }
199  else
200  {
201  double leader_distance = distance_between(village_loc, leader->get_location());
202  double value = village_value * (1.0 - leader_distance / corner_distance);
203  LOG_AI << "found village target... " << village_loc
204  << " with value: " << value
205  << " distance: " << leader_distance;
206  targets.emplace_back(village_loc,value,ai_target::type::village);
207  }
208  }
209  }
210 
211  std::vector<goal_ptr>& goals = get_goals();
212 
213  //find the enemy leaders and explicit targets
215  if (get_leader_value()>0.0) {
216  for(u = units_.begin(); u != units_.end(); ++u) {
217  //is a visible enemy leader
218  if (u->can_recruit() && current_team().is_enemy(u->side())
219  && !u->invisible(u->get_location())) {
220  assert(map_.on_board(u->get_location()));
221  LOG_AI << "found enemy leader (side: " << u->side() << ") target... " << u->get_location() << " with value: " << get_leader_value();
222  targets.emplace_back(u->get_location(), get_leader_value(), ai_target::type::leader);
223  }
224  }
225 
226  }
227 
228  //explicit targets for this team
229  for(std::vector<goal_ptr>::iterator j = goals.begin();
230  j != goals.end(); ++j) {
231 
232  if (!(*j)->active()) {
233  continue;
234  }
235  (*j)->add_targets(std::back_inserter(targets));
236 
237  }
238 
239  //=== end getting targets
240 
241  std::vector<double> new_values;
242 
243  for(std::vector<target>::iterator i = targets.begin();
244  i != targets.end(); ++i) {
245 
246  new_values.push_back(i->value);
247 
248  for(std::vector<target>::const_iterator j = targets.begin(); j != targets.end(); ++j) {
249  if(i->loc == j->loc) {
250  continue;
251  }
252 
253  const double distance = std::abs(j->loc.x - i->loc.x) +
254  std::abs(j->loc.y - i->loc.y);
255  new_values.back() += j->value/(distance*distance);
256  }
257  }
258 
259  assert(new_values.size() == targets.size());
260  for(std::size_t n = 0; n != new_values.size(); ++n) {
261  LOG_AI << "target value: " << targets[n].value << " -> " << new_values[n];
262  targets[n].value = new_values[n];
263  }
264 
265  return targets;
266 }
267 
268 const std::vector<target>& default_ai_context_impl::additional_targets() const
269 {
270  return additional_targets_;
271 }
272 
274 {
275  additional_targets_.push_back(t);
276 }
277 
279 {
280  additional_targets_.clear();
281 }
282 
284 {
285  return config();
286 }
287 
288 } //of namespace ai
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:278
virtual const std::vector< target > & additional_targets() const
Definition: contexts.cpp:268
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:125
virtual ~default_ai_context_impl()
Definition: contexts.cpp:63
virtual config to_default_ai_context_config() const
Definition: contexts.cpp:283
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:273
std::vector< target > additional_targets_
Definition: contexts.hpp:242
virtual ~default_ai_context_proxy()
Definition: contexts.cpp:53
default_ai_context * target_
Definition: contexts.hpp:205
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:450
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:497
virtual const std::vector< goal_ptr > & get_goals() const override
Definition: contexts.hpp:636
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:681
virtual double get_village_value() const override
Definition: contexts.hpp:746
virtual double get_leader_value() const override
Definition: contexts.hpp:661
virtual bool get_support_villages() const override
Definition: contexts.hpp:741
void init_readwrite_context_proxy(readwrite_context &target)
Definition: contexts.hpp:882
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:396
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
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:302
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:385
Encapsulates the map of the game.
Definition: map.hpp:172
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:237
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:75
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(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:133
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:1028
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:181
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit's defense on a given terrain.
Definition: unit.cpp:1716
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:479
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:550
Standard logging facilities (interface).
#define log_scope2(domain, description)
Definition: log.hpp:279
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:45
static const map_location & ZERO()
Definition: location.hpp:96
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
static map_location::direction n