games_dice 0.3.12 → 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,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