diff options
author | Jude Brown <bookofjude@users.sourceforge.net> | 2011-11-05 18:54:57 +1000 |
---|---|---|
committer | Jude Brown <bookofjude@users.sourceforge.net> | 2011-11-06 08:57:53 +1000 |
commit | f40b9beb8de1c6679f4136ccd2a8c33a754698c5 (patch) | |
tree | 620b6f9c6b8b6c4f75a32d44da21a8dad6e2a591 /crawl-ref/source/scripts | |
parent | ae2aeddc3112e22baf0d0649bf95f14229b1be7c (diff) | |
download | crawl-ref-f40b9beb8de1c6679f4136ccd2a8c33a754698c5.tar.gz crawl-ref-f40b9beb8de1c6679f4136ccd2a8c33a754698c5.zip |
Some quick code stolen from place-population to give EXP stats on a map.
Diffstat (limited to 'crawl-ref/source/scripts')
-rw-r--r-- | crawl-ref/source/scripts/genmap.lua | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/crawl-ref/source/scripts/genmap.lua b/crawl-ref/source/scripts/genmap.lua new file mode 100644 index 0000000000..441ae6ca7f --- /dev/null +++ b/crawl-ref/source/scripts/genmap.lua @@ -0,0 +1,256 @@ +-- Counts monsters in a specified map +local niters = 10 +local output_file = "monster-report.out" + +local sort_type = "XP" + +local function canonical_name(mons) + local shapeshifter = mons.shapeshifter + if shapeshifter then + return shapeshifter + end + + local dancing_weapon = mons.dancing_weapon + if dancing_weapon then + return dancing_weapon + end + + local mname = mons.name + + if string.find(mname, 'very ugly thing$') then + return "very ugly thing" + end + + if string.find(mname, 'ugly thing$') then + return "ugly thing" + end + + local _, _, hydra_suffix = string.find(mname, '.*-headed (.*)') + if hydra_suffix then + mname = hydra_suffix + end + + return mname +end + +local function count_monsters_at(place, set) + if place == nil then + return nil + end + + debug.goto_place("D:1") + test.regenerate_level(nil, true) + debug.dismiss_monsters() + dgn.place_map(dgn.map_by_name(place), true) + + local monsters_here = set or { } + for mons in test.level_monster_iterator() do + local unique = mons.unique + local mname = canonical_name(mons) + if unique and group_uniques then + mname = "[UNIQUE]" + end + local mstat = monsters_here[mname] or { count = 0, exp = 0 } + mstat.count = mstat.count + 1 + mstat.exp = mstat.exp + mons.experience + monsters_here[mname] = mstat + end + return monsters_here +end + +local function report_monster_counts_at(place, mcount_map) + local text = '' + text = text .. "\n------------------------------------------------------------\n" + text = text .. place .. " monsters, " .. niters .. " iteration\n" + text = text .. "------------------------------------------------------------\n" + + local monster_counts = util.pairs(mcount_map) + table.sort(monster_counts, function (a, b) +-- Further sort options can be added later if desired. Default is XP, -sort_count sorts by count/percentage instead. + if sort_type == 'count' then + return a[2].total > b[2].total + else + return a[2].etotal > b[2].etotal + end + end) + + local total = 0 + for _, monster_pop in ipairs(monster_counts) do + if monster_pop[1] ~= 'TOTAL' then + total = total + monster_pop[2].total + end + end + + text = text .. "Count | XPAv | XPMin | XPMax | XPSigma | CountMin | CountMax | CountSigma | % | Monster\n" + for _, monster_pop in ipairs(monster_counts) do + text = text .. string.format("%6.2f | %5d | %5d | %5d | %7d | %8d | %8d | %10.2f | %6.2f%% | %s\n", + monster_pop[2].mean, + monster_pop[2].emean, + monster_pop[2].emin, + monster_pop[2].emax, + monster_pop[2].esigma, + monster_pop[2].min, + monster_pop[2].max, + monster_pop[2].sigma, + monster_pop[2].total * 100.0 / total, + monster_pop[1]) + end + return text +end + +local function report_monster_counts(summary, mcounts) + local places = util.keys(mcounts) + table.sort(places) + + local text = '' + + if multiple_levels then + text = text .. report_monster_counts_at(start_level .. " to " .. end_level, + summary) + end + + for _, place in ipairs(places) do + text = text .. report_monster_counts_at(place, mcounts[place]) + end + file.writefile(output_file, text) + crawl.mpr("Dumped monster stats to " .. output_file) +end + +local function calculate_monster_stats(iter_mpops) + local function blank_stats() + return { + total = 0, max = 0, min = 10 ^ 10, + etotal = 0, emax = 0, emin = 10 ^ 10, eiters = { }, + pops = { } } + end + + local function update_mons_stats(stat, instance_stat) + local function update_total_max_min(value, total, max, min) + stat[total] = stat[total] + value + if value > stat[max] then + stat[max] = value + end + if value < stat[min] then + stat[min] = value + end + end + update_total_max_min(instance_stat.count, 'total', 'max', 'min') + update_total_max_min(instance_stat.exp, 'etotal', 'emax', 'emin') + table.insert(stat.pops, instance_stat.count) + table.insert(stat.eiters, instance_stat.exp) + return stat + end + + local function sum(list) + local total = 0 + for _, num in ipairs(list) do + total = total + num + end + return total + end + + local function combine_stats(target, src) + local total = sum(src.pops) + local etotal = sum(src.eiters) + table.insert(target.pops, total) + table.insert(target.eiters, etotal) + target.total = target.total + total + target.etotal = target.etotal + etotal + target.max = math.max(target.max, total) + target.min = math.min(target.min, total) + target.emax = math.max(target.emax, etotal) + target.emin = math.min(target.emin, etotal) + end + + local final_stats = { TOTAL = blank_stats() } + local n = #iter_mpops + for _, mpop in ipairs(iter_mpops) do + local global_stat = blank_stats() + for mons, istat in pairs(mpop) do + local mstats = final_stats[mons] or blank_stats() + final_stats[mons] = update_mons_stats(mstats, istat) + update_mons_stats(global_stat, istat) + end + combine_stats(final_stats.TOTAL, global_stat) + end + + local function calc_mean_sigma(total, instances) + local mean = total / n + local totaldelta2 = 0 + for _, count in ipairs(instances) do + local delta = count - mean + totaldelta2 = totaldelta2 + delta * delta + end + -- There will be no instances with count 0, handle those: + for extra = 1, (n - #instances) do + totaldelta2 = totaldelta2 + mean * mean + end + local sigma = math.sqrt(totaldelta2 / n) + return mean, sigma + end + + for mons, stat in pairs(final_stats) do + stat.mean, stat.sigma = calc_mean_sigma(stat.total, stat.pops) + stat.emean, stat.esigma = calc_mean_sigma(stat.etotal, stat.eiters) + end + return final_stats +end + +-- Given a table mapping places to lists of niters sets of monster counts +-- and a list of places, returns one summary stats table. +local function calculate_summary_stats(place_totals, places) + local function combine_iter_results(master, inst) + for mons, stat in pairs(inst) do + local mstat = master[mons] or { count = 0, exp = 0 } + mstat.count = mstat.count + stat.count + mstat.exp = mstat.exp + stat.exp + master[mons] = mstat + end + end + + local iters = { } + for i = 1, niters do + local iterstats = { } + for _, place in ipairs(places) do + combine_iter_results(iterstats, place_totals[place][i]) + end + table.insert(iters, iterstats) + end + return calculate_monster_stats(iters) +end + +local function count_monsters_in(map) + local mcounts = { } + local summary = { } + local places = { } + local place_totals = { } + + table.insert(places, map) + + local mset = { } + + local iter_mpops = { } + for i = 1, niters do + crawl.message("Counting monsters for " .. map .. ", depth: " .. + you.absdepth() .. + " (" .. + i .. "/" .. niters .. ")") + local res = count_monsters_at(map) + table.insert(iter_mpops, res) + end + + mcounts[map] = calculate_monster_stats(iter_mpops) + + report_monster_counts(summary, mcounts) +end + +local mplaces = script.simple_args() +if #mplaces == 0 then + script.usage("Usage: genmap <map>") +end + +if #mplaces == 4 then + output_file = mplaces[2] +end + +count_monsters_in(mplaces[1]) |