The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
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
25#include "ai/composite/rca.hpp"
26#include "units/map.hpp"
27#include "units/ptr.hpp"
28
29#include <iomanip>
30
31namespace pathfind {
32
33struct full_cost_map;
34
35} //of namespace pathfind
36
37namespace ai {
38
39namespace 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.
44typedef std::map<std::string, double> score_map;
45
46typedef std::map<t_translation::terrain_code, int> terrain_count_map;
47typedef std::map<std::string, int> count_map;
48
49struct 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.
58
61
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
97struct 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 {}
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
132struct 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 {}
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
155class recruitment_aspect : public standard_aspect<config> {
156 std::vector<std::shared_ptr<recruit_job>> jobs_;
157 std::vector<std::shared_ptr<recruit_limit>> limits_;
158public:
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
165typedef std::map<std::string, std::set<cached_combat_value>> table_row;
166typedef std::map<std::string, table_row> cache_table;
167
169public:
170 recruitment(rca_context &context, const config &cfg);
171 virtual ~recruitment() { }
172 virtual double evaluate();
173 virtual void execute();
174 config to_config() const;
175private:
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
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;
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;
237
238// Observer
240 public:
243
244 void handle_generic_event(const std::string& event);
245
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
void create_limit(std::vector< std::shared_ptr< recruit_limit > > &limits, const config &lim)
std::vector< std::shared_ptr< recruit_limit > > limits_
void create_job(std::vector< std::shared_ptr< recruit_job > > &jobs, const config &job)
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".
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::map< t_translation::terrain_code, int > terrain_count_map
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
std::map< std::string, int > count_map
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:81
std::string join(const Range &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
cached_combat_value(double a_def, double b_def, double v)
unit_map::const_iterator leader
std::string to_string() const
std::set< std::string > recruits
score_map get_normalized_scores() const
data(const unit_map::const_iterator leader)
std::vector< std::string > types
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 i
#define b