games_dice 0.3.12 → 0.4.0

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