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