statistics.cpp

Go to the documentation of this file.
00001 /* $Id: statistics.cpp 54232 2012-05-19 15:57:33Z fendrin $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  *  @file
00018  *  Manage statistics: recruitments, recalls, kills, losses, etc.
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 // This variable is true whenever the statistics are mid-scenario.
00035 // This means a new scenario shouldn't be added to the master stats record.
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 } // end anon namespace
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     // Only take the last value for this turn
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         // handle drain
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         //handle drain
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         // We are going from last to first to include correct turn stats in result
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 } // end namespace statistics
00655 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:10 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs