The Battle for Wesnoth  1.17.0-dev
recruitment.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013 - 2021
3  by Felix Bauer
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  * Recruitment Engine by flix
19  * See https://wiki.wesnoth.org/AI_Recruitment
20  */
21 
22 #pragma once
23 
24 #include "ai/composite/aspect.hpp"
25 #include "ai/composite/rca.hpp"
26 #include "units/map.hpp"
27 
28 #include <iomanip>
29 
30 namespace pathfind {
31 
32 struct full_cost_map;
33 
34 } //of namespace pathfind
35 
36 namespace ai {
37 
38 namespace default_recruitment {
39 
40 // each leader have a score_map
41 // the score map indicates what is the best mix of own units from the leader point of view.
42 // The leader will then recruit according to the map.
43 typedef std::map<std::string, double> score_map;
44 
45 typedef std::map<t_translation::terrain_code, int> terrain_count_map;
46 typedef std::map<std::string, int> count_map;
47 
48 struct data {
50  std::set<std::string> recruits;
51  score_map scores;
52 
53  // We use ratio_score to decide with which ratios the leaders recruit among each other.
54  // For example if leader1 have a ratio_score of 1 and leader2 have a ratio_score of 2
55  // then leader2 will recruit twice as much units than leader1.
56  double ratio_score;
57 
59  bool in_danger;
60 
61  explicit data(const unit_map::const_iterator leader)
62  : leader(leader), ratio_score(1.0), recruit_count(0), in_danger(false) { }
63  double get_score_sum() const {
64  double sum = 0.0;
65  for (const score_map::value_type& entry : scores) {
66  sum += entry.second;
67  }
68  return sum;
69  }
70  score_map get_normalized_scores() const {
71  const double sum = get_score_sum();
72  if (sum == 0.0) {
73  return scores;
74  }
75  score_map normalized;
76  for (const score_map::value_type& entry : scores) {
77  normalized[entry.first] = entry.second / sum;
78  }
79  return normalized;
80  }
81  std::string to_string() const;
82 };
83 
85  double a_defense;
86  double b_defense;
87  double value;
88  cached_combat_value(double a_def, double b_def, double v) :
89  a_defense(a_def), b_defense(b_def), value(v) {
90  }
91  bool operator<(const cached_combat_value& o) const {
92  return value < o.value;
93  }
94 };
95 
96 struct recruit_job : public component {
97  std::vector<std::string> types;
98  std::string leader, id;
99  int number, importance;
100  bool total, pattern, blocker;
101  recruit_job(std::vector<std::string> t, std::string L, std::string id, int n, int i, bool s, bool p, bool b)
102  : types(t), leader(L), id(id)
103  , number(n), importance(i)
104  , total(s), pattern(p), blocker(b)
105  {}
106  config to_config() const {
107  config cfg;
108  if(number > 0 && number < 99999) {
109  cfg["number"] = number;
110  }
111  cfg["importance"] = importance;
112  cfg["total"] = total;
113  cfg["pattern"] = pattern;
114  cfg["blocker"] = blocker;
115  if(!leader.empty()) {
116  cfg["leader_id"] = leader;
117  }
118  if(!id.empty()) {
119  cfg["id"] = id;
120  }
121  if(!types.empty()) {
122  cfg["type"] = utils::join(types);
123  }
124  return cfg;
125  }
126  std::string get_id() const {return id;}
127  std::string get_name() const {return "recruit_job";}
128  std::string get_engine() const {return "cpp";}
129 };
130 
131 struct recruit_limit : public component {
132  std::vector<std::string> types;
133  std::string id;
134  int limit;
135  recruit_limit(std::vector<std::string> t, std::string id, int lim)
136  : types(t), id(id), limit(lim)
137  {}
138  config to_config() const {
139  config cfg;
140  cfg["max"] = limit;
141  if(!id.empty()) {
142  cfg["id"] = id;
143  }
144  if(!types.empty()) {
145  cfg["type"] = utils::join(types);
146  }
147  return cfg;
148  }
149  std::string get_id() const {return id;}
150  std::string get_name() const {return "recruit_limit";}
151  std::string get_engine() const {return "cpp";}
152 };
153 
154 class recruitment_aspect : public standard_aspect<config> {
155  std::vector<std::shared_ptr<recruit_job>> jobs_;
156  std::vector<std::shared_ptr<recruit_limit>> limits_;
157 public:
158  recruitment_aspect(readonly_context &context, const config &cfg, const std::string &id);
159  void recalculate() const;
160  void create_job(std::vector<std::shared_ptr<recruit_job>> &jobs, const config &job);
161  void create_limit(std::vector<std::shared_ptr<recruit_limit>> &limits, const config &lim);
162 };
163 
164 typedef std::map<std::string, std::set<cached_combat_value>> table_row;
165 typedef std::map<std::string, table_row> cache_table;
166 
168 public:
169  recruitment(rca_context &context, const config &cfg);
170  virtual ~recruitment() { }
171  virtual double evaluate();
172  virtual void execute();
173  config to_config() const;
174 private:
175 // Helper functions for execute()
176  action_result_ptr execute_recall(const std::string& id, data& leader_data);
177  action_result_ptr execute_recruit(const std::string& type, data& leader_data);
178  double recall_unit_value(const unit_const_ptr & recall_unit) const;
179  const std::string* get_appropriate_recall(const std::string& type,
180  const data& leader_data) const;
181  data* get_best_leader_from_ratio_scores(std::vector<data>& leader_data,
182  const config* job) const;
183  const std::string get_best_recruit_from_scores(const data& leader_data,
184  const config* job);
185 
186 // Map Analysis
187  void compare_cost_maps_and_update_important_hexes(
188  const pathfind::full_cost_map& my_cost_map,
189  const pathfind::full_cost_map& enemy_cost_map);
190  double get_average_defense(const std::string& unit_type) const;
191  const pathfind::full_cost_map get_cost_map_of_side(int side) const;
192  void show_important_hexes() const; //Debug only
193  void update_average_lawful_bonus();
194  void update_average_local_cost();
195  void update_important_hexes();
196 
197 // Combat Analysis
198  double compare_unit_types(const std::string& a, const std::string& b);
199  void do_combat_analysis(std::vector<data>* leader_data);
200  const double* get_cached_combat_value(const std::string& a, const std::string& b,
201  double a_defense, double b_defense);
202  void simulate_attack(
203  const unit_type* const attacker, const unit_type* const defender,
204  double attacker_defense, double defender_defense,
205  double* damage_to_attacker, double* damage_to_defender) const;
206 
207 // Aspect recruitment_instruction
208  config* get_most_important_job();
209  const std::string get_random_pattern_type_if_exists(const data& leader_data,
210  const config* job) const;
211  void integrate_recruitment_pattern_in_recruitment_instructions();
212  bool leader_matches_job(const data& leader_data, const config* job) const;
213  bool limit_ok(const std::string& recruit) const;
214  bool recruit_matches_job(const std::string& recruit, const config* job) const;
215  bool recruit_matches_type(const std::string& recruit, const std::string& type) const;
216  bool recruit_matches_types(const std::string& recruit,
217  const std::vector<std::string>& types) const;
218  bool remove_job_if_no_blocker(config* job);
219 
220 // Aspect recruitment_save_gold
221  double get_estimated_income(int turns) const;
222  double get_estimated_unit_gain() const;
223  double get_estimated_village_gain() const;
224  double get_unit_ratio() const;
225  void update_state();
226 
227 // Other
228  void do_randomness(std::vector<data>* leader_data) const;
229  void do_similarity_penalty(std::vector<data>* leader_data) const;
230  int get_cheapest_unit_cost_for_leader(const unit_map::const_iterator& leader);
231  void handle_recruitment_more(std::vector<data>* leader_data) const;
232  bool is_enemy_in_radius(const map_location& loc, int radius) const;
233  void update_own_units_count();
234  void update_scouts_wanted();
235 
236 // Observer
238  public:
241 
242  void handle_generic_event(const std::string& event);
243 
244  bool recruit_list_changed();
245  void set_recruit_list_changed(bool changed);
246  int gamestate_changed();
247  void reset_gamestate_changed();
248 
249  private:
252 
253  };
254 
255  std::set<map_location> important_hexes_;
256  terrain_count_map important_terrain_;
258  std::map<map_location, double> average_local_cost_;
259  std::map<std::size_t, int> cheapest_unit_costs_;
260  cache_table combat_cache_;
261  enum states {NORMAL, SAVE_GOLD, SPEND_ALL_GOLD, LEADER_IN_DANGER};
267  count_map own_units_count_;
270 };
271 
272 } // of namespace default_recruitment
273 
274 } // of namespace ai
std::shared_ptr< action_result > action_result_ptr
Definition: game_info.hpp:79
std::vector< std::string > types
recruit_limit(std::vector< std::string > t, std::string id, int lim)
cached_combat_value(double a_def, double b_def, double v)
Definition: recruitment.hpp:88
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
#define a
std::map< map_location, double > average_local_cost_
bool recall_unit(const std::string &id, team &current_team, const map_location &loc, const map_location &from, map_location::DIRECTION facing, bool show, bool use_undo)
Recalls the unit with the indicated ID for the provided team.
Definition: create.cpp:737
std::vector< std::shared_ptr< recruit_job > > jobs_
std::map< std::string, int > count_map
Definition: recruitment.hpp:46
recruit_situation_change_observer recruit_situation_change_observer_
A single unit type that the player may recruit.
Definition: types.hpp:45
std::set< map_location > important_hexes_
#define b
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:61
score_map get_normalized_scores() const
Definition: recruitment.hpp:70
std::vector< std::string > types
Definition: recruitment.hpp:97
Encapsulates the map of the game.
Definition: location.hpp:38
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
data(const unit_map::const_iterator leader)
Definition: recruitment.hpp:61
std::size_t i
Definition: function.cpp:967
mock_party p
static map_location::DIRECTION s
Structure which uses find_routes() to build a cost map This maps each hex to a the movements a unit w...
Definition: pathfind.hpp:267
std::map< std::string, std::set< cached_combat_value > > table_row
std::vector< std::shared_ptr< recruit_limit > > limits_
std::map< t_translation::terrain_code, int > terrain_count_map
Definition: recruitment.hpp:45
int turns()
Definition: game.cpp:559
double t
Definition: astarsearch.cpp:65
recruit_job(std::vector< std::string > t, std::string L, std::string id, int n, int i, bool s, bool p, bool b)
unit_map::const_iterator leader
Definition: recruitment.hpp:49
std::map< std::string, table_row > cache_table
bool operator<(const cached_combat_value &o) const
Definition: recruitment.hpp:91
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
std::map< std::string, double > score_map
Definition: recruitment.hpp:43
std::set< std::string > recruits
Definition: recruitment.hpp:50
candidate action framework
std::map< std::size_t, int > cheapest_unit_costs_