The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
recruitment.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013 - 2017 by Felix Bauer
3  Part of the Battle for Wesnoth Project http://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  * @file
17  * Recruitment Engine by flix
18  * See http://wiki.wesnoth.org/AI_Recruitment
19  */
20 
21 #pragma once
22 
23 #include "ai/composite/aspect.hpp"
24 #include "ai/composite/rca.hpp"
25 #include "units/unit.hpp"
26 #include "units/map.hpp"
27 
28 #include <iomanip>
29 
30 #ifdef _MSC_VER
31 #pragma warning(push)
32 // silence "inherits via dominance" warnings
33 #pragma warning(disable:4250)
34 #endif
35 
36 namespace pathfind {
37 
38 struct full_cost_map;
39 
40 } //of namespace pathfind
41 
42 namespace ai {
43 
44 namespace default_recruitment {
45 
46 // each leader have a score_map
47 // the score map indicates what is the best mix of own units from the leader point of view.
48 // The leader will then recruit according to the map.
49 typedef std::map<std::string, double> score_map;
50 
51 typedef std::map<t_translation::terrain_code, int> terrain_count_map;
52 typedef std::map<std::string, int> count_map;
53 
54 struct data {
56  std::set<std::string> recruits;
57  score_map scores;
58 
59  // We use ratio_score to decide with which ratios the leaders recruit among each other.
60  // For example if leader1 have a ratio_score of 1 and leader2 have a ratio_score of 2
61  // then leader2 will recruit twice as much units than leader1.
62  double ratio_score;
63 
65  bool in_danger;
66 
67  explicit data(const unit_map::const_iterator leader)
68  : leader(leader), ratio_score(1.0), recruit_count(0), in_danger(false) { }
69  double get_score_sum() const {
70  double sum = 0.0;
71  for (const score_map::value_type& entry : scores) {
72  sum += entry.second;
73  }
74  return sum;
75  }
76  score_map get_normalized_scores() const {
77  const double sum = get_score_sum();
78  if (sum == 0.0) {
79  return scores;
80  }
81  score_map normalized;
82  for (const score_map::value_type& entry : scores) {
83  normalized[entry.first] = entry.second / sum;
84  }
85  return normalized;
86  }
88  std::stringstream s;
89  s << "---------------Content of leader data---------------\n";
90  s << "For leader: " << leader->name() << "\n";
91  s << "ratio_score: " << ratio_score << "\n";
92  s << "recruit_count: " << recruit_count << "\n\n";
93  for (const score_map::value_type& entry : scores) {
94  s << std::setw(20) << entry.first <<
95  " score: " << std::setw(7) << entry.second << "\n";
96  }
97  s << "----------------------------------------------------\n";
98  return s.str();
99  }
100 };
101 
103  double a_defense;
104  double b_defense;
105  double value;
106  cached_combat_value(double a_def, double b_def, double v) :
107  a_defense(a_def), b_defense(b_def), value(v) {
108  }
109  bool operator<(const cached_combat_value& o) const {
110  return value < o.value;
111  }
112 };
113 
114 struct recruit_job : public component {
115  std::vector<std::string> types;
119  recruit_job(std::vector<std::string> t, std::string L, std::string id, int n, int i, bool s, bool p, bool b)
120  : types(t), leader(L), id(id)
121  , number(n), importance(i)
122  , total(s), pattern(p), blocker(b)
123  {}
124  config to_config() const {
125  config cfg;
126  if(number > 0 && number < 99999) {
127  cfg["number"] = number;
128  }
129  cfg["importance"] = importance;
130  cfg["total"] = total;
131  cfg["pattern"] = pattern;
132  cfg["blocker"] = blocker;
133  if(!leader.empty()) {
134  cfg["leader_id"] = leader;
135  }
136  if(!id.empty()) {
137  cfg["id"] = id;
138  }
139  if(!types.empty()) {
140  cfg["type"] = utils::join(types);
141  }
142  return cfg;
143  }
144  std::string get_id() const {return id;}
145  std::string get_name() const {return "recruit_job";}
146  std::string get_engine() const {return "cpp";}
147 };
148 
149 struct recruit_limit : public component {
150  std::vector<std::string> types;
152  int limit;
153  recruit_limit(std::vector<std::string> t, std::string id, int lim)
154  : types(t), id(id), limit(lim)
155  {}
156  config to_config() const {
157  config cfg;
158  cfg["max"] = limit;
159  if(!id.empty()) {
160  cfg["id"] = id;
161  }
162  if(!types.empty()) {
163  cfg["type"] = utils::join(types);
164  }
165  return cfg;
166  }
167  std::string get_id() const {return id;}
168  std::string get_name() const {return "recruit_limit";}
169  std::string get_engine() const {return "cpp";}
170 };
171 
172 class recruitment_aspect : public standard_aspect<config> {
173  std::vector<std::shared_ptr<recruit_job> > jobs_;
174  std::vector<std::shared_ptr<recruit_limit> > limits_;
175 public:
176  recruitment_aspect(readonly_context &context, const config &cfg, const std::string &id);
177  void recalculate() const;
178  void create_job(std::vector<std::shared_ptr<recruit_job> > &jobs, const config &job);
179  void create_limit(std::vector<std::shared_ptr<recruit_limit> > &limits, const config &lim);
180 };
181 
182 typedef std::map<std::string, std::set<cached_combat_value> > table_row;
183 typedef std::map<std::string, table_row> cache_table;
184 
186 public:
187  recruitment(rca_context &context, const config &cfg);
188  virtual ~recruitment() { }
189  virtual double evaluate();
190  virtual void execute();
191  config to_config() const;
192 private:
193 // Helper functions for execute()
194  action_result_ptr execute_recall(const std::string& id, data& leader_data);
195  action_result_ptr execute_recruit(const std::string& type, data& leader_data);
197  const data& leader_data) const;
198  data* get_best_leader_from_ratio_scores(std::vector<data>& leader_data,
199  const config* job) const;
200  const std::string get_best_recruit_from_scores(const data& leader_data,
201  const config* job);
202 
203 // Map Analysis
205  const pathfind::full_cost_map& my_cost_map,
206  const pathfind::full_cost_map& enemy_cost_map);
207  double get_average_defense(const std::string& unit_type) const;
208  const pathfind::full_cost_map get_cost_map_of_side(int side) const;
209  void show_important_hexes() const; //Debug only
212  void update_important_hexes();
213 
214 // Combat Analysis
215  double compare_unit_types(const std::string& a, const std::string& b);
216  void do_combat_analysis(std::vector<data>* leader_data);
217  const double* get_cached_combat_value(const std::string& a, const std::string& b,
218  double a_defense, double b_defense);
219  void simulate_attack(
220  const unit_type* const attacker, const unit_type* const defender,
221  double attacker_defense, double defender_defense,
222  double* damage_to_attacker, double* damage_to_defender) const;
223 
224 // Aspect recruitment_instruction
226  const std::string get_random_pattern_type_if_exists(const data& leader_data,
227  const config* job) const;
229  bool leader_matches_job(const data& leader_data, const config* job) const;
230  bool limit_ok(const std::string& recruit) const;
231  bool recruit_matches_job(const std::string& recruit, const config* job) const;
232  bool recruit_matches_type(const std::string& recruit, const std::string& type) const;
233  bool recruit_matches_types(const std::string& recruit,
234  const std::vector<std::string>& types) const;
235  bool remove_job_if_no_blocker(config* job);
236 
237 // Aspect recruitment_save_gold
238  double get_estimated_income(int turns) const;
239  double get_estimated_unit_gain() const;
240  double get_estimated_village_gain() const;
241  double get_unit_ratio() const;
242  void update_state();
243 
244 // Other
245  void do_randomness(std::vector<data>* leader_data) const;
246  void do_similarity_penalty(std::vector<data>* leader_data) const;
248  void handle_recruitment_more(std::vector<data>* leader_data) const;
249  bool is_enemy_in_radius(const map_location& loc, int radius) const;
250  void update_own_units_count();
251  void update_scouts_wanted();
252 
253 // Observer
255  public:
258 
259  void handle_generic_event(const std::string& event);
260 
261  bool recruit_list_changed();
262  void set_recruit_list_changed(bool changed);
263  int gamestate_changed();
265 
266  private:
269 
270  };
271 
272  std::set<map_location> important_hexes_;
273  terrain_count_map important_terrain_;
275  std::map<map_location, double> average_local_cost_;
276  std::map<size_t, int> cheapest_unit_costs_;
277  cache_table combat_cache_;
284  count_map own_units_count_;
287 };
288 
289 } // of namespace default_recruitment
290 
291 } // of namespace ai
292 
293 #ifdef _MSC_VER
294 #pragma warning(pop)
295 #endif
std::shared_ptr< action_result > action_result_ptr
Definition: game_info.hpp:94
std::string to_string() const
Definition: recruitment.hpp:87
std::vector< std::string > types
const double * get_cached_combat_value(const std::string &a, const std::string &b, double a_defense, double b_defense)
For Combat Analysis.
recruit_limit(std::vector< std::string > t, std::string id, int lim)
std::vector< char_t > string
std::map< size_t, int > cheapest_unit_costs_
cached_combat_value(double a_def, double b_def, double v)
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 ...
void update_average_local_cost()
For Map Analysis.
bool remove_job_if_no_blocker(config *job)
For Configuration / Aspect "recruitment-instructions".
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
virtual void execute()
Execute the candidate action.
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...
bool limit_ok(const std::string &recruit) const
For Configuration / Aspect "recruitment-instructions" Checks if a recruit-type can be recruited accor...
void handle_recruitment_more(std::vector< data > *leader_data) const
For Aspect "recruitment_more".
#define a
std::map< map_location, double > average_local_cost_
void update_important_hexes()
For Map Analysis.
std::vector< std::shared_ptr< recruit_job > > jobs_
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...
int get_cheapest_unit_cost_for_leader(const unit_map::const_iterator &leader)
Called at the beginning and whenever the recruitment list changes.
std::map< std::string, int > count_map
Definition: recruitment.hpp:52
const std::string get_best_recruit_from_scores(const data &leader_data, const config *job)
A helper function for execute().
double get_estimated_unit_gain() const
For Aspect "recruitment_save_gold".
recruit_situation_change_observer recruit_situation_change_observer_
A single unit type that the player may recruit.
Definition: types.hpp:43
std::set< map_location > important_hexes_
double compare_unit_types(const std::string &a, const std::string &b)
For Combat Analysis.
#define b
double get_estimated_village_gain() const
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...
double get_unit_ratio() const
For Aspect "recruitment_save_gold".
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
double get_average_defense(const std::string &unit_type) const
For Map Analysis.
void create_limit(std::vector< std::shared_ptr< recruit_limit > > &limits, const config &lim)
void update_average_lawful_bonus()
Calculates a average lawful bonus, so Combat Analysis will work better in caves and custom time of da...
void integrate_recruitment_pattern_in_recruitment_instructions()
For Configuration / Aspect "recruitment_pattern" Converts the (old) recruitment_pattern into a recrui...
void create_job(std::vector< std::shared_ptr< recruit_job > > &jobs, const config &job)
config * get_most_important_job()
For Configuration / Aspect "recruitment-instructions" We call a [recruit] tag a "job".
bool operator<(const cached_combat_value &o) const
bool is_enemy_in_radius(const map_location &loc, int radius) const
Helper function.
std::vector< std::string > types
void do_combat_analysis(std::vector< data > *leader_data)
Combat Analysis.
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...
config to_config() const
serialize
action_result_ptr execute_recall(const std::string &id, data &leader_data)
A helper function for execute().
Encapsulates the map of the game.
Definition: location.hpp:40
data(const unit_map::const_iterator leader)
Definition: recruitment.hpp:67
mock_party p
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...
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:266
action_result_ptr execute_recruit(const std::string &type, data &leader_data)
A helper function for execute().
#define i
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:51
double get_estimated_income(int turns) const
For Aspect "recruitment_save_gold".
int turns()
Definition: game.cpp:560
double t
Definition: astarsearch.cpp:64
recruit_job(std::vector< std::string > t, std::string L, std::string id, int n, int i, bool s, bool p, bool b)
recruitment(rca_context &context, const config &cfg)
const std::string * get_appropriate_recall(const std::string &type, const data &leader_data) const
A helper function for execute().
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.
recruitment_aspect(readonly_context &context, const config &cfg, const std::string &id)
score_map get_normalized_scores() const
Definition: recruitment.hpp:76
unit_map::const_iterator leader
Definition: recruitment.hpp:55
std::map< std::string, table_row > cache_table
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
void update_state()
For Aspect "recruitment_save_gold".
const pathfind::full_cost_map get_cost_map_of_side(int side) const
For Map Analysis.
static map_location::DIRECTION n
void show_important_hexes() const
For Map Analysis.
std::map< std::string, double > score_map
Definition: recruitment.hpp:49
void do_similarity_penalty(std::vector< data > *leader_data) const
Will give a penalty to similar units.
std::set< std::string > recruits
Definition: recruitment.hpp:56
candidate action framework
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...