The Battle for Wesnoth  1.19.5+dev
recruitment.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013 - 2024
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;
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 
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  }
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;
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
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
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
209  const std::string get_random_pattern_type_if_exists(const data& leader_data,
210  const config* job) const;
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;
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();
248 
249  private:
252 
253  };
254 
255  std::set<map_location> important_hexes_;
258  std::map<map_location, double> average_local_cost_;
259  std::map<std::size_t, int> cheapest_unit_costs_;
270 };
271 
272 } // of namespace default_recruitment
273 
274 } // of namespace ai
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::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.
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:172
A single unit type that the player may recruit.
Definition: types.hpp:43
std::size_t i
Definition: function.cpp:1028
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:738
std::map< t_translation::terrain_code, int > terrain_count_map
Definition: recruitment.hpp:45
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:43
std::map< std::string, int > count_map
Definition: recruitment.hpp:46
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:91
cached_combat_value(double a_def, double b_def, double v)
Definition: recruitment.hpp:88
unit_map::const_iterator leader
Definition: recruitment.hpp:49
std::string to_string() const
std::set< std::string > recruits
Definition: recruitment.hpp:50
score_map get_normalized_scores() const
Definition: recruitment.hpp:70
data(const unit_map::const_iterator leader)
Definition: recruitment.hpp:61
std::vector< std::string > types
Definition: recruitment.hpp:97
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:45
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