This repository has been archived by the owner on Feb 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaeldardin_stats.rb
136 lines (107 loc) · 4.76 KB
/
aeldardin_stats.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File: aeldardin-to-dot.rb
#
# Script to calculate statistics over the rooms in a dungeon.
require 'dungeon'
module Aeldardin
class Stats
def self.run
# TODO: duplicated in Aeldardin::ToDot code.
case ARGV.length
when 0
input_file = STDIN
when 1
input_file = File.open(ARGV[0], 'r')
else
STDERR.puts "Usage: #{$PROGRAM_NAME} [<filename.yml>]"
exit 1
end
dungeon = Aeldardin::Dungeon.load(input_file)
puts "Statistics for '#{dungeon.title}':"
puts Aeldardin::Stats.new(dungeon).to_s
end
# @param [Aeldardin::Dungeon] dungeon The dungeon to render to Dot format.
def initialize(dungeon)
@dungeon = dungeon
end
# Returns a set of nested hashes mirroring the zone-and-region structure of the dungeon,
# but containing aggregate statistics rather than adventure data.
#
# For now, we only calculate one thing: number of rooms.
def stats_tree
@stats ||= calculate(@dungeon)
end
# Display the statistics.
# TODO: should probably be part of a separate presentational class.
def to_s
format_stats(stats_tree, '').join("\n")
end
private
# Actually calculates the stats for a given dungeon, recursively.
def calculate(dungeon)
# Recurse. Stops when we reach regions with no children.
recursive_stats = {}
dungeon.regions.each do |region|
recursive_stats[region.name] = calculate(region)
end
# Terminal case -- calculate stats for these rooms.
# Create a hash where every value starts out as 0, so we can use += safely.
local_stats = Hash.new { |h, k| h[k] = 0 }
# Count each room for the overall total and its particular type.
dungeon.local_rooms.each do |room|
local_stats[:all_rooms] += 1
local_stats[room.types] += 1
end
# Calculate aggregate totals by adding up counts for all types used by descendant rooms.
aggregate_stats = Hash.new { |h, k| h[k] = 0 }
recursive_aggregates = recursive_stats.values.map { |h| h[:aggregate] }
(recursive_aggregates + [local_stats]).each do |child_stats|
child_stats.each do |stat, subtotal|
aggregate_stats[stat] += subtotal
end
end
# Build and return the result:
{
:regions => recursive_stats,
:local => local_stats,
:aggregate => aggregate_stats
}
end
# Recursively format the dungeon statistics.
# @param tree [Hash] The stats tree, as returned by (#see calculate)
# @param indent [String] The indentation prefix -- two spaces are appended for each nesting level.
def format_stats(tree, indent)
stats_strings = []
tree[:regions].each do |name, details|
# Recurse. Again, base case is when we have no child regions.
child_indent = indent + ' '
child_stats = format_stats(details, child_indent)
stats_strings << "#{indent}#{name}:"
stats_strings += child_stats
end
# Format our local and aggregate stats.
# TODO: needs to be more flexible.
# Only show local count if present
if tree[:local][:all_rooms] > 0
stats_strings << "#{indent}#{format_room_counts(tree[:local])} locally"
end
# Only show aggregate count if there were rooms in child regions
if tree[:aggregate][:all_rooms] != tree[:local][:all_rooms]
stats_strings << "#{indent}#{format_room_counts(tree[:aggregate])}"
end
stats_strings
end
# Format a particular set of room statistics, showing counts by type and overall.
# @param [Hash{Object, Integer}] A hash from arrays of room types to totals, with an extra symbol key (:all_rooms) for the overall total.
def format_room_counts(counts)
# Format the counts for individual room types.
type_total_keys = counts.keys - [:all_rooms]
type_total_strings = type_total_keys.map do |key|
# Special case for empty rooms, as that type can't be inferred from the type array.
total_name = (key == []) ? 'empty' : key.join('+')
# Like '17 monster+treasure
"#{counts[key]} #{total_name}"
end
"#{counts[:all_rooms]} rooms (#{type_total_strings.join(', ')})"
end
end
end