games_dice 0.5.0 → 0.6.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +11 -1
  3. data/.gitignore +0 -1
  4. data/.rubocop.yml +13 -48
  5. data/CHANGELOG.md +19 -0
  6. data/Gemfile +5 -3
  7. data/README.md +46 -5
  8. data/Rakefile +12 -5
  9. data/ext/games_dice/extconf.rb +0 -3
  10. data/ext/games_dice/probabilities.c +293 -193
  11. data/ext/games_dice/probabilities.h +2 -12
  12. data/games_dice.gemspec +1 -1
  13. data/lib/games_dice/bunch.rb +50 -36
  14. data/lib/games_dice/bunch_helpers.rb +35 -14
  15. data/lib/games_dice/complex_die.rb +64 -22
  16. data/lib/games_dice/complex_die_helpers.rb +122 -144
  17. data/lib/games_dice/dice.rb +61 -17
  18. data/lib/games_dice/dice_helpers.rb +37 -0
  19. data/lib/games_dice/die_result.rb +6 -8
  20. data/lib/games_dice/marshal.rb +2 -7
  21. data/lib/games_dice/parser.rb +30 -0
  22. data/lib/games_dice/probability_budget.rb +90 -0
  23. data/lib/games_dice/probability_calculation.rb +95 -0
  24. data/lib/games_dice/probability_shortcuts.rb +65 -0
  25. data/lib/games_dice/recipe_cache.rb +155 -0
  26. data/lib/games_dice/version.rb +1 -1
  27. data/lib/games_dice.rb +4 -0
  28. data/script/check_documentation.rb +10 -0
  29. data/spec/games_dice/bunch_regressions_spec.rb +46 -0
  30. data/spec/{bunch_spec.rb → games_dice/bunch_spec.rb} +65 -112
  31. data/spec/games_dice/complex_die_budget_spec.rb +60 -0
  32. data/spec/games_dice/complex_die_limits_spec.rb +119 -0
  33. data/spec/games_dice/complex_die_spec.rb +228 -0
  34. data/spec/games_dice/dice_mutation_spec.rb +165 -0
  35. data/spec/games_dice/dice_regressions_spec.rb +50 -0
  36. data/spec/games_dice/dice_spec.rb +61 -0
  37. data/spec/games_dice/die_result_spec.rb +221 -0
  38. data/spec/{die_spec.rb → games_dice/die_spec.rb} +15 -14
  39. data/spec/{map_rule_spec.rb → games_dice/map_rule_spec.rb} +8 -1
  40. data/spec/{parser_spec.rb → games_dice/parser_spec.rb} +12 -2
  41. data/spec/games_dice/probabilities_boundaries_spec.rb +161 -0
  42. data/spec/games_dice/probabilities_marshal_spec.rb +62 -0
  43. data/spec/games_dice/probabilities_spec.rb +484 -0
  44. data/spec/games_dice/probability_budget_spec.rb +17 -0
  45. data/spec/{reroll_rule_spec.rb → games_dice/reroll_rule_spec.rb} +5 -1
  46. data/spec/games_dice_spec.rb +382 -0
  47. data/spec/helpers.rb +64 -2
  48. data/spec/simple_cov_spec.rb +42 -0
  49. data/spec/support/coverage.rb +12 -0
  50. data/spec/support/validation.rb +14 -0
  51. data/spec/yard/cli/stats_spec.rb +43 -0
  52. metadata +29 -12
  53. data/spec/complex_die_spec.rb +0 -314
  54. data/spec/dice_spec.rb +0 -33
  55. data/spec/die_result_spec.rb +0 -254
  56. data/spec/fixtures/probs_fair_die_6.dat +0 -2
  57. data/spec/probability_spec.rb +0 -584
  58. data/spec/readme_spec.rb +0 -410
