00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "global.hpp"
00022 #include "statistics.hpp"
00023 #include "foreach.hpp"
00024 #include "log.hpp"
00025 #include "serialization/binary_or_text.hpp"
00026 #include "unit.hpp"
00027 #include "util.hpp"
00028
00029 static lg::log_domain log_engine("engine");
00030 #define DBG_NG LOG_STREAM(debug, log_engine)
00031
00032 namespace {
00033
00034
00035
00036 bool mid_scenario = false;
00037
00038 typedef statistics::stats stats;
00039
00040 struct scenario_stats
00041 {
00042 explicit scenario_stats(const std::string& name) :
00043 team_stats(),
00044 scenario_name(name)
00045 {}
00046
00047 explicit scenario_stats(const config& cfg);
00048
00049 config write() const;
00050 void write(config_writer &out) const;
00051
00052 std::map<std::string,stats> team_stats;
00053 std::string scenario_name;
00054 };
00055
00056 scenario_stats::scenario_stats(const config& cfg) :
00057 team_stats(),
00058 scenario_name(cfg["scenario"])
00059 {
00060 foreach (const config &team, cfg.child_range("team")) {
00061 team_stats[team["save_id"]] = stats(team);
00062 }
00063 }
00064
00065 config scenario_stats::write() const
00066 {
00067 config res;
00068 res["scenario"] = scenario_name;
00069 for(std::map<std::string,stats>::const_iterator i = team_stats.begin(); i != team_stats.end(); ++i) {
00070 res.add_child("team",i->second.write());
00071 }
00072
00073 return res;
00074 }
00075
00076 void scenario_stats::write(config_writer &out) const
00077 {
00078 out.write_key_val("scenario", scenario_name);
00079 for(std::map<std::string,stats>::const_iterator i = team_stats.begin(); i != team_stats.end(); ++i) {
00080 out.open_child("team");
00081 i->second.write(out);
00082 out.close_child("team");
00083 }
00084 }
00085
00086 std::vector<scenario_stats> master_stats;
00087
00088 }
00089
00090 static stats &get_stats(const std::string &save_id)
00091 {
00092 if(master_stats.empty()) {
00093 master_stats.push_back(scenario_stats(std::string()));
00094 }
00095
00096 std::map<std::string,stats>& team_stats = master_stats.back().team_stats;
00097 return team_stats[save_id];
00098 }
00099
00100 static config write_str_int_map(const stats::str_int_map& m)
00101 {
00102 config res;
00103 for(stats::str_int_map::const_iterator i = m.begin(); i != m.end(); ++i) {
00104 char buf[50];
00105 snprintf(buf,sizeof(buf),"%d",i->second);
00106 res[i->first] = buf;
00107 }
00108
00109 return res;
00110 }
00111
00112 static void write_str_int_map(config_writer &out, const stats::str_int_map& m)
00113 {
00114 for(stats::str_int_map::const_iterator i = m.begin(); i != m.end(); ++i) {
00115 char buf[50];
00116 snprintf(buf,sizeof(buf),"%d",i->second);
00117 out.write_key_val(i->first, buf);
00118 }
00119 }
00120
00121 static stats::str_int_map read_str_int_map(const config& cfg)
00122 {
00123 stats::str_int_map m;
00124 foreach (const config::attribute &i, cfg.attribute_range()) {
00125 m[i.first] = i.second;
00126 }
00127
00128 return m;
00129 }
00130
00131 static config write_battle_result_map(const stats::battle_result_map& m)
00132 {
00133 config res;
00134 for(stats::battle_result_map::const_iterator i = m.begin(); i != m.end(); ++i) {
00135 config& new_cfg = res.add_child("sequence");
00136 new_cfg = write_str_int_map(i->second);
00137
00138 char buf[50];
00139 snprintf(buf,sizeof(buf),"%d",i->first);
00140 new_cfg["_num"] = buf;
00141 }
00142
00143 return res;
00144 }
00145
00146 static void write_battle_result_map(config_writer &out, const stats::battle_result_map& m)
00147 {
00148 for(stats::battle_result_map::const_iterator i = m.begin(); i != m.end(); ++i) {
00149 out.open_child("sequence");
00150 write_str_int_map(out, i->second);
00151
00152 char buf[50];
00153 snprintf(buf,sizeof(buf),"%d",i->first);
00154 out.write_key_val("_num", buf);
00155 out.close_child("sequence");
00156 }
00157 }
00158
00159 static stats::battle_result_map read_battle_result_map(const config& cfg)
00160 {
00161 stats::battle_result_map m;
00162 foreach (const config &i, cfg.child_range("sequence"))
00163 {
00164 config item = i;
00165 int key = item["_num"];
00166 item.remove_attribute("_num");
00167 m[key] = read_str_int_map(item);
00168 }
00169
00170 return m;
00171 }
00172
00173 static void merge_str_int_map(stats::str_int_map& a, const stats::str_int_map& b)
00174 {
00175 for(stats::str_int_map::const_iterator i = b.begin(); i != b.end(); ++i) {
00176 a[i->first] += i->second;
00177 }
00178 }
00179
00180 static void merge_battle_result_maps(stats::battle_result_map& a, const stats::battle_result_map& b)
00181 {
00182 for(stats::battle_result_map::const_iterator i = b.begin(); i != b.end(); ++i) {
00183 merge_str_int_map(a[i->first],i->second);
00184 }
00185 }
00186
00187 static void merge_stats(stats& a, const stats& b)
00188 {
00189 DBG_NG << "Merging statistics\n";
00190 merge_str_int_map(a.recruits,b.recruits);
00191 merge_str_int_map(a.recalls,b.recalls);
00192 merge_str_int_map(a.advanced_to,b.advanced_to);
00193 merge_str_int_map(a.deaths,b.deaths);
00194 merge_str_int_map(a.killed,b.killed);
00195
00196 merge_battle_result_maps(a.attacks,b.attacks);
00197 merge_battle_result_maps(a.defends,b.defends);
00198
00199 a.recruit_cost += b.recruit_cost;
00200 a.recall_cost += b.recall_cost;
00201
00202 a.damage_inflicted += b.damage_inflicted;
00203 a.damage_taken += b.damage_taken;
00204 a.expected_damage_inflicted += b.expected_damage_inflicted;
00205 a.expected_damage_taken += b.expected_damage_taken;
00206
00207 a.turn_damage_inflicted = b.turn_damage_inflicted;
00208 a.turn_damage_taken = b.turn_damage_taken;
00209 a.turn_expected_damage_inflicted = b.turn_expected_damage_inflicted;
00210 a.turn_expected_damage_taken = b.turn_expected_damage_taken;
00211 }
00212
00213 namespace statistics
00214 {
00215
00216 stats::stats() :
00217 recruits(),
00218 recalls(),
00219 advanced_to(),
00220 deaths(),
00221 killed(),
00222 recruit_cost(0),
00223 recall_cost(0),
00224 attacks(),
00225 defends(),
00226 damage_inflicted(0),
00227 damage_taken(0),
00228 turn_damage_inflicted(0),
00229 turn_damage_taken(0),
00230 expected_damage_inflicted(0),
00231 expected_damage_taken(0),
00232 turn_expected_damage_inflicted(0),
00233 turn_expected_damage_taken(0),
00234 save_id()
00235 {}
00236
00237 stats::stats(const config& cfg) :
00238 recruits(),
00239 recalls(),
00240 advanced_to(),
00241 deaths(),
00242 killed(),
00243 recruit_cost(0),
00244 recall_cost(0),
00245 attacks(),
00246 defends(),
00247 damage_inflicted(0),
00248 damage_taken(0),
00249 turn_damage_inflicted(0),
00250 turn_damage_taken(0),
00251 expected_damage_inflicted(0),
00252 expected_damage_taken(0),
00253 turn_expected_damage_inflicted(0),
00254 turn_expected_damage_taken(0),
00255 save_id(std::string())
00256 {
00257 read(cfg);
00258 }
00259
00260 config stats::write() const
00261 {
00262 config res;
00263 res.add_child("recruits",write_str_int_map(recruits));
00264 res.add_child("recalls",write_str_int_map(recalls));
00265 res.add_child("advances",write_str_int_map(advanced_to));
00266 res.add_child("deaths",write_str_int_map(deaths));
00267 res.add_child("killed",write_str_int_map(killed));
00268 res.add_child("attacks",write_battle_result_map(attacks));
00269 res.add_child("defends",write_battle_result_map(defends));
00270
00271 std::ostringstream ss;
00272 ss << recruit_cost;
00273 res["recruit_cost"] = ss.str();
00274 ss.str(std::string());
00275 ss << recall_cost;
00276 res["recall_cost"] = ss.str();
00277
00278 ss.str(std::string());
00279 ss << damage_inflicted;
00280 res["damage_inflicted"] = ss.str();
00281 ss.str(std::string());
00282 ss << damage_taken;
00283 res["damage_taken"] = ss.str();
00284 ss.str(std::string());
00285 ss << expected_damage_inflicted;
00286 res["expected_damage_inflicted"] = ss.str();
00287 ss.str(std::string());
00288 ss << expected_damage_taken;
00289 res["expected_damage_taken"] = ss.str();
00290 ss.str(std::string());
00291 ss << turn_damage_inflicted;
00292
00293 res["turn_damage_inflicted"] = ss.str();
00294 ss.str(std::string());
00295 ss << turn_damage_taken;
00296 res["turn_damage_taken"] = ss.str();
00297 ss.str(std::string());
00298 ss << turn_expected_damage_inflicted;
00299 res["turn_expected_damage_inflicted"] = ss.str();
00300 ss.str(std::string());
00301 ss << turn_expected_damage_taken;
00302 res["turn_expected_damage_taken"] = ss.str();
00303
00304 return res;
00305 }
00306
00307 void stats::write(config_writer &out) const
00308 {
00309 out.open_child("recruits");
00310 write_str_int_map(out, recruits);
00311 out.close_child("recruits");
00312 out.open_child("recalls");
00313 write_str_int_map(out, recalls);
00314 out.close_child("recalls");
00315 out.open_child("advances");
00316 write_str_int_map(out, advanced_to);
00317 out.close_child("advances");
00318 out.open_child("deaths");
00319 write_str_int_map(out, deaths);
00320 out.close_child("deaths");
00321 out.open_child("killed");
00322 write_str_int_map(out, killed);
00323 out.close_child("killed");
00324 out.open_child("attacks");
00325 write_battle_result_map(out, attacks);
00326 out.close_child("attacks");
00327 out.open_child("defends");
00328 write_battle_result_map(out, defends);
00329 out.close_child("defends");
00330
00331 std::ostringstream ss;
00332 ss << recruit_cost;
00333 out.write_key_val("recruit_cost", ss.str());
00334 ss.str(std::string());
00335 ss << recall_cost;
00336 out.write_key_val("recall_cost", ss.str());
00337
00338 ss.str(std::string());
00339 ss << damage_inflicted;
00340 out.write_key_val("damage_inflicted", ss.str());
00341 ss.str(std::string());
00342 ss << damage_taken;
00343 out.write_key_val("damage_taken", ss.str());
00344 ss.str(std::string());
00345 ss << expected_damage_inflicted;
00346 out.write_key_val("expected_damage_inflicted", ss.str());
00347 ss.str(std::string());
00348 ss << expected_damage_taken;
00349 out.write_key_val("expected_damage_taken", ss.str());
00350 ss.str(std::string());
00351 ss << turn_damage_inflicted;
00352
00353 out.write_key_val("turn_damage_inflicted", ss.str());
00354 ss.str(std::string());
00355 ss << turn_damage_taken;
00356 out.write_key_val("turn_damage_taken", ss.str());
00357 ss.str(std::string());
00358 ss << turn_expected_damage_inflicted;
00359 out.write_key_val("turn_expected_damage_inflicted", ss.str());
00360 ss.str(std::string());
00361 ss << turn_expected_damage_taken;
00362 out.write_key_val("turn_expected_damage_taken", ss.str());
00363
00364 out.write_key_val("save_id", save_id);
00365
00366 }
00367
00368 void stats::read(const config& cfg)
00369 {
00370 if (const config &c = cfg.child("recruits")) {
00371 recruits = read_str_int_map(c);
00372 }
00373 if (const config &c = cfg.child("recalls")) {
00374 recalls = read_str_int_map(c);
00375 }
00376 if (const config &c = cfg.child("advances")) {
00377 advanced_to = read_str_int_map(c);
00378 }
00379 if (const config &c = cfg.child("deaths")) {
00380 deaths = read_str_int_map(c);
00381 }
00382 if (const config &c = cfg.child("killed")) {
00383 killed = read_str_int_map(c);
00384 }
00385 if (const config &c = cfg.child("recalls")) {
00386 recalls = read_str_int_map(c);
00387 }
00388 if (const config &c = cfg.child("attacks")) {
00389 attacks = read_battle_result_map(c);
00390 }
00391 if (const config &c = cfg.child("defends")) {
00392 defends = read_battle_result_map(c);
00393 }
00394
00395 recruit_cost = cfg["recruit_cost"].to_double();
00396 recall_cost = cfg["recall_cost"].to_double();
00397
00398 damage_inflicted = cfg["damage_inflicted"].to_double();
00399 damage_taken = cfg["damage_taken"].to_double();
00400 expected_damage_inflicted = cfg["expected_damage_inflicted"].to_double();
00401 expected_damage_taken = cfg["expected_damage_taken"].to_double();
00402
00403 turn_damage_inflicted = cfg["turn_damage_inflicted"].to_double();
00404 turn_damage_taken = cfg["turn_damage_taken"].to_double();
00405 turn_expected_damage_inflicted = cfg["turn_expected_damage_inflicted"].to_double();
00406 turn_expected_damage_taken = cfg["turn_expected_damage_taken"].to_double();
00407
00408 save_id = cfg["save_id"].str();
00409 }
00410
00411 scenario_context::scenario_context(const std::string& name)
00412 {
00413 if(!mid_scenario || master_stats.empty()) {
00414 master_stats.push_back(scenario_stats(name));
00415 }
00416
00417 mid_scenario = true;
00418 }
00419
00420 scenario_context::~scenario_context()
00421 {
00422 mid_scenario = false;
00423 }
00424
00425 attack_context::attack_context(const unit& a,
00426 const unit& d, int a_cth, int d_cth) :
00427 attacker_type(a.type_id()),
00428 defender_type(d.type_id()),
00429 attacker_side(a.side_id()),
00430 defender_side(d.side_id()),
00431 chance_to_hit_defender(a_cth),
00432 chance_to_hit_attacker(d_cth),
00433 attacker_res(),
00434 defender_res()
00435 {
00436 }
00437
00438 attack_context::~attack_context()
00439 {
00440 std::string attacker_key = "s" + attacker_res;
00441 std::string defender_key = "s" + defender_res;
00442
00443 attacker_stats().attacks[chance_to_hit_defender][attacker_key]++;
00444 defender_stats().defends[chance_to_hit_attacker][defender_key]++;
00445 }
00446
00447 stats& attack_context::attacker_stats()
00448 {
00449 return get_stats(attacker_side);
00450 }
00451
00452 stats& attack_context::defender_stats()
00453 {
00454 return get_stats(defender_side);
00455 }
00456
00457 void attack_context::attack_expected_damage(double attacker_inflict_, double defender_inflict_)
00458 {
00459 int attacker_inflict = round_double(attacker_inflict_ * stats::decimal_shift);
00460 int defender_inflict = round_double(defender_inflict_ * stats::decimal_shift);
00461 stats &att_stats = attacker_stats(), &def_stats = defender_stats();
00462 att_stats.expected_damage_inflicted += attacker_inflict;
00463 att_stats.expected_damage_taken += defender_inflict;
00464 def_stats.expected_damage_inflicted += defender_inflict;
00465 def_stats.expected_damage_taken += attacker_inflict;
00466 att_stats.turn_expected_damage_inflicted += attacker_inflict;
00467 att_stats.turn_expected_damage_taken += defender_inflict;
00468 def_stats.turn_expected_damage_inflicted += defender_inflict;
00469 def_stats.turn_expected_damage_taken += attacker_inflict;
00470 }
00471
00472
00473 void attack_context::attack_result(hit_result res, int damage, int drain)
00474 {
00475 attacker_res.push_back(res == MISSES ? '0' : '1');
00476 stats &att_stats = attacker_stats(), &def_stats = defender_stats();
00477
00478 if(res != MISSES) {
00479
00480 att_stats.damage_taken -= drain;
00481 def_stats.damage_inflicted -= drain;
00482 att_stats.turn_damage_taken -= drain;
00483 def_stats.turn_damage_inflicted -= drain;
00484
00485 att_stats.damage_inflicted += damage;
00486 def_stats.damage_taken += damage;
00487 att_stats.turn_damage_inflicted += damage;
00488 def_stats.turn_damage_taken += damage;
00489 }
00490
00491 if(res == KILLS) {
00492 ++att_stats.killed[defender_type];
00493 ++def_stats.deaths[defender_type];
00494 }
00495 }
00496
00497 void attack_context::defend_result(hit_result res, int damage, int drain)
00498 {
00499 defender_res.push_back(res == MISSES ? '0' : '1');
00500 stats &att_stats = attacker_stats(), &def_stats = defender_stats();
00501
00502 if(res != MISSES) {
00503
00504 def_stats.damage_taken -= drain;
00505 att_stats.damage_inflicted -= drain;
00506 def_stats.turn_damage_taken -= drain;
00507 att_stats.turn_damage_inflicted -= drain;
00508
00509 att_stats.damage_taken += damage;
00510 def_stats.damage_inflicted += damage;
00511 att_stats.turn_damage_taken += damage;
00512 def_stats.turn_damage_inflicted += damage;
00513 }
00514
00515 if(res == KILLS) {
00516 ++att_stats.deaths[attacker_type];
00517 ++def_stats.killed[attacker_type];
00518 }
00519 }
00520
00521 void recruit_unit(const unit& u)
00522 {
00523 stats& s = get_stats(u.side_id());
00524 s.recruits[u.type_id()]++;
00525 s.recruit_cost += u.cost();
00526 }
00527
00528 void recall_unit(const unit& u)
00529 {
00530 stats& s = get_stats(u.side_id());
00531 s.recalls[u.type_id()]++;
00532 s.recall_cost += u.cost();
00533 }
00534
00535 void un_recall_unit(const unit& u)
00536 {
00537 stats& s = get_stats(u.side_id());
00538 s.recalls[u.type_id()]--;
00539 s.recall_cost -= u.cost();
00540 }
00541
00542 void un_recruit_unit(const unit& u)
00543 {
00544 stats& s = get_stats(u.side_id());
00545 s.recruits[u.type_id()]--;
00546 s.recruit_cost -= u.cost();
00547 }
00548
00549
00550 void advance_unit(const unit& u)
00551 {
00552 stats& s = get_stats(u.side_id());
00553 s.advanced_to[u.type_id()]++;
00554 }
00555
00556 void reset_turn_stats(std::string save_id)
00557 {
00558 stats &s = get_stats(save_id);
00559 s.turn_damage_inflicted = 0;
00560 s.turn_damage_taken = 0;
00561 s.turn_expected_damage_inflicted = 0;
00562 s.turn_expected_damage_taken = 0;
00563 s.save_id = save_id;
00564 }
00565
00566 stats calculate_stats(int category, std::string save_id)
00567 {
00568 DBG_NG << "calculate_stats, category: " << category << " side: " << save_id << " master_stats.size: " << master_stats.size() << "\n";
00569 if(category == 0) {
00570 stats res;
00571
00572 for(int i = int(master_stats.size()); i > 0 ; --i) {
00573 merge_stats(res,calculate_stats(i,save_id));
00574 }
00575
00576 return res;
00577 } else {
00578 const size_t index = master_stats.size() - size_t(category);
00579 if(index < master_stats.size() && master_stats[index].team_stats.find(save_id) != master_stats[index].team_stats.end()) {
00580 return master_stats[index].team_stats[save_id];
00581 } else {
00582 return stats();
00583 }
00584 }
00585 }
00586
00587 config write_stats()
00588 {
00589 config res;
00590 res["mid_scenario"] = mid_scenario;
00591
00592 for(std::vector<scenario_stats>::const_iterator i = master_stats.begin(); i != master_stats.end(); ++i) {
00593 res.add_child("scenario",i->write());
00594 }
00595
00596 return res;
00597 }
00598
00599 void write_stats(config_writer &out)
00600 {
00601 out.write_key_val("mid_scenario", mid_scenario ? "yes" : "no");
00602
00603 for(std::vector<scenario_stats>::const_iterator i = master_stats.begin(); i != master_stats.end(); ++i) {
00604 out.open_child("scenario");
00605 i->write(out);
00606 out.close_child("scenario");
00607 }
00608 }
00609
00610 void read_stats(const config& cfg)
00611 {
00612 fresh_stats();
00613 mid_scenario = cfg["mid_scenario"].to_bool();
00614
00615 foreach (const config &s, cfg.child_range("scenario")) {
00616 master_stats.push_back(scenario_stats(s));
00617 }
00618 }
00619
00620 void fresh_stats()
00621 {
00622 master_stats.clear();
00623 mid_scenario = false;
00624 }
00625
00626 void clear_current_scenario()
00627 {
00628 if(master_stats.empty() == false) {
00629 master_stats.pop_back();
00630 mid_scenario = false;
00631 }
00632 }
00633
00634 int sum_str_int_map(const stats::str_int_map& m)
00635 {
00636 int res = 0;
00637 for(stats::str_int_map::const_iterator i = m.begin(); i != m.end(); ++i) {
00638 res += i->second;
00639 }
00640
00641 return res;
00642 }
00643
00644 int sum_cost_str_int_map(const stats::str_int_map &m)
00645 {
00646 int cost = 0;
00647 for (stats::str_int_map::const_iterator i = m.begin(); i != m.end(); ++i) {
00648 cost += i->second * unit_types.find(i->first)->cost();
00649 }
00650
00651 return cost;
00652 }
00653
00654 }
00655