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.
- checksums.yaml +7 -0
- data/.rubocop.yml +15 -0
- data/.travis.yml +9 -12
- data/CHANGELOG.md +29 -13
- data/Gemfile +2 -0
- data/README.md +5 -5
- data/Rakefile +14 -11
- data/ext/games_dice/extconf.rb +4 -22
- data/ext/games_dice/probabilities.c +1 -1
- data/games_dice.gemspec +26 -28
- data/lib/games_dice/bunch.rb +241 -247
- data/lib/games_dice/complex_die.rb +287 -303
- data/lib/games_dice/complex_die_helpers.rb +68 -0
- data/lib/games_dice/constants.rb +10 -10
- data/lib/games_dice/dice.rb +146 -143
- data/lib/games_dice/die.rb +101 -97
- data/lib/games_dice/die_result.rb +193 -189
- data/lib/games_dice/map_rule.rb +72 -70
- data/lib/games_dice/marshal.rb +18 -13
- data/lib/games_dice/parser.rb +219 -218
- data/lib/games_dice/reroll_rule.rb +76 -77
- data/lib/games_dice/version.rb +3 -1
- data/lib/games_dice.rb +19 -16
- data/spec/bunch_spec.rb +399 -421
- data/spec/complex_die_spec.rb +314 -306
- data/spec/dice_spec.rb +33 -34
- data/spec/die_result_spec.rb +163 -170
- data/spec/die_spec.rb +81 -82
- data/spec/helpers.rb +26 -22
- data/spec/map_rule_spec.rb +40 -44
- data/spec/parser_spec.rb +106 -82
- data/spec/probability_spec.rb +530 -527
- data/spec/readme_spec.rb +404 -384
- data/spec/reroll_rule_spec.rb +40 -44
- metadata +63 -74
- data/lib/games_dice/probabilities.rb +0 -445
data/spec/complex_die_spec.rb
CHANGED
@@ -1,306 +1,314 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
before do
|
7
|
-
# Set state of default PRNG
|
8
|
-
srand(4567)
|
9
|
-
end
|
10
|
-
|
11
|
-
it
|
12
|
-
die = GamesDice::ComplexDie.new(6)
|
13
|
-
die.min.
|
14
|
-
die.max.
|
15
|
-
die.sides.
|
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.
|
22
|
-
die.result.
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
it
|
27
|
-
prng = TestPRNG.new
|
28
|
-
die = GamesDice::ComplexDie.new(20, :
|
29
|
-
[16,7,3,11,16,18,20,7].each do |expected|
|
30
|
-
die.roll.
|
31
|
-
die.result.
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
it
|
36
|
-
GamesDice::ComplexDie.new(
|
37
|
-
GamesDice::ComplexDie.new(
|
38
|
-
GamesDice::ComplexDie.new(
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
GamesDice::ComplexDie.new(
|
71
|
-
GamesDice::ComplexDie.new(
|
72
|
-
GamesDice::ComplexDie.new(
|
73
|
-
GamesDice::ComplexDie.new(
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
die
|
103
|
-
die.
|
104
|
-
|
105
|
-
|
106
|
-
die
|
107
|
-
die.
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
die = GamesDice::ComplexDie.new(10, :
|
132
|
-
die.probabilities.expected.
|
133
|
-
|
134
|
-
die = GamesDice::ComplexDie.new(10, :
|
135
|
-
die.probabilities.expected.
|
136
|
-
|
137
|
-
die = GamesDice::ComplexDie.new(
|
138
|
-
die.probabilities.expected.
|
139
|
-
|
140
|
-
die = GamesDice::ComplexDie.new(
|
141
|
-
die.probabilities.expected.
|
142
|
-
|
143
|
-
die = GamesDice::ComplexDie.new(
|
144
|
-
die.probabilities.expected.
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
probs
|
153
|
-
|
154
|
-
|
155
|
-
probs
|
156
|
-
|
157
|
-
|
158
|
-
probs
|
159
|
-
probs[
|
160
|
-
probs
|
161
|
-
|
162
|
-
|
163
|
-
probs
|
164
|
-
|
165
|
-
|
166
|
-
probs
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
probs
|
175
|
-
|
176
|
-
probs.
|
177
|
-
probs.
|
178
|
-
|
179
|
-
|
180
|
-
probs.
|
181
|
-
probs.
|
182
|
-
|
183
|
-
|
184
|
-
probs.
|
185
|
-
probs.
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
probs
|
239
|
-
probs.
|
240
|
-
probs.
|
241
|
-
|
242
|
-
|
243
|
-
probs.
|
244
|
-
probs.
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
end
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
probs
|
282
|
-
|
283
|
-
probs.
|
284
|
-
probs.
|
285
|
-
probs.
|
286
|
-
|
287
|
-
probs.
|
288
|
-
probs.
|
289
|
-
probs.
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
@die.
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
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
|