The Battle for Wesnoth  1.17.0-dev
contexts.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2021
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 {
59  init_readwrite_context_proxy(target);
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  std::vector<team> teams_ = resources::gameboard->teams();
133  const bool has_leader = leader != units_.end();
134 
135  std::vector<target> targets;
136 
137  //=== start getting targets
138 
139  //if enemy units are in range of the leader, then we target the enemies who are in range.
140  if(has_leader) {
141  double threat = power_projection(leader->get_location(), enemy_dstsrc);
142  if(threat > 0.0) {
143  //find the location of enemy threats
144  std::set<map_location> threats;
145 
146  for(const map_location& adj : get_adjacent_tiles(leader->get_location())) {
147  std::pair<move_map::const_iterator,move_map::const_iterator> itors = enemy_dstsrc.equal_range(adj);
148  while(itors.first != itors.second) {
149  if(units_.count(itors.first->second)) {
150  threats.insert(itors.first->second);
151  }
152 
153  ++itors.first;
154  }
155  }
156 
157  assert(threats.empty() == false);
158 
159  const double value = threat/static_cast<double>(threats.size());
160  for(std::set<map_location>::const_iterator i = threats.begin(); i != threats.end(); ++i) {
161  LOG_AI << "found threat target... " << *i << " with value: " << value << "\n";
162  targets.emplace_back(*i,value,target::TYPE::THREAT);
163  }
164  }
165  }
166 
167  double corner_distance = distance_between(map_location::ZERO(), map_location(map_.w(),map_.h()));
168  double village_value = get_village_value();
169  if(has_leader && village_value > 0.0) {
170  std::map<map_location,pathfind::paths> friends_possible_moves;
171  move_map friends_srcdst, friends_dstsrc;
172  calculate_possible_moves(friends_possible_moves, friends_srcdst, friends_dstsrc, false, true);
173 
174  const std::vector<map_location>& villages = map_.villages();
175  for(std::vector<map_location>::const_iterator t =
176  villages.begin(); t != villages.end(); ++t) {
177 
178  assert(map_.on_board(*t));
179  bool ally_village = false;
180  for (std::size_t i = 0; i != teams_.size(); ++i)
181  {
182  if (!current_team().is_enemy(i + 1) && teams_[i].owns_village(*t)) {
183  ally_village = true;
184  break;
185  }
186  }
187 
188  if (ally_village)
189  {
190  //Support seems to cause the AI to just 'sit around' a lot, so
191  //only turn it on if it's explicitly enabled.
192  if(get_support_villages()) {
193  double enemy = power_projection(*t, enemy_dstsrc);
194  if (enemy > 0)
195  {
196  enemy *= 1.7;
197  double our = power_projection(*t, friends_dstsrc);
198  double value = village_value * our / enemy;
199  add_target(target(*t, value, target::TYPE::SUPPORT));
200  }
201  }
202  }
203  else
204  {
205  double leader_distance = distance_between(*t, leader->get_location());
206  double value = village_value * (1.0 - leader_distance / corner_distance);
207  LOG_AI << "found village target... " << *t
208  << " with value: " << value
209  << " distance: " << leader_distance << '\n';
210  targets.emplace_back(*t,value,target::TYPE::VILLAGE);
211  }
212  }
213  }
214 
215  std::vector<goal_ptr>& goals = get_goals();
216 
217  //find the enemy leaders and explicit targets
219  if (get_leader_value()>0.0) {
220  for(u = units_.begin(); u != units_.end(); ++u) {
221  //is a visible enemy leader
222  if (u->can_recruit() && current_team().is_enemy(u->side())
223  && !u->invisible(u->get_location())) {
224  assert(map_.on_board(u->get_location()));
225  LOG_AI << "found enemy leader (side: " << u->side() << ") target... " << u->get_location() << " with value: " << get_leader_value() << "\n";
226  targets.emplace_back(u->get_location(), get_leader_value(), target::TYPE::LEADER);
227  }
228  }
229 
230  }
231 
232  //explicit targets for this team
233  for(std::vector<goal_ptr>::iterator j = goals.begin();
234  j != goals.end(); ++j) {
235 
236  if (!(*j)->active()) {
237  continue;
238  }
239  (*j)->add_targets(std::back_inserter(targets));
240 
241  }
242 
243  //=== end getting targets
244 
245  std::vector<double> new_values;
246 
247  for(std::vector<target>::iterator i = targets.begin();
248  i != targets.end(); ++i) {
249 
250  new_values.push_back(i->value);
251 
252  for(std::vector<target>::const_iterator j = targets.begin(); j != targets.end(); ++j) {
253  if(i->loc == j->loc) {
254  continue;
255  }
256 
257  const double distance = std::abs(j->loc.x - i->loc.x) +
258  std::abs(j->loc.y - i->loc.y);
259  new_values.back() += j->value/(distance*distance);
260  }
261  }
262 
263  assert(new_values.size() == targets.size());
264  for(std::size_t n = 0; n != new_values.size(); ++n) {
265  LOG_AI << "target value: " << targets[n].value << " -> " << new_values[n] << "\n";
266  targets[n].value = new_values[n];
267  }
268 
269  return targets;
270 }
271 
272 const std::vector<target>& default_ai_context_impl::additional_targets() const
273 {
274  return additional_targets_;
275 }
276 
278 {
279  additional_targets_.push_back(t);
280 }
281 
283 {
284  additional_targets_.clear();
285 }
286 
288 {
289  return config();
290 }
291 
292 } //of namespace ai
virtual default_ai_context & get_default_ai_context()=0
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...
unit_iterator end()
Definition: map.hpp:429
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:85
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:475
virtual const unit_map & units() const override
Definition: game_board.hpp:112
This class represents a single unit of a specific type.
Definition: unit.hpp:121
static const map_location & ZERO()
Definition: location.hpp:75
unit_iterator find_leader(int side)
Definition: map.cpp:328
virtual config to_default_ai_context_config() const
Definition: contexts.cpp:287
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 =0
virtual const gamemap & map() const override
Definition: game_board.hpp:102
unit_iterator begin()
Definition: map.hpp:419
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
virtual bool get_support_villages() const =0
int count_free_hexes_in_castle(const map_location &loc, std::set< map_location > &checked_hexes)
Definition: contexts.cpp:67
virtual void clear_additional_targets() const
Definition: contexts.cpp:282
virtual double get_village_value() const =0
virtual std::vector< target > find_targets(const move_map &enemy_dstsrc)
Definition: contexts.cpp:125
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
#define LOG_AI
Definition: contexts.cpp:35
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit&#39;s defense on a given terrain.
Definition: unit.cpp:1575
Belongs to a non-friendly side; normally visualised by not displaying an orb.
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:61
int gives_healing(const map_location &loc) const
Definition: map.cpp:68
int w() const
Effective map width.
Definition: map.hpp:50
default_ai_context()
Constructor.
Definition: contexts.cpp:43
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:302
virtual default_ai_context & get_default_ai_context()
Definition: contexts.cpp:90
static lg::log_domain log_ai("ai/general")
game_board * gameboard
Definition: resources.cpp:21
Encapsulates the map of the game.
Definition: map.hpp:171
virtual const std::vector< target > & additional_targets() const
Definition: contexts.cpp:272
bool is_enemy(int n) const
Definition: team.hpp:255
#define log_scope2(domain, description)
Definition: log.hpp:219
std::size_t count(const map_location &loc) const
Definition: map.hpp:414
virtual double power_projection(const map_location &loc, const move_map &dstsrc) const =0
Function which finds how much &#39;power&#39; a side can attack a certain location with.
virtual void add_target(const target &t) const
Definition: contexts.cpp:277
void init_default_ai_context_proxy(default_ai_context &target)
Definition: contexts.cpp:57
Encapsulates the map of the game.
Definition: location.hpp:38
unit_iterator find(std::size_t id)
Definition: map.cpp:310
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:149
virtual int count_free_hexes_in_castle(const map_location &loc, std::set< map_location > &checked_hexes)=0
std::size_t i
Definition: function.cpp:967
virtual const std::vector< goal_ptr > & get_goals() const =0
virtual ~default_ai_context()
Destructor.
Definition: contexts.cpp:47
virtual const team & current_team() const =0
Default AI contexts.
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
virtual double get_leader_value() const =0
virtual ~default_ai_context_proxy()
Definition: contexts.cpp:53
bool is_village(const map_location &loc) const
Definition: map.cpp:66
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:546
double t
Definition: astarsearch.cpp:65
const std::vector< map_location > & villages() const
Return a list of the locations of villages on the map.
Definition: map.hpp:237
Standard logging facilities (interface).
virtual side_number get_side() const =0
Get the side number.
virtual int rate_terrain(const unit &u, const map_location &loc) const
Definition: contexts.cpp:94
Container associating units to locations.
Definition: map.hpp:98
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
static map_location::DIRECTION n
int h() const
Effective map height.
Definition: map.hpp:53
virtual ~default_ai_context_impl()
Definition: contexts.cpp:63
This module contains various pathfinding functions and utilities.
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
virtual void add_target(const target &t) const =0