@@ -1,314 +0,0 @@
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 'represent a basic die as an object' do
12
- die = described_class.new(6)
13
- expect(die.min).to be 1
14
- expect(die.max).to be 6
15
- expect(die.sides).to be 6
16
- end
17
-
18
- it "return results based on Ruby's internal rand() by default" do
19
- die = described_class.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 'use any object with a rand(Integer) method' do
27
- prng = TestPRNG.new
28
- die = described_class.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 'optionally accept a rerolls param' do
36
- described_class.new(10, rerolls: [])
37
- described_class.new(10, rerolls: [GamesDice::RerollRule.new(6, :<=, :reroll_add)])
38
- described_class.new(10,
39
- rerolls: [GamesDice::RerollRule.new(6, :<=, :reroll_add),
40
- GamesDice::RerollRule.new(1, :>=, :reroll_subtract)])
41
- described_class.new(10, rerolls: [[6, :<=, :reroll_add]])
42
- described_class.new(10, rerolls: [[6, :<=, :reroll_add], [1, :>=, :reroll_subtract]])
43
-
44
- expect do
45
- described_class.new(10, rerolls: 7)
46
- end.to raise_error(TypeError)
47
-
48
- expect do
49
- described_class.new(10, rerolls: ['hello'])
50
- end.to raise_error(TypeError)
51
-
52
- expect do
53
- described_class.new(10, rerolls: [GamesDice::RerollRule.new(6, :<=, :reroll_add), :reroll_add])
54
- end.to raise_error(TypeError)
55
-
56
- expect do
57
- described_class.new(10, rerolls: [7])
58
- end.to raise_error(TypeError)
59
-
60
- expect do
61
- described_class.new(10, rerolls: [['hello']])
62
- end.to raise_error(ArgumentError)
63
-
64
- expect do
65
- described_class.new(10, rerolls: [[6, :<=, :reroll_add], :reroll_add])
66
- end.to raise_error(TypeError)
67
- end
68
-
69
- it 'optionally accept a maps param' do
70
- described_class.new(10, maps: [])
71
- described_class.new(10, maps: [GamesDice::MapRule.new(7, :<=, 1)])
72
- described_class.new(10, maps: [GamesDice::MapRule.new(7, :<=, 1), GamesDice::MapRule.new(1, :>, -1)])
73
- described_class.new(10, maps: [[7, :<=, 1]])
74
- described_class.new(10, maps: [[7, :<=, 1], [1, :>, -1]])
75
-
76
- expect do
77
- described_class.new(10, maps: 7)
78
- end.to raise_error(TypeError)
79
-
80
- expect do
81
- described_class.new(10, maps: [7])
82
- end.to raise_error(TypeError)
83
-
84
- expect do
85
- described_class.new(10, maps: [[7]])
86
- end.to raise_error(ArgumentError)
87
-
88
- expect do
89
- described_class.new(10, maps: ['hello'])
90
- end.to raise_error(TypeError)
91
-
92
- expect do
93
- described_class.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 'calculate correct minimum and maximum results' do
101
- die = described_class.new(10, rerolls: [GamesDice::RerollRule.new(10, :<=, :reroll_add, 3)])
102
- expect(die.min).to be 1
103
- expect(die.max).to be 40
104
-
105
- die = described_class.new(10, rerolls: [[1, :>=, :reroll_subtract]])
106
- expect(die.min).to be(-9)
107
- expect(die.max).to be 10
108
-
109
- die = described_class.new(10, rerolls: [GamesDice::RerollRule.new(10, :<=, :reroll_add)])
110
- expect(die.min).to be 1
111
- expect(die.max).to be 10_010
112
- end
113
-
114
- it 'simulate a d10 that rerolls and adds on a result of 10' do
115
- die = described_class.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 'explain how it got results outside range 1 to 10 on a d10' do
123
- die = described_class.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 'calculate an expected result' do
131
- die = described_class.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 = described_class.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 = described_class.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 = described_class.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 = described_class.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 = described_class.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 'calculate probabilities of each possible result' do
151
- die = described_class.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.sum).to be_within(1e-9).of 1.0
156
-
157
- die = described_class.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.sum).to be_within(1e-9).of 1.0
164
-
165
- die = described_class.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.sum).to be_within(1e-9).of 1.0
170
- end
171
-
172
- it 'calculate aggregate probabilities' do
173
- die = described_class.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 be 1.0
177
- expect(probs.p_gt(12)).to be 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 be 1.0
181
- expect(probs.p_ge(15)).to be 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 be 0.0
185
- expect(probs.p_lt(13)).to be 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 be 0.0
189
- expect(probs.p_le(12)).to be 1.0
190
- end
191
- end
192
-
193
- describe 'with maps' do
194
- it 'calculate correct minimum and maximum results' do
195
- die = described_class.new(10, maps: [GamesDice::MapRule.new(7, :<=, 1, 'S')])
196
- expect(die.min).to be 0
197
- expect(die.max).to be 1
198
- end
199
-
200
- it 'simulate a d10 that scores 1 for success on a value of 7 or more' do
201
- die = described_class.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 'label the mappings applied with the provided names' do
209
- die = described_class.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 'calculate an expected result' do
217
- die = described_class.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 'calculate probabilities of each possible result' do
224
- die = described_class.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.sum).to be_within(1e-9).of 1.0
232
- end
233
-
234
- it 'calculate aggregate probabilities' do
235
- die = described_class.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 be 1.0
240
- expect(probs.p_gt(-1)).to be_within(1e-10).of 0.9
241
- expect(probs.p_gt(1)).to be 0.0
242
-
243
- expect(probs.p_ge(1)).to be_within(1e-10).of 0.4
244
- expect(probs.p_ge(-1)).to be 1.0
245
- expect(probs.p_ge(2)).to be 0.0
246
-
247
- expect(probs.p_lt(1)).to be_within(1e-10).of 0.6
248
- expect(probs.p_lt(-1)).to be 0.0
249
- expect(probs.p_lt(2)).to be 1.0
250
-
251
- expect(probs.p_le(-1)).to be_within(1e-10).of 0.1
252
- expect(probs.p_le(-2)).to be 0.0
253
- expect(probs.p_le(1)).to be 1.0
254
- end
255
- end
256
-
257
- describe 'with rerolls and maps together' do
258
- before do
259
- @die = described_class.new(6,
260
- rerolls: [[6, :<=, :reroll_add]],
261
- maps: [GamesDice::MapRule.new(9, :<=, 1, 'Success')])
262
- end
263
-
264
- it 'calculate correct minimum and maximum results' do
265
- expect(@die.min).to be 0
266
- expect(@die.max).to be 1
267
- end
268
-
269
- it 'calculate an expected result' do
270
- expect(@die.probabilities.expected).to be_within(1e-10).of 4 / 36.0
271
- end
272
-
273
- it '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.sum).to be_within(1e-9).of 1.0
278
- end
279
-
280
- it '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 be 1.0
285
- expect(probs.p_gt(1)).to be 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 be 1.0
289
- expect(probs.p_ge(2)).to be 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 be 0.0
293
- expect(probs.p_lt(2)).to be 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 be 0.0
297
- expect(probs.p_le(1)).to be 1.0
298
- end
299
-
300
- it '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 '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
data/spec/dice_spec.rb DELETED
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'helpers'
4
-
5
- describe GamesDice::Dice do
6
- describe 'dice scheme' do
7
- before do
8
- srand(67_809)
9
- end
10
-
11
- describe '1d10+2' do
12
- let(:dice) { described_class.new([{ sides: 10, ndice: 1 }], 2) }
13
-
14
- it 'simulate rolling a ten-sided die, and adding two to each result' do
15
- [5, 4, 10, 10, 7, 5, 9].each do |expected_total|
16
- expect(dice.roll).to eql expected_total
17
- expect(dice.result).to eql expected_total
18
- end
19
- end
20
- end
21
-
22
- describe '2d6+6' do
23
- let(:dice) { described_class.new([{ sides: 6, ndice: 2 }], 6) }
24
-
25
- it 'simulate rolling two six-sided dice and adding six to the result' do
26
- [15, 12, 17, 15, 13, 13, 16].each do |expected_total|
27
- expect(dice.roll).to eql expected_total
28
- expect(dice.result).to eql expected_total
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,254 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'helpers'
4
-
5
- describe GamesDice::DieResult do
6
- describe '.new' do
7
- it "work without parameters to represent 'no results yet'" do
8
- die_result = described_class.new
9
- expect(die_result.value).to be_nil
10
- expect(die_result.rolls).to eql []
11
- expect(die_result.roll_reasons).to eql []
12
- end
13
-
14
- it 'work with a single Integer param to represent an initial result' do
15
- die_result = described_class.new(8)
16
- expect(die_result.value).to be 8
17
- expect(die_result.rolls).to eql [8]
18
- expect(die_result.roll_reasons).to eql [:basic]
19
- end
20
-
21
- it 'not accept a param that cannot be coerced to Integer' do
22
- expect { described_class.new([]) }.to raise_error(TypeError)
23
- expect { described_class.new('N') }.to raise_error(ArgumentError)
24
- end
25
-
26
- it 'not accept unknown reasons for making a roll' do
27
- expect { described_class.new(8, 'wooo') }.to raise_error(ArgumentError)
28
- expect { described_class.new(8, :frabulous) }.to raise_error(ArgumentError)
29
- end
30
- end
31
-
32
- describe '#add_roll' do
33
- context "when starting from 'no results yet'" do
34
- let(:die_result) { described_class.new }
35
-
36
- it 'create an initial result' do
37
- die_result.add_roll(5)
38
- expect(die_result.value).to be 5
39
- expect(die_result.rolls).to eql [5]
40
- expect(die_result.roll_reasons).to eql [:basic]
41
- end
42
-
43
- it 'accept non-basic reasons for the first roll' do
44
- die_result.add_roll(4, :reroll_subtract)
45
- expect(die_result.value).to be(-4)
46
- expect(die_result.rolls).to eql [4]
47
- expect(die_result.roll_reasons).to eql [:reroll_subtract]
48
- end
49
-
50
- it 'not accept a first param that cannot be coerced to Integer' do
51
- expect { die_result.add_roll([]) }.to raise_error(TypeError)
52
- expect { die_result.add_roll('N') }.to raise_error(ArgumentError)
53
- end
54
-
55
- it 'not accept an unsupported second param' do
56
- expect { die_result.add_roll(5, []) }.to raise_error(ArgumentError)
57
- expect { die_result.add_roll(15, :bam) }.to raise_error(ArgumentError)
58
- end
59
- end
60
-
61
- context 'when starting with an initial result' do
62
- let(:die_result) { described_class.new(7) }
63
-
64
- it 'not accept a first param that cannot be coerced to Integer' do
65
- expect { die_result.add_roll([]) }.to raise_error(TypeError)
66
- expect { die_result.add_roll('N') }.to raise_error(ArgumentError)
67
- end
68
-
69
- it 'not accept an unsupported second param' do
70
- expect { die_result.add_roll(5, []) }.to raise_error(ArgumentError)
71
- expect { die_result.add_roll(15, :bam) }.to raise_error(ArgumentError)
72
- end
73
-
74
- context 'when adding another basic roll' do
75
- it 'replace an initial result, as if the die were re-rolled' do
76
- die_result.add_roll(5)
77
- expect(die_result.value).to be 5
78
- expect(die_result.rolls).to eql [7, 5]
79
- expect(die_result.roll_reasons).to eql %i[basic basic]
80
- end
81
- end
82
-
83
- context 'with exploding dice' do
84
- it 'add to value when exploding up' do
85
- die_result.add_roll(6, :reroll_add)
86
- expect(die_result.value).to be 13
87
- expect(die_result.rolls).to eql [7, 6]
88
- expect(die_result.roll_reasons).to eql %i[basic reroll_add]
89
- end
90
-
91
- it 'subtract from value when exploding down' do
92
- die_result.add_roll(4, :reroll_subtract)
93
- expect(die_result.value).to be 3
94
- expect(die_result.rolls).to eql [7, 4]
95
- expect(die_result.roll_reasons).to eql %i[basic reroll_subtract]
96
- end
97
- end
98
-
99
- context 'with re-roll dice' do
100
- it 'optionally replace roll unconditionally' do
101
- die_result.add_roll(2, :reroll_replace)
102
- expect(die_result.value).to be 2
103
- expect(die_result.rolls).to eql [7, 2]
104
- expect(die_result.roll_reasons).to eql %i[basic reroll_replace]
105
-
106
- die_result.add_roll(5, :reroll_replace)
107
- expect(die_result.value).to be 5
108
- expect(die_result.rolls).to eql [7, 2, 5]
109
- expect(die_result.roll_reasons).to eql %i[basic reroll_replace reroll_replace]
110
- end
111
-
112
- it 'optionally use best roll' do
113
- die_result.add_roll(2, :reroll_use_best)
114
- expect(die_result.value).to be 7
115
- expect(die_result.rolls).to eql [7, 2]
116
- expect(die_result.roll_reasons).to eql %i[basic reroll_use_best]
117
-
118
- die_result.add_roll(9, :reroll_use_best)
119
- expect(die_result.value).to be 9
120
- expect(die_result.rolls).to eql [7, 2, 9]
121
- expect(die_result.roll_reasons).to eql %i[basic reroll_use_best reroll_use_best]
122
- end
123
-
124
- it 'optionally use worst roll' do
125
- die_result.add_roll(4, :reroll_use_worst)
126
- expect(die_result.value).to be 4
127
- expect(die_result.rolls).to eql [7, 4]
128
- expect(die_result.roll_reasons).to eql %i[basic reroll_use_worst]
129
-
130
- die_result.add_roll(5, :reroll_use_worst)
131
- expect(die_result.value).to be 4
132
- expect(die_result.rolls).to eql [7, 4, 5]
133
- expect(die_result.roll_reasons).to eql %i[basic reroll_use_worst reroll_use_worst]
134
- end
135
- end
136
-
137
- context 'with combinations of reroll reasons' do
138
- it 'correctly handle valid reasons for extra rolls in combination' do
139
- die_result.add_roll(10, :reroll_add)
140
- die_result.add_roll(3, :reroll_subtract)
141
- expect(die_result.value).to be 14
142
- expect(die_result.rolls).to eql [7, 10, 3]
143
- expect(die_result.roll_reasons).to eql %i[basic reroll_add reroll_subtract]
144
-
145
- die_result.add_roll(12, :reroll_replace)
146
- expect(die_result.value).to be 12
147
- expect(die_result.rolls).to eql [7, 10, 3, 12]
148
- expect(die_result.roll_reasons).to eql %i[basic reroll_add reroll_subtract reroll_replace]
149
-
150
- die_result.add_roll(9, :reroll_use_best)
151
- expect(die_result.value).to be 12
152
- expect(die_result.rolls).to eql [7, 10, 3, 12, 9]
153
- expect(die_result.roll_reasons).to eql %i[basic reroll_add reroll_subtract reroll_replace
154
- reroll_use_best]
155
-
156
- die_result.add_roll(15, :reroll_add)
157
- expect(die_result.value).to be 27
158
- expect(die_result.rolls).to eql [7, 10, 3, 12, 9, 15]
159
- expect(die_result.roll_reasons).to eql %i[basic reroll_add reroll_subtract reroll_replace
160
- reroll_use_best reroll_add]
161
- end
162
- end
163
- end
164
- end
165
-
166
- describe '#explain_value' do
167
- let(:die_result) { described_class.new }
168
-
169
- it "be empty string for 'no results yet'" do
170
- expect(die_result.explain_value).to eql ''
171
- end
172
-
173
- it 'be a simple stringified number when there is one die roll' do
174
- die_result.add_roll(3)
175
- expect(die_result.explain_value).to eql '3'
176
- end
177
-
178
- it 'describe all single rolls made and how they combine' do
179
- die_result.add_roll(6)
180
- expect(die_result.explain_value).to eql '6'
181
-
182
- die_result.add_roll(5, :reroll_add)
183
- expect(die_result.explain_value).to eql '[6+5] 11'
184
-
185
- die_result.add_roll(2, :reroll_replace)
186
- expect(die_result.explain_value).to eql '[6+5|2] 2'
187
-
188
- die_result.add_roll(7, :reroll_subtract)
189
- expect(die_result.explain_value).to eql '[6+5|2-7] -5'
190
-
191
- die_result.add_roll(4, :reroll_use_worst)
192
- expect(die_result.explain_value).to eql '[6+5|2-7\\4] -5'
193
-
194
- die_result.add_roll(3, :reroll_use_best)
195
- expect(die_result.explain_value).to eql '[6+5|2-7\\4/3] 3'
196
- end
197
- end
198
-
199
- it 'combine via +,- and * intuitively based on #value' do
200
- die_result = described_class.new(7)
201
- expect(die_result + 3).to be 10
202
- expect(4 + die_result).to be 11
203
- expect(die_result - 2).to be 5
204
- expect(9 - die_result).to be 2
205
-
206
- expect(die_result + 7.7).to be 14.7
207
- expect(4.1 + die_result).to be 11.1
208
-
209
- expect(die_result * 2).to be 14
210
- expect(1 * die_result).to be 7
211
-
212
- other_die_result = described_class.new(6)
213
- other_die_result.add_roll(6, :reroll_add)
214
- expect(die_result + other_die_result).to be 19
215
- expect(other_die_result - die_result).to be 5
216
- end
217
-
218
- it 'support comparison with >,<,>=,<= as if it were an integer, based on #value' do
219
- die_result = described_class.new(7)
220
-
221
- expect(die_result > 3).to be true
222
- expect(die_result < 14).to be true
223
- expect(die_result >= 7).to be true
224
- expect(die_result <= 9.5).to be true
225
- expect(die_result < 3).to be false
226
- expect(die_result > 14).to be false
227
- expect(die_result <= 8).to be true
228
- expect(die_result >= 14).to be false
229
-
230
- other_die_result = described_class.new(6)
231
- other_die_result.add_roll(6, :reroll_add)
232
- expect(die_result > other_die_result).to be false
233
- expect(other_die_result > die_result).to be true
234
- expect(die_result >= other_die_result).to be false
235
- expect(other_die_result >= die_result).to be true
236
- expect(die_result < other_die_result).to be true
237
- expect(other_die_result < die_result).to be false
238
- expect(die_result <= other_die_result).to be true
239
- expect(other_die_result <= die_result).to be false
240
- end
241
-
242
- it 'sort, based on #value' do
243
- die_results = [
244
- described_class.new(7), described_class.new(5), described_class.new(8), described_class.new(3)
245
- ]
246
-
247
- die_results.sort!
248
-
249
- expect(die_results[0].value).to be 3
250
- expect(die_results[1].value).to be 5
251
- expect(die_results[2].value).to be 7
252
- expect(die_results[3].value).to be 8
253
- end
254
- end
@@ -1,2 +0,0 @@
1
- u:GamesDice::Probabilities�{ if0.16666666666666666if0.16666666666666666if0.16666666666666666i f0.16666666666666666i
2
- f0.16666666666666666i f0.16666666666666666