The Battle for Wesnoth  1.19.0-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 void goal::on_create(std::shared_ptr<ai::lua_ai_context>)
57 {
58  unrecognized();
59 }
60 
62 {
63  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. ";
64  ok_ = false;
65 }
66 
68 {
69 }
70 
71 void goal::add_targets(std::back_insert_iterator< std::vector< target >> /*target_list*/)
72 {
73 }
74 
76 {
77  return cfg_;
78 }
79 
80 std::string goal::get_id() const
81 {
82  return cfg_["id"];
83 }
84 
85 std::string goal::get_name() const
86 {
87  return cfg_["id"];
88 }
89 
90 std::string goal::get_engine() const
91 {
92  return cfg_["engine"];
93 }
94 
95 bool goal::redeploy(const config &cfg)
96 {
97  cfg_ = cfg;
98  on_create();
99  return true;
100 }
101 
102 bool goal::ok() const
103 {
104  return ok_;
105 }
106 
107 bool goal::active() const
108 {
109  return is_active(cfg_["time_of_day"],cfg_["turns"]);
110 }
111 
113 {
114  goal::on_create();
115  if (!cfg_["engine"].empty() && cfg_["engine"] != "cpp") {
116  unrecognized();
117  value_ = 0;
118  return;
119  }
120  if (const config::attribute_value *v = cfg_.get("value")) {
121  value_ = v->to_double(0);
122  }
123 }
124 
125 void target_unit_goal::add_targets(std::back_insert_iterator< std::vector< target >> target_list)
126 {
127  if (!(this)->active()) {
128  return;
129  }
130 
131  auto criteria = cfg_.optional_child("criteria");
132  if (!criteria) return;
133 
134  //find the enemy leaders and explicit targets
135  const unit_filter ufilt{ vconfig(*criteria) };
136  for (const unit &u : resources::gameboard->units()) {
137  if (ufilt( u )) {
138  LOG_AI_GOAL << "found explicit target unit at ... " << u.get_location() << " with value: " << value();
139  *target_list = target(u.get_location(), value(), ai_target::type::xplicit);
140  }
141  }
142 
143 }
144 
146  : goal(context,cfg)
147  , value_(0.0)
148 {
149 }
150 
152 {
153  goal::on_create();
154  if (!cfg_["engine"].empty() && cfg_["engine"] != "cpp") {
155  unrecognized();
156  value_ = 0;
157  return;
158  }
159  if (cfg_.has_attribute("value")) {
160  value_ = cfg_["value"].to_double(0);
161  }
162  auto criteria = cfg_.optional_child("criteria");
163  if (criteria) {
164  filter_ptr_.reset(new terrain_filter(vconfig(*criteria),resources::filter_con, false));
165  }
166 }
167 
168 void target_location_goal::add_targets(std::back_insert_iterator< std::vector< target >> target_list)
169 {
170  if (!(this)->active()) {
171  return;
172  }
173 
174  if (!filter_ptr_) return;
175 
176  std::set<map_location> items;
177  filter_ptr_->get_locations(items);
178  for (const map_location &loc : items)
179  {
180  LOG_AI_GOAL << "found explicit target location ... " << loc << " with value: " << value();
181  *target_list = target(loc, value(), ai_target::type::xplicit);
182  }
183 
184 }
185 
187  : goal(context,cfg)
188  , filter_ptr_()
189  , value_(0.0)
190 {
191 }
192 
194 {
195  goal::on_create();
196  if (!cfg_["engine"].empty() && cfg_["engine"] != "cpp") {
197  unrecognized();
198  value_ = 0;
199  return;
200  }
201  if (const config::attribute_value *v = cfg_.get("value")) {
202  value_ = v->to_double(0);
203  }
204  if (const config::attribute_value *v = cfg_.get("protect_radius")) {
205  radius_ = (*v).to_int(1);
206  }
207 
208  if (radius_<1) {
209  radius_=20;
210  }
211  auto criteria = cfg_.optional_child("criteria");
212  if (criteria) {
213  filter_ptr_.reset(new terrain_filter(vconfig(*criteria), resources::filter_con, false));
214  }
215 
216 }
217 
218 void protect_goal::add_targets(std::back_insert_iterator< std::vector< target >> target_list)
219 {
220  std::string goal_type;
221  if (protect_unit_) {
222  goal_type = "protect_unit";
223  } else {
224  goal_type ="protect_location";
225  }
226 
227  if (!(this)->active()) {
228  LOG_AI_GOAL << "skipping " << goal_type << " goal - not active";
229  return;
230  }
231 
232  auto criteria = cfg_.optional_child("criteria");
233  if (!criteria) {
234  LOG_AI_GOAL << "skipping " << goal_type << " goal - no criteria given";
235  return;
236  } else {
237  DBG_AI_GOAL << "side " << get_side() << ": "<< goal_type << " goal with criteria" << std::endl << cfg_.mandatory_child("criteria");
238  }
239 
240  unit_map &units = resources::gameboard->units();
241 
242  std::set<map_location> items;
243  if (protect_unit_) {
244  const unit_filter ufilt{ vconfig(*criteria) };
245  for (const unit &u : units)
246  {
247  // 'protect_unit' can be set to any unit of any side -> exclude hidden units
248  // unless they are visible to the AI side (e.g. allies with shared vision).
249  // As is done in other parts of the AI, units under fog/shroud count as visible to the AI.
250  if (ufilt(u)
251  && (!u.invisible(u.get_location()) || u.is_visible_to_team(current_team(), false)))
252  {
253  DBG_AI_GOAL << "side " << get_side() << ": in " << goal_type << ": " << u.get_location() << " should be protected";
254  items.insert(u.get_location());
255  }
256  }
257  } else {
258  filter_ptr_->get_locations(items);
259  }
260  DBG_AI_GOAL << "side " << get_side() << ": searching for threats in "+goal_type+" goal";
261  // Look for directions to protect a specific location or specific unit.
262  for (const map_location &loc : items)
263  {
264  for (const unit &u : units)
265  {
266  int distance = distance_between(u.get_location(), loc);
267  if (current_team().is_enemy(u.side()) && distance < radius_ &&
268  !u.invisible(u.get_location()))
269  {
270  DBG_AI_GOAL << "side " << get_side() << ": in " << goal_type << ": found threat target. " << u.get_location() << " is a threat to "<< loc;
271  *target_list = target(u.get_location(),
272  value_ * static_cast<double>(radius_ - distance) /
273  radius_, ai_target::type::threat);
274  }
275  }
276  }
277 
278 }
279 
280 protect_goal::protect_goal(readonly_context &context, const config &cfg, bool protect_unit)
281  : goal(context,cfg)
282  , filter_ptr_()
283  , protect_unit_(protect_unit)
284  , radius_(20) //this default radius is taken from old code
285  , value_(1.0) //this default value taken from old code
286 {
287 }
288 
290  : goal(context, cfg)
291  , code_()
292  , handler_()
293 {
294  if (cfg.has_attribute("code")) {
295  code_ = cfg["code"].str();
296  }
297  else
298  {
299  ERR_AI_GOAL << "side " << get_side() << " : Error creating Lua goal (missing code= key)";
300  }
301 }
302 
303 void lua_goal::on_create(std::shared_ptr<ai::lua_ai_context> l_ctx)
304 {
305  handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), *l_ctx));
306 }
307 
308 void lua_goal::add_targets(std::back_insert_iterator< std::vector< target >> target_list)
309 {
310  std::shared_ptr<lua_object<std::vector<target>>> l_obj = std::make_shared<lua_object<std::vector<target>>>();
311  config c(cfg_.child_or_empty("args"));
312  const config empty_cfg;
313  handler_->handle(c, empty_cfg, true, l_obj);
314 
315  std::vector < target > targets = *(l_obj->get());
316 
317  for (target tg : targets)
318  {
319  *target_list = tg;
320  }
321 }
322 
323 // This is defined in the source file so that it can easily access the logger
324 bool goal_factory::is_duplicate(const std::string& name)
325 {
326  if (get_list().find(name) != get_list().end()) {
327  ERR_AI_GOAL << "Error: Attempt to double-register goal " << name;
328  return true;
329  }
330  return false;
331 }
332 
333 } //end of namespace ai
static factory_map & get_list()
Definition: goal.hpp:158
bool is_duplicate(const std::string &name)
Definition: goal.cpp:324
bool ok_
Definition: goal.hpp:66
bool redeploy(const config &cfg)
Definition: goal.cpp:95
virtual config to_config() const
Definition: goal.cpp:75
virtual ~goal()
Definition: goal.cpp:67
void unrecognized()
Definition: goal.cpp:61
virtual std::string get_engine() const
Definition: goal.cpp:90
virtual std::string get_name() const
Definition: goal.cpp:85
config cfg_
Definition: goal.hpp:65
virtual void on_create()
Definition: goal.cpp:51
virtual std::string get_id() const
Definition: goal.cpp:80
goal(readonly_context &context, const config &cfg)
Definition: goal.cpp:45
bool active() const
Definition: goal.cpp:107
bool ok() const
Definition: goal.cpp:102
virtual void add_targets(std::back_insert_iterator< std::vector< target >> target_list)
Definition: goal.cpp:71
std::string code_
Definition: goal.hpp:147
lua_goal(readonly_context &context, const config &cfg)
Definition: goal.cpp:289
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:308
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:280
virtual void add_targets(std::back_insert_iterator< std::vector< target >> target_list)
Definition: goal.cpp:218
virtual void on_create()
Definition: goal.cpp:193
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:186
virtual void add_targets(std::back_insert_iterator< std::vector< target >> target_list)
Definition: goal.cpp:168
double value() const
Definition: goal.hpp:95
virtual void on_create()
Definition: goal.cpp:151
virtual void on_create()
Definition: goal.cpp:112
virtual void add_targets(std::back_insert_iterator< std::vector< target >> target_list)
Definition: goal.cpp:125
double value() const
Definition: goal.hpp:79
target_unit_goal(readonly_context &context, const config &cfg)
Definition: goal.cpp:145
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
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:395
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:367
bool has_attribute(config_key_type key) const
Definition: config.cpp:155
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:687
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:385
virtual const unit_map & units() const override
Definition: game_board.hpp:106
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:545
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
const std::vector< std::string > items
game_board * gameboard
Definition: resources.cpp:20
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
filter_context * filter_con
Definition: resources.cpp:23
Encapsulates the map of the game.
Definition: location.hpp:38
mock_char c