| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Games::Dice::Probability; |
|
2
|
|
|
|
|
|
|
|
|
3
|
29
|
|
|
29
|
|
669610
|
use 5.006; |
|
|
29
|
|
|
|
|
196
|
|
|
|
29
|
|
|
|
|
1316
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# Be a Good Module |
|
6
|
29
|
|
|
29
|
|
179
|
use strict; |
|
|
29
|
|
|
|
|
61
|
|
|
|
29
|
|
|
|
|
990
|
|
|
7
|
29
|
|
|
29
|
|
805
|
use warnings; |
|
|
29
|
|
|
|
|
64
|
|
|
|
29
|
|
|
|
|
1614
|
|
|
8
|
|
|
|
|
|
|
#use diagnostics; |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# Our version number. |
|
11
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# Required Modules |
|
14
|
|
|
|
|
|
|
# Math::Sumbolic::AuxFunctions is for the calcs of binomial coefficients. |
|
15
|
|
|
|
|
|
|
# |
|
16
|
|
|
|
|
|
|
# Parse::RecDescent parses the dice expressions. |
|
17
|
29
|
|
|
29
|
|
32161
|
use Math::Symbolic::AuxFunctions; |
|
|
29
|
|
|
|
|
2863530
|
|
|
|
29
|
|
|
|
|
1133
|
|
|
18
|
29
|
|
|
29
|
|
58556
|
use Parse::RecDescent; |
|
|
29
|
|
|
|
|
30093252
|
|
|
|
29
|
|
|
|
|
296
|
|
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Optional Modules |
|
21
|
|
|
|
|
|
|
# Debug::ShowStuff is used by $self->debug() to descend and display hashes |
|
22
|
|
|
|
|
|
|
# in the object. |
|
23
|
|
|
|
|
|
|
# |
|
24
|
|
|
|
|
|
|
# Memoize is used by $self->new() to speed up calculations on individual |
|
25
|
|
|
|
|
|
|
# nodes in the expression tree. If there are duplicate nodes, the second |
|
26
|
|
|
|
|
|
|
# and all subsequent calc_distribution calls with the same parameters will |
|
27
|
|
|
|
|
|
|
# just return the cached values. |
|
28
|
|
|
|
|
|
|
# |
|
29
|
|
|
|
|
|
|
# Thanks to Mark Mills for the optional-module code snippet, and general |
|
30
|
|
|
|
|
|
|
# ideas, tips, and tricks for the whole module. He's my local Perl |
|
31
|
|
|
|
|
|
|
# Monk...you can email him thanks and offers of cookies at: |
|
32
|
|
|
|
|
|
|
# extremely{plus}pm{at}hostile{dot}org |
|
33
|
|
|
|
|
|
|
BEGIN { |
|
34
|
|
|
|
|
|
|
# Is Debug::ShowStuff available? |
|
35
|
29
|
50
|
|
29
|
|
5194
|
if ( eval q/ require Debug::ShowStuff / ) { |
|
36
|
|
|
|
|
|
|
# If so, import the routines. |
|
37
|
0
|
|
|
|
|
0
|
Debug::ShowStuff->import("showref"); |
|
38
|
|
|
|
|
|
|
} else { |
|
39
|
|
|
|
|
|
|
# Not available. Place stub in its place. |
|
40
|
29
|
|
|
0
|
0
|
1557
|
eval q/ sub showref { print @_; print "\n"; } /; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
41
|
|
|
|
|
|
|
} |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# Is Memoize available? |
|
44
|
29
|
50
|
|
|
|
1722
|
if ( eval q/ require Memoize / ) { |
|
45
|
|
|
|
|
|
|
# If so, import the routines. |
|
46
|
29
|
|
|
|
|
93772
|
Memoize->import(); |
|
47
|
|
|
|
|
|
|
} else { |
|
48
|
|
|
|
|
|
|
# Not available. Place stub in its place. |
|
49
|
0
|
|
|
|
|
0
|
eval q/ sub memoize { return; } /; |
|
50
|
|
|
|
|
|
|
} |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# Binomial Coefficient Shortcut |
|
54
|
|
|
|
|
|
|
# Non-polluted namespaces are cool and all, but sometimes it's too much to |
|
55
|
|
|
|
|
|
|
# type a 44 character long subroutine name multiple times in a simple |
|
56
|
|
|
|
|
|
|
# equation. Sheesh. |
|
57
|
|
|
|
|
|
|
my $binco = \&Math::Symbolic::AuxFunctions::binomial_coeff; |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# Recursive Parsing Grammar |
|
60
|
|
|
|
|
|
|
# Parsing grammar and tree code outright stolen from Sam Holden's |
|
61
|
|
|
|
|
|
|
# DiceDistribution.pm at http://sam.holden.id.au/junk/DICEDISTRIBUTION/. |
|
62
|
|
|
|
|
|
|
# Added a divide expression for division calculations. Changed the way |
|
63
|
|
|
|
|
|
|
# Fudge Dice are expressed to be #d[fF] which is more inline with how |
|
64
|
|
|
|
|
|
|
# they are represented elsewhere. Added mid# dice expression as part |
|
65
|
|
|
|
|
|
|
# of dicenode. |
|
66
|
|
|
|
|
|
|
my $DiceGrammar = <<'END_GRAMMAR'; |
|
67
|
|
|
|
|
|
|
expression: add_sub end { $item[1] } |
|
68
|
|
|
|
|
|
|
add_sub: mult_div '+' add_sub { { left => $item[1], op => '+', right => $item[3] } } |
|
69
|
|
|
|
|
|
|
add_sub: mult_div '-' add_sub { { left => $item[1], op => '-', right => $item[3] } } |
|
70
|
|
|
|
|
|
|
add_sub: mult_div |
|
71
|
|
|
|
|
|
|
mult_div: bracket '/' mult_div { { left => $item[1], op => '/', right => $item[3] } } |
|
72
|
|
|
|
|
|
|
mult_div: bracket '*' mult_div { { left => $item[1], op => '*', right => $item[3] } } |
|
73
|
|
|
|
|
|
|
mult_div: bracket |
|
74
|
|
|
|
|
|
|
bracket: '(' add_sub ')' { $item[2] } |
|
75
|
|
|
|
|
|
|
bracket: dicenode |
|
76
|
|
|
|
|
|
|
dicenode: /(\d+|mi)d(\d+|f)/i |
|
77
|
|
|
|
|
|
|
dicenode: /\d+/ |
|
78
|
|
|
|
|
|
|
end: /\s*$/ |
|
79
|
|
|
|
|
|
|
END_GRAMMAR |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# Dice Parsing Object |
|
82
|
|
|
|
|
|
|
my $DiceParser = Parse::RecDescent->new($DiceGrammar) || die("bad grammar"); |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# import() |
|
85
|
|
|
|
|
|
|
# Faux import function that either memoizes the calc portions of the |
|
86
|
|
|
|
|
|
|
# module (default) or doesn't. |
|
87
|
|
|
|
|
|
|
sub import { |
|
88
|
|
|
|
|
|
|
# Default is to memoize. Saving cycles is a Good Thing. However, if |
|
89
|
|
|
|
|
|
|
# someone passes an unmemoize argument, then we will respect their wishes. |
|
90
|
29
|
50
|
|
29
|
|
983
|
if ( ! grep(/(un|no)memo(ize)*/i,@_) ) { |
|
91
|
|
|
|
|
|
|
# Attempt to memoize the calculation subroutines. This will either |
|
92
|
|
|
|
|
|
|
# truly memoize, or the stub memoize function will simply return having |
|
93
|
|
|
|
|
|
|
# done nothing. |
|
94
|
29
|
|
|
|
|
555
|
memoize('calc_distribution'); |
|
95
|
|
|
|
|
|
|
# Not certain if calc_combination will benefit from Memoize. Need to |
|
96
|
|
|
|
|
|
|
# test further. |
|
97
|
29
|
|
|
|
|
8690
|
memoize('calc_combination'); |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# All is well. Return. |
|
101
|
29
|
|
|
|
|
265266
|
return; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# debug() |
|
105
|
|
|
|
|
|
|
# Print debugging information about object. |
|
106
|
|
|
|
|
|
|
sub debug { |
|
107
|
|
|
|
|
|
|
# The object of our attention. |
|
108
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# For every piece of the object... |
|
111
|
0
|
|
|
|
|
0
|
foreach my $key ( sort(keys(%$self)) ) { |
|
112
|
|
|
|
|
|
|
# Output the name... |
|
113
|
0
|
|
|
|
|
0
|
print "self->{$key}="; |
|
114
|
|
|
|
|
|
|
# And if it is a reference... |
|
115
|
0
|
0
|
|
|
|
0
|
if ( ref($self->{$key}) ) { |
|
116
|
|
|
|
|
|
|
# Print the contents of the reference... |
|
117
|
0
|
|
|
|
|
0
|
print "\n"; |
|
118
|
0
|
|
|
|
|
0
|
showref($self->{$key}); |
|
119
|
|
|
|
|
|
|
} else { |
|
120
|
|
|
|
|
|
|
# Or, print the value. |
|
121
|
0
|
|
|
|
|
0
|
print $self->{$key} . "\n"; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# All is well. |
|
126
|
0
|
|
|
|
|
0
|
return(0); |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# new(expression) |
|
130
|
|
|
|
|
|
|
# Creates a new object based on the provided dice expression. |
|
131
|
|
|
|
|
|
|
sub new { |
|
132
|
29
|
|
|
29
|
0
|
461
|
my $invocant = shift; |
|
133
|
29
|
|
33
|
|
|
318
|
my $class = ref($invocant) || $invocant; |
|
134
|
29
|
|
|
|
|
81
|
my $self = {}; |
|
135
|
|
|
|
|
|
|
|
|
136
|
29
|
50
|
|
|
|
140
|
die("must provide dice expression") unless (@_); |
|
137
|
|
|
|
|
|
|
|
|
138
|
29
|
50
|
|
|
|
158
|
if (@_ != 1) { |
|
139
|
0
|
|
|
|
|
0
|
die("new() called with too many arguments"); |
|
140
|
|
|
|
|
|
|
} else { |
|
141
|
29
|
|
50
|
|
|
509
|
$self->{EXPRESSION} = $DiceParser->expression(@_) || die "could not parse expression"; |
|
142
|
29
|
|
50
|
|
|
388373
|
$self->{DISTRIBUTION} = travel($self->{EXPRESSION}) || die "could not travel parsed expression"; |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
29
|
|
|
|
|
1424
|
return(bless($self, $class)); |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# travel(expression) |
|
149
|
|
|
|
|
|
|
# Travel the parsed expression, returning hash of value => permutations. |
|
150
|
|
|
|
|
|
|
sub travel { |
|
151
|
61
|
|
50
|
61
|
0
|
382
|
my $node = shift || die "invalid or missing expression node"; |
|
152
|
|
|
|
|
|
|
|
|
153
|
61
|
100
|
|
|
|
212
|
if ( ref($node) ) { |
|
154
|
16
|
|
|
|
|
48
|
for ($node->{op}) { |
|
155
|
16
|
100
|
|
|
|
110
|
/(\+)/ && return( calc_combination($1, travel($node->{left}), travel($node->{right})) ); |
|
156
|
9
|
100
|
|
|
|
62
|
/(\-)/ && return( calc_combination($1, travel($node->{left}), travel($node->{right})) ); |
|
157
|
6
|
100
|
|
|
|
47
|
/(\*)/ && return( calc_combination($1, travel($node->{left}), travel($node->{right})) ); |
|
158
|
2
|
50
|
|
|
|
43
|
/(\/)/ && return( calc_combination($1, travel($node->{left}), travel($node->{right})) ); |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
|
|
162
|
45
|
|
|
|
|
133
|
for ($node) { |
|
163
|
45
|
100
|
|
|
|
1256
|
/^(\d+)(d)(\d+|f)/i && return( calc_distribution($2,$1,$3) ); |
|
164
|
8
|
100
|
|
|
|
208
|
/^(mi)(d)(\d+|f)/i && return( calc_distribution("m",3,$3) ); |
|
165
|
2
|
50
|
|
|
|
49
|
/^\d+$/ && return( {$node => 1} ); |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
|
|
168
|
0
|
|
|
|
|
0
|
die("invalid token in expression."); |
|
169
|
|
|
|
|
|
|
} |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# calc_distribution(method,numdice,numsides) |
|
172
|
|
|
|
|
|
|
# Calculate distribution of values/permutations given: method, number of |
|
173
|
|
|
|
|
|
|
# dice, and number of sides per die. |
|
174
|
|
|
|
|
|
|
sub calc_distribution { |
|
175
|
|
|
|
|
|
|
# The dice method requested. |
|
176
|
|
|
|
|
|
|
my $method = shift; |
|
177
|
|
|
|
|
|
|
# The number of dice. |
|
178
|
|
|
|
|
|
|
my $n = shift; $n += 0; |
|
179
|
|
|
|
|
|
|
# The number of sides/faces on each die. |
|
180
|
|
|
|
|
|
|
# f/F = Fudge dice = d3. |
|
181
|
|
|
|
|
|
|
my $s = shift; $s += 0 unless $s =~ /f/i; |
|
182
|
|
|
|
|
|
|
# Loop value based on total/face. |
|
183
|
|
|
|
|
|
|
my $t1; |
|
184
|
|
|
|
|
|
|
# Hash to return containing the distribution. |
|
185
|
|
|
|
|
|
|
my %dist; |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# If dice node method is simple-sum nDs or nDf... |
|
188
|
|
|
|
|
|
|
if ( $method =~ /d/i ) { |
|
189
|
|
|
|
|
|
|
if ( $s =~ /f/i ) { |
|
190
|
|
|
|
|
|
|
# If dice node method is fudge nDf... |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# First, get the distribution for nDs... |
|
193
|
|
|
|
|
|
|
my $tempdist = calc_distribution("d",$n,3); |
|
194
|
|
|
|
|
|
|
# Then loop to build the new distribution from (-n..0)... |
|
195
|
|
|
|
|
|
|
my $origkey = $n; |
|
196
|
|
|
|
|
|
|
for ($t1=-1 * $n; $t1 <= 0; $t1++) { |
|
197
|
|
|
|
|
|
|
# Changing the original values in (n..ns) to (-n..n), |
|
198
|
|
|
|
|
|
|
# copy the original distribution to the new one with |
|
199
|
|
|
|
|
|
|
# the correct values... |
|
200
|
|
|
|
|
|
|
$dist{$t1} = $$tempdist{$origkey}; |
|
201
|
|
|
|
|
|
|
$dist{$t1*-1} = $$tempdist{$origkey}; |
|
202
|
|
|
|
|
|
|
$origkey++; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
} elsif ( $n eq 1 ) { |
|
205
|
|
|
|
|
|
|
# Save compute cycles if only one die... |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# Each face has a single chance... |
|
208
|
|
|
|
|
|
|
for ($t1=1; $t1 <= $s; $t1++) { |
|
209
|
|
|
|
|
|
|
$dist{$t1} = 1; |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
} else { |
|
212
|
|
|
|
|
|
|
# Else compute number of combinations for a total on the face of n |
|
213
|
|
|
|
|
|
|
# dice. |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# Minimum and maximum sums. |
|
216
|
|
|
|
|
|
|
my $tmin = $n; |
|
217
|
|
|
|
|
|
|
my $tmax = $n*$s; |
|
218
|
|
|
|
|
|
|
# Peak sum is the sum around which the bell-curve mirrors, saving |
|
219
|
|
|
|
|
|
|
# compute time. |
|
220
|
|
|
|
|
|
|
my $tpeak = ($tmin+$tmax)/2; |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
# For each total (t1) in $tmin..$tmax, calculate the number of |
|
223
|
|
|
|
|
|
|
# combinations giving that total. |
|
224
|
|
|
|
|
|
|
for ($t1=$tmin; $t1 <= $tpeak; $t1++) { |
|
225
|
|
|
|
|
|
|
# Total (t2) that is the mirror point in the bell curve. |
|
226
|
|
|
|
|
|
|
my $t2 = $tmin + $tmax - $t1; |
|
227
|
|
|
|
|
|
|
# Ceiling for the sum function. |
|
228
|
|
|
|
|
|
|
my $ceil = int( ($t1-$n) / $s ); |
|
229
|
|
|
|
|
|
|
# Result. |
|
230
|
|
|
|
|
|
|
my $res = 0; |
|
231
|
|
|
|
|
|
|
# Loop control for the sum funtion. |
|
232
|
|
|
|
|
|
|
my $k; |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# Sum Function: For each k in 0 to $ceil... |
|
235
|
|
|
|
|
|
|
for ($k=0; $k <= $ceil; $k++) { |
|
236
|
|
|
|
|
|
|
# Calculate and add to previous results. |
|
237
|
|
|
|
|
|
|
$res += ((-1)**$k) * &$binco($n,$k) * &$binco(($t1-($s*$k)-1),($n-1)); |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# Set the mirrored points of the distribution. |
|
241
|
|
|
|
|
|
|
# Note: $t1 and $t2 can be equal once at |
|
242
|
|
|
|
|
|
|
# $t1=$tpeak when s is even. |
|
243
|
|
|
|
|
|
|
$dist{$t1} = $res; |
|
244
|
|
|
|
|
|
|
$dist{$t2} = $res; |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
} |
|
247
|
|
|
|
|
|
|
} elsif ( $method =~ /m/i ) { |
|
248
|
|
|
|
|
|
|
# If dice method is take-the-middle-value nMs... |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# Minimum and maximum values. |
|
251
|
|
|
|
|
|
|
my $tmin = 1; |
|
252
|
|
|
|
|
|
|
my $tmax = $s; |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# For each value (t1) in $tmin..$tmax, calculate the number of |
|
255
|
|
|
|
|
|
|
# combinations giving t1 as the middle face value. |
|
256
|
|
|
|
|
|
|
for ($t1=$tmin; $t1 <= $tmax; $t1++) { |
|
257
|
|
|
|
|
|
|
$dist{$t1} = 1 + ( 3 * ($s - 1) ) + ( 6 * ($t1 - 1) * ($s - $t1) ); |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# Return the value=>combinations distribution hash for this node. |
|
262
|
|
|
|
|
|
|
return({%dist}); |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# calc_combination(operand,distribution1,distribution2) |
|
266
|
|
|
|
|
|
|
# Combine two distributions with the method provided. |
|
267
|
|
|
|
|
|
|
sub calc_combination { |
|
268
|
|
|
|
|
|
|
# Calculation to perform on values. |
|
269
|
|
|
|
|
|
|
my $op = shift; |
|
270
|
|
|
|
|
|
|
# Distributions to combine. |
|
271
|
|
|
|
|
|
|
my $dist1 = shift; |
|
272
|
|
|
|
|
|
|
my $dist2 = shift; |
|
273
|
|
|
|
|
|
|
# The combined distribution. |
|
274
|
|
|
|
|
|
|
my %cdist; |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# For each value in the first distribution... |
|
277
|
|
|
|
|
|
|
foreach my $val1 ( sort {$a+0 <=> $b+0} keys(%$dist1) ) { |
|
278
|
|
|
|
|
|
|
# Combine it with every value in the second distribution... |
|
279
|
|
|
|
|
|
|
foreach my $val2 ( sort {$a+0 <=> $b+0} keys(%$dist2) ) { |
|
280
|
|
|
|
|
|
|
# The new value of which is calculated based on combine method... |
|
281
|
|
|
|
|
|
|
my $newval; |
|
282
|
|
|
|
|
|
|
for ($op) { |
|
283
|
|
|
|
|
|
|
/\+/ && do { $newval = $val1 + $val2 }; |
|
284
|
|
|
|
|
|
|
/\-/ && do { $newval = $val1 - $val2 }; |
|
285
|
|
|
|
|
|
|
/\*/ && do { $newval = $val1 * $val2 }; |
|
286
|
|
|
|
|
|
|
/\// && do { $newval = int($val1 / $val2) }; |
|
287
|
|
|
|
|
|
|
} |
|
288
|
|
|
|
|
|
|
# Calculate the new combined combinations and set it to the new |
|
289
|
|
|
|
|
|
|
# value in the new distribution. |
|
290
|
|
|
|
|
|
|
$cdist{$newval} += $$dist1{$val1} * $$dist2{$val2}; |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
} |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# Return the combined distribution. |
|
295
|
|
|
|
|
|
|
return({%cdist}); |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# combinations(targetvalue) |
|
299
|
|
|
|
|
|
|
# Calculate number of combinations for a target value. |
|
300
|
|
|
|
|
|
|
sub combinations { |
|
301
|
303
|
|
|
303
|
0
|
547
|
my $self = shift; |
|
302
|
303
|
|
|
|
|
378
|
my $targetvalue = shift; |
|
303
|
|
|
|
|
|
|
|
|
304
|
303
|
100
|
|
|
|
738
|
$targetvalue = "ALL" unless defined($targetvalue); |
|
305
|
|
|
|
|
|
|
|
|
306
|
303
|
50
|
|
|
|
651
|
if ( ref($self) ) { |
|
307
|
303
|
50
|
|
|
|
608
|
if (@_ != 0) { |
|
308
|
0
|
|
|
|
|
0
|
die("combinations() called incorrectly"); |
|
309
|
|
|
|
|
|
|
} else { |
|
310
|
303
|
100
|
|
|
|
679
|
if ( $targetvalue eq "ALL" ) { |
|
311
|
28
|
50
|
|
|
|
133
|
if ( $self->{COMBINATIONS} ) { |
|
312
|
0
|
|
|
|
|
0
|
return ( $self->{COMBINATIONS} ); |
|
313
|
|
|
|
|
|
|
} else { |
|
314
|
28
|
|
|
|
|
56
|
foreach my $value ( values(%{$self->{DISTRIBUTION}}) ) { |
|
|
28
|
|
|
|
|
880
|
|
|
315
|
12201
|
|
|
|
|
12281
|
$self->{COMBINATIONS} += $value; |
|
316
|
|
|
|
|
|
|
} |
|
317
|
28
|
|
|
|
|
125
|
return($self->{COMBINATIONS}); |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
} else { |
|
320
|
275
|
|
|
|
|
1355
|
return( $self->{DISTRIBUTION}->{$targetvalue} ); |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
} else { |
|
324
|
0
|
|
|
|
|
0
|
die("combinations() called on non-object"); |
|
325
|
|
|
|
|
|
|
} |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# distribution() |
|
329
|
|
|
|
|
|
|
# Returns a hash containing the distribution in value=>combinations format. |
|
330
|
|
|
|
|
|
|
sub distribution { |
|
331
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
332
|
|
|
|
|
|
|
|
|
333
|
0
|
0
|
|
|
|
0
|
if ( ref($self) ) { |
|
334
|
0
|
0
|
|
|
|
0
|
if (@_ != 0) { |
|
335
|
0
|
|
|
|
|
0
|
die("distribution() called with argument on object"); |
|
336
|
|
|
|
|
|
|
} else { |
|
337
|
0
|
|
|
|
|
0
|
return( $self->{DISTRIBUTION} ); |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
} else { |
|
340
|
0
|
0
|
|
|
|
0
|
if (@_ != 0) { |
|
341
|
0
|
|
0
|
|
|
0
|
my $expression = $DiceParser->expression(@_) || die "could not parse expression"; |
|
342
|
0
|
|
|
|
|
0
|
return( travel($expression) ); |
|
343
|
|
|
|
|
|
|
} else { |
|
344
|
0
|
|
|
|
|
0
|
die("no expression provided for non-object distribution() call"); |
|
345
|
|
|
|
|
|
|
} |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
# probability(targetvalue) |
|
350
|
|
|
|
|
|
|
# Returns the probability for targetvalue, or a hash of probabilities in |
|
351
|
|
|
|
|
|
|
# value=>probability format. |
|
352
|
|
|
|
|
|
|
sub probability { |
|
353
|
12201
|
|
|
12201
|
0
|
17924
|
my $self = shift; |
|
354
|
12201
|
|
|
|
|
13823
|
my $targetvalue = shift; |
|
355
|
|
|
|
|
|
|
|
|
356
|
12201
|
50
|
|
|
|
23596
|
$targetvalue = "ALL" unless defined($targetvalue); |
|
357
|
|
|
|
|
|
|
|
|
358
|
12201
|
50
|
|
|
|
22107
|
if ( ref($self) ) { |
|
359
|
12201
|
50
|
|
|
|
21747
|
if (@_ != 0) { |
|
360
|
0
|
|
|
|
|
0
|
die("probability() called incorrectly"); |
|
361
|
|
|
|
|
|
|
} else { |
|
362
|
12201
|
100
|
|
|
|
27573
|
if ( ! exists($self->{PROBABILITIES}) ) { |
|
363
|
28
|
|
|
|
|
157
|
my $combs = $self->combinations(); |
|
364
|
28
|
|
|
|
|
67
|
my %probs; |
|
365
|
28
|
|
|
|
|
71
|
foreach my $value ( keys(%{$self->{DISTRIBUTION}}) ) { |
|
|
28
|
|
|
|
|
36321
|
|
|
366
|
12201
|
|
|
|
|
26113
|
$probs{$value} = $self->{DISTRIBUTION}->{$value} / $combs; |
|
367
|
|
|
|
|
|
|
} |
|
368
|
28
|
|
|
|
|
11049
|
$self->{PROBABILITIES} = {%probs}; |
|
369
|
|
|
|
|
|
|
} |
|
370
|
12201
|
50
|
|
|
|
22987
|
if ( $targetvalue eq "ALL" ) { |
|
371
|
0
|
|
|
|
|
0
|
return( $self->{PROBABILITIES} ); |
|
372
|
|
|
|
|
|
|
} else { |
|
373
|
12201
|
|
50
|
|
|
78851
|
return( $self->{PROBABILITIES}->{$targetvalue} || undef ); |
|
374
|
|
|
|
|
|
|
} |
|
375
|
|
|
|
|
|
|
} |
|
376
|
|
|
|
|
|
|
} else { |
|
377
|
0
|
|
|
|
|
0
|
die("probability() called on non-object"); |
|
378
|
|
|
|
|
|
|
} |
|
379
|
|
|
|
|
|
|
} |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# bounds() |
|
382
|
|
|
|
|
|
|
# Returns the min and max values of the valueset. |
|
383
|
|
|
|
|
|
|
sub bounds { |
|
384
|
56
|
|
|
56
|
0
|
125
|
my $self = shift; |
|
385
|
|
|
|
|
|
|
|
|
386
|
56
|
50
|
|
|
|
190
|
if ( ref($self) ) { |
|
387
|
56
|
50
|
|
|
|
180
|
if (@_ != 0) { |
|
388
|
0
|
|
|
|
|
0
|
die("bounds() called with argument on object"); |
|
389
|
|
|
|
|
|
|
} else { |
|
390
|
56
|
|
|
|
|
161
|
return( [ $self->min(), $self->max() ] ); |
|
391
|
|
|
|
|
|
|
} |
|
392
|
|
|
|
|
|
|
} else { |
|
393
|
0
|
|
|
|
|
0
|
die("bounds() called on non-object"); |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
} |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# max() |
|
398
|
|
|
|
|
|
|
# Returns the max value of the valueset. |
|
399
|
|
|
|
|
|
|
sub max { |
|
400
|
84
|
|
|
84
|
0
|
160
|
my $self = shift; |
|
401
|
|
|
|
|
|
|
|
|
402
|
84
|
50
|
|
|
|
220
|
if ( ref($self) ) { |
|
403
|
84
|
50
|
|
|
|
236
|
if (@_ != 0) { |
|
404
|
0
|
|
|
|
|
0
|
die("max() called with argument on object"); |
|
405
|
|
|
|
|
|
|
} else { |
|
406
|
84
|
100
|
|
|
|
210
|
if ( $self->{MAX} ) { |
|
407
|
56
|
|
|
|
|
401
|
return( $self->{MAX} ); |
|
408
|
|
|
|
|
|
|
} else { |
|
409
|
28
|
|
|
|
|
58
|
my @values = sort {$b+0 <=> $a+0} keys(%{$self->{DISTRIBUTION}}); |
|
|
145546
|
|
|
|
|
170861
|
|
|
|
28
|
|
|
|
|
2033
|
|
|
410
|
28
|
|
|
|
|
1013
|
$self->{MAX} = shift(@values); |
|
411
|
28
|
|
|
|
|
726
|
return( $self->{MAX} ); |
|
412
|
|
|
|
|
|
|
} |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
} else { |
|
415
|
0
|
|
|
|
|
0
|
die("max() called on non-object"); |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# min() |
|
420
|
|
|
|
|
|
|
# Returns the min value of the valueset. |
|
421
|
|
|
|
|
|
|
sub min { |
|
422
|
84
|
|
|
84
|
0
|
12866
|
my $self = shift; |
|
423
|
|
|
|
|
|
|
|
|
424
|
84
|
50
|
|
|
|
292
|
if ( ref($self) ) { |
|
425
|
84
|
50
|
|
|
|
234
|
if (@_ != 0) { |
|
426
|
0
|
|
|
|
|
0
|
die("min() called with argument on object"); |
|
427
|
|
|
|
|
|
|
} else { |
|
428
|
84
|
100
|
|
|
|
508
|
if ( $self->{MIN} ) { |
|
429
|
52
|
|
|
|
|
225
|
return( $self->{MIN} ); |
|
430
|
|
|
|
|
|
|
} else { |
|
431
|
32
|
|
|
|
|
98
|
my @values = sort {$a+0 <=> $b+0} keys(%{$self->{DISTRIBUTION}}); |
|
|
145751
|
|
|
|
|
168339
|
|
|
|
32
|
|
|
|
|
1538
|
|
|
432
|
32
|
|
|
|
|
1546
|
$self->{MIN} = shift(@values); |
|
433
|
32
|
|
|
|
|
1235
|
return( $self->{MIN} ); |
|
434
|
|
|
|
|
|
|
} |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
} else { |
|
437
|
0
|
|
|
|
|
|
die("min() called on non-object"); |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
} |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
1; |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
__END__ |