highscore.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. #include <math.h>
  2. #include <iostream>
  3. #include <set>
  4. #include <algorithm>
  5. #include <stdexcept>
  6. #include "../strings_bank.h"
  7. #include "../highscore.h"
  8. std::string quote(const std::string& s) {
  9. std::string ret;
  10. for (unsigned char c : s) {
  11. if (c == '\\') ret += "\\\\";
  12. else if (c == '"') ret += "\\\"";
  13. else if (c <= 31) ret += " ";
  14. else ret += c;
  15. }
  16. return ret;
  17. }
  18. void _process(size_t n, const bones::bone_t::fakeobj& name, const bones::bone_t::fakeobj& cause,
  19. unsigned int plev, int dlev, double worth, bool victory, size_t rcodes) {
  20. if (n != 0)
  21. std::cout << ",";
  22. auto _cause = cause;
  23. _cause.name = quote(_cause.name);
  24. auto _name = name;
  25. _name.name = quote(_name.name);
  26. std::cout << nlp::message("\n{\"dlev\": %d, \"plev\": %d, \"name\": \"%S\", \"cause\": \"%s\","
  27. " \"worth\": %d, \"victory\": %s, \"rcodes\": %d}",
  28. dlev+1, plev+1, _name, _cause, worth,
  29. std::string(victory ? "true" : "false"), rcodes);
  30. }
  31. void split_name(const std::string& in, std::string& out, size_t& nach) {
  32. std::string::size_type achpos = in.find(" (");
  33. if (achpos == std::string::npos) {
  34. out = in;
  35. nach = 0;
  36. return;
  37. }
  38. out = in.substr(0, achpos);
  39. nach = 1;
  40. std::string tmp = in.substr(achpos);
  41. for (unsigned char cc : tmp) {
  42. if (cc == ',') {
  43. nach++;
  44. }
  45. }
  46. }
  47. struct bank_stats_t {
  48. enum OP_t {
  49. PURCHASE = 0,
  50. DEPOSIT = 1,
  51. WITHDRAWAL = 2
  52. };
  53. double base;
  54. std::set<tag_t> item_kinds_bought;
  55. size_t items_bought;
  56. double items_prices;
  57. std::map<unsigned int, double> accounts;
  58. double deposits;
  59. void load() {
  60. base = 0;
  61. item_kinds_bought.clear();
  62. items_bought = 0;
  63. items_prices = 0;
  64. accounts.clear();
  65. deposits = 0;
  66. try {
  67. serialize::Source source("finance.dat");
  68. while (1) {
  69. try {
  70. int type;
  71. double amount;
  72. tag_t purchase;
  73. unsigned int count;
  74. unsigned int account;
  75. serialize::read(source, type);
  76. switch (type) {
  77. case PURCHASE:
  78. // Purchases.
  79. serialize::read(source, amount);
  80. serialize::read(source, purchase);
  81. serialize::read(source, count);
  82. base += amount;
  83. item_kinds_bought.insert(purchase);
  84. items_bought += count;
  85. items_prices += amount;
  86. break;
  87. case DEPOSIT:
  88. // Deposits.
  89. serialize::read(source, amount);
  90. serialize::read(source, account);
  91. base += amount;
  92. accounts[account] += amount;
  93. break;
  94. case WITHDRAWAL:
  95. // Withdrawals.
  96. serialize::read(source, account);
  97. base -= accounts[account];
  98. accounts.erase(account);
  99. break;
  100. }
  101. } catch (...) {
  102. break;
  103. }
  104. }
  105. } catch (...) {
  106. }
  107. for (const auto& i : accounts) {
  108. deposits += i.second;
  109. }
  110. }
  111. void print() {
  112. std::cout << nlp::message("\n{\n"
  113. "\"base\": %d,\n"
  114. "\"purchases\": { \"num\": %d, \"kinds\": %d, \"cost\": %d },\n"
  115. "\"accounts\": { \"num\": %d, \"val\": %d }\n"
  116. "}\n",
  117. base, items_bought, item_kinds_bought.size(), items_prices,
  118. accounts.size(), deposits);
  119. }
  120. };
  121. struct other_stats_t {
  122. std::pair<std::string,size_t> cause_raw;
  123. std::pair<std::string,size_t> cause_plev;
  124. std::pair<std::string,size_t> cause_worth;
  125. double gdp1;
  126. double gdp2;
  127. double gdp3;
  128. double avg_plev;
  129. double avg_dlev;
  130. unsigned int median_plev;
  131. int median_dlev;
  132. size_t players;
  133. std::pair<std::string,size_t> most_active_player;
  134. std::pair<std::string,size_t> scummer;
  135. size_t ngames;
  136. std::pair< std::string, std::pair<double, std::string> > got_rich_quick;
  137. std::pair<std::string, std::pair<size_t,unsigned int> > most_achievements;
  138. std::string rare_cause;
  139. other_stats_t() : gdp1(0), gdp2(0), gdp3(0), avg_plev(0), avg_dlev(0),
  140. median_plev(0), median_dlev(0), players(0), ngames(0) {}
  141. struct cause_stat_t {
  142. unsigned int n = 0;
  143. double plev = 1;
  144. double worth = 1;
  145. double dlev = 1;
  146. template <typename N>
  147. bool tiebreak_ordering(N x, N y, const cause_stat_t& a) const {
  148. if (x < y)
  149. return true;
  150. if (x == y && (plev * worth * dlev) / n < (a.plev * a.worth * a.dlev) / a.n)
  151. return true;
  152. return false;
  153. }
  154. };
  155. void operator()(std::vector<highscore::Scores::order_t>& v) {
  156. if (v.size() == 0)
  157. return;
  158. std::reverse(v.begin(), v.end());
  159. ngames = v.size();
  160. typedef std::map< std::string, cause_stat_t> by_cause_t;
  161. by_cause_t by_cause;
  162. typedef std::map< std::string, std::pair<unsigned int, size_t> > by_players_t;
  163. by_players_t by_players;
  164. std::map<int, double> gdp_lev;
  165. std::string real_name;
  166. size_t num_aches;
  167. for (const auto& i : v) {
  168. auto& tmp = by_cause[i.bone.cause.name];
  169. tmp.n++;
  170. tmp.plev *= ::log(i.plev + 1);
  171. tmp.worth *= ::log(i.worth + 1);
  172. tmp.dlev *= abs(::log(abs(i.dlev) + 0.9));
  173. gdp1 += i.worth;
  174. gdp2 += i.bone.worth;
  175. avg_plev += i.plev;
  176. avg_dlev += i.dlev;
  177. split_name(i.bone.name.name, real_name, num_aches);
  178. auto& tmb = by_players[real_name];
  179. tmb.first++;
  180. tmb.second = std::max(tmb.second, i.scum_streak);
  181. if (i.plev == 0 && i.worth >= got_rich_quick.second.first) {
  182. got_rich_quick.first = i.bone.name.name;
  183. got_rich_quick.second.first = i.worth;
  184. got_rich_quick.second.second = i.bone.cause.name;
  185. }
  186. gdp_lev[i.dlev] += i.bone.worth;
  187. if (num_aches > most_achievements.second.first ||
  188. (num_aches == most_achievements.second.first && i.plev > most_achievements.second.second)) {
  189. most_achievements.first = i.bone.name.name;
  190. most_achievements.second.first = num_aches;
  191. most_achievements.second.second = i.plev;
  192. }
  193. }
  194. for (const auto& i : gdp_lev) {
  195. if (i.second > 0) {
  196. gdp3 += i.second;
  197. }
  198. }
  199. avg_plev /= v.size();
  200. avg_dlev /= v.size();
  201. std::sort(v.begin(), v.end(), [](const highscore::Scores::order_t& a,
  202. const highscore::Scores::order_t& b) {
  203. return a.plev < b.plev; });
  204. median_plev = v[v.size() / 2].plev;
  205. std::sort(v.begin(), v.end(), [](const highscore::Scores::order_t& a,
  206. const highscore::Scores::order_t& b) {
  207. return a.dlev < b.dlev; });
  208. median_dlev = v[v.size() / 2].dlev;
  209. auto i = std::max_element(by_cause.begin(), by_cause.end(),
  210. [](const by_cause_t::value_type& a, const by_cause_t::value_type& b) {
  211. return a.second.tiebreak_ordering(a.second.n, b.second.n, b.second);
  212. });
  213. cause_raw.first = i->first;
  214. cause_raw.second = i->second.n;
  215. i = std::max_element(by_cause.begin(), by_cause.end(),
  216. [](const by_cause_t::value_type& a, const by_cause_t::value_type& b) {
  217. return a.second.tiebreak_ordering(a.second.plev / a.second.n, b.second.plev / b.second.n, b.second);
  218. });
  219. cause_plev.first = i->first;
  220. cause_plev.second = i->second.n;
  221. i = std::max_element(by_cause.begin(), by_cause.end(),
  222. [](const by_cause_t::value_type& a, const by_cause_t::value_type& b) {
  223. return a.second.tiebreak_ordering(a.second.worth / a.second.n, b.second.worth / b.second.n, b.second);
  224. });
  225. cause_worth.first = i->first;
  226. cause_worth.second = i->second.n;
  227. i = std::min_element(by_cause.begin(), by_cause.end(),
  228. [](const by_cause_t::value_type& a, const by_cause_t::value_type& b) {
  229. return b.second.tiebreak_ordering(a.second.n, b.second.n, a.second);
  230. });
  231. rare_cause = i->first;
  232. players = by_players.size();
  233. auto j = std::max_element(by_players.begin(), by_players.end(),
  234. [](const by_players_t::value_type& a, const by_players_t::value_type& b) {
  235. return (a.second.first < b.second.first); });
  236. most_active_player.first = j->first;
  237. most_active_player.second = j->second.first;
  238. j = std::max_element(by_players.begin(), by_players.end(),
  239. [](const by_players_t::value_type& a, const by_players_t::value_type& b) {
  240. return (a.second.second < b.second.second); });
  241. scummer.first = j->first;
  242. scummer.second = j->second.second;
  243. }
  244. void print() {
  245. bones::bone_t::fakeobj cause_raw_name(quote(cause_raw.first));
  246. bones::bone_t::fakeobj cause_plev_name(quote(cause_plev.first));
  247. bones::bone_t::fakeobj cause_worth_name(quote(cause_worth.first));
  248. bones::bone_t::fakeobj rare_cause_name(quote(rare_cause));
  249. most_active_player.first = quote(most_active_player.first);
  250. scummer.first = quote(scummer.first);
  251. most_achievements.first = quote(most_achievements.first);
  252. got_rich_quick.first = quote(got_rich_quick.first);
  253. bones::bone_t::fakeobj cause_g_r_q(quote(got_rich_quick.second.second));
  254. std::cout << nlp::message("\n{\n"
  255. "\"games\": %d,\n"
  256. "\"kills_raw\": { \"cause\": \"%s\", \"kills\": %d },\n"
  257. "\"kills_plev\": { \"cause\": \"%s\", \"kills\": %d },\n"
  258. "\"kills_worth\": { \"cause\": \"%s\", \"kills\": %d },\n"
  259. "\"kills_rare\": \"%s\",\n"
  260. "\"gdp\": { \"a\": %d, \"b\": %d, \"c\": %d },\n"
  261. "\"plev\": { \"avg\": %d, \"median\": %d },\n"
  262. "\"dlev\": { \"avg\": %d, \"median\": %d },\n"
  263. "\"players\": %d,\n"
  264. "\"most_active\": { \"name\": \"%S\", \"games\": %d },\n"
  265. "\"scummer\": { \"name\": \"%S\", \"streak\": %d },\n"
  266. "\"most_achievements\": { \"name\": \"%S\", \"number\": %d },\n"
  267. "\"got_rich_quick\": { \"name\": \"%S\", \"worth\": %d, \"cause\": \"%s\" }\n"
  268. "}\n",
  269. ngames,
  270. cause_raw_name, cause_raw.second,
  271. cause_plev_name, cause_plev.second,
  272. cause_worth_name, cause_worth.second,
  273. rare_cause_name,
  274. gdp1, gdp2, gdp3,
  275. avg_plev+1, median_plev+1,
  276. avg_dlev+1, median_dlev+1,
  277. players,
  278. most_active_player.first, most_active_player.second,
  279. scummer.first, scummer.second,
  280. most_achievements.first, most_achievements.second.first,
  281. got_rich_quick.first, got_rich_quick.second.first, cause_g_r_q);
  282. }
  283. };
  284. int main(int argc, char** argv) {
  285. try {
  286. highscore::Scores scores;
  287. auto scores_saved = scores.scores;
  288. std::cout << nlp::message("{\"num_games\": %d,\n", scores.scores.size());
  289. std::cout << "\"highscores\": {";
  290. scores.by_ts();
  291. std::cout << "\"victors\": [";
  292. scores.process(_process, 1, 5);
  293. std::cout << "]," << std::endl;
  294. std::cout << "\"ts\": [";
  295. scores.process(_process, 0);
  296. std::cout << "]," << std::endl;
  297. scores.by_plev();
  298. std::cout << "\"plev\": [";
  299. scores.process(_process, 0);
  300. std::cout << "]," << std::endl;
  301. scores.by_dlev_d();
  302. std::cout << "\"dlev_d\": [";
  303. scores.process(_process, 0);
  304. std::cout << "]," << std::endl;
  305. scores.by_dlev_a();
  306. std::cout << "\"dlev_a\": [";
  307. scores.process(_process, 0);
  308. std::cout << "]," << std::endl;
  309. scores.by_worth();
  310. std::cout << "\"worth\": [";
  311. scores.process(_process, 0);
  312. std::cout << "]," << std::endl;
  313. scores.by_rank();
  314. std::cout << "\"rank\": [";
  315. scores.process(_process, 0, 10, true);
  316. std::cout << "]" << std::endl;
  317. std::cout << "}," << std::endl;
  318. other_stats_t other;
  319. other(scores_saved);
  320. std::cout << "\"stats\": ";
  321. other.print();
  322. bank_stats_t money;
  323. money.load();
  324. std::cout << ",\"money\": ";
  325. money.print();
  326. std::cout << "}" << std::endl;
  327. } catch (std::exception& e) {
  328. std::cerr << "Fatal error: " << e.what() << std::endl;
  329. return 1;
  330. } catch (...) {
  331. std::cerr << "Unknown fatal error." << std::endl;
  332. return 1;
  333. }
  334. return 0;
  335. }