games_dice 0.3.12 → 0.4.2
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 +5 -5
- data/.github/workflows/ci.yml +38 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +53 -0
- data/.yardopts +1 -1
- data/CHANGELOG.md +18 -0
- data/Gemfile +12 -0
- data/README.md +11 -33
- data/Rakefile +10 -23
- data/ext/games_dice/extconf.rb +4 -22
- data/ext/games_dice/games_dice.c +1 -1
- data/ext/games_dice/probabilities.c +128 -9
- data/ext/games_dice/probabilities.h +1 -1
- data/games_dice.gemspec +22 -36
- data/lib/games_dice/bunch.rb +203 -247
- data/lib/games_dice/bunch_helpers.rb +68 -0
- data/lib/games_dice/complex_die.rb +151 -270
- data/lib/games_dice/complex_die_helpers.rb +260 -60
- data/lib/games_dice/constants.rb +10 -10
- data/lib/games_dice/dice.rb +153 -143
- data/lib/games_dice/die.rb +101 -97
- data/lib/games_dice/die_result.rb +206 -189
- data/lib/games_dice/map_rule.rb +72 -70
- data/lib/games_dice/marshal.rb +41 -13
- data/lib/games_dice/parser.rb +245 -218
- data/lib/games_dice/reroll_rule.rb +76 -77
- data/lib/games_dice/version.rb +4 -1
- data/lib/games_dice.rb +23 -25
- data/spec/bunch_spec.rb +399 -420
- data/spec/complex_die_spec.rb +314 -305
- data/spec/dice_spec.rb +33 -34
- data/spec/die_result_spec.rb +174 -181
- data/spec/die_spec.rb +81 -81
- data/spec/helpers.rb +25 -25
- data/spec/map_rule_spec.rb +40 -44
- data/spec/parser_spec.rb +106 -82
- data/spec/probability_spec.rb +522 -526
- data/spec/readme_spec.rb +410 -390
- data/spec/reroll_rule_spec.rb +40 -44
- metadata +17 -129
- data/.travis.yml +0 -14
- data/lib/games_dice/prob_helpers.rb +0 -259
- data/lib/games_dice/probabilities.rb +0 -244
data/spec/probability_spec.rb
CHANGED
|
@@ -1,526 +1,522 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
pr.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
pr2
|
|
32
|
-
(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
h
|
|
38
|
-
h
|
|
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
|
-
h
|
|
66
|
-
h
|
|
67
|
-
h[
|
|
68
|
-
h[
|
|
69
|
-
h[
|
|
70
|
-
h[
|
|
71
|
-
h[
|
|
72
|
-
h[
|
|
73
|
-
h[
|
|
74
|
-
h[
|
|
75
|
-
h[
|
|
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
|
-
h
|
|
101
|
-
h
|
|
102
|
-
h[-
|
|
103
|
-
h[
|
|
104
|
-
h[1].
|
|
105
|
-
h[
|
|
106
|
-
h[
|
|
107
|
-
h[
|
|
108
|
-
h[
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
h
|
|
118
|
-
h
|
|
119
|
-
h[
|
|
120
|
-
h[
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
it
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
it
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
it
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
pr4.p_ge(
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
it
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
pr4.p_le(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
it
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
it
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
it
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
h
|
|
425
|
-
h.
|
|
426
|
-
h[
|
|
427
|
-
h[
|
|
428
|
-
h[
|
|
429
|
-
h[
|
|
430
|
-
h[
|
|
431
|
-
h[
|
|
432
|
-
h[
|
|
433
|
-
h[
|
|
434
|
-
h[
|
|
435
|
-
h[
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
end
|
|
454
|
-
|
|
455
|
-
it
|
|
456
|
-
d6 =
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
h
|
|
470
|
-
h.
|
|
471
|
-
h[
|
|
472
|
-
h[
|
|
473
|
-
h[
|
|
474
|
-
h[
|
|
475
|
-
h[
|
|
476
|
-
h[
|
|
477
|
-
h[
|
|
478
|
-
h[
|
|
479
|
-
h[
|
|
480
|
-
h[
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
h
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
h
|
|
493
|
-
h.
|
|
494
|
-
h[
|
|
495
|
-
h[
|
|
496
|
-
h[
|
|
497
|
-
h[
|
|
498
|
-
h[
|
|
499
|
-
h[
|
|
500
|
-
h[
|
|
501
|
-
h[
|
|
502
|
-
h[
|
|
503
|
-
h[
|
|
504
|
-
h[
|
|
505
|
-
h[
|
|
506
|
-
h[
|
|
507
|
-
h[
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
pd6.p_gt(4).should be_within(1e-10).of 1.0/3
|
|
524
|
-
end
|
|
525
|
-
end
|
|
526
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'helpers'
|
|
4
|
+
|
|
5
|
+
describe GamesDice::Probabilities do
|
|
6
|
+
describe 'class methods' do
|
|
7
|
+
describe '#new' do
|
|
8
|
+
it 'create a new distribution from an array and offset' do
|
|
9
|
+
pr = described_class.new([1.0], 1)
|
|
10
|
+
expect(pr).to be_a described_class
|
|
11
|
+
expect(pr.to_h).to be_valid_distribution
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'raise an error if passed incorrect parameter types' do
|
|
15
|
+
expect { described_class.new([nil], 20) }.to raise_error TypeError
|
|
16
|
+
expect { described_class.new([0.3, nil, 0.5], 7) }.to raise_error TypeError
|
|
17
|
+
expect { described_class.new([0.3, 0.2, 0.5], {}) }.to raise_error TypeError
|
|
18
|
+
expect { described_class.new({ x: :y }, 17) }.to raise_error TypeError
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'raise an error if distribution is incomplete or inaccurate' do
|
|
22
|
+
expect { described_class.new([0.3, 0.2, 0.6], 3) }.to raise_error ArgumentError
|
|
23
|
+
expect { described_class.new([], 1) }.to raise_error ArgumentError
|
|
24
|
+
expect { described_class.new([0.9], 1) }.to raise_error ArgumentError
|
|
25
|
+
expect { described_class.new([-0.9, 0.2, 0.9], 1) }.to raise_error ArgumentError
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#for_fair_die' do
|
|
30
|
+
it 'create a new distribution based on number of sides' do
|
|
31
|
+
pr2 = described_class.for_fair_die(2)
|
|
32
|
+
expect(pr2).to be_a described_class
|
|
33
|
+
expect(pr2.to_h).to eql({ 1 => 0.5, 2 => 0.5 })
|
|
34
|
+
(1..20).each do |sides|
|
|
35
|
+
pr = described_class.for_fair_die(sides)
|
|
36
|
+
expect(pr).to be_a described_class
|
|
37
|
+
h = pr.to_h
|
|
38
|
+
expect(h).to be_valid_distribution
|
|
39
|
+
expect(h.keys.count).to eql sides
|
|
40
|
+
h.each_value { |v| expect(v).to be_within(1e-10).of 1.0 / sides }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'raise an error if number of sides is not an integer' do
|
|
45
|
+
expect { described_class.for_fair_die({}) }.to raise_error TypeError
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'raise an error if number of sides is too low or too high' do
|
|
49
|
+
expect { described_class.for_fair_die(0) }.to raise_error ArgumentError
|
|
50
|
+
expect { described_class.for_fair_die(1_000_001) }.to raise_error ArgumentError
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe '#add_distributions' do
|
|
55
|
+
it 'combine two distributions to create a third one' do
|
|
56
|
+
d4a = described_class.new([1.0 / 4, 1.0 / 4, 1.0 / 4, 1.0 / 4], 1)
|
|
57
|
+
d4b = described_class.new([1.0 / 10, 2.0 / 10, 3.0 / 10, 4.0 / 10], 1)
|
|
58
|
+
pr = described_class.add_distributions(d4a, d4b)
|
|
59
|
+
expect(pr.to_h).to be_valid_distribution
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'calculate a classic 2d6 distribution accurately' do
|
|
63
|
+
d6 = described_class.for_fair_die(6)
|
|
64
|
+
pr = described_class.add_distributions(d6, d6)
|
|
65
|
+
h = pr.to_h
|
|
66
|
+
expect(h).to be_valid_distribution
|
|
67
|
+
expect(h[2]).to be_within(1e-9).of 1.0 / 36
|
|
68
|
+
expect(h[3]).to be_within(1e-9).of 2.0 / 36
|
|
69
|
+
expect(h[4]).to be_within(1e-9).of 3.0 / 36
|
|
70
|
+
expect(h[5]).to be_within(1e-9).of 4.0 / 36
|
|
71
|
+
expect(h[6]).to be_within(1e-9).of 5.0 / 36
|
|
72
|
+
expect(h[7]).to be_within(1e-9).of 6.0 / 36
|
|
73
|
+
expect(h[8]).to be_within(1e-9).of 5.0 / 36
|
|
74
|
+
expect(h[9]).to be_within(1e-9).of 4.0 / 36
|
|
75
|
+
expect(h[10]).to be_within(1e-9).of 3.0 / 36
|
|
76
|
+
expect(h[11]).to be_within(1e-9).of 2.0 / 36
|
|
77
|
+
expect(h[12]).to be_within(1e-9).of 1.0 / 36
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'raise an error if either parameter is not a GamesDice::Probabilities object' do
|
|
81
|
+
d10 = described_class.for_fair_die(10)
|
|
82
|
+
expect { described_class.add_distributions('', 6) }.to raise_error TypeError
|
|
83
|
+
expect { described_class.add_distributions(d10, 6) }.to raise_error TypeError
|
|
84
|
+
expect { described_class.add_distributions('', d10) }.to raise_error TypeError
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe '#add_distributions_mult' do
|
|
89
|
+
it 'combine two multiplied distributions to create a third one' do
|
|
90
|
+
d4a = described_class.new([1.0 / 4, 1.0 / 4, 1.0 / 4, 1.0 / 4], 1)
|
|
91
|
+
d4b = described_class.new([1.0 / 10, 2.0 / 10, 3.0 / 10, 4.0 / 10], 1)
|
|
92
|
+
pr = described_class.add_distributions_mult(2, d4a, -1, d4b)
|
|
93
|
+
expect(pr.to_h).to be_valid_distribution
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "calculate a distribution for '1d6 - 1d4' accurately" do
|
|
97
|
+
d6 = described_class.for_fair_die(6)
|
|
98
|
+
d4 = described_class.for_fair_die(4)
|
|
99
|
+
pr = described_class.add_distributions_mult(1, d6, -1, d4)
|
|
100
|
+
h = pr.to_h
|
|
101
|
+
expect(h).to be_valid_distribution
|
|
102
|
+
expect(h[-3]).to be_within(1e-9).of 1.0 / 24
|
|
103
|
+
expect(h[-2]).to be_within(1e-9).of 2.0 / 24
|
|
104
|
+
expect(h[-1]).to be_within(1e-9).of 3.0 / 24
|
|
105
|
+
expect(h[0]).to be_within(1e-9).of 4.0 / 24
|
|
106
|
+
expect(h[1]).to be_within(1e-9).of 4.0 / 24
|
|
107
|
+
expect(h[2]).to be_within(1e-9).of 4.0 / 24
|
|
108
|
+
expect(h[3]).to be_within(1e-9).of 3.0 / 24
|
|
109
|
+
expect(h[4]).to be_within(1e-9).of 2.0 / 24
|
|
110
|
+
expect(h[5]).to be_within(1e-9).of 1.0 / 24
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'add asymmetric distributions accurately' do
|
|
114
|
+
da = described_class.new([0.7, 0.0, 0.3], 2)
|
|
115
|
+
db = described_class.new([0.5, 0.3, 0.2], 2)
|
|
116
|
+
pr = described_class.add_distributions_mult(1, da, 2, db)
|
|
117
|
+
h = pr.to_h
|
|
118
|
+
expect(h).to be_valid_distribution
|
|
119
|
+
expect(h[6]).to be_within(1e-9).of 0.7 * 0.5
|
|
120
|
+
expect(h[8]).to be_within(1e-9).of (0.7 * 0.3) + (0.3 * 0.5)
|
|
121
|
+
expect(h[10]).to be_within(1e-9).of (0.7 * 0.2) + (0.3 * 0.3)
|
|
122
|
+
expect(h[12]).to be_within(1e-9).of 0.3 * 0.2
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'raise an error if passed incorrect objects for distributions' do
|
|
126
|
+
d10 = described_class.for_fair_die(10)
|
|
127
|
+
expect { described_class.add_distributions_mult(1, '', -1, 6) }.to raise_error TypeError
|
|
128
|
+
expect { described_class.add_distributions_mult(2, d10, 3, 6) }.to raise_error TypeError
|
|
129
|
+
expect { described_class.add_distributions_mult(1, '', -1, d10) }.to raise_error TypeError
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'raise an error if passed incorrect objects for multipliers' do
|
|
133
|
+
d10 = described_class.for_fair_die(10)
|
|
134
|
+
expect { described_class.add_distributions_mult({}, d10, [], d10) }.to raise_error TypeError
|
|
135
|
+
expect { described_class.add_distributions_mult([7], d10, 3, d10) }.to raise_error TypeError
|
|
136
|
+
expect { described_class.add_distributions_mult(1, d10, {}, d10) }.to raise_error TypeError
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
describe '#from_h' do
|
|
141
|
+
it 'create a Probabilities object from a valid hash' do
|
|
142
|
+
pr = described_class.from_h({ 7 => 0.5, 9 => 0.5 })
|
|
143
|
+
expect(pr).to be_a described_class
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'raise an ArgumentError when called with a non-valid hash' do
|
|
147
|
+
expect { described_class.from_h({ 7 => 0.5, 9 => 0.6 }) }.to raise_error ArgumentError
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'raise an TypeError when called with data that is not a hash' do
|
|
151
|
+
expect { described_class.from_h(:foo) }.to raise_error TypeError
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'raise a TypeError when called when keys and values are not all integers and floats' do
|
|
155
|
+
expect { described_class.from_h({ 'x' => 0.5, 9 => 0.5 }) }.to raise_error TypeError
|
|
156
|
+
expect { described_class.from_h({ 7 => [], 9 => 0.5 }) }.to raise_error TypeError
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it 'raise an ArgumentError when results are spread very far apart' do
|
|
160
|
+
expect { described_class.from_h({ 0 => 0.5, 2_000_000 => 0.5 }) }.to raise_error ArgumentError
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
describe 'instance methods' do
|
|
166
|
+
let(:pr2) { described_class.for_fair_die(2) }
|
|
167
|
+
let(:pr4) { described_class.for_fair_die(4) }
|
|
168
|
+
let(:pr6) { described_class.for_fair_die(6) }
|
|
169
|
+
let(:pr10) { described_class.for_fair_die(10) }
|
|
170
|
+
let(:pra) { described_class.new([0.4, 0.2, 0.4], -1) }
|
|
171
|
+
|
|
172
|
+
describe '#each' do
|
|
173
|
+
it 'iterate through all result/probability pairs' do
|
|
174
|
+
yielded = []
|
|
175
|
+
pr4.each { |r, p| yielded << [r, p] }
|
|
176
|
+
expect(yielded).to eql [[1, 0.25], [2, 0.25], [3, 0.25], [4, 0.25]]
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'skip zero probabilities' do
|
|
180
|
+
pr_plus_minus = described_class.new([0.5, 0.0, 0.5], -1)
|
|
181
|
+
yielded = []
|
|
182
|
+
pr_plus_minus.each { |r, p| yielded << [r, p] }
|
|
183
|
+
expect(yielded).to eql [[-1, 0.5], [1, 0.5]]
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
describe '#p_eql' do
|
|
188
|
+
it 'return probability of getting a number inside the range' do
|
|
189
|
+
expect(pr2.p_eql(2)).to be_within(1.0e-9).of 0.5
|
|
190
|
+
expect(pr4.p_eql(1)).to be_within(1.0e-9).of 0.25
|
|
191
|
+
expect(pr6.p_eql(6)).to be_within(1.0e-9).of 1.0 / 6
|
|
192
|
+
expect(pr10.p_eql(3)).to be_within(1.0e-9).of 0.1
|
|
193
|
+
expect(pra.p_eql(-1)).to be_within(1.0e-9).of 0.4
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it 'return 0.0 for values not covered by distribution' do
|
|
197
|
+
expect(pr2.p_eql(3)).to be 0.0
|
|
198
|
+
expect(pr4.p_eql(-1)).to be 0.0
|
|
199
|
+
expect(pr6.p_eql(8)).to be 0.0
|
|
200
|
+
expect(pr10.p_eql(11)).to be 0.0
|
|
201
|
+
expect(pra.p_eql(2)).to be 0.0
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it 'raise a TypeError if asked for probability of non-Integer' do
|
|
205
|
+
expect { pr2.p_eql([]) }.to raise_error TypeError
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
describe '#p_gt' do
|
|
210
|
+
it 'return probability of getting a number greater than target' do
|
|
211
|
+
expect(pr2.p_gt(1)).to be_within(1.0e-9).of 0.5
|
|
212
|
+
expect(pr4.p_gt(3)).to be_within(1.0e-9).of 0.25
|
|
213
|
+
expect(pr6.p_gt(2)).to be_within(1.0e-9).of 4.0 / 6
|
|
214
|
+
expect(pr10.p_gt(6)).to be_within(1.0e-9).of 0.4
|
|
215
|
+
|
|
216
|
+
# Trying more than one, due to possibilities of caching error (in pure Ruby implementation)
|
|
217
|
+
expect(pra.p_gt(-2)).to be_within(1.0e-9).of 1.0
|
|
218
|
+
expect(pra.p_gt(-1)).to be_within(1.0e-9).of 0.6
|
|
219
|
+
expect(pra.p_gt(0)).to be_within(1.0e-9).of 0.4
|
|
220
|
+
expect(pra.p_gt(1)).to be_within(1.0e-9).of 0.0
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'return 0.0 when the target number is equal or higher than maximum possible' do
|
|
224
|
+
expect(pr2.p_gt(2)).to be 0.0
|
|
225
|
+
expect(pr4.p_gt(5)).to be 0.0
|
|
226
|
+
expect(pr6.p_gt(6)).to be 0.0
|
|
227
|
+
expect(pr10.p_gt(20)).to be 0.0
|
|
228
|
+
expect(pra.p_gt(3)).to be 0.0
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
it 'return 1.0 when the target number is lower than minimum' do
|
|
232
|
+
expect(pr2.p_gt(0)).to be 1.0
|
|
233
|
+
expect(pr4.p_gt(-5)).to be 1.0
|
|
234
|
+
expect(pr6.p_gt(0)).to be 1.0
|
|
235
|
+
expect(pr10.p_gt(-200)).to be 1.0
|
|
236
|
+
expect(pra.p_gt(-2)).to be 1.0
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it 'raise a TypeError if asked for probability of non-Integer' do
|
|
240
|
+
expect { pr2.p_gt({}) }.to raise_error TypeError
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
describe '#p_ge' do
|
|
245
|
+
it 'return probability of getting a number greater than or equal to target' do
|
|
246
|
+
expect(pr2.p_ge(2)).to be_within(1.0e-9).of 0.5
|
|
247
|
+
expect(pr4.p_ge(3)).to be_within(1.0e-9).of 0.5
|
|
248
|
+
expect(pr6.p_ge(2)).to be_within(1.0e-9).of 5.0 / 6
|
|
249
|
+
expect(pr10.p_ge(6)).to be_within(1.0e-9).of 0.5
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it 'return 0.0 when the target number is higher than maximum possible' do
|
|
253
|
+
expect(pr2.p_ge(6)).to be 0.0
|
|
254
|
+
expect(pr4.p_ge(5)).to be 0.0
|
|
255
|
+
expect(pr6.p_ge(7)).to be 0.0
|
|
256
|
+
expect(pr10.p_ge(20)).to be 0.0
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it 'return 1.0 when the target number is lower than or equal to minimum possible' do
|
|
260
|
+
expect(pr2.p_ge(1)).to be 1.0
|
|
261
|
+
expect(pr4.p_ge(-5)).to be 1.0
|
|
262
|
+
expect(pr6.p_ge(1)).to be 1.0
|
|
263
|
+
expect(pr10.p_ge(-200)).to be 1.0
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it 'raise a TypeError if asked for probability of non-Integer' do
|
|
267
|
+
expect { pr4.p_ge({}) }.to raise_error TypeError
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
describe '#p_le' do
|
|
272
|
+
it 'return probability of getting a number less than or equal to target' do
|
|
273
|
+
expect(pr2.p_le(1)).to be_within(1.0e-9).of 0.5
|
|
274
|
+
expect(pr4.p_le(2)).to be_within(1.0e-9).of 0.5
|
|
275
|
+
expect(pr6.p_le(2)).to be_within(1.0e-9).of 2.0 / 6
|
|
276
|
+
expect(pr10.p_le(6)).to be_within(1.0e-9).of 0.6
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
it 'return 1.0 when the target number is higher than or equal to maximum possible' do
|
|
280
|
+
expect(pr2.p_le(6)).to be 1.0
|
|
281
|
+
expect(pr4.p_le(4)).to be 1.0
|
|
282
|
+
expect(pr6.p_le(7)).to be 1.0
|
|
283
|
+
expect(pr10.p_le(10)).to be 1.0
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
it 'return 0.0 when the target number is lower than minimum possible' do
|
|
287
|
+
expect(pr2.p_le(0)).to be 0.0
|
|
288
|
+
expect(pr4.p_le(-5)).to be 0.0
|
|
289
|
+
expect(pr6.p_le(0)).to be 0.0
|
|
290
|
+
expect(pr10.p_le(-200)).to be 0.0
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it 'raise a TypeError if asked for probability of non-Integer' do
|
|
294
|
+
expect { pr4.p_le([]) }.to raise_error TypeError
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
describe '#p_lt' do
|
|
299
|
+
it 'return probability of getting a number less than target' do
|
|
300
|
+
expect(pr2.p_lt(2)).to be_within(1.0e-9).of 0.5
|
|
301
|
+
expect(pr4.p_lt(3)).to be_within(1.0e-9).of 0.5
|
|
302
|
+
expect(pr6.p_lt(2)).to be_within(1.0e-9).of 1 / 6.0
|
|
303
|
+
expect(pr10.p_lt(6)).to be_within(1.0e-9).of 0.5
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it 'return 1.0 when the target number is higher than maximum possible' do
|
|
307
|
+
expect(pr2.p_lt(6)).to be 1.0
|
|
308
|
+
expect(pr4.p_lt(5)).to be 1.0
|
|
309
|
+
expect(pr6.p_lt(7)).to be 1.0
|
|
310
|
+
expect(pr10.p_lt(20)).to be 1.0
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
it 'return 0.0 when the target number is lower than or equal to minimum possible' do
|
|
314
|
+
expect(pr2.p_lt(1)).to be 0.0
|
|
315
|
+
expect(pr4.p_lt(-5)).to be 0.0
|
|
316
|
+
expect(pr6.p_lt(1)).to be 0.0
|
|
317
|
+
expect(pr10.p_lt(-200)).to be 0.0
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
it 'raise a TypeError if asked for probability of non-Integer' do
|
|
321
|
+
expect { pr6.p_lt({}) }.to raise_error TypeError
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
describe '#to_h' do
|
|
326
|
+
# This is used loads in other tests
|
|
327
|
+
it 'represent a valid distribution with each integer result associated with its probability' do
|
|
328
|
+
expect(pr2.to_h).to be_valid_distribution
|
|
329
|
+
expect(pr4.to_h).to be_valid_distribution
|
|
330
|
+
expect(pr6.to_h).to be_valid_distribution
|
|
331
|
+
expect(pr10.to_h).to be_valid_distribution
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
describe '#min' do
|
|
336
|
+
it 'return lowest possible result allowed by distribution' do
|
|
337
|
+
expect(pr2.min).to be 1
|
|
338
|
+
expect(pr4.min).to be 1
|
|
339
|
+
expect(pr6.min).to be 1
|
|
340
|
+
expect(pr10.min).to be 1
|
|
341
|
+
expect(described_class.add_distributions(pr6, pr10).min).to be 2
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
describe '#max' do
|
|
346
|
+
it 'return highest possible result allowed by distribution' do
|
|
347
|
+
expect(pr2.max).to be 2
|
|
348
|
+
expect(pr4.max).to be 4
|
|
349
|
+
expect(pr6.max).to be 6
|
|
350
|
+
expect(pr10.max).to be 10
|
|
351
|
+
expect(described_class.add_distributions(pr6, pr10).max).to be 16
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
describe '#expected' do
|
|
356
|
+
it 'return the weighted mean value' do
|
|
357
|
+
expect(pr2.expected).to be_within(1.0e-9).of 1.5
|
|
358
|
+
expect(pr4.expected).to be_within(1.0e-9).of 2.5
|
|
359
|
+
expect(pr6.expected).to be_within(1.0e-9).of 3.5
|
|
360
|
+
expect(pr10.expected).to be_within(1.0e-9).of 5.5
|
|
361
|
+
expect(described_class.add_distributions(pr6, pr10).expected).to be_within(1.0e-9).of 9.0
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
describe '#given_ge' do
|
|
366
|
+
it 'return a new distribution with probabilities calculated assuming value is >= target' do
|
|
367
|
+
pd = pr2.given_ge(2)
|
|
368
|
+
expect(pd.to_h).to eql({ 2 => 1.0 })
|
|
369
|
+
pd = pr10.given_ge(4)
|
|
370
|
+
expect(pd.to_h).to be_valid_distribution
|
|
371
|
+
expect(pd.p_eql(3)).to be 0.0
|
|
372
|
+
expect(pd.p_eql(10)).to be_within(1.0e-9).of 0.1 / 0.7
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
it 'raise a TypeError if asked for probability of non-Integer' do
|
|
376
|
+
expect { pr10.given_ge([]) }.to raise_error TypeError
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
describe '#given_le' do
|
|
381
|
+
it 'return a new distribution with probabilities calculated assuming value is <= target' do
|
|
382
|
+
pd = pr2.given_le(2)
|
|
383
|
+
expect(pd.to_h).to eql({ 1 => 0.5, 2 => 0.5 })
|
|
384
|
+
pd = pr10.given_le(4)
|
|
385
|
+
expect(pd.to_h).to be_valid_distribution
|
|
386
|
+
expect(pd.p_eql(3)).to be_within(1.0e-9).of 0.1 / 0.4
|
|
387
|
+
expect(pd.p_eql(10)).to be 0.0
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
it 'raise a TypeError if asked for probability of non-Integer' do
|
|
391
|
+
expect { pr10.given_le({}) }.to raise_error TypeError
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
describe '#repeat_sum' do
|
|
396
|
+
it 'output a valid distribution if params are valid' do
|
|
397
|
+
d4a = described_class.new([1.0 / 4, 1.0 / 4, 1.0 / 4, 1.0 / 4], 1)
|
|
398
|
+
d4b = described_class.new([1.0 / 10, 2.0 / 10, 3.0 / 10, 4.0 / 10], 1)
|
|
399
|
+
pr = d4a.repeat_sum(7)
|
|
400
|
+
expect(pr.to_h).to be_valid_distribution
|
|
401
|
+
pr = d4b.repeat_sum(12)
|
|
402
|
+
expect(pr.to_h).to be_valid_distribution
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
it 'raise an error if any param is unexpected type' do
|
|
406
|
+
d6 = described_class.for_fair_die(6)
|
|
407
|
+
expect { d6.repeat_sum({}) }.to raise_error TypeError
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
it 'raise an error if distribution would have more than a million results' do
|
|
411
|
+
d1000 = described_class.for_fair_die(1000)
|
|
412
|
+
expect { d1000.repeat_sum(11_000) }.to raise_error(RuntimeError, /Too many probability slots/)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
it "calculate a '3d6' distribution accurately" do
|
|
416
|
+
d6 = described_class.for_fair_die(6)
|
|
417
|
+
pr = d6.repeat_sum(3)
|
|
418
|
+
h = pr.to_h
|
|
419
|
+
expect(h).to be_valid_distribution
|
|
420
|
+
expect(h[3]).to be_within(1e-9).of 1.0 / 216
|
|
421
|
+
expect(h[4]).to be_within(1e-9).of 3.0 / 216
|
|
422
|
+
expect(h[5]).to be_within(1e-9).of 6.0 / 216
|
|
423
|
+
expect(h[6]).to be_within(1e-9).of 10.0 / 216
|
|
424
|
+
expect(h[7]).to be_within(1e-9).of 15.0 / 216
|
|
425
|
+
expect(h[8]).to be_within(1e-9).of 21.0 / 216
|
|
426
|
+
expect(h[9]).to be_within(1e-9).of 25.0 / 216
|
|
427
|
+
expect(h[10]).to be_within(1e-9).of 27.0 / 216
|
|
428
|
+
expect(h[11]).to be_within(1e-9).of 27.0 / 216
|
|
429
|
+
expect(h[12]).to be_within(1e-9).of 25.0 / 216
|
|
430
|
+
expect(h[13]).to be_within(1e-9).of 21.0 / 216
|
|
431
|
+
expect(h[14]).to be_within(1e-9).of 15.0 / 216
|
|
432
|
+
expect(h[15]).to be_within(1e-9).of 10.0 / 216
|
|
433
|
+
expect(h[16]).to be_within(1e-9).of 6.0 / 216
|
|
434
|
+
expect(h[17]).to be_within(1e-9).of 3.0 / 216
|
|
435
|
+
expect(h[18]).to be_within(1e-9).of 1.0 / 216
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
describe '#repeat_n_sum_k' do
|
|
440
|
+
it 'output a valid distribution if params are valid' do
|
|
441
|
+
d4a = described_class.new([1.0 / 4, 1.0 / 4, 1.0 / 4, 1.0 / 4], 1)
|
|
442
|
+
d4b = described_class.new([1.0 / 10, 2.0 / 10, 3.0 / 10, 4.0 / 10], 1)
|
|
443
|
+
pr = d4a.repeat_n_sum_k(3, 2)
|
|
444
|
+
expect(pr.to_h).to be_valid_distribution
|
|
445
|
+
pr = d4b.repeat_n_sum_k(12, 4)
|
|
446
|
+
expect(pr.to_h).to be_valid_distribution
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
it 'raise an error if any param is unexpected type' do
|
|
450
|
+
d6 = described_class.for_fair_die(6)
|
|
451
|
+
expect { d6.repeat_n_sum_k({}, 10) }.to raise_error TypeError
|
|
452
|
+
expect { d6.repeat_n_sum_k(10, {}) }.to raise_error TypeError
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
it 'raise an error if n is greater than 170' do
|
|
456
|
+
d6 = described_class.for_fair_die(6)
|
|
457
|
+
expect { d6.repeat_n_sum_k(171, 10) }.to raise_error(RuntimeError, /Too many dice/)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
it "calculate a '4d6 keep best 3' distribution accurately" do
|
|
461
|
+
d6 = described_class.for_fair_die(6)
|
|
462
|
+
pr = d6.repeat_n_sum_k(4, 3)
|
|
463
|
+
h = pr.to_h
|
|
464
|
+
expect(h).to be_valid_distribution
|
|
465
|
+
expect(h[3]).to be_within(1e-10).of 1 / 1296.0
|
|
466
|
+
expect(h[4]).to be_within(1e-10).of 4 / 1296.0
|
|
467
|
+
expect(h[5]).to be_within(1e-10).of 10 / 1296.0
|
|
468
|
+
expect(h[6]).to be_within(1e-10).of 21 / 1296.0
|
|
469
|
+
expect(h[7]).to be_within(1e-10).of 38 / 1296.0
|
|
470
|
+
expect(h[8]).to be_within(1e-10).of 62 / 1296.0
|
|
471
|
+
expect(h[9]).to be_within(1e-10).of 91 / 1296.0
|
|
472
|
+
expect(h[10]).to be_within(1e-10).of 122 / 1296.0
|
|
473
|
+
expect(h[11]).to be_within(1e-10).of 148 / 1296.0
|
|
474
|
+
expect(h[12]).to be_within(1e-10).of 167 / 1296.0
|
|
475
|
+
expect(h[13]).to be_within(1e-10).of 172 / 1296.0
|
|
476
|
+
expect(h[14]).to be_within(1e-10).of 160 / 1296.0
|
|
477
|
+
expect(h[15]).to be_within(1e-10).of 131 / 1296.0
|
|
478
|
+
expect(h[16]).to be_within(1e-10).of 94 / 1296.0
|
|
479
|
+
expect(h[17]).to be_within(1e-10).of 54 / 1296.0
|
|
480
|
+
expect(h[18]).to be_within(1e-10).of 21 / 1296.0
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
it "calculate a '2d20 keep worst result' distribution accurately" do
|
|
484
|
+
d20 = described_class.for_fair_die(20)
|
|
485
|
+
pr = d20.repeat_n_sum_k(2, 1, :keep_worst)
|
|
486
|
+
h = pr.to_h
|
|
487
|
+
expect(h).to be_valid_distribution
|
|
488
|
+
expect(h[1]).to be_within(1e-10).of 39 / 400.0
|
|
489
|
+
expect(h[2]).to be_within(1e-10).of 37 / 400.0
|
|
490
|
+
expect(h[3]).to be_within(1e-10).of 35 / 400.0
|
|
491
|
+
expect(h[4]).to be_within(1e-10).of 33 / 400.0
|
|
492
|
+
expect(h[5]).to be_within(1e-10).of 31 / 400.0
|
|
493
|
+
expect(h[6]).to be_within(1e-10).of 29 / 400.0
|
|
494
|
+
expect(h[7]).to be_within(1e-10).of 27 / 400.0
|
|
495
|
+
expect(h[8]).to be_within(1e-10).of 25 / 400.0
|
|
496
|
+
expect(h[9]).to be_within(1e-10).of 23 / 400.0
|
|
497
|
+
expect(h[10]).to be_within(1e-10).of 21 / 400.0
|
|
498
|
+
expect(h[11]).to be_within(1e-10).of 19 / 400.0
|
|
499
|
+
expect(h[12]).to be_within(1e-10).of 17 / 400.0
|
|
500
|
+
expect(h[13]).to be_within(1e-10).of 15 / 400.0
|
|
501
|
+
expect(h[14]).to be_within(1e-10).of 13 / 400.0
|
|
502
|
+
expect(h[15]).to be_within(1e-10).of 11 / 400.0
|
|
503
|
+
expect(h[16]).to be_within(1e-10).of 9 / 400.0
|
|
504
|
+
expect(h[17]).to be_within(1e-10).of 7 / 400.0
|
|
505
|
+
expect(h[18]).to be_within(1e-10).of 5 / 400.0
|
|
506
|
+
expect(h[19]).to be_within(1e-10).of 3 / 400.0
|
|
507
|
+
expect(h[20]).to be_within(1e-10).of 1 / 400.0
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
describe 'serialisation via Marshall' do
|
|
513
|
+
it 'can load a saved GamesDice::Probabilities' do
|
|
514
|
+
# rubocop:disable Security/MarshalLoad
|
|
515
|
+
# This is a test of using Marshal on a fixed test file
|
|
516
|
+
pd6 = File.open(fixture('probs_fair_die_6.dat')) { |file| Marshal.load(file) }
|
|
517
|
+
# rubocop:enable Security/MarshalLoad
|
|
518
|
+
expect(pd6.to_h).to be_valid_distribution
|
|
519
|
+
expect(pd6.p_gt(4)).to be_within(1e-10).of 1.0 / 3
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
end
|