The Battle for Wesnoth  1.15.12+dev
aspect_attacks.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Yurii Chernyi <terraninfo@terraninfo.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * Stage: fallback to other AI
17  * @file
18  */
19 
21 
22 #include "actions/attack.hpp"
23 #include "ai/manager.hpp"
24 #include "game_board.hpp"
25 #include "log.hpp"
26 #include "lua/lauxlib.h"
27 #include "map/map.hpp"
28 #include "pathfind/pathfind.hpp"
29 #include "resources.hpp"
30 #include "scripting/lua_unit.hpp"
31 #include "team.hpp"
32 #include "tod_manager.hpp"
33 #include "units/filter.hpp"
34 #include "units/unit.hpp"
35 
36 namespace ai
37 {
38 namespace ai_default_rca
39 {
40 static lg::log_domain log_ai_testing_aspect_attacks("ai/aspect/attacks");
41 #define DBG_AI LOG_STREAM(debug, log_ai_testing_aspect_attacks)
42 #define LOG_AI LOG_STREAM(info, log_ai_testing_aspect_attacks)
43 #define ERR_AI LOG_STREAM(err, log_ai_testing_aspect_attacks)
44 
45 aspect_attacks_base::aspect_attacks_base(readonly_context& context, const config& cfg, const std::string& id)
46  : typesafe_aspect<attacks_vector>(context, cfg, id)
47 {
48 }
49 
50 aspect_attacks::aspect_attacks(readonly_context& context, const config& cfg, const std::string& id)
51  : aspect_attacks_base(context, cfg, id)
52  , filter_own_()
53  , filter_enemy_()
54 {
55  if(const config& filter_own = cfg.child("filter_own")) {
56  vconfig vcfg(filter_own);
57  vcfg.make_safe();
58  filter_own_.reset(new unit_filter(vcfg));
59  }
60 
61  if(const config& filter_enemy = cfg.child("filter_enemy")) {
62  vconfig vcfg(filter_enemy);
63  vcfg.make_safe();
64  filter_enemy_.reset(new unit_filter(vcfg));
65  }
66 }
67 
69 {
70  this->value_ = analyze_targets();
71  this->valid_ = true;
72 }
73 
74 std::shared_ptr<attacks_vector> aspect_attacks_base::analyze_targets() const
75 {
76  const move_map& srcdst = get_srcdst();
77  const move_map& dstsrc = get_dstsrc();
78  const move_map& enemy_srcdst = get_enemy_srcdst();
79  const move_map& enemy_dstsrc = get_enemy_dstsrc();
80 
81  auto res = std::make_shared<attacks_vector>();
82  const unit_map& units_ = resources::gameboard->units();
83 
84  std::vector<map_location> unit_locs;
85  for(const unit& u : units_) {
86  if(u.side() == get_side() && u.attacks_left() && !(u.can_recruit() && is_passive_leader(u.id()))) {
87  if(!is_allowed_attacker(u)) {
88  continue;
89  }
90 
91  unit_locs.push_back(u.get_location());
92  }
93  }
94 
95  std::array<bool, 6> used_locations;
96  used_locations.fill(false);
97 
98  moves_map dummy_moves;
99  move_map fullmove_srcdst, fullmove_dstsrc;
100  calculate_possible_moves(dummy_moves, fullmove_srcdst, fullmove_dstsrc, false, true);
101 
102  unit_stats_cache().clear();
103 
104  for(const unit& u : units_) {
105  // Attack anyone who is on the enemy side,
106  // and who is not invisible or petrified.
107  if(current_team().is_enemy(u.side()) && !u.incapacitated() && !u.invisible(u.get_location())) {
108  if(!is_allowed_enemy(u)) {
109  continue;
110  }
111 
112  const auto adjacent = get_adjacent_tiles(u.get_location());
113  attack_analysis analysis;
114  analysis.target = u.get_location();
115  analysis.vulnerability = 0.0;
116  analysis.support = 0.0;
117 
118  do_attack_analysis(u.get_location(), srcdst, dstsrc, fullmove_srcdst, fullmove_dstsrc, enemy_srcdst,
119  enemy_dstsrc, adjacent, used_locations, unit_locs, *res, analysis, current_team());
120  }
121  }
122  return res;
123 }
124 
126  const move_map& srcdst,
127  const move_map& dstsrc,
128  const move_map& fullmove_srcdst,
129  const move_map& fullmove_dstsrc,
130  const move_map& enemy_srcdst,
131  const move_map& enemy_dstsrc,
132  const std::array<map_location, 6>& tiles,
133  std::array<bool, 6>& used_locations,
134  std::vector<map_location>& units,
135  std::vector<attack_analysis>& result,
136  attack_analysis& cur_analysis,
137  const team& current_team) const
138 {
139  // This function is called fairly frequently, so interact with the user here.
140 
142  const int max_attack_depth = 5;
143  if(cur_analysis.movements.size() >= std::size_t(max_attack_depth)) {
144  return;
145  }
146 
147  const gamemap& map_ = resources::gameboard->map();
148  unit_map& units_ = resources::gameboard->units();
149  const std::vector<team>& teams_ = resources::gameboard->teams();
150 
151  const std::size_t max_positions = 1000;
152  if(result.size() > max_positions && !cur_analysis.movements.empty()) {
153  LOG_AI << "cut analysis short with number of positions\n";
154  return;
155  }
156 
157  for(std::size_t i = 0; i != units.size(); ++i) {
158  const map_location current_unit = units[i];
159 
160  unit_map::iterator unit_itor = units_.find(current_unit);
161  assert(unit_itor != units_.end());
162 
163  // See if the unit has the backstab ability.
164  // Units with backstab will want to try to have a
165  // friendly unit opposite the position they move to.
166  //
167  // See if the unit has the slow ability -- units with slow only attack first.
168  bool backstab = false, slow = false;
169  for(const attack_type& a : unit_itor->attacks()) {
170  // For speed, just assume these specials will be active if they are present.
171  if(a.has_special("backstab", true)) {
172  backstab = true;
173  }
174 
175  if(a.has_special("slow", true)) {
176  slow = true;
177  }
178  }
179 
180  if(slow && cur_analysis.movements.empty() == false) {
181  continue;
182  }
183 
184  // Check if the friendly unit is surrounded,
185  // A unit is surrounded if it is flanked by enemy units
186  // and at least one other enemy unit is nearby
187  // or if the unit is totally surrounded by enemies
188  // with max. one tile to escape.
189  bool is_surrounded = false;
190  bool is_flanked = false;
191  int enemy_units_around = 0;
192  int accessible_tiles = 0;
193  const auto adj = get_adjacent_tiles(current_unit);
194 
195  for(std::size_t tile = 0; tile != 3; ++tile) {
196  const unit_map::const_iterator tmp_unit = units_.find(adj[tile]);
197  bool possible_flanked = false;
198 
199  if(map_.on_board(adj[tile])) {
200  ++accessible_tiles;
201  if(tmp_unit != units_.end() && current_team.is_enemy(tmp_unit->side())) {
202  ++enemy_units_around;
203  possible_flanked = true;
204  }
205  }
206 
207  const unit_map::const_iterator tmp_opposite_unit = units_.find(adj[tile + 3]);
208  if(map_.on_board(adj[tile + 3])) {
209  ++accessible_tiles;
210  if(tmp_opposite_unit != units_.end() && current_team.is_enemy(tmp_opposite_unit->side())) {
211  ++enemy_units_around;
212  if(possible_flanked) {
213  is_flanked = true;
214  }
215  }
216  }
217  }
218 
219  if((is_flanked && enemy_units_around > 2) || enemy_units_around >= accessible_tiles - 1) {
220  is_surrounded = true;
221  }
222 
223  double best_vulnerability = 0.0, best_support = 0.0;
224  int best_rating = 0;
225  int cur_position = -1;
226 
227  // Iterate over positions adjacent to the unit, finding the best rated one.
228  for(unsigned j = 0; j < tiles.size(); ++j) {
229  // If in this planned attack, a unit is already in this location.
230  if(used_locations[j]) {
231  continue;
232  }
233 
234  // See if the current unit can reach that position.
235  if(tiles[j] != current_unit) {
236  auto its = dstsrc.equal_range(tiles[j]);
237  while(its.first != its.second) {
238  if(its.first->second == current_unit) {
239  break;
240  }
241 
242  ++its.first;
243  }
244 
245  // If the unit can't move to this location.
246  if(its.first == its.second || units_.find(tiles[j]) != units_.end()) {
247  continue;
248  }
249  }
250 
251  int best_leadership_bonus = under_leadership(*unit_itor, tiles[j]);
252  double leadership_bonus = static_cast<double>(best_leadership_bonus + 100) / 100.0;
253  if(leadership_bonus > 1.1) {
254  LOG_AI << unit_itor->name() << " is getting leadership " << leadership_bonus << "\n";
255  }
256 
257  // Check to see whether this move would be a backstab.
258  int backstab_bonus = 1;
259  double surround_bonus = 1.0;
260 
261  if(tiles[(j + 3) % 6] != current_unit) {
262  const unit_map::const_iterator itor = units_.find(tiles[(j + 3) % 6]);
263 
264  // Note that we *could* also check if a unit plans to move there
265  // before we're at this stage, but we don't because, since the
266  // attack calculations don't actually take backstab into account (too complicated),
267  // this could actually make our analysis look *worse* instead of better.
268  // So we only check for 'concrete' backstab opportunities.
269  // That would also break backstab_check, since it assumes
270  // the defender is in place.
271  if(itor != units_.end() && backstab_check(tiles[j], loc, units_, teams_)) {
272  if(backstab) {
273  backstab_bonus = 2;
274  }
275 
276  // No surround bonus if target is skirmisher
277  if(!itor->get_ability_bool("skirmisher")) {
278  surround_bonus = 1.2;
279  }
280  }
281  }
282 
283  // See if this position is the best rated we've seen so far.
284  int rating = static_cast<int>(rate_terrain(*unit_itor, tiles[j]) * backstab_bonus * leadership_bonus);
285  if(cur_position >= 0 && rating < best_rating) {
286  continue;
287  }
288 
289  // Find out how vulnerable we are to attack from enemy units in this hex.
290  // FIXME: suokko's r29531 multiplied this by a constant 1.5. ?
291  const double vulnerability = power_projection(tiles[j], enemy_dstsrc); //?
292 
293  // Calculate how much support we have on this hex from allies.
294  const double support = power_projection(tiles[j], fullmove_dstsrc); //?
295 
296  // If this is a position with equal defense to another position,
297  // but more vulnerability then we don't want to use it.
298  if(cur_position >= 0 && rating == best_rating
299  && vulnerability / surround_bonus - support * surround_bonus >= best_vulnerability - best_support) {
300  continue;
301  }
302 
303  cur_position = j;
304  best_rating = rating;
305  best_vulnerability = vulnerability / surround_bonus;
306  best_support = support * surround_bonus;
307  }
308 
309  if(cur_position != -1) {
310  units.erase(units.begin() + i);
311 
312  cur_analysis.movements.emplace_back(current_unit, tiles[cur_position]);
313  cur_analysis.vulnerability += best_vulnerability;
314  cur_analysis.support += best_support;
315  cur_analysis.is_surrounded = is_surrounded;
316  cur_analysis.analyze(map_, units_, *this, dstsrc, srcdst, enemy_dstsrc, get_aggression());
317  result.push_back(cur_analysis);
318 
319  used_locations[cur_position] = true;
320 
321  do_attack_analysis(loc, srcdst, dstsrc, fullmove_srcdst, fullmove_dstsrc, enemy_srcdst, enemy_dstsrc, tiles,
322  used_locations, units, result, cur_analysis, current_team);
323 
324  used_locations[cur_position] = false;
325 
326  cur_analysis.vulnerability -= best_vulnerability;
327  cur_analysis.support -= best_support;
328  cur_analysis.movements.pop_back();
329 
330  units.insert(units.begin() + i, current_unit);
331  }
332  }
333 }
334 
336 {
337  const gamemap& map_ = resources::gameboard->map();
338  const t_translation::terrain_code terrain = map_.get_terrain(loc);
339  const int defense = u.defense_modifier(terrain);
340  int rating = 100 - defense;
341 
342  const int healing_value = 10;
343  const int friendly_village_value = 5;
344  const int neutral_village_value = 10;
345  const int enemy_village_value = 15;
346 
347  if(map_.gives_healing(terrain) && u.get_ability_bool("regenerate", loc) == false) {
348  rating += healing_value;
349  }
350 
351  if(map_.is_village(terrain)) {
352  int owner = resources::gameboard->village_owner(loc);
353 
354  if(owner == u.side()) {
355  rating += friendly_village_value;
356  } else if(owner == 0) {
357  rating += neutral_village_value;
358  } else {
359  rating += enemy_village_value;
360  }
361  }
362 
363  return rating;
364 }
365 
367 {
369  if(filter_own_ && !filter_own_->empty()) {
370  cfg.add_child("filter_own", filter_own_->to_config());
371  }
372 
373  if(filter_enemy_ && !filter_enemy_->empty()) {
374  cfg.add_child("filter_enemy", filter_enemy_->to_config());
375  }
376 
377  return cfg;
378 }
379 
381 {
382  if(u.side() != get_side()) {
383  return false;
384  }
385 
386  if(filter_own_) {
387  return (*filter_own_)(u);
388  }
389 
390  return true;
391 }
392 
394 {
395  const team& my_team = resources::gameboard->get_team(get_side());
396  if(!my_team.is_enemy(u.side())) {
397  return false;
398  }
399 
400  if(filter_enemy_) {
401  return (*filter_enemy_)(u);
402  }
403 
404  return true;
405 }
406 
407 } // namespace ai_default_rca
408 
410  readonly_context& context, const config& cfg, const std::string& id, std::shared_ptr<lua_ai_context>& l_ctx)
411  : aspect_attacks_base(context, cfg, id)
412  , handler_()
413  , code_()
414  , params_(cfg.child_or_empty("args"))
415 {
416  this->name_ = "lua_aspect";
417  if(cfg.has_attribute("code")) {
418  code_ = cfg["code"].str();
419  } else if(cfg.has_attribute("value")) {
420  code_ = "return " + cfg["value"].apply_visitor(lua_aspect_visitor());
421  } else {
422  // error
423  return;
424  }
425 
426  handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), *l_ctx));
427 }
428 
430 {
432  const config empty_cfg;
433  handler_->handle(params_, empty_cfg, true, obj_);
434 
435  aspect_attacks_lua_filter filt = *obj_->get();
436  aspect_attacks_base::recalculate();
437 
438  if(filt.lua) {
439  if(filt.ref_own_ != -1) {
441  }
442  if(filt.ref_enemy_ != -1) {
444  }
445  }
446 
447  obj_.reset();
448 }
449 
451 {
452  config cfg = aspect::to_config();
453  cfg["code"] = code_;
454  if(!params_.empty()) {
455  cfg.add_child("args", params_);
456  }
457 
458  return cfg;
459 }
460 
461 static bool call_lua_filter_fcn(lua_State* L, const unit& u, int idx)
462 {
465  luaW_pcall(L, 1, 1);
466  bool result = luaW_toboolean(L, -1);
467  lua_pop(L, 1);
468  return result;
469 }
470 
472 {
473  const aspect_attacks_lua_filter& filt = *obj_->get();
474  if(filt.lua && filt.ref_own_ != -1) {
475  return call_lua_filter_fcn(filt.lua, u, filt.ref_own_);
476  } else if(filt.filter_own_) {
477  return (*filt.filter_own_)(u);
478  } else {
479  return true;
480  }
481 }
482 
484 {
485  const aspect_attacks_lua_filter& filt = *obj_->get();
486  if(filt.lua && filt.ref_enemy_ != -1) {
487  return call_lua_filter_fcn(filt.lua, u, filt.ref_enemy_);
488  } else if(filt.filter_enemy_) {
489  return (*filt.filter_enemy_)(u);
490  } else {
491  return true;
492  }
493 }
494 
495 } // end of namespace ai
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...
static std::unique_ptr< class sdl_event_handler > handler_
Definition: handler.cpp:61
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:414
virtual const move_map & get_enemy_srcdst() const override
Definition: contexts.hpp:610
lua_unit * luaW_pushunit(lua_State *L, Args... args)
Definition: lua_unit.hpp:115
unit_iterator end()
Definition: map.hpp:428
aspect_attacks_lua(readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr< lua_ai_context > &l_ctx)
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:84
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:474
virtual const unit_map & units() const override
Definition: game_board.hpp:111
static bool call_lua_filter_fcn(lua_State *L, const unit &u, int idx)
bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error)
Calls a Lua function stored below its nArgs arguments at the top of the stack.
This class represents a single unit of a specific type.
Definition: unit.hpp:120
static manager & get_singleton()
Definition: manager.hpp:143
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:710
Various functions that implement attacks and attack calculations.
bool has_attribute(config_key_type key) const
Definition: config.cpp:207
#define a
virtual bool is_allowed_attacker(const unit &u) const
int under_leadership(const unit &u, const map_location &loc, const_attack_ptr weapon, const_attack_ptr opp_weapon)
Tests if the unit at loc is currently affected by leadership.
Definition: attack.cpp:1584
virtual const move_map & get_srcdst() const override
Definition: contexts.hpp:715
map_location target
Definition: contexts.hpp:83
virtual const gamemap & map() const override
Definition: game_board.hpp:101
double vulnerability
The vulnerability is the power projection of enemy units onto the hex we&#39;re standing on...
Definition: contexts.hpp:120
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
aspect_attacks_base(readonly_context &context, const config &cfg, const std::string &id)
std::shared_ptr< attacks_vector > value_
Definition: aspect.hpp:177
#define LOG_AI
#define lua_pop(L, n)
Definition: lua.h:364
virtual unit_stats_cache_t & unit_stats_cache() const override
Definition: contexts.hpp:865
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
Definition: game_info.hpp:42
std::vector< attack_analysis > attacks_vector
Definition: game_info.hpp:50
std::map< map_location, pathfind::paths > moves_map
The standard way in which a map of possible movement routes to location is recorded.
Definition: game_info.hpp:45
void do_attack_analysis(const map_location &loc, const move_map &srcdst, const move_map &dstsrc, const move_map &fullmove_srcdst, const move_map &fullmove_dstsrc, const move_map &enemy_srcdst, const move_map &enemy_dstsrc, const std::array< map_location, 6 > &tiles, std::array< bool, 6 > &used_locations, std::vector< map_location > &units, std::vector< attack_analysis > &result, attack_analysis &cur_analysis, const team &current_team) const
std::shared_ptr< unit_filter > filter_enemy_
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit&#39;s defense on a given terrain.
Definition: unit.cpp:1568
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition: variable.cpp:163
std::string name_
Definition: aspect.hpp:94
int gives_healing(const map_location &loc) const
Definition: map.cpp:67
team & get_team(int i)
Definition: game_board.hpp:96
static lg::log_domain log_ai_testing_aspect_attacks("ai/aspect/attacks")
void analyze(const gamemap &map, unit_map &units, const readonly_context &ai_obj, const move_map &dstsrc, const move_map &srcdst, const move_map &enemy_dstsrc, double aggression)
Definition: attack.cpp:45
std::shared_ptr< unit_filter > filter_own_
std::shared_ptr< unit_filter > filter_enemy_
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:893
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:297
game_board * gameboard
Definition: resources.cpp:20
Encapsulates the map of the game.
Definition: map.hpp:170
aspect_attacks(readonly_context &context, const config &cfg, const std::string &id)
bool is_enemy(int n) const
Definition: team.hpp:251
virtual bool is_passive_leader(const std::string &id) const override
Definition: contexts.hpp:765
virtual const move_map & get_enemy_dstsrc() const override
Definition: contexts.hpp:600
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
virtual config to_config() const
Definition: aspect.cpp:105
Encapsulates the map of the game.
Definition: location.hpp:37
unit_iterator find(std::size_t id)
Definition: map.cpp:309
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:145
void raise_user_interact()
Notifies all observers of &#39;ai_user_interact&#39; event.
Definition: manager.cpp:388
bool backstab_check(const map_location &attacker_loc, const map_location &defender_loc, const unit_map &units, const std::vector< team > &teams)
Function to check if an attack will satisfy the requirements for backstab.
Definition: attack.cpp:1639
virtual bool is_allowed_attacker(const unit &u) const
std::size_t i
Definition: function.cpp:940
virtual const team & current_team() const override
Definition: contexts.hpp:454
std::shared_ptr< lua_ai_action_handler > handler_
std::vector< std::pair< map_location, map_location > > movements
Definition: contexts.hpp:84
virtual double power_projection(const map_location &loc, const move_map &dstsrc) const override
Function which finds how much &#39;power&#39; a side can attack a certain location with.
Definition: contexts.hpp:680
virtual bool is_allowed_enemy(const unit &u) const
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:400
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:380
Aspect: attacks.
std::shared_ptr< unit_filter > filter_own_
config & add_child(config_key_type key)
Definition: config.cpp:500
virtual void recalculate() const
bool valid_
Definition: aspect.hpp:85
bool is_village(const map_location &loc) const
Definition: map.cpp:65
virtual const move_map & get_dstsrc() const override
Definition: contexts.hpp:595
#define LUA_REGISTRYINDEX
Definition: lua.h:44
virtual double get_aggression() const override
Definition: contexts.hpp:550
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:501
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Standard logging facilities (interface).
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
Container associating units to locations.
Definition: map.hpp:97
LUALIB_API void luaL_unref(lua_State *L, int t, int ref)
Definition: lauxlib.cpp:667
bool is_surrounded
Is true if the units involved in this attack sequence are surrounded.
Definition: contexts.hpp:129
int side() const
The side this unit belongs to.
Definition: unit.hpp:333
virtual bool is_allowed_enemy(const unit &u) const
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
std::shared_ptr< lua_object< aspect_attacks_lua_filter > > obj_
static int rate_terrain(const unit &u, const map_location &loc)
virtual config to_config() const
This module contains various pathfinding functions and utilities.
std::size_t underlying_id() const
This unit&#39;s unique internal ID.
Definition: unit.hpp:382
bool empty() const
Definition: config.cpp:916
std::shared_ptr< attacks_vector > analyze_targets() const