1 /**
2  * @file
3  * @brief Objstat: monster and item generation statistics
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "dbg-objstat.h"
9 
10 #include <cerrno>
11 #include <cmath>
12 #include <sstream>
13 
14 #include "artefact.h"
15 #include "branch.h"
16 #include "corpse.h"
17 #include "dbg-maps.h"
18 #include "dbg-util.h"
19 #include "dungeon.h"
20 #include "end.h"
21 #include "env.h"
22 #include "feature.h"
23 #include "initfile.h"
24 #include "invent.h"
25 #include "item-name.h"
26 #include "item-prop.h"
27 #include "item-prop-enum.h"
28 #include "item-status-flag-type.h"
29 #include "items.h"
30 #include "libutil.h"
31 #include "maps.h"
32 #include "message.h"
33 #include "mon-util.h"
34 #include "ng-init.h"
35 #include "shopping.h"
36 #include "spl-book.h"
37 #include "state.h"
38 #include "stepdown.h"
39 #include "stringutil.h"
40 #include "tag-version.h"
41 #include "version.h"
42 
43 #ifdef DEBUG_STATISTICS
44 
45 #define STAT_PRECISION 2
46 
47 const static char *stat_out_prefix = "objstat_";
48 const static char *stat_out_ext = ".txt";
49 static FILE *stat_outf;
50 
51 enum item_base_type
52 {
53     ITEM_GOLD,
54     ITEM_SCROLLS,
55     ITEM_POTIONS,
56     ITEM_WANDS,
57     ITEM_WEAPONS,
58     ITEM_MISSILES,
59     ITEM_STAVES,
60     ITEM_ARMOUR,
61     ITEM_JEWELLERY,
62     ITEM_MISCELLANY,
63     ITEM_SPELLBOOKS,
64     ITEM_MANUALS,
65     NUM_ITEM_BASE_TYPES,
66     ITEM_IGNORE = 100,
67 };
68 
69 // Holds item data needed for item statistics and maps values from item_def
70 // where changes are needed.
71 class objstat_item
72 {
73 public:
objstat_item(item_base_type ct,int st)74     objstat_item(item_base_type ct, int st) : base_type(ct), sub_type(st)
75     {
76     }
77 
78     objstat_item(const item_def &item);
79 
80     item_base_type base_type;
81     int sub_type;
82     int quantity;
83     int plus;
84     int brand;
85     vector<spell_type> spells;
86 
87     bool in_vault;
88     bool is_arte;
89     bool in_shop;
90     bool held_mons;
91 };
92 
93 static level_id all_lev(NUM_BRANCHES, -1);
94 static set<level_id> stat_levels;
95 static int num_branches = 0;
96 static int num_levels = 0;
97 
98 // Ex: item_recs[level][item.base_type][item.sub_type][field]
99 static map<level_id, map<item_base_type, map<int, map<string, int> > > >
100        item_recs;
101 static map<item_base_type, vector<string> > item_fields = {
102     { ITEM_GOLD,
103         { "Num", "NumVault", "NumMons", "NumMin", "NumMax", "NumSD" }
104     },
105     { ITEM_SCROLLS,
106         { "Num", "NumVault", "NumShop", "NumMons", "NumMin", "NumMax", "NumSD" }
107     },
108     { ITEM_POTIONS,
109         { "Num", "NumVault", "NumShop", "NumMons", "NumMin", "NumMax", "NumSD" }
110     },
111     { ITEM_WANDS,
112         { "Num", "NumVault", "NumShop", "NumMons", "NumMin", "NumMax", "NumSD",
113             "Chrg", "ChrgVault", "ChrgShop", "ChrgMons" },
114     },
115     { ITEM_WEAPONS,
116         { "Num", "NumBrand", "NumArte", "NumVault", "NumShop", "NumMons",
117             "NumMin", "NumMax", "NumSD", "Ench", "EnchBrand", "EnchArte",
118             "EnchVault", "EnchShop", "EnchMons" },
119     },
120     { ITEM_MISSILES,
121         { "Num", "NumBrand", "NumVault", "NumShop", "NumMons", "Num",
122             "NumMin", "NumMax", "NumSD" },
123     },
124     { ITEM_STAVES,
125         { "Num", "NumVault", "NumShop", "NumMons", "Num", "NumMin", "NumMax",
126             "NumSD" },
127     },
128     { ITEM_ARMOUR,
129         { "Num", "NumBrand", "NumArte", "NumVault", "NumShop", "NumMons",
130             "NumMin", "NumMax", "NumSD", "Ench", "EnchBrand", "EnchArte",
131             "EnchVault", "EnchShop", "EnchMons" }
132     },
133     { ITEM_JEWELLERY,
134         { "Num", "NumArte", "NumVault", "NumShop", "NumMons", "NumMin",
135             "NumMax", "NumSD" },
136     },
137     { ITEM_MISCELLANY,
138         { "Num", "NumVault", "NumShop", "NumMin", "NumMax", "NumSD" },
139     },
140     { ITEM_SPELLBOOKS,
141         { "Num", "NumVault", "NumShop", "NumMin", "NumMax", "NumSD" },
142     },
143     { ITEM_MANUALS,
144         { "Num", "NumVault", "NumShop", "NumMin", "NumMax", "NumSD" },
145     },
146 };
147 
148 enum stat_category_type
149 {
150     CATEGORY_ALL,      // regardless of below categories
151     CATEGORY_ARTEFACT, // is artefact
152     CATEGORY_VAULT,    // in a vault
153     CATEGORY_SHOP,     // in a shop
154     CATEGORY_MONSTER,  // held by a monster
155     NUM_STAT_CATEGORIES
156 };
157 
158 // Ex: brand_recs[level][item.base_type][item.sub_type][CATEGORY_ALL][brand];
159 static map<level_id, map<item_base_type, map<int, map<stat_category_type,
160             map<int, int> > > > > brand_recs;
161 static map<item_base_type, vector<string> > brand_fields = {
162     {ITEM_WEAPONS,
163         { "Brands", "BrandsArte", "BrandsVault", "BrandsShop", "BrandsMons" } },
164     {ITEM_ARMOUR,
165         {"Brands", "BrandsArte", "BrandsVault", "BrandsShop", "BrandsMons" } },
166     {ITEM_MISSILES, { "Brands", "BrandsVault", "BrandsShop", "BrandsMons" } },
167 };
168 
169 static set<monster_type> objstat_monsters;
170 // Ex: monster_recs[level][mc]["Num"]
171 static map<level_id, map<monster_type, map <string, int> > > monster_recs;
172 static const vector<string> monster_fields = {
173     "Num", "NumVault", "NumMin", "NumMax", "NumSD", "MonsHD", "MonsHP",
174     "MonsXP", "TotalXP", "TotalXPVault"
175 };
176 
177 static set<dungeon_feature_type> objstat_features;
178 typedef map<dungeon_feature_type, map <string, int> > feature_stats;
179 static map<level_id, feature_stats> feature_recs;
180 static const vector<string> feature_fields = {
181     "Num", "NumVault", "NumMin", "NumMax", "NumSD",
182 };
183 
184 static set<spell_type> objstat_spells;
185 // Ex: spell_recs[level][spell]["Num"]
186 static map<level_id, map<spell_type, map<string, int> > > spell_recs;
187 static const vector<string> spell_fields = {
188     "Num", "NumVault", "NumShop", "NumArte", "NumMin", "NumMax", "NumSD",
189     "Chance", "ChanceVault", "ChanceShop", "ChanceArte"
190 };
191 
_item_base_type(const item_def & item)192 static item_base_type _item_base_type(const item_def &item)
193 {
194     item_base_type type;
195     switch (item.base_type)
196     {
197     case OBJ_MISCELLANY:
198         type = ITEM_MISCELLANY;
199         break;
200     case OBJ_BOOKS:
201         if (item.sub_type == BOOK_MANUAL)
202             type = ITEM_MANUALS;
203         else
204             type = ITEM_SPELLBOOKS;
205         break;
206     case OBJ_GOLD:
207         type = ITEM_GOLD;
208         break;
209     case OBJ_SCROLLS:
210         type = ITEM_SCROLLS;
211         break;
212     case OBJ_POTIONS:
213         type = ITEM_POTIONS;
214         break;
215     case OBJ_WANDS:
216         type = ITEM_WANDS;
217         break;
218     case OBJ_WEAPONS:
219         type = ITEM_WEAPONS;
220         break;
221     case OBJ_MISSILES:
222         type = ITEM_MISSILES;
223         break;
224     case OBJ_STAVES:
225         type = ITEM_STAVES;
226         break;
227     case OBJ_ARMOUR:
228         type = ITEM_ARMOUR;
229         break;
230     case OBJ_JEWELLERY:
231         type = ITEM_JEWELLERY;
232         break;
233     default:
234         type = ITEM_IGNORE;
235         break;
236     }
237     return type;
238 }
239 
_item_orig_base_type(item_base_type base_type)240 static object_class_type _item_orig_base_type(item_base_type base_type)
241 {
242     object_class_type type;
243     switch (base_type)
244     {
245     case ITEM_GOLD:
246         type = OBJ_GOLD;
247         break;
248     case ITEM_SCROLLS:
249         type = OBJ_SCROLLS;
250         break;
251     case ITEM_POTIONS:
252         type = OBJ_POTIONS;
253         break;
254     case ITEM_WANDS:
255         type = OBJ_WANDS;
256         break;
257     case ITEM_WEAPONS:
258         type = OBJ_WEAPONS;
259         break;
260     case ITEM_MISSILES:
261         type = OBJ_MISSILES;
262         break;
263     case ITEM_STAVES:
264         type = OBJ_STAVES;
265         break;
266     case ITEM_ARMOUR:
267         type = OBJ_ARMOUR;
268         break;
269     case ITEM_JEWELLERY:
270         type = OBJ_JEWELLERY;
271         break;
272     case ITEM_MISCELLANY:
273         type = OBJ_MISCELLANY;
274         break;
275     case ITEM_MANUALS:
276     case ITEM_SPELLBOOKS:
277         type = OBJ_BOOKS;
278         break;
279     default:
280         type = OBJ_UNASSIGNED;
281         break;
282     }
283     return type;
284 }
285 
_item_class_name(item_base_type base_type)286 static string _item_class_name(item_base_type base_type)
287 {
288     string name;
289     switch (base_type)
290     {
291     case ITEM_WEAPONS:
292         name = "Weapons";
293         break;
294     case ITEM_SPELLBOOKS:
295         name = "Spellbooks";
296         break;
297     case ITEM_MANUALS:
298         name = "Manuals";
299         break;
300     default:
301         name = item_class_name(_item_orig_base_type(base_type));
302     }
303     return name;
304 }
305 
_item_orig_sub_type(item_base_type base_type,int sub_type)306 static int _item_orig_sub_type(item_base_type base_type, int sub_type)
307 {
308     int type;
309     switch (base_type)
310     {
311     case ITEM_MANUALS:
312         type = BOOK_MANUAL;
313         break;
314     default:
315         type = sub_type;
316         break;
317     }
318     return type;
319 }
320 
_item_max_sub_type(item_base_type base_type)321 static int _item_max_sub_type(item_base_type base_type)
322 {
323     int num = 0;
324     switch (base_type)
325     {
326     case ITEM_MANUALS:
327         num = NUM_SKILLS;
328         break;
329     default:
330         num = get_max_subtype(_item_orig_base_type(base_type));
331         break;
332     }
333     return num;
334 }
335 
_item_tracks_artefact(item_base_type base_type)336 static bool _item_tracks_artefact(item_base_type base_type)
337 {
338     switch (base_type)
339     {
340     case ITEM_WEAPONS:
341     case ITEM_ARMOUR:
342     case ITEM_JEWELLERY:
343     case ITEM_SPELLBOOKS:
344         return true;
345     default:
346         return false;
347     }
348 }
349 
_item_tracks_plus(item_base_type base_type)350 static bool _item_tracks_plus(item_base_type base_type)
351 {
352     switch (base_type)
353     {
354     case ITEM_WEAPONS:
355     case ITEM_ARMOUR:
356     case ITEM_JEWELLERY:
357     case ITEM_WANDS:
358     case ITEM_MISCELLANY:
359         return true;
360     default:
361         return false;
362     }
363 }
364 
_item_tracks_brand(item_base_type base_type)365 static bool _item_tracks_brand(item_base_type base_type)
366 {
367     switch (base_type)
368     {
369     case ITEM_WEAPONS:
370     case ITEM_ARMOUR:
371     case ITEM_MISSILES:
372         return true;
373     default:
374         return false;
375     }
376 }
377 
_item_tracks_monster(item_base_type base_type)378 static bool _item_tracks_monster(item_base_type base_type)
379 {
380     switch (base_type)
381     {
382     case ITEM_MISCELLANY:
383     case ITEM_SPELLBOOKS:
384     case ITEM_MANUALS:
385         return false;
386     default:
387         return true;
388     }
389 }
390 
_item_tracks_shop(item_base_type base_type)391 static bool _item_tracks_shop(item_base_type base_type)
392 {
393     switch (base_type)
394     {
395     case ITEM_GOLD:
396         return false;
397     default:
398         return true;
399     }
400 }
401 
_dummy_item(item_base_type base_type,int sub_type,int brand=0)402 static item_def _dummy_item(item_base_type base_type, int sub_type,
403         int brand = 0)
404 {
405     item_def dummy_item;
406 
407     dummy_item.base_type = _item_orig_base_type(base_type);
408     if (sub_type == _item_max_sub_type(base_type))
409         dummy_item.sub_type = 0;
410     else
411         dummy_item.sub_type = _item_orig_sub_type(base_type, sub_type);
412 
413     dummy_item.brand = brand;
414 
415     if (base_type == ITEM_MANUALS)
416         dummy_item.skill = static_cast<skill_type>(sub_type);
417 
418     dummy_item.quantity = 1;
419 
420     return dummy_item;
421 }
422 
_brand_name(item_base_type base_type,int sub_type,int brand)423 static string _brand_name(item_base_type base_type, int sub_type, int brand)
424 {
425     if (!brand)
426         return "none";
427 
428     string brand_name = "";
429     const item_def dummy_item = _dummy_item(base_type, sub_type, brand);
430     switch (base_type)
431     {
432     case ITEM_WEAPONS:
433         brand_name = weapon_brand_name(dummy_item, true);
434         break;
435     case ITEM_ARMOUR:
436         brand_name = armour_ego_name(dummy_item, true);
437         break;
438     case ITEM_MISSILES:
439         brand_name = missile_brand_name(dummy_item, MBN_TERSE);
440         break;
441     default:
442         break;
443     }
444     return brand_name;
445 }
446 
_item_name(item_base_type base_type,int sub_type)447 static string _item_name(item_base_type base_type, int sub_type)
448 {
449     string name = "";
450     description_level_type desc_type = DESC_DBNAME;
451 
452     // These types need special handling, otherwise we use the original
453     // item_def name.
454     if (sub_type == _item_max_sub_type(base_type))
455         name = "All " + _item_class_name(base_type);
456     else if (base_type == ITEM_SPELLBOOKS)
457     {
458         int orig_type = _item_orig_sub_type(base_type, sub_type);
459         if (orig_type == BOOK_RANDART_LEVEL)
460             name = "Level Artefact Book";
461         else if (orig_type == BOOK_RANDART_LEVEL)
462             name = "Theme Artefact Book";
463     }
464     else if (base_type == ITEM_MANUALS)
465         desc_type = DESC_QUALNAME;
466 
467     if (name.empty())
468     {
469         const item_def item = _dummy_item(base_type, sub_type);
470         name = item.name(desc_type, true, true);
471     }
472     return name;
473 }
474 
_level_name(const level_id & lev)475 static string _level_name(const level_id &lev)
476 {
477     string name;
478     if (lev.branch == NUM_BRANCHES)
479         name = "AllLevels";
480     else if (lev.depth == -1 || brdepth[lev.branch] == 1)
481         name = lev.describe(false, false);
482     else if (brdepth[lev.branch] < 10)
483         name = lev.describe(false, true);
484     else
485     {
486         name = make_stringf("%s:%02d", lev.describe(false, false).c_str(),
487                             lev.depth);
488     }
489     return name;
490 }
491 
objstat_item(const item_def & item)492 objstat_item::objstat_item(const item_def &item)
493 {
494     base_type = _item_base_type(item);
495 
496     if (base_type == ITEM_MANUALS)
497         sub_type = item.skill;
498     else
499         sub_type = item.sub_type;
500 
501     quantity = item.quantity;
502     plus = item.plus;
503 
504     is_arte = is_artefact(item);
505 
506     // The item's position won't be valid for these two first cases, as it's
507     // set to a special indicator value.
508     if (item.holding_monster())
509     {
510         in_vault = map_masked(item.holding_monster()->pos(), MMT_VAULT);
511         held_mons = true;
512         in_shop = false;
513     }
514     else if (is_shop_item(item))
515     {
516         // XXX I don't think shops can place outside of vaults, but it would be
517         // nice to find the item's true location, so we wouldn't have to
518         // assume. -gammafunk
519         in_vault = true;
520         held_mons = false;
521         in_shop = true;
522     }
523     else
524     {
525         in_vault = map_masked(item.pos, MMT_VAULT);
526         held_mons = false;
527         in_shop = false;
528     }
529 
530     brand = item.brand;
531     if (base_type == ITEM_MISSILES)
532         brand = get_ammo_brand(item);
533     else if (base_type == ITEM_WEAPONS)
534         brand = get_weapon_brand(item);
535     else if (base_type == ITEM_ARMOUR)
536         brand = get_armour_ego_type(item);
537 
538     if (base_type == ITEM_SPELLBOOKS)
539         spells = spells_in_book(item);
540 }
541 
_init_monsters()542 static void _init_monsters()
543 {
544     for (int i = 0; i < NUM_MONSTERS; i++)
545     {
546         const monster_type mc = static_cast<monster_type>(i);
547 
548         if (mons_class_gives_xp(mc) && !mons_class_flag(mc, M_CANT_SPAWN))
549             objstat_monsters.insert(mc);
550     }
551     // For the all-monsters summary
552     objstat_monsters.insert(NUM_MONSTERS);
553 }
554 
_init_features()555 static void _init_features()
556 {
557     for (int i = 0; i < NUM_FEATURES; i++)
558     {
559         const dungeon_feature_type feat = static_cast<dungeon_feature_type>(i);
560 
561         if (is_valid_feature_type(feat))
562             objstat_features.insert(feat);
563     }
564 }
565 
_init_spells()566 static void _init_spells()
567 {
568     for (int i = 0; i < NUM_SPELLS; i++)
569     {
570         const auto spell = static_cast<spell_type>(i);
571 
572         if (is_valid_spell(spell) && is_player_book_spell(spell))
573             objstat_spells.insert(spell);
574     }
575     // For the all-spells summary
576     objstat_spells.insert(NUM_SPELLS);
577 }
578 
579 // Initialize field data that needs non-default intialization (i.e. to
580 // something other than zero). Handling this in one pass creates a lot of
581 // potentially unused entries, but it's better than trying to guard against
582 // default initialization everywhere. For the rest of the fields, it's fine to
583 // default initialize to zero whenever they're first referenced.
_init_stats()584 static void _init_stats()
585 {
586     for (const auto &level : stat_levels)
587     {
588         for (int i = 0; i < NUM_ITEM_BASE_TYPES; i++)
589         {
590             const item_base_type base_type = static_cast<item_base_type>(i);
591 
592             int num_entries = _item_max_sub_type(base_type);
593             // An additional entry for the across-subtype summary if there's
594             // more than one subtype.
595             num_entries += num_entries > 1;
596             for (int  j = 0; j < num_entries; j++)
597             {
598                 item_recs[level][base_type][j]["NumMin"] = INT_MAX;
599                 item_recs[level][base_type][j]["NumMax"] = -1;
600             }
601         }
602 
603         for (auto mc : objstat_monsters)
604         {
605             monster_recs[level][mc]["NumMin"] = INT_MAX;
606             monster_recs[level][mc]["NumMax"] = -1;
607         }
608 
609         for (auto feat : objstat_features)
610         {
611             feature_recs[level][feat]["NumMin"] = INT_MAX;
612             feature_recs[level][feat]["NumMax"] = -1;
613         }
614 
615         for (auto spell : objstat_spells)
616         {
617             spell_recs[level][spell]["NumMin"] = INT_MAX;
618             spell_recs[level][spell]["NumMax"] = -1;
619         }
620     }
621 }
622 
_record_item_stat(const objstat_item & item,string field,int value)623 static void _record_item_stat(const objstat_item &item, string field, int value)
624 {
625     const level_id cur_lev = level_id::current();
626 
627     bool need_all = false;
628     if (stat_levels.count(all_lev))
629         need_all = true;
630 
631     const level_id br_lev = level_id(cur_lev.branch, -1);
632     bool need_branch = false;
633     if (stat_levels.count(br_lev))
634         need_branch = true;
635 
636     item_recs[cur_lev][item.base_type][item.sub_type][field] += value;
637 
638     if (need_all)
639         item_recs[all_lev][item.base_type][item.sub_type][field] += value;
640 
641     if (need_branch)
642         item_recs[br_lev][item.base_type][item.sub_type][field] += value;
643 
644     // Record a class summary if more than one subtype exists.
645     const int class_sum = _item_max_sub_type(item.base_type);
646     if (class_sum > 1)
647     {
648         item_recs[cur_lev][item.base_type][class_sum][field] += value;
649 
650         if (need_all)
651             item_recs[all_lev][item.base_type][class_sum][field] += value;
652 
653         if (need_branch)
654             item_recs[br_lev][item.base_type][class_sum][field] += value;
655     }
656 }
657 
_record_item_brand_category(const objstat_item & item,stat_category_type cat)658 static void _record_item_brand_category(const objstat_item &item,
659         stat_category_type cat)
660 {
661     const level_id cur_lev = level_id::current();
662 
663     bool need_all = false;
664     if (stat_levels.count(all_lev))
665         need_all = true;
666 
667     const level_id br_lev = level_id(cur_lev.branch, -1);
668     bool need_branch = false;
669     if (stat_levels.count(br_lev))
670         need_branch = true;
671 
672     brand_recs[cur_lev][item.base_type][item.sub_type][cat][item.brand] +=
673         item.quantity;
674 
675     if (need_all)
676     {
677         brand_recs[all_lev][item.base_type][item.sub_type][cat][item.brand] +=
678             item.quantity;
679     }
680 
681     if (need_branch)
682     {
683         brand_recs[br_lev][item.base_type][item.sub_type][cat][item.brand] +=
684             item.quantity;
685     }
686 
687     // Record a class summary if more than one subtype exists.
688     const int cls = _item_max_sub_type(item.base_type);
689     if (cls > 1)
690     {
691         brand_recs[cur_lev][item.base_type][cls][cat][item.brand] +=
692             item.quantity;
693 
694         if (need_all)
695         {
696             brand_recs[all_lev][item.base_type][cls][cat][item.brand] +=
697                 item.quantity;
698         }
699 
700         if (need_branch)
701         {
702             brand_recs[br_lev][item.base_type][cls][cat][item.brand] +=
703                 item.quantity;
704         }
705     }
706 }
707 
_record_item_brand(const objstat_item & item)708 static void _record_item_brand(const objstat_item &item)
709 {
710     ASSERT(_item_tracks_brand(item.base_type));
711 
712     _record_item_brand_category(item, CATEGORY_ALL);
713 
714     if (item.in_vault)
715         _record_item_brand_category(item, CATEGORY_VAULT);
716 
717     if (item.is_arte)
718         _record_item_brand_category(item, CATEGORY_ARTEFACT);
719 
720     if (item.in_shop)
721         _record_item_brand_category(item, CATEGORY_SHOP);
722 
723     if (item.held_mons)
724         _record_item_brand_category(item, CATEGORY_MONSTER);
725 }
726 
_record_spell_stat(spell_type spell,string field,int value)727 static void _record_spell_stat(spell_type spell, string field, int value)
728 {
729     const level_id cur_lev = level_id::current();
730 
731     bool need_all = false;
732     if (stat_levels.count(all_lev))
733         need_all = true;
734 
735     const level_id br_lev = level_id(cur_lev.branch, -1);
736     bool need_branch = false;
737     if (stat_levels.count(br_lev))
738         need_branch = true;
739 
740     spell_recs[cur_lev][spell][field] += value;
741     spell_recs[cur_lev][NUM_SPELLS][field] += value;
742 
743     if (need_all)
744     {
745         spell_recs[all_lev][spell][field] += value;
746         spell_recs[all_lev][NUM_SPELLS][field] += value;
747     }
748 
749     if (need_branch)
750     {
751         spell_recs[br_lev][spell][field] += value;
752         spell_recs[br_lev][NUM_SPELLS][field] += value;
753     }
754 }
755 
_record_book_spells(const objstat_item & item)756 static void _record_book_spells(const objstat_item &item)
757 {
758     for (auto spell : item.spells)
759     {
760         _record_spell_stat(spell, "Num", 1);
761         _record_spell_stat(spell, "NumForIter", 1);
762 
763         if (item.in_vault)
764         {
765             _record_spell_stat(spell, "NumVault", 1);
766             _record_spell_stat(spell, "NumForIterVault", 1);
767         }
768 
769         if (item.is_arte)
770         {
771             _record_spell_stat(spell, "NumArte", 1);
772             _record_spell_stat(spell, "NumForIterArte", 1);
773         }
774 
775         if (item.in_shop)
776         {
777             _record_spell_stat(spell, "NumShop", 1);
778             _record_spell_stat(spell, "NumForIterShop", 1);
779         }
780     }
781 }
782 
objstat_record_item(const item_def & item)783 void objstat_record_item(const item_def &item)
784 {
785     const objstat_item objs_item(item);
786 
787     // Just in case, don't count mimics as items; these are converted
788     // explicitely in mg_do_build_level().
789     if (item.flags & ISFLAG_MIMIC || objs_item.base_type == ITEM_IGNORE)
790         return;
791 
792     const bool track_plus = _item_tracks_plus(objs_item.base_type);
793     string plus_field = "Ench";
794     if (objs_item.base_type == ITEM_WANDS)
795         plus_field = "Chrg";
796 
797     _record_item_stat(objs_item, "Num", objs_item.quantity);
798     _record_item_stat(objs_item, "NumForIter", objs_item.quantity);
799 
800     if (track_plus)
801         _record_item_stat(objs_item, plus_field, objs_item.plus);
802 
803     if (objs_item.in_vault)
804     {
805         _record_item_stat(objs_item, "NumVault", objs_item.quantity);
806 
807         if (track_plus)
808             _record_item_stat(objs_item, plus_field + "Vault", objs_item.plus);
809     }
810 
811     if (_item_tracks_artefact(objs_item.base_type) && objs_item.is_arte)
812     {
813         _record_item_stat(objs_item, "NumArte", objs_item.quantity);
814 
815         if (track_plus)
816             _record_item_stat(objs_item, plus_field + "Arte", objs_item.plus);
817     }
818 
819     if (_item_tracks_brand(objs_item.base_type) && objs_item.brand > 0)
820     {
821         _record_item_stat(objs_item, "NumBrand", objs_item.quantity);
822 
823         if (track_plus)
824             _record_item_stat(objs_item, plus_field + "Brand", objs_item.plus);
825 
826         _record_item_brand(objs_item);
827     }
828 
829     if (_item_tracks_shop(objs_item.base_type) && objs_item.in_shop)
830     {
831         _record_item_stat(objs_item, "NumShop", objs_item.quantity);
832 
833         if (track_plus)
834             _record_item_stat(objs_item, plus_field + "Shop", objs_item.plus);
835     }
836 
837     if (_item_tracks_monster(objs_item.base_type) && objs_item.held_mons)
838     {
839         _record_item_stat(objs_item, "NumMons", objs_item.quantity);
840 
841         if (track_plus)
842             _record_item_stat(objs_item, plus_field + "Mons", objs_item.plus);
843     }
844 
845     _record_book_spells(objs_item);
846 }
847 
_record_monster_stat(monster_type mc,string field,int value)848 static void _record_monster_stat(monster_type mc, string field, int value)
849 {
850     const level_id cur_lev = level_id::current();
851 
852     bool need_all = false;
853     if (stat_levels.count(all_lev))
854         need_all = true;
855 
856     const level_id br_lev = level_id(cur_lev.branch, -1);
857     bool need_branch = false;
858     if (stat_levels.count(br_lev))
859         need_branch = true;
860 
861     monster_recs[cur_lev][mc][field] += value;
862     monster_recs[cur_lev][NUM_MONSTERS][field] += value;
863 
864     if (need_all)
865     {
866         monster_recs[all_lev][mc][field] += value;
867         monster_recs[all_lev][NUM_MONSTERS][field] += value;
868     }
869 
870     if (need_branch)
871     {
872         monster_recs[br_lev][mc][field] += value;
873         monster_recs[br_lev][NUM_MONSTERS][field] += value;
874     }
875 }
876 
objstat_record_monster(const monster * mons)877 void objstat_record_monster(const monster *mons)
878 {
879     monster_type type;
880     if (mons->has_ench(ENCH_GLOWING_SHAPESHIFTER))
881         type = MONS_GLOWING_SHAPESHIFTER;
882     else if (mons->has_ench(ENCH_SHAPESHIFTER))
883         type = MONS_SHAPESHIFTER;
884     else
885         type = mons->type;
886 
887     if (!objstat_monsters.count(type))
888         return;
889 
890     _record_monster_stat(type, "Num", 1);
891     _record_monster_stat(type, "NumForIter", 1);
892     _record_monster_stat(type, "MonsXP", exper_value(*mons));
893     _record_monster_stat(type, "TotalXP", exper_value(*mons));
894     _record_monster_stat(type, "MonsHP", mons->max_hit_points);
895     _record_monster_stat(type, "MonsHD", mons->get_experience_level());
896 
897     if (!mons->originating_map().empty())
898     {
899         _record_monster_stat(type, "NumVault", 1);
900         _record_monster_stat(type, "TotalXPVault", exper_value(*mons));
901     }
902 }
903 
_record_feature_stat(dungeon_feature_type feat_type,string field,int value)904 static void _record_feature_stat(dungeon_feature_type feat_type, string field,
905         int value)
906 {
907     const level_id cur_lev = level_id::current();
908 
909     bool need_all = false;
910     if (stat_levels.count(all_lev))
911         need_all = true;
912 
913     const level_id br_lev = level_id(cur_lev.branch, -1);
914     bool need_branch = false;
915     if (stat_levels.count(br_lev))
916         need_branch = true;
917 
918     feature_recs[cur_lev][feat_type][field] += value;
919 
920     if (need_all)
921         feature_recs[all_lev][feat_type][field] += value;
922 
923     if (need_branch)
924         feature_recs[br_lev][feat_type][field] += value;
925 }
926 
objstat_record_feature(dungeon_feature_type feat,bool in_vault)927 void objstat_record_feature(dungeon_feature_type feat, bool in_vault)
928 {
929     if (!objstat_features.count(feat))
930         return;
931 
932     _record_feature_stat(feat, "Num", 1);
933     _record_feature_stat(feat, "NumForIter", 1);
934 
935     if (in_vault)
936         _record_feature_stat(feat, "NumVault", 1);
937 }
938 
objstat_iteration_stats()939 void objstat_iteration_stats()
940 {
941     for (const auto level : stat_levels)
942     {
943         for (int i = 0; i < NUM_ITEM_BASE_TYPES; i++)
944         {
945             item_base_type base_type = static_cast<item_base_type>(i);
946 
947             int num_entries = _item_max_sub_type(base_type);
948             num_entries = num_entries == 1 ? 1 : num_entries + 1;
949             for (int  j = 0; j < num_entries ; j++)
950             {
951                 map<string, int> &stats = item_recs[level][base_type][j];
952 
953                 if (stats["NumForIter"] > stats["NumMax"])
954                     stats["NumMax"] = stats["NumForIter"];
955 
956                 if (stats["NumForIter"] < stats["NumMin"])
957                     stats["NumMin"] = stats["NumForIter"];
958 
959                 stats["NumSD"] += stats["NumForIter"] * stats["NumForIter"];
960 
961                 stats["NumForIter"] = 0;
962             }
963         }
964 
965         for (auto spell : objstat_spells)
966         {
967             map<string, int> &stats = spell_recs[level][spell];
968 
969             if (stats["NumForIter"] > 0)
970                 stats["Chance"] += 1;
971 
972             if (stats["NumForIter"] > stats["NumMax"])
973                 stats["NumMax"] = stats["NumForIter"];
974 
975             if (stats["NumForIter"] < stats["NumMin"])
976                 stats["NumMin"] = stats["NumForIter"];
977 
978             stats["NumSD"] += stats["NumForIter"] * stats["NumForIter"];
979 
980             stats["NumForIter"] = 0;
981 
982             if (stats["NumForIterVault"])
983                 stats["ChanceVault"] += 1;
984             stats["NumForIterVault"] = 0;
985 
986             if (stats["NumForIterArte"])
987                 stats["ChanceArte"] += 1;
988             stats["NumForIterArte"] = 0;
989 
990             if (stats["NumForIterShop"])
991                 stats["ChanceShop"] += 1;
992             stats["NumForIterShop"] = 0;
993         }
994 
995         for (auto mc : objstat_monsters)
996         {
997             map<string, int> &stats = monster_recs[level][mc];
998 
999             if (stats["NumForIter"] > stats["NumMax"])
1000                 stats["NumMax"] = stats["NumForIter"];
1001 
1002             if (stats["NumForIter"] < stats["NumMin"])
1003                 stats["NumMin"] = stats["NumForIter"];
1004 
1005             stats["NumSD"] += stats["NumForIter"] * stats["NumForIter"];
1006 
1007             stats["NumForIter"] = 0;
1008         }
1009 
1010         for (auto feat : objstat_features)
1011         {
1012             map<string, int> &stats = feature_recs[level][feat];
1013 
1014             if (stats["NumForIter"] > stats["NumMax"])
1015                 stats["NumMax"] = stats["NumForIter"];
1016 
1017             if (stats["NumForIter"] < stats["NumMin"])
1018                 stats["NumMin"] = stats["NumForIter"];
1019 
1020             stats["NumSD"] += stats["NumForIter"] * stats["NumForIter"];
1021 
1022             stats["NumForIter"] = 0;
1023         }
1024     }
1025 }
1026 
_open_stat_file(string stat_file)1027 static FILE * _open_stat_file(string stat_file)
1028 {
1029     FILE *stat_fh = nullptr;
1030     stat_fh = fopen(stat_file.c_str(), "w");
1031 
1032     if (!stat_fh)
1033     {
1034         end(1, false, "Unable to open objstat output file: %s\n"
1035                 "Error: %s", stat_file.c_str(), strerror(errno));
1036     }
1037 
1038     return stat_fh;
1039 }
1040 
_write_stat_info()1041 static void _write_stat_info()
1042 {
1043     string all_desc;
1044     if (num_branches > 1)
1045     {
1046         if (SysEnv.map_gen_range)
1047             all_desc = SysEnv.map_gen_range->describe();
1048         else
1049             all_desc = "All Levels";
1050         all_desc = "Levels included in AllLevels: " + all_desc + "\n";
1051     }
1052 
1053     const string out_file = make_stringf("%s%s%s", stat_out_prefix,
1054                                          "Info", stat_out_ext);
1055     stat_outf = _open_stat_file(out_file.c_str());
1056 
1057     fprintf(stat_outf, "Object Generation Stats\n"
1058             "Number of iterations: %d\n"
1059             "Number of branches: %d\n"
1060             "%s"
1061             "Number of levels: %d\n"
1062             "Version: %s\n", SysEnv.map_gen_iters, num_branches,
1063             all_desc.c_str(), num_levels, Version::Long);
1064 
1065     fclose(stat_outf);
1066     printf("Wrote Objstat Info to %s.\n", out_file.c_str());
1067 }
1068 
_write_stat_headers(const vector<string> & fields,const string & desc)1069 static void _write_stat_headers(const vector<string> &fields, const string &desc)
1070 {
1071     fprintf(stat_outf, "%s\tLevel", desc.c_str());
1072 
1073     for (const string &field : fields)
1074         fprintf(stat_outf, "\t%s", field.c_str());
1075 
1076     fprintf(stat_outf, "\n");
1077 }
1078 
_write_stat(map<string,int> & stats,const string & field)1079 static void _write_stat(map<string, int> &stats, const string &field)
1080 {
1081     const double field_val = stats[field];
1082     double out_val = 0;
1083     ostringstream output;
1084     bool is_chance = false;
1085 
1086     output.precision(STAT_PRECISION);
1087     output.setf(ios_base::fixed);
1088 
1089     // These fields want a per-instance average.
1090     if (starts_with(field, "Ench")
1091         || starts_with(field, "Chrg")
1092         || field == "MonsHD"
1093         || field == "MonsHP"
1094         || field == "MonsXP")
1095     {
1096         out_val = field_val / stats["Num"];
1097     }
1098     // Turn the sum of squares into the standard deviation.
1099     else if (field == "NumSD")
1100     {
1101         if (SysEnv.map_gen_iters == 1)
1102             out_val = 0;
1103         else
1104         {
1105             const double mean = (double) stats["Num"] / SysEnv.map_gen_iters;
1106             out_val = sqrt((SysEnv.map_gen_iters / (SysEnv.map_gen_iters - 1.0))
1107                          * (field_val / SysEnv.map_gen_iters - mean * mean));
1108         }
1109     }
1110     else if (ends_with(field, "Min") || ends_with(field, "Max"))
1111         out_val = field_val;
1112     // Turn a probability into a chance percentage.
1113     else if (field.find("Chance") == 0)
1114     {
1115         out_val = 100 * field_val / SysEnv.map_gen_iters;
1116         is_chance = true;
1117     }
1118     else
1119         out_val = field_val / SysEnv.map_gen_iters;
1120 
1121     output << "\t" << out_val;
1122 
1123     if (is_chance)
1124         output << "%";
1125 
1126     fprintf(stat_outf, "%s", output.str().c_str());
1127 }
1128 
_write_level_item_brand_stats(const level_id & level,item_base_type base_type,int sub_type)1129 static void _write_level_item_brand_stats(const level_id &level,
1130         item_base_type base_type, int sub_type)
1131 {
1132     for (int i = 0; i < NUM_STAT_CATEGORIES; i++)
1133     {
1134         bool first_brand = true;
1135 
1136         ostringstream brand_summary;
1137         brand_summary.setf(ios_base::fixed);
1138         brand_summary.precision(STAT_PRECISION);
1139 
1140         const auto cat = static_cast<stat_category_type>(i);
1141 
1142         for (auto mentry : brand_recs[level][base_type][sub_type][cat])
1143         {
1144             // No instances for this brand.
1145             if (mentry.second == 0)
1146                 continue;
1147 
1148             const double value = (double) mentry.second / SysEnv.map_gen_iters;
1149 
1150             if (first_brand)
1151                 first_brand = false;
1152             else
1153                 brand_summary << ";";
1154 
1155             string brand_name = _brand_name(base_type, sub_type, mentry.first);
1156             brand_summary << brand_name.c_str() << ":" << value;
1157         }
1158 
1159         fprintf(stat_outf, "\t%s", brand_summary.str().c_str());
1160     }
1161 }
1162 
_write_level_item_stats(const level_id & level,item_base_type base_type,int sub_type)1163 static void _write_level_item_stats(const level_id &level,
1164         item_base_type base_type, int sub_type)
1165 {
1166     // Don't print stats if no instances.
1167     if (item_recs[level][base_type][sub_type]["Num"] < 1)
1168         return;
1169 
1170     fprintf(stat_outf, "%s\t%s", _item_name(base_type, sub_type).c_str(),
1171             _level_name(level).c_str());
1172 
1173     for (const auto &field : item_fields[base_type])
1174         _write_stat(item_recs[level][base_type][sub_type], field);
1175 
1176     if (_item_tracks_brand(base_type))
1177         _write_level_item_brand_stats(level, base_type, sub_type);
1178 
1179     fprintf(stat_outf, "\n");
1180 }
1181 
_write_base_item_stats(item_base_type base_type)1182 static void _write_base_item_stats(item_base_type base_type)
1183 {
1184     const string out_file = make_stringf("%s%s%s", stat_out_prefix,
1185             strip_filename_unsafe_chars(_item_class_name(base_type)).c_str(),
1186             stat_out_ext);
1187     stat_outf = _open_stat_file(out_file.c_str());
1188 
1189     vector<string> fields = item_fields[base_type];
1190 
1191     if (_item_tracks_brand(base_type))
1192     {
1193         fields.insert(fields.end(), brand_fields[base_type].begin(),
1194                 brand_fields[base_type].end());
1195     }
1196 
1197     _write_stat_headers(fields, "Item");
1198 
1199     // If there is more than one subtype, we have an additional entry for
1200     // the sum across subtypes.
1201     const int num_types = _item_max_sub_type(base_type);
1202     int num_entries = num_types == 1 ? 1 : num_types + 1;
1203     for (int j = 0; j < num_entries; j++)
1204         for (const auto &level : stat_levels)
1205             _write_level_item_stats(level, base_type, j);
1206 
1207     fclose(stat_outf);
1208     printf("Wrote %s item stats to %s.\n", _item_class_name(base_type).c_str(),
1209            out_file.c_str());
1210 }
1211 
_write_monster_stats(monster_type mc)1212 static void _write_monster_stats(monster_type mc)
1213 {
1214     string mons_name;
1215     if (mc == NUM_MONSTERS)
1216         mons_name = "All Monsters";
1217     else
1218         mons_name = mons_type_name(mc, DESC_PLAIN);
1219 
1220     for (const level_id &level: stat_levels)
1221     {
1222         if (monster_recs[level][mc]["Num"] < 1)
1223             continue;
1224 
1225         fprintf(stat_outf, "%s\t%s", mons_name.c_str(),
1226                 _level_name(level).c_str());
1227 
1228         for (const string &field : monster_fields)
1229             _write_stat(monster_recs[level][mc], field);
1230 
1231         fprintf(stat_outf, "\n");
1232     }
1233 }
1234 
_write_feature_stats(dungeon_feature_type feat_type)1235 static void _write_feature_stats(dungeon_feature_type feat_type)
1236 {
1237     const char *feat_name = get_feature_def(feat_type).name;
1238 
1239     for (const level_id &level : stat_levels)
1240     {
1241         if (feature_recs[level][feat_type]["Num"] < 1)
1242             continue;
1243 
1244         fprintf(stat_outf, "%s\t%s", feat_name, _level_name(level).c_str());
1245 
1246         for (const string &field : feature_fields)
1247             _write_stat(feature_recs[level][feat_type], field);
1248 
1249         fprintf(stat_outf, "\n");
1250     }
1251 }
1252 
_write_spell_stats(spell_type spell)1253 static void _write_spell_stats(spell_type spell)
1254 {
1255     string spell_name;
1256     if (spell == NUM_SPELLS)
1257         spell_name = "All Spells";
1258     else
1259         spell_name = spell_title(spell);
1260 
1261     for (const level_id &level : stat_levels)
1262     {
1263         if (spell_recs[level][spell]["Num"] < 1)
1264             continue;
1265 
1266         fprintf(stat_outf, "%s\t%s", spell_name.c_str(),
1267                 _level_name(level).c_str());
1268 
1269         for (const string &field : spell_fields)
1270             _write_stat(spell_recs[level][spell], field);
1271 
1272         fprintf(stat_outf, "\n");
1273     }
1274 }
1275 
_write_object_stats()1276 static void _write_object_stats()
1277 {
1278     _write_stat_info();
1279 
1280     for (int i = 0; i < NUM_ITEM_BASE_TYPES; i++)
1281     {
1282         item_base_type base_type = static_cast<item_base_type>(i);
1283         _write_base_item_stats(base_type);
1284     }
1285 
1286     string out_file = make_stringf("%s%s%s", stat_out_prefix, "Spells",
1287                                    stat_out_ext);
1288     stat_outf = _open_stat_file(out_file.c_str());
1289 
1290     _write_stat_headers(spell_fields, "Spell");
1291     for (const auto spell : objstat_spells)
1292         _write_spell_stats(spell);
1293 
1294     fclose(stat_outf);
1295     printf("Wrote Spell stats to %s.\n", out_file.c_str());
1296 
1297     out_file = make_stringf("%s%s%s", stat_out_prefix, "Monsters",
1298                                    stat_out_ext);
1299     stat_outf = _open_stat_file(out_file.c_str());
1300 
1301     _write_stat_headers(monster_fields, "Monster");
1302     for (const auto mc : objstat_monsters)
1303         _write_monster_stats(mc);
1304 
1305     fclose(stat_outf);
1306     printf("Wrote Monster stats to %s.\n", out_file.c_str());
1307 
1308     out_file = make_stringf("%s%s%s", stat_out_prefix, "Features",
1309                             stat_out_ext);
1310     stat_outf = _open_stat_file(out_file.c_str());
1311 
1312     _write_stat_headers(feature_fields, "Feature");
1313     for (auto feat : objstat_features)
1314         _write_feature_stats(feat);
1315 
1316     fclose(stat_outf);
1317     printf("Wrote Feature stats to %s.\n", out_file.c_str());
1318 }
1319 
objstat_generate_stats()1320 void objstat_generate_stats()
1321 {
1322     // Warn assertions about possible oddities like the artefact list being
1323     // cleared.
1324     you.wizard = true;
1325     // Let "acquire foo" have skill aptitudes to work with.
1326     you.species = SP_HUMAN;
1327 
1328     if (!crawl_state.force_map.empty() && !mapstat_find_forced_map())
1329         return;
1330 
1331     initialise_item_descriptions();
1332     initialise_branch_depths();
1333 
1334     // We have to run map preludes ourselves.
1335     run_map_global_preludes();
1336     run_map_local_preludes();
1337 
1338     // Populate a vector of the levels ids for levels we're tabulating.
1339     for (branch_iterator it; it; ++it)
1340     {
1341         if (brdepth[it->id] == -1)
1342             continue;
1343 
1344         const branch_type br = it->id;
1345 #if TAG_MAJOR_VERSION == 34
1346         // Don't want to include Forest since it doesn't generate
1347         if (br == BRANCH_FOREST)
1348             continue;
1349 #endif
1350         vector<level_id> levels;
1351         int branch_level_count = 0;
1352         for (int dep = 1; dep <= brdepth[br]; ++dep)
1353         {
1354             const level_id lid(br, dep);
1355             if (SysEnv.map_gen_range
1356                 && !SysEnv.map_gen_range->is_usable_in(lid))
1357             {
1358                 continue;
1359             }
1360             stat_levels.insert(lid);
1361             ++branch_level_count;
1362             ++num_levels;
1363         }
1364 
1365         if (branch_level_count > 0)
1366         {
1367             ++num_branches;
1368             // Multiple levels for this branch, so we do a branch-wise summary.
1369             if (branch_level_count > 1)
1370                 stat_levels.insert(level_id(br, -1));
1371 
1372         }
1373     }
1374 
1375     // If there's only one branch, an all-levels summary isn't necessary.
1376     if (num_branches > 1)
1377         stat_levels.insert(all_lev);
1378 
1379     printf("Generating object statistics for %d iteration(s) of %d "
1380            "level(s) over %d branch(es).\n", SysEnv.map_gen_iters,
1381            num_levels, num_branches);
1382 
1383     _init_spells();
1384     _init_features();
1385     _init_monsters();
1386 
1387     _init_stats();
1388 
1389     if (mapstat_build_levels())
1390     {
1391         _write_object_stats();
1392         printf("Object statistics complete.\n");
1393     }
1394 }
1395 #endif // DEBUG_STATISTICS
1396