The Battle for Wesnoth  1.19.7+dev
goal.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  * @file
18  */
19 
20 #include "ai/composite/goal.hpp"
21 
22 #include "ai/default/contexts.hpp"
23 #include "ai/lua/core.hpp"
24 #include "ai/lua/lua_object.hpp"
25 #include "game_board.hpp"
26 #include "log.hpp"
27 #include "map/location.hpp"
28 #include "resources.hpp"
30 #include "terrain/filter.hpp"
31 #include "units/unit.hpp"
32 #include "units/map.hpp"
33 #include "units/filter.hpp"
34 
35 #include <set>
36 #include <sstream>
37 
38 namespace ai {
39 
40 static lg::log_domain log_ai_goal("ai/goal");
41 #define DBG_AI_GOAL LOG_STREAM(debug, log_ai_goal)
42 #define LOG_AI_GOAL LOG_STREAM(info, log_ai_goal)
43 #define ERR_AI_GOAL LOG_STREAM(err, log_ai_goal)
44 
45 goal::goal(readonly_context &context, const config &cfg)
46  : readonly_context_proxy(), cfg_(cfg), ok_(true)
47 {
49 }
50 
52 {
53  LOG_AI_GOAL << "side " << get_side() << " : " << " created goal with name=[" << cfg_["name"] << "]";
54 }
55 
56 // In this case, the API is intended to cause an error with this specific type.
57 // NOLINTNEXTLINE(performance-unnecessary-value-param)
58 void goal::on_create(std::shared_ptr<ai::lua_ai_context>)
59 {
60  unrecognized();
61 }
62 
64 {
65  ERR_AI_GOAL << "side " << get_side() << " : " << " tried to create goal with name=[" << cfg_["name"] << "], but the [" << cfg_["engine"] << "] engine did not recognize that type of goal. ";
66  ok_ = false;
67 }
68 
70 {
71 }
72 
73 void goal::add_targets(std::back_insert_iterator< std::vector< target >> /*target_list*/)
74 {
75 }
76 
78 {
79  return cfg_;
80 }
81 
82 std::string goal::get_id() const
83 {
84  return cfg_["id"];
85 }
86 
87 std::string goal::get_name() const
88 {
89  return cfg_["id"];
90 }
91 
92 std::string goal::get_engine() const
93 {
94  return cfg_["engine"];
95 }
96 
97 bool goal::redeploy(const config &cfg)
98 {
99  cfg_ = cfg;
100  on_create();
101  return true;
102 }
103 
104 bool goal::ok() const
105 {
106  return ok_;
107 }
108 
109 bool goal::active() const
110 {
111  return is_active(cfg_["time_of_day"],cfg_["turns"]);
112 }
113 
115 {
116  goal::on_create();
117  if (!cfg_["engine"].empty() && cfg_["engine"] != "cpp") {
118  unrecognized();
119  value_ = 0;
120  return;
121  }
122  if (const config::attribute_value *v = cfg_.get("value")) {
123  value_ = v->to_double(0);
124  }
125 }
126 
127 void target_unit_goal::add_targets(std::back_insert_iterator< std::vector< target >> target_list)
128 {
129  if (!(this)->active()) {
130  return;
131  }
132 
133  auto criteria = cfg_.optional_child("criteria");
134  if (!criteria) return;
135 
136  //find the enemy leaders and explicit targets
137  const unit_filter ufilt{ vconfig(*criteria) };
138  for (const unit &u : resources::gameboard->units()) {
139  if (ufilt( u )) {
140  LOG_AI_GOAL << "found explicit target unit at ... " << u.get_location() << " with value: " << value();
141  *target_list = target(u.get_location(), value(), ai_target::type::xplicit);
142  }
143  }
144 
145 }
146 
148  : goal(context,cfg)
149  , value_(0.0)
150 {
151 }
152 
154 {
155  goal::on_create();
156  if (!cfg_["engine"].empty() && cfg_["engine"] != "cpp") {
157  unrecognized();
158  value_ = 0;
159  return;
160  }
161  if (cfg_.has_attribute("value")) {
162  value_ = cfg_["value"].to_double(0);
163  }
164  auto criteria = cfg_.optional_child("criteria");
165  if (criteria) {
166  filter_ptr_.reset(new terrain_filter(vconfig(*criteria),resources::filter_con, false));
167  }
168 }
169 
170 void target_location_goal::add_targets(std::back_insert_iterator< std::vector< target >> target_list)
171 {
172  if (!(this)->active()) {
173  return;
174  }
175 
176  if (!filter_ptr_) return;
177 
178  std::set<map_location> items;
179  filter_ptr_->get_locations(items);
180  for (const map_location &loc : items)
181  {
182  LOG_AI_GOAL << "found explicit target location ... " << loc << " with value: " << value();
183  *target_list = target(loc, value(), ai_target::type::xplicit);
184  }
185 
186 }
187 
189  : goal(context,cfg)
190  , filter_ptr_()
191  , value_(0.0)
192 {
193 }
194 
196 {
197  goal::on_create();
198  if (!cfg_["engine"].empty() && cfg_["engine"] != "cpp") {
199  unrecognized();
200  value_ = 0;
201  return;
202  }
203  if (const config::attribute_value *v = cfg_.get("value")) {
204  value_ = v->to_double(0);
205  }
206  if (const config::attribute_value *v = cfg_.get("protect_radius")) {
207  radius_ = (*v).to_int(1);
208  }
209 
210  if (radius_<1) {
211  radius_=20;
212  }
213  auto criteria = cfg_.optional_child("criteria");
214  if (criteria) {
215  filter_ptr_.reset(new terrain_filter(vconfig(*criteria), resources::filter_con, false));
216  }
217 
218 }
219 
220 void protect_goal::add_targets(std::back_insert_iterator< std::vector< target >> target_list)
221 {
222  std::string goal_type;
223  if (protect_unit_) {
224  goal_type = "protect_unit";
225  } else {
226  goal_type ="protect_location";
227  }
228 
229  if (!(this)->active()) {
230  LOG_AI_GOAL << "skipping " << goal_type << " goal - not active";
231  return;
232  }
233 
234  auto criteria = cfg_.optional_child("criteria");
235  if (!criteria) {
236  LOG_AI_GOAL << "skipping " << goal_type << " goal - no criteria given";
237  return;
238  } else {
239  DBG_AI_GOAL << "side " << get_side() << ": "<< goal_type << " goal with criteria" << std::endl << cfg_.mandatory_child("criteria");
240  }
241 
242  unit_map &units = resources::gameboard->units();
243 
244  std::set<map_location> items;
245  if (protect_unit_) {
246  const unit_filter ufilt{ vconfig(*criteria) };
247  for (const unit &u : units)
248  {
249  // 'protect_unit' can be set to any unit of any side -> exclude hidden units
250  // unless they are visible to the AI side (e.g. allies with shared vision).
251  // As is done in other parts of the AI, units under fog/shroud count as visible to the AI.
252  if (ufilt(u)
253  && (!u.invisible(u.get_location()) || u.is_visible_to_team(current_team(), false)))
254  {
255  DBG_AI_GOAL << "side " << get_side() << ": in " << goal_type << ": " << u.get_location() << " should be protected";
256  items.insert(u.get_location());
257  }
258  }
259  } else {
260  filter_ptr_->get_locations(items);
261  }
262  DBG_AI_GOAL << "side " << get_side() << ": searching for threats in "+goal_type+" goal";
263  // Look for directions to protect a specific location or specific unit.
264  for (const map_location &loc : items)
265  {
266  for (const unit &u : units)
267  {
268  int distance = distance_between(u.get_location(), loc);
269  if (current_team().is_enemy(u.side()) && distance < radius_ &&
270  !u.invisible(u.get_location()))
271  {
272  DBG_AI_GOAL << "side " << get_side() << ": in " << goal_type << ": found threat target. " << u.get_location() << " is a threat to "<< loc;
273  *target_list = target(u.get_location(),
274  value_ * static_cast<double>(radius_ - distance) /
275  radius_, ai_target::type::threat);
276  }
277  }
278  }
279 
280 }
281 
282 protect_goal::protect_goal(readonly_context &context, const config &cfg, bool protect_unit)
283  : goal(context,cfg)
284  , filter_ptr_()
285  , protect_unit_(protect_unit)
286  , radius_(20) //this default radius is taken from old code
287  , value_(1.0) //this default value taken from old code
288 {
289 }
290 
292  : goal(context, cfg)
293  , code_()
294  , handler_()
295 {
296  if (cfg.has_attribute("code")) {
297  code_ = cfg["code"].str();
298  }
299  else
300  {
301  ERR_AI_GOAL << "side " << get_side() << " : Error creating Lua goal (missing code= key)";
302  }
303 }
304 
305 void lua_goal::on_create(std::shared_ptr<ai::lua_ai_context> l_ctx)
306 {
307  handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), *l_ctx));
308 }
309 
310 void lua_goal::add_targets(std::back_insert_iterator< std::vector< target >> target_list)
311 {
312  std::shared_ptr<lua_object<std::vector<target>>> l_obj = std::make_shared<lua_object<std::vector<target>>>();
313  config c(cfg_.child_or_empty("args"));
314  const config empty_cfg;
315  handler_->handle(c, empty_cfg, true, l_obj);
316 
317  std::vector < target > targets = *(l_obj->get());
318 
319  for (target tg : targets)
320  {
321  *target_list = tg;
322  }
323 }
324 
325 // This is defined in the source file so that it can easily access the logger
326 bool goal_factory::is_duplicate(const std::string& name)
327 {
328  if (get_list().find(name) != get_list().end()) {
329  ERR_AI_GOAL << "Error: Attempt to double-register goal " << name;
330  return true;
331  }
332  return false;
333 }
334 
335 } //end of namespace ai
map_location loc
Definition: move.cpp:172
static factory_map & get_list()
Definition: goal.hpp:158
bool is_duplicate(const std::string &name)
Definition: goal.cpp:326
bool ok_
Definition: goal.hpp:66
bool redeploy(const config &cfg)
Definition: goal.cpp:97
virtual config to_config() const
Definition: goal.cpp:77
virtual ~goal()
Definition: goal.cpp:69
void unrecognized()
Definition: goal.cpp:63
virtual std::string get_engine() const
Definition: goal.cpp:92
virtual std::string get_name() const
Definition: goal.cpp:87
config cfg_
Definition: goal.hpp:65
virtual void on_create()
Definition: goal.cpp:51
virtual std::string get_id() const
Definition: goal.cpp:82
goal(readonly_context &context, const config &cfg)
Definition: goal.cpp:45
bool active() const
Definition: goal.cpp:109
bool ok() const
Definition: goal.cpp:104
virtual void add_targets(std::back_insert_iterator< std::vector< target >> target_list)
Definition: goal.cpp:73
std::string code_
Definition: goal.hpp:147
lua_goal(readonly_context &context, const config &cfg)
Definition: goal.cpp:291
std::shared_ptr< lua_ai_action_handler > handler_
Definition: goal.hpp:148
virtual void add_targets(std::back_insert_iterator< std::vector< target >> target_list)
Definition: goal.cpp:310
std::shared_ptr< terrain_filter > filter_ptr_
Definition: goal.hpp:118
bool protect_unit_
Definition: goal.hpp:119
protect_goal(readonly_context &context, const config &cfg, bool protect_unit)
Definition: goal.cpp:282
virtual void add_targets(std::back_insert_iterator< std::vector< target >> target_list)
Definition: goal.cpp:220
virtual void on_create()
Definition: goal.cpp:195
double value_
Definition: goal.hpp:121
virtual const team & current_team() const override
Definition: contexts.hpp:450
virtual bool is_active(const std::string &time_of_day, const std::string &turns) const override
Definition: contexts.hpp:756
void init_readonly_context_proxy(readonly_context &target)
Definition: contexts.hpp:434
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:396
std::shared_ptr< terrain_filter > filter_ptr_
Definition: goal.hpp:99
target_location_goal(readonly_context &context, const config &cfg)
Definition: goal.cpp:188
virtual void add_targets(std::back_insert_iterator< std::vector< target >> target_list)
Definition: goal.cpp:170
double value() const
Definition: goal.hpp:95
virtual void on_create()
Definition: goal.cpp:153
virtual void on_create()
Definition: goal.cpp:114
virtual void add_targets(std::back_insert_iterator< std::vector< target >> target_list)
Definition: goal.cpp:127
double value() const
Definition: goal.hpp:79
target_unit_goal(readonly_context &context, const config &cfg)
Definition: goal.cpp:147
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:394
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:366
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:685
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:384
virtual const unit_map & units() const override
Definition: game_board.hpp:107
Container associating units to locations.
Definition: map.hpp:98
This class represents a single unit of a specific type.
Definition: unit.hpp:133
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
Default AI contexts.
#define ERR_AI_GOAL
Definition: goal.cpp:43
#define LOG_AI_GOAL
Definition: goal.cpp:42
#define DBG_AI_GOAL
Definition: goal.cpp:41
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).
Lua object(value) wrapper implementation.
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
static lg::log_domain log_ai_goal("ai/goal")
static std::unique_ptr< class sdl_event_handler > handler_
Definition: handler.cpp:61
game_board * gameboard
Definition: resources.cpp:20
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
filter_context * filter_con
Definition: resources.cpp:23
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:140
Encapsulates the map of the game.
Definition: location.hpp:45
mock_char c