The Battle for Wesnoth  1.19.17+dev
recruitment.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013 - 2025
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 #include "units/ptr.hpp"
28 
29 #include <iomanip>
30 
31 namespace pathfind {
32 
33 struct full_cost_map;
34 
35 } //of namespace pathfind
36 
37 namespace ai {
38 
39 namespace default_recruitment {
40 
41 // each leader have a score_map
42 // the score map indicates what is the best mix of own units from the leader point of view.
43 // The leader will then recruit according to the map.
44 typedef std::map<std::string, double> score_map;
45 
46 typedef std::map<t_translation::terrain_code, int> terrain_count_map;
47 typedef std::map<std::string, int> count_map;
48 
49 struct data {
51  std::set<std::string> recruits;
53 
54  // We use ratio_score to decide with which ratios the leaders recruit among each other.
55  // For example if leader1 have a ratio_score of 1 and leader2 have a ratio_score of 2
56  // then leader2 will recruit twice as much units than leader1.
57  double ratio_score;
58 
60  bool in_danger;
61 
63  : leader(leader), ratio_score(1.0), recruit_count(0), in_danger(false) { }
64  double get_score_sum() const {
65  double sum = 0.0;
66  for (const score_map::value_type& entry : scores) {
67  sum += entry.second;
68  }
69  return sum;
70  }
72  const double sum = get_score_sum();
73  if (sum == 0.0) {
74  return scores;
75  }
76  score_map normalized;
77  for (const score_map::value_type& entry : scores) {
78  normalized[entry.first] = entry.second / sum;
79  }
80  return normalized;
81  }
82  std::string to_string() const;
83 };
84 
86  double a_defense;
87  double b_defense;
88  double value;
89  cached_combat_value(double a_def, double b_def, double v) :
90  a_defense(a_def), b_defense(b_def), value(v) {
91  }
92  bool operator<(const cached_combat_value& o) const {
93  return value < o.value;
94  }
95 };
96 
97 struct recruit_job : public component {
98  std::vector<std::string> types;
99  std::string leader, id;
102  recruit_job(std::vector<std::string> t, std::string L, std::string id, int n, int i, bool s, bool p, bool b)
103  : types(t), leader(L), id(id)
104  , number(n), importance(i)
105  , total(s), pattern(p), blocker(b)
106  {}
107  config to_config() const {
108  config cfg;
109  if(number > 0 && number < 99999) {
110  cfg["number"] = number;
111  }
112  cfg["importance"] = importance;
113  cfg["total"] = total;
114  cfg["pattern"] = pattern;
115  cfg["blocker"] = blocker;
116  if(!leader.empty()) {
117  cfg["leader_id"] = leader;
118  }
119  if(!id.empty()) {
120  cfg["id"] = id;
121  }
122  if(!types.empty()) {
123  cfg["type"] = utils::join(types);
124  }
125  return cfg;
126  }
127  std::string get_id() const {return id;}
128  std::string get_name() const {return "recruit_job";}
129  std::string get_engine() const {return "cpp";}
130 };
131 
132 struct recruit_limit : public component {
133  std::vector<std::string> types;
134  std::string id;
135  int limit;
136  recruit_limit(std::vector<std::string> t, std::string id, int lim)
137  : types(t), id(id), limit(lim)
138  {}
139  config to_config() const {
140  config cfg;
141  cfg["max"] = limit;
142  if(!id.empty()) {
143  cfg["id"] = id;
144  }
145  if(!types.empty()) {
146  cfg["type"] = utils::join(types);
147  }
148  return cfg;
149  }
150  std::string get_id() const {return id;}
151  std::string get_name() const {return "recruit_limit";}
152  std::string get_engine() const {return "cpp";}
153 };
154 
155 class recruitment_aspect : public standard_aspect<config> {
156  std::vector<std::shared_ptr<recruit_job>> jobs_;
157  std::vector<std::shared_ptr<recruit_limit>> limits_;
158 public:
159  recruitment_aspect(readonly_context &context, const config &cfg, const std::string &id);
160  void recalculate() const;
161  void create_job(std::vector<std::shared_ptr<recruit_job>> &jobs, const config &job);
162  void create_limit(std::vector<std::shared_ptr<recruit_limit>> &limits, const config &lim);
163 };
164 
165 typedef std::map<std::string, std::set<cached_combat_value>> table_row;
166 typedef std::map<std::string, table_row> cache_table;
167 
169 public:
170  recruitment(rca_context &context, const config &cfg);
171  virtual ~recruitment() { }
172  virtual double evaluate();
173  virtual void execute();
174  config to_config() const;
175 private:
176 // Helper functions for execute()
177  action_result_ptr execute_recall(const std::string& id, data& leader_data);
178  action_result_ptr execute_recruit(const std::string& type, data& leader_data);
179  double recall_unit_value(const unit_const_ptr & recall_unit) const;
180  const std::string* get_appropriate_recall(const std::string& type,
181  const data& leader_data) const;
182  data* get_best_leader_from_ratio_scores(std::vector<data>& leader_data,
183  const config* job) const;
184  const std::string get_best_recruit_from_scores(const data& leader_data,
185  const config* job);
186 
187 // Map Analysis
189  const pathfind::full_cost_map& my_cost_map,
190  const pathfind::full_cost_map& enemy_cost_map);
191  double get_average_defense(const std::string& unit_type) const;
192  const pathfind::full_cost_map get_cost_map_of_side(int side) const;
193  void show_important_hexes() const; //Debug only
196  void update_important_hexes();
197 
198 // Combat Analysis
199  double compare_unit_types(const std::string& a, const std::string& b);
200  void do_combat_analysis(std::vector<data>* leader_data);
201  const double* get_cached_combat_value(const std::string& a, const std::string& b,
202  double a_defense, double b_defense);
203  void simulate_attack(
204  const unit_type* const attacker, const unit_type* const defender,
205  double attacker_defense, double defender_defense,
206  double* damage_to_attacker, double* damage_to_defender) const;
207  std::pair<nonempty_unit_const_ptr, nonempty_unit_const_ptr> get_dummy_of_type(const unit_type* const attacker) const;
208 
209 // Aspect recruitment_instruction
211  const std::string get_random_pattern_type_if_exists(const data& leader_data,
212  const config* job) const;
214  bool leader_matches_job(const data& leader_data, const config* job) const;
215  bool limit_ok(const std::string& recruit) const;
216  bool recruit_matches_job(const std::string& recruit, const config* job) const;
217  bool recruit_matches_type(const std::string& recruit, const std::string& type) const;
218  bool recruit_matches_types(const std::string& recruit,
219  const std::vector<std::string>& types) const;
220  bool remove_job_if_no_blocker(config* job);
221 
222 // Aspect recruitment_save_gold
223  double get_estimated_income(int turns) const;
224  double get_estimated_unit_gain() const;
225  double get_estimated_village_gain() const;
226  double get_unit_ratio() const;
227  void update_state();
228 
229 // Other
230  void do_randomness(std::vector<data>* leader_data) const;
231  void do_similarity_penalty(std::vector<data>* leader_data) const;
233  void handle_recruitment_more(std::vector<data>* leader_data) const;
234  bool is_enemy_in_radius(const map_location& loc, int radius) const;
235  void update_own_units_count();
236  void update_scouts_wanted();
237 
238 // Observer
240  public:
243 
244  void handle_generic_event(const std::string& event);
245 
246  bool recruit_list_changed();
247  void set_recruit_list_changed(bool changed);
248  int gamestate_changed();
250 
251  private:
254 
255  };
256 
257  std::set<map_location> important_hexes_;
260  std::map<map_location, double> average_local_cost_;
261  std::map<std::size_t, int> cheapest_unit_costs_;
272  mutable std::map<const unit_type*, std::pair<nonempty_unit_const_ptr, nonempty_unit_const_ptr>> combat_dummies_;
273 };
274 
275 } // of namespace default_recruitment
276 
277 } // of namespace ai
map_location loc
Definition: move.cpp:172
double t
Definition: astarsearch.cpp:63
void create_job(std::vector< std::shared_ptr< recruit_job >> &jobs, const config &job)
std::vector< std::shared_ptr< recruit_limit > > limits_
recruitment_aspect(readonly_context &context, const config &cfg, const std::string &id)
void create_limit(std::vector< std::shared_ptr< recruit_limit >> &limits, const config &lim)
std::vector< std::shared_ptr< recruit_job > > jobs_
double get_estimated_unit_gain() const
For Aspect "recruitment_save_gold".
bool leader_matches_job(const data &leader_data, const config *job) const
For Configuration / Aspect "recruitment-instructions" Checks if a given leader is specified in the "l...
void update_average_local_cost()
For Map Analysis.
std::map< std::size_t, int > cheapest_unit_costs_
config to_config() const
serialize
virtual void execute()
Execute the candidate action.
config * get_most_important_job()
For Configuration / Aspect "recruitment-instructions" We call a [recruit] tag a "job".
std::set< map_location > important_hexes_
void compare_cost_maps_and_update_important_hexes(const pathfind::full_cost_map &my_cost_map, const pathfind::full_cost_map &enemy_cost_map)
For Map Analysis Computes from our cost map and the combined cost map of all enemies the important he...
void do_combat_analysis(std::vector< data > *leader_data)
Combat Analysis.
const std::string * get_appropriate_recall(const std::string &type, const data &leader_data) const
void show_important_hexes() const
For Map Analysis.
void integrate_recruitment_pattern_in_recruitment_instructions()
For Configuration / Aspect "recruitment_pattern" Converts the (old) recruitment_pattern into a recrui...
bool recruit_matches_job(const std::string &recruit, const config *job) const
For Configuration / Aspect "recruitment-instructions" Checks if a given recruit-type is specified in ...
action_result_ptr execute_recall(const std::string &id, data &leader_data)
A helper function for execute().
double get_estimated_village_gain() const
For Aspect "recruitment_save_gold".
bool recruit_matches_types(const std::string &recruit, const std::vector< std::string > &types) const
For Configuration / Aspect "recruitment-instructions" Checks if a given recruit-type matches one of t...
void update_average_lawful_bonus()
Calculates a average lawful bonus, so Combat Analysis will work better in caves and custom time of da...
std::pair< nonempty_unit_const_ptr, nonempty_unit_const_ptr > get_dummy_of_type(const unit_type *const attacker) const
std::map< map_location, double > average_local_cost_
double compare_unit_types(const std::string &a, const std::string &b)
For Combat Analysis.
void handle_recruitment_more(std::vector< data > *leader_data) const
For Aspect "recruitment_more".
void update_important_hexes()
For Map Analysis.
action_result_ptr execute_recruit(const std::string &type, data &leader_data)
A helper function for execute().
bool is_enemy_in_radius(const map_location &loc, int radius) const
Helper function.
bool remove_job_if_no_blocker(config *job)
For Configuration / Aspect "recruitment-instructions".
recruitment(rca_context &context, const config &cfg)
const pathfind::full_cost_map get_cost_map_of_side(int side) const
For Map Analysis.
double get_estimated_income(int turns) const
For Aspect "recruitment_save_gold".
const std::string get_random_pattern_type_if_exists(const data &leader_data, const config *job) const
For Configuration / Aspect "recruitment-instructions" If the flag pattern is set, this method returns...
void simulate_attack(const unit_type *const attacker, const unit_type *const defender, double attacker_defense, double defender_defense, double *damage_to_attacker, double *damage_to_defender) const
For Combat Analysis.
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
int get_cheapest_unit_cost_for_leader(const unit_map::const_iterator &leader)
Called at the beginning and whenever the recruitment list changes.
double recall_unit_value(const unit_const_ptr &recall_unit) const
A helper function for execute().
data * get_best_leader_from_ratio_scores(std::vector< data > &leader_data, const config *job) const
A helper function for execute().
void do_randomness(std::vector< data > *leader_data) const
Will add a random value between 0 and "recruitment_randomness" to all recruits.
void update_scouts_wanted()
This function will use the aspect villages_per_scout to decide how many scouts we want to recruit.
recruit_situation_change_observer recruit_situation_change_observer_
bool limit_ok(const std::string &recruit) const
For Configuration / Aspect "recruitment-instructions" Checks if a recruit-type can be recruited accor...
double get_unit_ratio() const
For Aspect "recruitment_save_gold".
void update_state()
For Aspect "recruitment_save_gold".
bool recruit_matches_type(const std::string &recruit, const std::string &type) const
For Configuration / Aspect "recruitment-instructions" Checks if a given recruit-type matches one atom...
const double * get_cached_combat_value(const std::string &a, const std::string &b, double a_defense, double b_defense)
For Combat Analysis.
double get_average_defense(const std::string &unit_type) const
For Map Analysis.
std::map< const unit_type *, std::pair< nonempty_unit_const_ptr, nonempty_unit_const_ptr > > combat_dummies_
void do_similarity_penalty(std::vector< data > *leader_data) const
Will give a penalty to similar units.
const std::string get_best_recruit_from_scores(const data &leader_data, const config *job)
A helper function for execute().
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
A single unit type that the player may recruit.
Definition: types.hpp:43
const config * cfg
std::size_t i
Definition: function.cpp:1032
bool recall_unit(const std::string &id, team &current_team, const map_location &loc, const map_location &from, map_location::direction facing)
Recalls the unit with the indicated ID for the provided team.
Definition: create.cpp:740
std::map< t_translation::terrain_code, int > terrain_count_map
Definition: recruitment.hpp:46
std::map< std::string, std::set< cached_combat_value > > table_row
std::map< std::string, table_row > cache_table
std::map< std::string, double > score_map
Definition: recruitment.hpp:44
std::map< std::string, int > count_map
Definition: recruitment.hpp:47
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
std::shared_ptr< action_result > action_result_ptr
Definition: game_info.hpp:79
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
candidate action framework
bool operator<(const cached_combat_value &o) const
Definition: recruitment.hpp:92
cached_combat_value(double a_def, double b_def, double v)
Definition: recruitment.hpp:89
unit_map::const_iterator leader
Definition: recruitment.hpp:50
std::string to_string() const
std::set< std::string > recruits
Definition: recruitment.hpp:51
score_map get_normalized_scores() const
Definition: recruitment.hpp:71
data(const unit_map::const_iterator leader)
Definition: recruitment.hpp:62
std::vector< std::string > types
Definition: recruitment.hpp:98
recruit_job(std::vector< std::string > t, std::string L, std::string id, int n, int i, bool s, bool p, bool b)
recruit_limit(std::vector< std::string > t, std::string id, int lim)
std::vector< std::string > types
Encapsulates the map of the game.
Definition: location.hpp:46
Structure which uses find_routes() to build a cost map This maps each hex to a the movements a unit w...
Definition: pathfind.hpp:268
mock_party p
static map_location::direction n
static map_location::direction s
#define b