33 #define DBG_NG LOG_STREAM(debug, log_engine) 34 #define ERR_NG LOG_STREAM(err, log_engine) 40 bool mid_scenario =
false;
43 typedef std::map<std::string,stats> team_stats_t;
45 std::string get_team_save_id(
const unit & u)
53 explicit scenario_stats(
const std::string& name) :
58 explicit scenario_stats(
const config& cfg);
63 team_stats_t team_stats;
64 std::string scenario_name;
67 scenario_stats::scenario_stats(
const config& cfg) :
69 scenario_name(cfg[
"scenario"])
72 team_stats[
team[
"save_id"]] = stats(team);
79 res[
"scenario"] = scenario_name;
80 for(team_stats_t::const_iterator
i = team_stats.begin();
i != team_stats.end(); ++
i) {
90 for(team_stats_t::const_iterator
i = team_stats.begin();
i != team_stats.end(); ++
i) {
97 std::vector<scenario_stats> master_stats;
103 if(master_stats.empty()) {
104 master_stats.emplace_back(std::string());
107 team_stats_t& team_stats = master_stats.back().team_stats;
108 return team_stats[save_id];
114 for(stats::str_int_map::const_iterator
i = m.begin();
i != m.end(); ++
i) {
115 std::string
n = std::to_string(
i->second);
117 res[
n] = res[
n].str() +
"," +
i->first;
128 using reverse_map = std::multimap<int, std::string>;
132 std::inserter(rev, rev.begin()),
133 [](
const stats::str_int_map::value_type
p) {
134 return std::pair(p.second, p.first);
137 reverse_map::const_iterator
i = rev.begin(), j;
138 while(i != rev.end()) {
139 j = rev.upper_bound(i->first);
140 std::vector<std::string> vals;
141 std::transform(i, j, std::back_inserter(vals), [](
const reverse_map::value_type& p) {
151 stats::str_int_map m;
155 m[val] = std::stoi(
i.first);
157 }
catch(
const std::invalid_argument&) {
158 ERR_NG <<
"Invalid statistics entry; skipping";
168 for(stats::battle_result_map::const_iterator
i = m.begin();
i != m.end(); ++
i) {
171 new_cfg[
"_num"] =
i->first;
179 for(stats::battle_result_map::const_iterator
i = m.begin();
i != m.end(); ++
i) {
189 stats::battle_result_map m;
193 int key = item[
"_num"];
204 for(
const auto&
i : m) {
207 "stats",
i.second.write()
216 stats::hitrate_map m;
221 for(
const auto&
i : merged) {
224 for(
const auto& j : frequency_map) {
225 const std::string& res = j.first;
226 const int occurrences = j.second;
227 unsigned int misses = std::count(res.begin(), res.end(),
'0');
228 unsigned int hits = std::count(res.begin(), res.end(),
'1');
229 if(misses + hits == 0) {
232 misses *= occurrences;
234 m[cth].strikes += misses + hits;
244 stats::hitrate_map m;
253 for(stats::str_int_map::const_iterator
i = b.begin();
i != b.end(); ++
i) {
254 a[
i->first] +=
i->second;
260 for(stats::battle_result_map::const_iterator
i = b.begin();
i != b.end(); ++
i) {
267 for(
const auto&
i : b) {
268 a[
i.first].hits +=
i.second.hits;
269 a[
i.first].strikes +=
i.second.strikes;
275 DBG_NG <<
"Merging statistics";
290 a.recruit_cost += b.recruit_cost;
291 a.recall_cost += b.recall_cost;
293 a.damage_inflicted += b.damage_inflicted;
294 a.damage_taken += b.damage_taken;
295 a.expected_damage_inflicted += b.expected_damage_inflicted;
296 a.expected_damage_taken += b.expected_damage_taken;
298 a.turn_damage_inflicted = b.turn_damage_inflicted;
299 a.turn_damage_taken = b.turn_damage_taken;
300 a.turn_expected_damage_inflicted = b.turn_expected_damage_inflicted;
301 a.turn_expected_damage_taken = b.turn_expected_damage_taken;
302 a.turn_by_cth_inflicted = b.turn_by_cth_inflicted;
303 a.turn_by_cth_taken = b.turn_by_cth_taken;
324 turn_damage_inflicted(0),
325 turn_damage_taken(0),
328 turn_by_cth_inflicted(),
330 expected_damage_inflicted(0),
331 expected_damage_taken(0),
332 turn_expected_damage_inflicted(0),
333 turn_expected_damage_taken(0),
509 save_id = cfg[
"save_id"].str();
514 if(!mid_scenario || master_stats.empty()) {
515 master_stats.emplace_back(name);
523 mid_scenario =
false;
527 const unit&
d,
int a_cth,
int d_cth) :
528 attacker_type(a.type_id()),
529 defender_type(d.type_id()),
530 attacker_side(get_team_save_id(a)),
531 defender_side(get_team_save_id(d)),
532 chance_to_hit_defender(a_cth),
533 chance_to_hit_attacker(d_cth),
564 int defender_inflict = std::round(defender_inflict_ * stats::decimal_shift);
568 def_stats.expected_damage_inflicted += defender_inflict;
569 def_stats.expected_damage_taken += attacker_inflict;
572 def_stats.turn_expected_damage_inflicted += defender_inflict;
573 def_stats.turn_expected_damage_taken += attacker_inflict;
585 ++def_stats.by_cth_taken[cth].hits;
586 ++def_stats.turn_by_cth_taken[cth].hits;
590 ++def_stats.by_cth_taken[cth].strikes;
591 ++def_stats.turn_by_cth_taken[cth].strikes;
596 def_stats.damage_inflicted -= drain;
598 def_stats.turn_damage_inflicted -= drain;
601 def_stats.damage_taken += damage;
603 def_stats.turn_damage_taken += damage;
618 ++def_stats.by_cth_inflicted[cth].hits;
619 ++def_stats.turn_by_cth_inflicted[cth].hits;
623 ++def_stats.by_cth_inflicted[cth].strikes;
624 ++def_stats.turn_by_cth_inflicted[cth].strikes;
630 def_stats.damage_taken -= drain;
632 def_stats.turn_damage_taken -= drain;
636 def_stats.damage_inflicted += damage;
638 def_stats.turn_damage_inflicted += damage;
703 DBG_NG <<
"calculate_stats, side: " << save_id <<
" master_stats.size: " << master_stats.size();
706 for ( std::size_t
i = 0;
i != master_stats.size(); ++
i ) {
707 team_stats_t::const_iterator find_it = master_stats[
i].team_stats.find(save_id);
708 if ( find_it != master_stats[
i].team_stats.end() )
728 static const stats null_stats;
729 static const std::string null_name(
"");
734 const team_stats_t & team_stats = master_stats[
level].team_stats;
736 team_stats_t::const_iterator find_it = team_stats.find(save_id);
737 if ( find_it != team_stats.end() )
738 level_list.emplace_back(&master_stats[
level].scenario_name, &find_it->second);
743 if ( level_list.empty() )
744 level_list.emplace_back(&null_name, &null_stats);
753 res[
"mid_scenario"] = mid_scenario;
755 for(std::vector<scenario_stats>::const_iterator
i = master_stats.begin();
i != master_stats.end(); ++
i) {
766 for(std::vector<scenario_stats>::const_iterator
i = master_stats.begin();
i != master_stats.end(); ++
i) {
776 mid_scenario = cfg[
"mid_scenario"].to_bool();
779 master_stats.emplace_back(
s);
785 master_stats.clear();
786 mid_scenario =
false;
791 if(master_stats.empty() ==
false) {
792 master_stats.pop_back();
793 mid_scenario =
false;
799 assert(!master_stats.empty());
800 master_stats.back().team_stats = {};
801 mid_scenario =
false;
807 for(stats::str_int_map::const_iterator
i = m.begin();
i != m.end(); ++
i) {
817 for (stats::str_int_map::const_iterator
i = m.begin();
i != m.end(); ++
i) {
820 ERR_NG <<
"Statistics refer to unknown unit type '" <<
i->first <<
"'. Discarding.";
822 cost +=
i->second * t->
cost();
831 return config(
"hits", hits,
"strikes", strikes);
835 strikes(cfg[
"strikes"]),
842 outstream <<
"[" << by_cth.
hits <<
"/" << by_cth.
strikes <<
"]";
int chance_to_hit_defender
const std::string & parent_id() const
The id of the original type from which this (variation) descended.
static stats::battle_result_map read_battle_result_map(const config &cfg)
long long damage_inflicted
void write(const config &cfg)
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
battle_result_map defends_taken
Statistics of enemies' attacks against this side on their turns.
scenario_context(const std::string &name)
This class represents a single unit of a specific type.
const std::string & type_id() const
The id of this unit's type.
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
void un_recall_unit(const unit &u)
bool has_attribute(config_key_type key) const
static stats::hitrate_map read_by_cth_map(const config &cfg)
child_itors child_range(config_key_type key)
attribute_map::value_type attribute
long long turn_damage_taken
int sum_str_int_map(const std::map< std::string, int > &m)
unit_type_data unit_types
std::string attacker_type
void remove_attribute(config_key_type key)
void reset_current_scenario()
Reset the stats of the current scenario to the beginning.
void reset_turn_stats(const std::string &save_id)
int cost() const
How much gold is required to recruit this unit.
void recruit_unit(const unit &u)
const_attr_itors attribute_range() const
std::vector< std::pair< const std::string *, const stats * > > levels
Stats (and name) for each scenario.
A single unit type that the player may recruit.
long long expected_damage_taken
const unit_type & type() const
This unit's type, accounting for gender and variation.
long long turn_expected_damage_inflicted
battle_result_map attacks_inflicted
Statistics of this side's attacks on its own turns.
static config write_by_cth_map(const stats::hitrate_map &m)
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
This class stores all the data for a single 'side' (in game nomenclature).
void write_key_val(const std::string &key, const T &value)
This template function will work with any type that can be assigned to an attribute_value.
std::string defender_side
int chance_to_hit_attacker
static void merge_str_int_map(stats::str_int_map &a, const stats::str_int_map &b)
void close_child(const std::string &key)
static const int decimal_shift
void attack_result(hit_result res, int cth, int damage, int drain)
Class for writing a config out to a file in pieces.
std::map< int, battle_sequence_frequency_map > battle_result_map
A type that will map different % chances to hit to different results.
int un_recall_unit_cost(const unit &u)
static stats::hitrate_map read_by_cth_map_from_battle_result_maps(const statistics::stats::battle_result_map &attacks, const statistics::stats::battle_result_map &defends)
void open_child(const std::string &key)
battle_result_map defends_inflicted
Statistics of this side's attacks on enemies' turns.
static stats & get_stats(const std::string &save_id)
levels level_stats(const std::string &save_id)
Returns a list of names and stats for each scenario in the current campaign.
static config write_battle_result_map(const stats::battle_result_map &m)
hitrate_map turn_by_cth_taken
void read_stats(const config &cfg)
void read(const config &cfg)
utils::optional_reference< config > optional_child(config_key_type key, int n=0)
Euivalent to child, but returns an empty optional if the nth child was not found. ...
battle_result_map attacks_taken
Statistics of enemies' counter attacks on this side's turns.
hitrate_map turn_by_cth_inflicted
int sum_cost_str_int_map(const std::map< std::string, int > &m)
void advance_unit(const unit &u)
static lg::log_domain log_engine("engine")
stats calculate_stats(const std::string &save_id)
static map_location::DIRECTION s
static stats::str_int_map read_str_int_map(const config &cfg)
long long turn_damage_inflicted
static void merge_battle_result_maps(stats::battle_result_map &a, const stats::battle_result_map &b)
std::string attacker_side
void recall_unit(const unit &u)
config & add_child(config_key_type key)
attack_context(const unit &a, const unit &d, int a_cth, int d_cth)
long long expected_damage_inflicted
std::string defender_type
str_int_map battle_sequence_frequency_map
static void merge_stats(stats &a, const stats &b)
std::vector< std::string > split(const config_attribute_value &val)
void attack_expected_damage(double attacker_inflict, double defender_inflict)
void defend_result(hit_result res, int cth, int damage, int drain)
Standard logging facilities (interface).
long long turn_expected_damage_taken
int side() const
The side this unit belongs to.
void un_recruit_unit(const unit &u)
A config object defines a single node in a WML file, with access to child nodes.
void clear_current_scenario()
Delete the current scenario from the stats.
static config write_str_int_map(const stats::str_int_map &m)
static map_location::DIRECTION n
static void merge_cth_map(stats::hitrate_map &a, const stats::hitrate_map &b)
int recall_cost() const
How much gold it costs to recall this unit, or -1 if the side's default recall cost is used...
std::ostream & operator<<(std::ostream &outstream, const statistics::stats::hitrate_t &by_cth)
std::pair< std::string, unsigned > item
hitrate_map by_cth_inflicted