games_dice 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,306 +1,314 @@
1
- require 'games_dice'
2
- require 'helpers'
3
-
4
- describe GamesDice::ComplexDie do
5
-
6
- before do
7
- # Set state of default PRNG
8
- srand(4567)
9
- end
10
-
11
- it "should represent a basic die as an object" do
12
- die = GamesDice::ComplexDie.new(6)
13
- die.min.should == 1
14
- die.max.should == 6
15
- die.sides.should == 6
16
- end
17
-
18
- it "should return results based on Ruby's internal rand() by default" do
19
- die = GamesDice::ComplexDie.new(10)
20
- [5,4,10,4,7,8,1,9].each do |expected|
21
- die.roll.should == expected
22
- die.result.should == expected
23
- end
24
- end
25
-
26
- it "should use any object with a rand(Integer) method" do
27
- prng = TestPRNG.new()
28
- die = GamesDice::ComplexDie.new(20, :prng => prng)
29
- [16,7,3,11,16,18,20,7].each do |expected|
30
- die.roll.should == expected
31
- die.result.should == expected
32
- end
33
- end
34
-
35
- it "should optionally accept a rerolls param" do
36
- GamesDice::ComplexDie.new( 10, :rerolls => [] )
37
- GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add)] )
38
- GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add),GamesDice::RerollRule.new(1, :>=, :reroll_subtract)] )
39
- GamesDice::ComplexDie.new( 10, :rerolls => [[6, :<=, :reroll_add]] )
40
- GamesDice::ComplexDie.new( 10, :rerolls => [[6, :<=, :reroll_add],[1, :>=, :reroll_subtract]] )
41
-
42
- lambda do
43
- GamesDice::ComplexDie.new( 10, :rerolls => 7 )
44
- end.should raise_error( TypeError )
45
-
46
- lambda do
47
- GamesDice::ComplexDie.new( 10, :rerolls => ['hello'] )
48
- end.should raise_error( TypeError )
49
-
50
- lambda do
51
- GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add), :reroll_add] )
52
- end.should raise_error( TypeError )
53
-
54
- lambda do
55
- GamesDice::ComplexDie.new( 10, :rerolls => [7] )
56
- end.should raise_error( TypeError )
57
-
58
- lambda do
59
- GamesDice::ComplexDie.new( 10, :rerolls => [['hello']] )
60
- end.should raise_error( ArgumentError )
61
-
62
- lambda do
63
- GamesDice::ComplexDie.new( 10, :rerolls => [ [6, :<=, :reroll_add ], :reroll_add] )
64
- end.should raise_error( TypeError )
65
-
66
- end
67
-
68
- it "should optionally accept a maps param" do
69
- GamesDice::ComplexDie.new( 10, :maps => [] )
70
- GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1)] )
71
- GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1), GamesDice::MapRule.new(1, :>, -1) ] )
72
- GamesDice::ComplexDie.new( 10, :maps => [ [7, :<=, 1] ] )
73
- GamesDice::ComplexDie.new( 10, :maps => [ [7, :<=, 1], [1, :>, -1] ] )
74
-
75
- lambda do
76
- GamesDice::ComplexDie.new( 10, :maps => 7 )
77
- end.should raise_error( TypeError )
78
-
79
- lambda do
80
- GamesDice::ComplexDie.new( 10, :maps => [7] )
81
- end.should raise_error( TypeError )
82
-
83
- lambda do
84
- GamesDice::ComplexDie.new( 10, :maps => [ [7] ] )
85
- end.should raise_error( ArgumentError )
86
-
87
- lambda do
88
- GamesDice::ComplexDie.new( 10, :maps => ['hello'] )
89
- end.should raise_error( TypeError )
90
-
91
- lambda do
92
- GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1),GamesDice::RerollRule.new(6, :<=, :reroll_add)] )
93
- end.should raise_error( TypeError )
94
- end
95
-
96
- describe "with rerolls" do
97
- it "should calculate correct minimum and maximum results" do
98
- die = GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add, 3)] )
99
- die.min.should == 1
100
- die.max.should == 40
101
-
102
- die = GamesDice::ComplexDie.new( 10, :rerolls => [[1, :>=, :reroll_subtract]] )
103
- die.min.should == -9
104
- die.max.should == 10
105
-
106
- die = GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add)] )
107
- die.min.should == 1
108
- die.max.should == 10_010
109
- end
110
-
111
- it "should simulate a d10 that rerolls and adds on a result of 10" do
112
- die = GamesDice::ComplexDie.new(10, :rerolls => [[10, :<=, :reroll_add]] )
113
- [5,4,14,7,8,1,9].each do |expected|
114
- die.roll.should == expected
115
- die.result.should == expected
116
- end
117
- end
118
-
119
- it "should explain how it got results outside range 1 to 10 on a d10" do
120
- die = GamesDice::ComplexDie.new(10, :rerolls => [[10, :<=, :reroll_add],[1, :>=, :reroll_subtract]] )
121
- ["5","4","[10+4] 14","7","8","[1-9] -8"].each do |expected|
122
- die.roll
123
- die.explain_result.should == expected
124
- end
125
- end
126
-
127
- it "should calculate an expected result" do
128
- die = GamesDice::ComplexDie.new(10, :rerolls => [[10, :<=, :reroll_add],[1, :>=, :reroll_subtract]] )
129
- die.probabilities.expected.should be_within(1e-10).of 5.5
130
-
131
- die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(1, :<=, :reroll_use_best, 1)] )
132
- die.probabilities.expected.should be_within(1e-10).of 7.15
133
-
134
- die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(1, :<=, :reroll_use_worst, 2)] )
135
- die.probabilities.expected.should be_within(1e-10).of 3.025
136
-
137
- die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add)] )
138
- die.probabilities.expected.should be_within(1e-10).of 4.2
139
-
140
- die = GamesDice::ComplexDie.new(8, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_use_best)] )
141
- die.probabilities.expected.should be_within(1e-10).of 5.0
142
-
143
- die = GamesDice::ComplexDie.new(4, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_replace, 1)] )
144
- die.probabilities.expected.should be_within(1e-10).of 2.875
145
- end
146
-
147
- it "should calculate probabilities of each possible result" do
148
- die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(7, :>, :reroll_add, 1)] )
149
- probs = die.probabilities.to_h
150
- probs[11].should be_within(1e-10).of 2/36.0
151
- probs[8].should be_within(1e-10).of 5/36.0
152
- probs.values.inject(:+).should be_within(1e-9).of 1.0
153
-
154
- die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add)] )
155
- probs = die.probabilities.to_h
156
- probs[8].should be_within(1e-10).of 0.1
157
- probs[10].should be_false
158
- probs[13].should be_within(1e-10).of 0.01
159
- probs[27].should be_within(1e-10).of 0.001
160
- probs.values.inject(:+).should be_within(1e-9).of 1.0
161
-
162
- die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_replace, 1)] )
163
- probs = die.probabilities.to_h
164
- probs[1].should be_within(1e-10).of 1/36.0
165
- probs[2].should be_within(1e-10).of 7/36.0
166
- probs.values.inject(:+).should be_within(1e-9).of 1.0
167
- end
168
-
169
- it "should calculate aggregate probabilities" do
170
- die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(7, :>, :reroll_add, 1)] )
171
- probs = die.probabilities
172
- probs.p_gt(7).should be_within(1e-10).of 15/36.0
173
- probs.p_gt(-10).should == 1.0
174
- probs.p_gt(12).should == 0.0
175
-
176
- probs.p_ge(7).should be_within(1e-10).of 21/36.0
177
- probs.p_ge(2).should == 1.0
178
- probs.p_ge(15).should == 0.0
179
-
180
- probs.p_lt(7).should be_within(1e-10).of 15/36.0
181
- probs.p_lt(-10).should == 0.0
182
- probs.p_lt(13).should == 1.0
183
-
184
- probs.p_le(7).should be_within(1e-10).of 21/36.0
185
- probs.p_le(1).should == 0.0
186
- probs.p_le(12).should == 1.0
187
- end
188
- end
189
-
190
- describe "with maps" do
191
- it "should calculate correct minimum and maximum results" do
192
- die = GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S')] )
193
- die.min.should == 0
194
- die.max.should == 1
195
- end
196
-
197
- it "should simulate a d10 that scores 1 for success on a value of 7 or more" do
198
- die = GamesDice::ComplexDie.new( 10, :maps => [ [ 7, :<=, 1, 'S' ] ] )
199
- [0,0,1,0,1,1,0,1].each do |expected|
200
- die.roll.should == expected
201
- die.result.should == expected
202
- end
203
- end
204
-
205
- it "should label the mappings applied with the provided names" do
206
- die = GamesDice::ComplexDie.new( 10, :maps => [ [7, :<=, 1, 'S'], [1, :>=, -1, 'F'] ] )
207
- ["5", "4", "10 S", "4", "7 S", "8 S", "1 F", "9 S"].each do |expected|
208
- die.roll
209
- die.explain_result.should == expected
210
- end
211
- end
212
-
213
- it "should calculate an expected result" do
214
- die = GamesDice::ComplexDie.new(10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
215
- die.probabilities.expected.should be_within(1e-10).of 0.3
216
- end
217
-
218
- it "should calculate probabilities of each possible result" do
219
- die = GamesDice::ComplexDie.new(10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
220
- probs_hash = die.probabilities.to_h
221
- probs_hash[1].should be_within(1e-10).of 0.4
222
- probs_hash[0].should be_within(1e-10).of 0.5
223
- probs_hash[-1].should be_within(1e-10).of 0.1
224
- probs_hash.values.inject(:+).should be_within(1e-9).of 1.0
225
- end
226
-
227
- it "should calculate aggregate probabilities" do
228
- die = GamesDice::ComplexDie.new(10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
229
- probs = die.probabilities
230
- probs.p_gt(-2).should == 1.0
231
- probs.p_gt(-1).should be_within(1e-10).of 0.9
232
- probs.p_gt(1).should == 0.0
233
-
234
- probs.p_ge(1).should be_within(1e-10).of 0.4
235
- probs.p_ge(-1).should == 1.0
236
- probs.p_ge(2).should == 0.0
237
-
238
- probs.p_lt(1).should be_within(1e-10).of 0.6
239
- probs.p_lt(-1).should == 0.0
240
- probs.p_lt(2).should == 1.0
241
-
242
- probs.p_le(-1).should be_within(1e-10).of 0.1
243
- probs.p_le(-2).should == 0.0
244
- probs.p_le(1).should == 1.0
245
- end
246
- end
247
-
248
- describe "with rerolls and maps together" do
249
- before do
250
- @die = GamesDice::ComplexDie.new( 6,
251
- :rerolls => [[6, :<=, :reroll_add]],
252
- :maps => [GamesDice::MapRule.new(9, :<=, 1, 'Success')]
253
- )
254
- end
255
-
256
- it "should calculate correct minimum and maximum results" do
257
- @die.min.should == 0
258
- @die.max.should == 1
259
- end
260
-
261
- it "should calculate an expected result" do
262
- @die.probabilities.expected.should be_within(1e-10).of 4/36.0
263
- end
264
-
265
- it "should calculate probabilities of each possible result" do
266
- probs_hash = @die.probabilities.to_h
267
- probs_hash[1].should be_within(1e-10).of 4/36.0
268
- probs_hash[0].should be_within(1e-10).of 32/36.0
269
- probs_hash.values.inject(:+).should be_within(1e-9).of 1.0
270
- end
271
-
272
- it "should calculate aggregate probabilities" do
273
- probs = @die.probabilities
274
-
275
- probs.p_gt(0).should be_within(1e-10).of 4/36.0
276
- probs.p_gt(-2).should == 1.0
277
- probs.p_gt(1).should == 0.0
278
-
279
- probs.p_ge(1).should be_within(1e-10).of 4/36.0
280
- probs.p_ge(-1).should == 1.0
281
- probs.p_ge(2).should == 0.0
282
-
283
- probs.p_lt(1).should be_within(1e-10).of 32/36.0
284
- probs.p_lt(0).should == 0.0
285
- probs.p_lt(2).should == 1.0
286
-
287
- probs.p_le(0).should be_within(1e-10).of 32/36.0
288
- probs.p_le(-1).should == 0.0
289
- probs.p_le(1).should == 1.0
290
- end
291
-
292
- it "should apply mapping to final re-rolled result" do
293
- [0,1,0,0].each do |expected|
294
- @die.roll.should == expected
295
- @die.result.should == expected
296
- end
297
- end
298
-
299
- it "should explain how it got each result" do
300
- ["5", "[6+4] 10 Success", "[6+2] 8", "5"].each do |expected|
301
- @die.roll
302
- @die.explain_result.should == expected
303
- end
304
- end
305
- end # describe "with rerolls and maps"
306
- end # describe GamesDice::ComplexDie
1
+ # frozen_string_literal: true
2
+
3
+ require 'helpers'
4
+
5
+ describe GamesDice::ComplexDie do
6
+ before do
7
+ # Set state of default PRNG
8
+ srand(4567)
9
+ end
10
+
11
+ it 'should represent a basic die as an object' do
12
+ die = GamesDice::ComplexDie.new(6)
13
+ expect(die.min).to eql 1
14
+ expect(die.max).to eql 6
15
+ expect(die.sides).to eql 6
16
+ end
17
+
18
+ it "should return results based on Ruby's internal rand() by default" do
19
+ die = GamesDice::ComplexDie.new(10)
20
+ [5, 4, 10, 4, 7, 8, 1, 9].each do |expected|
21
+ expect(die.roll.value).to eql expected
22
+ expect(die.result.value).to eql expected
23
+ end
24
+ end
25
+
26
+ it 'should use any object with a rand(Integer) method' do
27
+ prng = TestPRNG.new
28
+ die = GamesDice::ComplexDie.new(20, prng: prng)
29
+ [16, 7, 3, 11, 16, 18, 20, 7].each do |expected|
30
+ expect(die.roll.value).to eql expected
31
+ expect(die.result.value).to eql expected
32
+ end
33
+ end
34
+
35
+ it 'should optionally accept a rerolls param' do
36
+ GamesDice::ComplexDie.new(10, rerolls: [])
37
+ GamesDice::ComplexDie.new(10, rerolls: [GamesDice::RerollRule.new(6, :<=, :reroll_add)])
38
+ GamesDice::ComplexDie.new(10,
39
+ rerolls: [GamesDice::RerollRule.new(6, :<=, :reroll_add),
40
+ GamesDice::RerollRule.new(1, :>=, :reroll_subtract)])
41
+ GamesDice::ComplexDie.new(10, rerolls: [[6, :<=, :reroll_add]])
42
+ GamesDice::ComplexDie.new(10, rerolls: [[6, :<=, :reroll_add], [1, :>=, :reroll_subtract]])
43
+
44
+ expect(lambda do
45
+ GamesDice::ComplexDie.new(10, rerolls: 7)
46
+ end).to raise_error(TypeError)
47
+
48
+ expect(lambda do
49
+ GamesDice::ComplexDie.new(10, rerolls: ['hello'])
50
+ end).to raise_error(TypeError)
51
+
52
+ expect(lambda do
53
+ GamesDice::ComplexDie.new(10, rerolls: [GamesDice::RerollRule.new(6, :<=, :reroll_add), :reroll_add])
54
+ end).to raise_error(TypeError)
55
+
56
+ expect(lambda do
57
+ GamesDice::ComplexDie.new(10, rerolls: [7])
58
+ end).to raise_error(TypeError)
59
+
60
+ expect(lambda do
61
+ GamesDice::ComplexDie.new(10, rerolls: [['hello']])
62
+ end).to raise_error(ArgumentError)
63
+
64
+ expect(lambda do
65
+ GamesDice::ComplexDie.new(10, rerolls: [[6, :<=, :reroll_add], :reroll_add])
66
+ end).to raise_error(TypeError)
67
+ end
68
+
69
+ it 'should optionally accept a maps param' do
70
+ GamesDice::ComplexDie.new(10, maps: [])
71
+ GamesDice::ComplexDie.new(10, maps: [GamesDice::MapRule.new(7, :<=, 1)])
72
+ GamesDice::ComplexDie.new(10, maps: [GamesDice::MapRule.new(7, :<=, 1), GamesDice::MapRule.new(1, :>, -1)])
73
+ GamesDice::ComplexDie.new(10, maps: [[7, :<=, 1]])
74
+ GamesDice::ComplexDie.new(10, maps: [[7, :<=, 1], [1, :>, -1]])
75
+
76
+ expect(lambda do
77
+ GamesDice::ComplexDie.new(10, maps: 7)
78
+ end).to raise_error(TypeError)
79
+
80
+ expect(lambda do
81
+ GamesDice::ComplexDie.new(10, maps: [7])
82
+ end).to raise_error(TypeError)
83
+
84
+ expect(lambda do
85
+ GamesDice::ComplexDie.new(10, maps: [[7]])
86
+ end).to raise_error(ArgumentError)
87
+
88
+ expect(lambda do
89
+ GamesDice::ComplexDie.new(10, maps: ['hello'])
90
+ end).to raise_error(TypeError)
91
+
92
+ expect(lambda do
93
+ GamesDice::ComplexDie.new(10,
94
+ maps: [GamesDice::MapRule.new(7, :<=, 1),
95
+ GamesDice::RerollRule.new(6, :<=, :reroll_add)])
96
+ end).to raise_error(TypeError)
97
+ end
98
+
99
+ describe 'with rerolls' do
100
+ it 'should calculate correct minimum and maximum results' do
101
+ die = GamesDice::ComplexDie.new(10, rerolls: [GamesDice::RerollRule.new(10, :<=, :reroll_add, 3)])
102
+ expect(die.min).to eql 1
103
+ expect(die.max).to eql 40
104
+
105
+ die = GamesDice::ComplexDie.new(10, rerolls: [[1, :>=, :reroll_subtract]])
106
+ expect(die.min).to eql(-9)
107
+ expect(die.max).to eql 10
108
+
109
+ die = GamesDice::ComplexDie.new(10, rerolls: [GamesDice::RerollRule.new(10, :<=, :reroll_add)])
110
+ expect(die.min).to eql 1
111
+ expect(die.max).to eql 10_010
112
+ end
113
+
114
+ it 'should simulate a d10 that rerolls and adds on a result of 10' do
115
+ die = GamesDice::ComplexDie.new(10, rerolls: [[10, :<=, :reroll_add]])
116
+ [5, 4, 14, 7, 8, 1, 9].each do |expected|
117
+ expect(die.roll.value).to eql expected
118
+ expect(die.result.value).to eql expected
119
+ end
120
+ end
121
+
122
+ it 'should explain how it got results outside range 1 to 10 on a d10' do
123
+ die = GamesDice::ComplexDie.new(10, rerolls: [[10, :<=, :reroll_add], [1, :>=, :reroll_subtract]])
124
+ ['5', '4', '[10+4] 14', '7', '8', '[1-9] -8'].each do |expected|
125
+ die.roll
126
+ expect(die.explain_result).to eql expected
127
+ end
128
+ end
129
+
130
+ it 'should calculate an expected result' do
131
+ die = GamesDice::ComplexDie.new(10, rerolls: [[10, :<=, :reroll_add], [1, :>=, :reroll_subtract]])
132
+ expect(die.probabilities.expected).to be_within(1e-10).of 5.5
133
+
134
+ die = GamesDice::ComplexDie.new(10, rerolls: [GamesDice::RerollRule.new(1, :<=, :reroll_use_best, 1)])
135
+ expect(die.probabilities.expected).to be_within(1e-10).of 7.15
136
+
137
+ die = GamesDice::ComplexDie.new(10, rerolls: [GamesDice::RerollRule.new(1, :<=, :reroll_use_worst, 2)])
138
+ expect(die.probabilities.expected).to be_within(1e-10).of 3.025
139
+
140
+ die = GamesDice::ComplexDie.new(6, rerolls: [GamesDice::RerollRule.new(6, :<=, :reroll_add)])
141
+ expect(die.probabilities.expected).to be_within(1e-10).of 4.2
142
+
143
+ die = GamesDice::ComplexDie.new(8, rerolls: [GamesDice::RerollRule.new(1, :>=, :reroll_use_best)])
144
+ expect(die.probabilities.expected).to be_within(1e-10).of 5.0
145
+
146
+ die = GamesDice::ComplexDie.new(4, rerolls: [GamesDice::RerollRule.new(1, :>=, :reroll_replace, 1)])
147
+ expect(die.probabilities.expected).to be_within(1e-10).of 2.875
148
+ end
149
+
150
+ it 'should calculate probabilities of each possible result' do
151
+ die = GamesDice::ComplexDie.new(6, rerolls: [GamesDice::RerollRule.new(7, :>, :reroll_add, 1)])
152
+ probs = die.probabilities.to_h
153
+ expect(probs[11]).to be_within(1e-10).of 2 / 36.0
154
+ expect(probs[8]).to be_within(1e-10).of 5 / 36.0
155
+ expect(probs.values.inject(:+)).to be_within(1e-9).of 1.0
156
+
157
+ die = GamesDice::ComplexDie.new(10, rerolls: [GamesDice::RerollRule.new(10, :<=, :reroll_add)])
158
+ probs = die.probabilities.to_h
159
+ expect(probs[8]).to be_within(1e-10).of 0.1
160
+ expect(probs[10]).to be_nil
161
+ expect(probs[13]).to be_within(1e-10).of 0.01
162
+ expect(probs[27]).to be_within(1e-10).of 0.001
163
+ expect(probs.values.inject(:+)).to be_within(1e-9).of 1.0
164
+
165
+ die = GamesDice::ComplexDie.new(6, rerolls: [GamesDice::RerollRule.new(1, :>=, :reroll_replace, 1)])
166
+ probs = die.probabilities.to_h
167
+ expect(probs[1]).to be_within(1e-10).of 1 / 36.0
168
+ expect(probs[2]).to be_within(1e-10).of 7 / 36.0
169
+ expect(probs.values.inject(:+)).to be_within(1e-9).of 1.0
170
+ end
171
+
172
+ it 'should calculate aggregate probabilities' do
173
+ die = GamesDice::ComplexDie.new(6, rerolls: [GamesDice::RerollRule.new(7, :>, :reroll_add, 1)])
174
+ probs = die.probabilities
175
+ expect(probs.p_gt(7)).to be_within(1e-10).of 15 / 36.0
176
+ expect(probs.p_gt(-10)).to eql 1.0
177
+ expect(probs.p_gt(12)).to eql 0.0
178
+
179
+ expect(probs.p_ge(7)).to be_within(1e-10).of 21 / 36.0
180
+ expect(probs.p_ge(2)).to eql 1.0
181
+ expect(probs.p_ge(15)).to eql 0.0
182
+
183
+ expect(probs.p_lt(7)).to be_within(1e-10).of 15 / 36.0
184
+ expect(probs.p_lt(-10)).to eql 0.0
185
+ expect(probs.p_lt(13)).to eql 1.0
186
+
187
+ expect(probs.p_le(7)).to be_within(1e-10).of 21 / 36.0
188
+ expect(probs.p_le(1)).to eql 0.0
189
+ expect(probs.p_le(12)).to eql 1.0
190
+ end
191
+ end
192
+
193
+ describe 'with maps' do
194
+ it 'should calculate correct minimum and maximum results' do
195
+ die = GamesDice::ComplexDie.new(10, maps: [GamesDice::MapRule.new(7, :<=, 1, 'S')])
196
+ expect(die.min).to eql 0
197
+ expect(die.max).to eql 1
198
+ end
199
+
200
+ it 'should simulate a d10 that scores 1 for success on a value of 7 or more' do
201
+ die = GamesDice::ComplexDie.new(10, maps: [[7, :<=, 1, 'S']])
202
+ [0, 0, 1, 0, 1, 1, 0, 1].each do |expected|
203
+ expect(die.roll.value).to eql expected
204
+ expect(die.result.value).to eql expected
205
+ end
206
+ end
207
+
208
+ it 'should label the mappings applied with the provided names' do
209
+ die = GamesDice::ComplexDie.new(10, maps: [[7, :<=, 1, 'S'], [1, :>=, -1, 'F']])
210
+ ['5', '4', '10 S', '4', '7 S', '8 S', '1 F', '9 S'].each do |expected|
211
+ die.roll
212
+ expect(die.explain_result).to eql expected
213
+ end
214
+ end
215
+
216
+ it 'should calculate an expected result' do
217
+ die = GamesDice::ComplexDie.new(10,
218
+ maps: [GamesDice::MapRule.new(7, :<=, 1, 'S'),
219
+ GamesDice::MapRule.new(1, :>=, -1, 'F')])
220
+ expect(die.probabilities.expected).to be_within(1e-10).of 0.3
221
+ end
222
+
223
+ it 'should calculate probabilities of each possible result' do
224
+ die = GamesDice::ComplexDie.new(10,
225
+ maps: [GamesDice::MapRule.new(7, :<=, 1, 'S'),
226
+ GamesDice::MapRule.new(1, :>=, -1, 'F')])
227
+ probs_hash = die.probabilities.to_h
228
+ expect(probs_hash[1]).to be_within(1e-10).of 0.4
229
+ expect(probs_hash[0]).to be_within(1e-10).of 0.5
230
+ expect(probs_hash[-1]).to be_within(1e-10).of 0.1
231
+ expect(probs_hash.values.inject(:+)).to be_within(1e-9).of 1.0
232
+ end
233
+
234
+ it 'should calculate aggregate probabilities' do
235
+ die = GamesDice::ComplexDie.new(10,
236
+ maps: [GamesDice::MapRule.new(7, :<=, 1, 'S'),
237
+ GamesDice::MapRule.new(1, :>=, -1, 'F')])
238
+ probs = die.probabilities
239
+ expect(probs.p_gt(-2)).to eql 1.0
240
+ expect(probs.p_gt(-1)).to be_within(1e-10).of 0.9
241
+ expect(probs.p_gt(1)).to eql 0.0
242
+
243
+ expect(probs.p_ge(1)).to be_within(1e-10).of 0.4
244
+ expect(probs.p_ge(-1)).to eql 1.0
245
+ expect(probs.p_ge(2)).to eql 0.0
246
+
247
+ expect(probs.p_lt(1)).to be_within(1e-10).of 0.6
248
+ expect(probs.p_lt(-1)).to eql 0.0
249
+ expect(probs.p_lt(2)).to eql 1.0
250
+
251
+ expect(probs.p_le(-1)).to be_within(1e-10).of 0.1
252
+ expect(probs.p_le(-2)).to eql 0.0
253
+ expect(probs.p_le(1)).to eql 1.0
254
+ end
255
+ end
256
+
257
+ describe 'with rerolls and maps together' do
258
+ before do
259
+ @die = GamesDice::ComplexDie.new(6,
260
+ rerolls: [[6, :<=, :reroll_add]],
261
+ maps: [GamesDice::MapRule.new(9, :<=, 1, 'Success')])
262
+ end
263
+
264
+ it 'should calculate correct minimum and maximum results' do
265
+ expect(@die.min).to eql 0
266
+ expect(@die.max).to eql 1
267
+ end
268
+
269
+ it 'should calculate an expected result' do
270
+ expect(@die.probabilities.expected).to be_within(1e-10).of 4 / 36.0
271
+ end
272
+
273
+ it 'should calculate probabilities of each possible result' do
274
+ probs_hash = @die.probabilities.to_h
275
+ expect(probs_hash[1]).to be_within(1e-10).of 4 / 36.0
276
+ expect(probs_hash[0]).to be_within(1e-10).of 32 / 36.0
277
+ expect(probs_hash.values.inject(:+)).to be_within(1e-9).of 1.0
278
+ end
279
+
280
+ it 'should calculate aggregate probabilities' do
281
+ probs = @die.probabilities
282
+
283
+ expect(probs.p_gt(0)).to be_within(1e-10).of 4 / 36.0
284
+ expect(probs.p_gt(-2)).to eql 1.0
285
+ expect(probs.p_gt(1)).to eql 0.0
286
+
287
+ expect(probs.p_ge(1)).to be_within(1e-10).of 4 / 36.0
288
+ expect(probs.p_ge(-1)).to eql 1.0
289
+ expect(probs.p_ge(2)).to eql 0.0
290
+
291
+ expect(probs.p_lt(1)).to be_within(1e-10).of 32 / 36.0
292
+ expect(probs.p_lt(0)).to eql 0.0
293
+ expect(probs.p_lt(2)).to eql 1.0
294
+
295
+ expect(probs.p_le(0)).to be_within(1e-10).of 32 / 36.0
296
+ expect(probs.p_le(-1)).to eql 0.0
297
+ expect(probs.p_le(1)).to eql 1.0
298
+ end
299
+
300
+ it 'should apply mapping to final re-rolled result' do
301
+ [0, 1, 0, 0].each do |expected|
302
+ expect(@die.roll.value).to eql expected
303
+ expect(@die.result.value).to eql expected
304
+ end
305
+ end
306
+
307
+ it 'should explain how it got each result' do
308
+ ['5', '[6+4] 10 Success', '[6+2] 8', '5'].each do |expected|
309
+ @die.roll
310
+ expect(@die.explain_result).to eql expected
311
+ end
312
+ end
313
+ end
314
+ end