games_dice 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/games_dice.gemspec +4 -2
- data/lib/games_dice.rb +3 -0
- data/lib/games_dice/bunch.rb +275 -320
- data/lib/games_dice/complex_die.rb +226 -269
- data/lib/games_dice/constants.rb +16 -0
- data/lib/games_dice/dice.rb +43 -0
- data/lib/games_dice/die.rb +58 -92
- data/lib/games_dice/die_result.rb +3 -15
- data/lib/games_dice/map_rule.rb +41 -43
- data/lib/games_dice/probabilities.rb +97 -0
- data/lib/games_dice/reroll_rule.rb +41 -58
- data/lib/games_dice/version.rb +1 -1
- data/spec/bunch_spec.rb +196 -188
- data/spec/complex_die_spec.rb +77 -68
- data/spec/dice_spec.rb +34 -0
- data/spec/die_spec.rb +25 -29
- data/spec/probability_spec.rb +265 -0
- metadata +31 -8
data/spec/complex_die_spec.rb
CHANGED
@@ -110,60 +110,64 @@ describe GamesDice::ComplexDie do
|
|
110
110
|
|
111
111
|
it "should calculate an expected result" do
|
112
112
|
die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add),GamesDice::RerollRule.new(1, :>=, :reroll_subtract)] )
|
113
|
-
die.
|
113
|
+
die.probabilities.expected.should be_within(1e-10).of 5.5
|
114
114
|
|
115
115
|
die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(1, :<=, :reroll_use_best, 1)] )
|
116
|
-
die.
|
116
|
+
die.probabilities.expected.should be_within(1e-10).of 7.15
|
117
117
|
|
118
118
|
die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(1, :<=, :reroll_use_worst, 2)] )
|
119
|
-
die.
|
119
|
+
die.probabilities.expected.should be_within(1e-10).of 3.025
|
120
120
|
|
121
121
|
die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add)] )
|
122
|
-
die.
|
122
|
+
die.probabilities.expected.should be_within(1e-10).of 4.2
|
123
123
|
|
124
124
|
die = GamesDice::ComplexDie.new(8, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_use_best)] )
|
125
|
-
die.
|
125
|
+
die.probabilities.expected.should be_within(1e-10).of 5.0
|
126
126
|
|
127
127
|
die = GamesDice::ComplexDie.new(4, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_replace, 1)] )
|
128
|
-
die.
|
128
|
+
die.probabilities.expected.should be_within(1e-10).of 2.875
|
129
129
|
end
|
130
130
|
|
131
131
|
it "should calculate probabilities of each possible result" do
|
132
132
|
die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(7, :>, :reroll_add, 1)] )
|
133
|
-
die.probabilities
|
134
|
-
|
135
|
-
|
133
|
+
probs = die.probabilities.to_h
|
134
|
+
probs[11].should be_within(1e-10).of 2/36.0
|
135
|
+
probs[8].should be_within(1e-10).of 5/36.0
|
136
|
+
probs.values.inject(:+).should be_within(1e-9).of 1.0
|
136
137
|
|
137
138
|
die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add)] )
|
138
|
-
die.probabilities
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
139
|
+
probs = die.probabilities.to_h
|
140
|
+
probs[8].should be_within(1e-10).of 0.1
|
141
|
+
probs[10].should be_false
|
142
|
+
probs[13].should be_within(1e-10).of 0.01
|
143
|
+
probs[27].should be_within(1e-10).of 0.001
|
144
|
+
probs.values.inject(:+).should be_within(1e-9).of 1.0
|
143
145
|
|
144
146
|
die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_replace, 1)] )
|
145
|
-
die.probabilities
|
146
|
-
|
147
|
-
|
147
|
+
probs = die.probabilities.to_h
|
148
|
+
probs[1].should be_within(1e-10).of 1/36.0
|
149
|
+
probs[2].should be_within(1e-10).of 7/36.0
|
150
|
+
probs.values.inject(:+).should be_within(1e-9).of 1.0
|
148
151
|
end
|
149
152
|
|
150
153
|
it "should calculate aggregate probabilities" do
|
151
154
|
die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(7, :>, :reroll_add, 1)] )
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
155
|
+
probs = die.probabilities
|
156
|
+
probs.p_gt(7).should be_within(1e-10).of 15/36.0
|
157
|
+
probs.p_gt(-10).should == 1.0
|
158
|
+
probs.p_gt(12).should == 0.0
|
159
|
+
|
160
|
+
probs.p_ge(7).should be_within(1e-10).of 21/36.0
|
161
|
+
probs.p_ge(2).should == 1.0
|
162
|
+
probs.p_ge(15).should == 0.0
|
163
|
+
|
164
|
+
probs.p_lt(7).should be_within(1e-10).of 15/36.0
|
165
|
+
probs.p_lt(-10).should == 0.0
|
166
|
+
probs.p_lt(13).should == 1.0
|
167
|
+
|
168
|
+
probs.p_le(7).should be_within(1e-10).of 21/36.0
|
169
|
+
probs.p_le(1).should == 0.0
|
170
|
+
probs.p_le(12).should == 1.0
|
167
171
|
end
|
168
172
|
end
|
169
173
|
|
@@ -192,34 +196,36 @@ describe GamesDice::ComplexDie do
|
|
192
196
|
|
193
197
|
it "should calculate an expected result" do
|
194
198
|
die = GamesDice::ComplexDie.new(10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
|
195
|
-
die.
|
199
|
+
die.probabilities.expected.should be_within(1e-10).of 0.3
|
196
200
|
end
|
197
201
|
|
198
202
|
it "should calculate probabilities of each possible result" do
|
199
203
|
die = GamesDice::ComplexDie.new(10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
|
200
|
-
die.probabilities
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
+
probs_hash = die.probabilities.to_h
|
205
|
+
probs_hash[1].should be_within(1e-10).of 0.4
|
206
|
+
probs_hash[0].should be_within(1e-10).of 0.5
|
207
|
+
probs_hash[-1].should be_within(1e-10).of 0.1
|
208
|
+
probs_hash.values.inject(:+).should be_within(1e-9).of 1.0
|
204
209
|
end
|
205
210
|
|
206
211
|
it "should calculate aggregate probabilities" do
|
207
212
|
die = GamesDice::ComplexDie.new(10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
213
|
+
probs = die.probabilities
|
214
|
+
probs.p_gt(-2).should == 1.0
|
215
|
+
probs.p_gt(-1).should be_within(1e-10).of 0.9
|
216
|
+
probs.p_gt(1).should == 0.0
|
217
|
+
|
218
|
+
probs.p_ge(1).should be_within(1e-10).of 0.4
|
219
|
+
probs.p_ge(-1).should == 1.0
|
220
|
+
probs.p_ge(2).should == 0.0
|
221
|
+
|
222
|
+
probs.p_lt(1).should be_within(1e-10).of 0.6
|
223
|
+
probs.p_lt(-1).should == 0.0
|
224
|
+
probs.p_lt(2).should == 1.0
|
225
|
+
|
226
|
+
probs.p_le(-1).should be_within(1e-10).of 0.1
|
227
|
+
probs.p_le(-2).should == 0.0
|
228
|
+
probs.p_le(1).should == 1.0
|
223
229
|
end
|
224
230
|
end
|
225
231
|
|
@@ -237,31 +243,34 @@ describe GamesDice::ComplexDie do
|
|
237
243
|
end
|
238
244
|
|
239
245
|
it "should calculate an expected result" do
|
240
|
-
@die.
|
246
|
+
@die.probabilities.expected.should be_within(1e-10).of 4/36.0
|
241
247
|
end
|
242
248
|
|
243
249
|
it "should calculate probabilities of each possible result" do
|
244
|
-
@die.probabilities
|
245
|
-
|
246
|
-
|
250
|
+
probs_hash = @die.probabilities.to_h
|
251
|
+
probs_hash[1].should be_within(1e-10).of 4/36.0
|
252
|
+
probs_hash[0].should be_within(1e-10).of 32/36.0
|
253
|
+
probs_hash.values.inject(:+).should be_within(1e-9).of 1.0
|
247
254
|
end
|
248
255
|
|
249
256
|
it "should calculate aggregate probabilities" do
|
250
|
-
@die.
|
251
|
-
|
252
|
-
|
257
|
+
probs = @die.probabilities
|
258
|
+
|
259
|
+
probs.p_gt(0).should be_within(1e-10).of 4/36.0
|
260
|
+
probs.p_gt(-2).should == 1.0
|
261
|
+
probs.p_gt(1).should == 0.0
|
253
262
|
|
254
|
-
|
255
|
-
|
256
|
-
|
263
|
+
probs.p_ge(1).should be_within(1e-10).of 4/36.0
|
264
|
+
probs.p_ge(-1).should == 1.0
|
265
|
+
probs.p_ge(2).should == 0.0
|
257
266
|
|
258
|
-
|
259
|
-
|
260
|
-
|
267
|
+
probs.p_lt(1).should be_within(1e-10).of 32/36.0
|
268
|
+
probs.p_lt(0).should == 0.0
|
269
|
+
probs.p_lt(2).should == 1.0
|
261
270
|
|
262
|
-
|
263
|
-
|
264
|
-
|
271
|
+
probs.p_le(0).should be_within(1e-10).of 32/36.0
|
272
|
+
probs.p_le(-1).should == 0.0
|
273
|
+
probs.p_le(1).should == 1.0
|
265
274
|
end
|
266
275
|
|
267
276
|
it "should apply mapping to final re-rolled result" do
|
data/spec/dice_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'games_dice'
|
2
|
+
|
3
|
+
describe GamesDice::Dice do
|
4
|
+
|
5
|
+
describe "dice scheme" do
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
srand(67809)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '1d10+2' do
|
12
|
+
let(:dice) { GamesDice::Dice.new( [ { :sides => 10, :ndice => 1 } ], 2 ) }
|
13
|
+
|
14
|
+
it "should 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
|
+
dice.roll.should == expected_total
|
17
|
+
dice.result.should == expected_total
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '2d6+6' do
|
23
|
+
let(:dice) { GamesDice::Dice.new( [ { :sides => 6, :ndice => 2 } ], 6) }
|
24
|
+
|
25
|
+
it "should 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
|
+
dice.roll.should == expected_total
|
28
|
+
dice.result.should == expected_total
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/spec/die_spec.rb
CHANGED
@@ -24,8 +24,6 @@ describe GamesDice::Die do
|
|
24
24
|
die.min.should == 1
|
25
25
|
die.max.should == 6
|
26
26
|
die.sides.should == 6
|
27
|
-
die.probabilities.should == { 1 => 1.0/6, 2 => 1.0/6, 3 => 1.0/6, 4 => 1.0/6, 5 => 1.0/6, 6 => 1.0/6 }
|
28
|
-
die.expected_result.should == 3.5
|
29
27
|
end
|
30
28
|
|
31
29
|
it "should accept any object with a rand(Integer) method as the second param" do
|
@@ -56,39 +54,37 @@ describe GamesDice::Die do
|
|
56
54
|
end
|
57
55
|
end
|
58
56
|
|
59
|
-
describe "#expected_result" do
|
60
|
-
it "should calculate correct weighted mean" do
|
61
|
-
die = GamesDice::Die.new(20)
|
62
|
-
die.expected_result.should be_within(1e-10).of 10.5
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
57
|
describe "#probabilities" do
|
67
|
-
it "should
|
58
|
+
it "should return the die's probability distribution as a GamesDice::Probabilities object" do
|
68
59
|
die = GamesDice::Die.new(6)
|
69
|
-
die.probabilities
|
70
|
-
die.probabilities
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
60
|
+
die.probabilities.is_a?( GamesDice::Probabilities ).should be_true
|
61
|
+
probs = die.probabilities
|
62
|
+
|
63
|
+
probs.p_eql(1).should be_within(1e-10).of 1/6.0
|
64
|
+
probs.p_eql(2).should be_within(1e-10).of 1/6.0
|
65
|
+
probs.p_eql(3).should be_within(1e-10).of 1/6.0
|
66
|
+
probs.p_eql(4).should be_within(1e-10).of 1/6.0
|
67
|
+
probs.p_eql(5).should be_within(1e-10).of 1/6.0
|
68
|
+
probs.p_eql(6).should be_within(1e-10).of 1/6.0
|
69
|
+
probs.to_h.values.inject(:+).should be_within(1e-9).of 1.0
|
70
|
+
|
71
|
+
probs.p_gt(6).should == 0.0
|
72
|
+
probs.p_gt(-20).should == 1.0
|
73
|
+
probs.p_gt(4).should be_within(1e-10).of 2/6.0
|
76
74
|
|
77
|
-
|
78
|
-
|
79
|
-
|
75
|
+
probs.p_ge(20).should == 0.0
|
76
|
+
probs.p_ge(1).should == 1.0
|
77
|
+
probs.p_ge(4).should be_within(1e-10).of 0.5
|
80
78
|
|
81
|
-
|
82
|
-
|
83
|
-
|
79
|
+
probs.p_le(6).should == 1.0
|
80
|
+
probs.p_le(-3).should == 0.0
|
81
|
+
probs.p_le(5).should be_within(1e-10).of 5/6.0
|
84
82
|
|
85
|
-
|
86
|
-
|
87
|
-
|
83
|
+
probs.p_lt(7).should == 1.0
|
84
|
+
probs.p_lt(1).should == 0.0
|
85
|
+
probs.p_lt(3).should be_within(1e-10).of 2/6.0
|
88
86
|
|
89
|
-
|
90
|
-
die.probability_lt(1).should == 0.0
|
91
|
-
die.probability_lt(3).should be_within(1e-10).of 2/6.0
|
87
|
+
probs.expected.should be_within(1e-10).of 3.5
|
92
88
|
end
|
93
89
|
end
|
94
90
|
|
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'games_dice'
|
2
|
+
|
3
|
+
# A valid distribution is:
|
4
|
+
# A hash
|
5
|
+
# Keys are all Integers
|
6
|
+
# Values are all positive Floats, between 0.0 and 1.0
|
7
|
+
# Sum of values is 1.0
|
8
|
+
RSpec::Matchers.define :be_valid_distribution do
|
9
|
+
match do |given|
|
10
|
+
@error = nil
|
11
|
+
if ! given.is_a?(Hash)
|
12
|
+
@error = "distribution should be a Hash, but it is a #{given.class}"
|
13
|
+
elsif given.keys.any? { |k| ! k.is_a?(Fixnum) }
|
14
|
+
bad_key = given.keys.first { |k| ! k.is_a?(Fixnum) }
|
15
|
+
@error = "all keys should be Fixnums, but found '#{bad_key.inspect}' which is a #{bad_key.class}"
|
16
|
+
elsif given.values.any? { |v| ! v.is_a?(Float) }
|
17
|
+
bad_value = given.values.first { |v| ! v.is_a?(Float) }
|
18
|
+
@error = "all values should be Floats, but found '#{bad_value.inspect}' which is a #{bad_value.class}"
|
19
|
+
elsif given.values.any? { |v| v < 0.0 || v > 1.0 }
|
20
|
+
bad_value = given.values.first { |v| v < 0.0 || v > 1.0 }
|
21
|
+
@error = "all values should be in range (0.0..1.0), but found #{bad_value}"
|
22
|
+
elsif (1.0 - given.values.inject(:+)).abs > 1e-6
|
23
|
+
total_probs = given.values.inject(:+)
|
24
|
+
@error = "sum of values should be 1.0, but got #{total_probs}"
|
25
|
+
end
|
26
|
+
! @error
|
27
|
+
end
|
28
|
+
|
29
|
+
failure_message_for_should do |given|
|
30
|
+
@error ? @error : 'Distribution is valid and complete'
|
31
|
+
end
|
32
|
+
|
33
|
+
failure_message_for_should_not do |given|
|
34
|
+
@error ? @error : 'Distribution is valid and complete'
|
35
|
+
end
|
36
|
+
|
37
|
+
description do |given|
|
38
|
+
"a hash describing a complete discrete probability distribution of integers"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe GamesDice::Probabilities do
|
43
|
+
|
44
|
+
describe "class methods" do
|
45
|
+
|
46
|
+
describe "#new" do
|
47
|
+
it "should create a new distribution from a hash" do
|
48
|
+
p = GamesDice::Probabilities.new( { 1 => 1.0 } )
|
49
|
+
p.is_a?( GamesDice::Probabilities ).should be_true
|
50
|
+
p.to_h.should be_valid_distribution
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#for_fair_die" do
|
55
|
+
it "should create a new distribution based on number of sides" do
|
56
|
+
p2 = GamesDice::Probabilities.for_fair_die( 2 )
|
57
|
+
p2.is_a?( GamesDice::Probabilities ).should be_true
|
58
|
+
p2.to_h.should == { 1 => 0.5, 2 => 0.5 }
|
59
|
+
(1..20).each do |sides|
|
60
|
+
p = GamesDice::Probabilities.for_fair_die( sides )
|
61
|
+
p.is_a?( GamesDice::Probabilities ).should be_true
|
62
|
+
h = p.to_h
|
63
|
+
h.should be_valid_distribution
|
64
|
+
h.keys.count.should == sides
|
65
|
+
h.values.each { |v| v.should be_within(1e-10).of 1.0/sides }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#add_distributions" do
|
71
|
+
it "should combine two distributions to create a third one" do
|
72
|
+
d4a = GamesDice::Probabilities.new( { 1 => 1.0/4, 2 => 1.0/4, 3 => 1.0/4, 4 => 1.0/4 } )
|
73
|
+
d4b = GamesDice::Probabilities.new( { 1 => 1.0/10, 2 => 2.0/10, 3 => 3.0/10, 4 => 4.0/10 } )
|
74
|
+
p = GamesDice::Probabilities.add_distributions( d4a, d4b )
|
75
|
+
p.to_h.should be_valid_distribution
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should calculate a classic 2d6 distribution accurately" do
|
79
|
+
d6 = GamesDice::Probabilities.for_fair_die( 6 )
|
80
|
+
p = GamesDice::Probabilities.add_distributions( d6, d6 )
|
81
|
+
p.to_h.should be_valid_distribution
|
82
|
+
p.to_h[2].should be_within(1e-9).of 1.0/36
|
83
|
+
p.to_h[3].should be_within(1e-9).of 2.0/36
|
84
|
+
p.to_h[4].should be_within(1e-9).of 3.0/36
|
85
|
+
p.to_h[5].should be_within(1e-9).of 4.0/36
|
86
|
+
p.to_h[6].should be_within(1e-9).of 5.0/36
|
87
|
+
p.to_h[7].should be_within(1e-9).of 6.0/36
|
88
|
+
p.to_h[8].should be_within(1e-9).of 5.0/36
|
89
|
+
p.to_h[9].should be_within(1e-9).of 4.0/36
|
90
|
+
p.to_h[10].should be_within(1e-9).of 3.0/36
|
91
|
+
p.to_h[11].should be_within(1e-9).of 2.0/36
|
92
|
+
p.to_h[12].should be_within(1e-9).of 1.0/36
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end # describe "class methods"
|
97
|
+
|
98
|
+
describe "instance methods" do
|
99
|
+
let(:p2) { GamesDice::Probabilities.for_fair_die( 2 ) }
|
100
|
+
let(:p4) { GamesDice::Probabilities.for_fair_die( 4 ) }
|
101
|
+
let(:p6) { GamesDice::Probabilities.for_fair_die( 6 ) }
|
102
|
+
let(:p10) { GamesDice::Probabilities.for_fair_die( 10 ) }
|
103
|
+
let(:pa) { GamesDice::Probabilities.new( { -1 => 0.4, 0 => 0.2, 1 => 0.4 } ) }
|
104
|
+
|
105
|
+
describe "#p_eql" do
|
106
|
+
it "should return probability of getting a number inside the range" do
|
107
|
+
p2.p_eql(2).should be_within(1.0e-9).of 0.5
|
108
|
+
p4.p_eql(1).should be_within(1.0e-9).of 0.25
|
109
|
+
p6.p_eql(6).should be_within(1.0e-9).of 1.0/6
|
110
|
+
p10.p_eql(3).should be_within(1.0e-9).of 0.1
|
111
|
+
pa.p_eql(-1).should be_within(1.0e-9).of 0.4
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should return 0.0 for values not covered by distribution" do
|
115
|
+
p2.p_eql(3).should == 0.0
|
116
|
+
p4.p_eql(-1).should == 0.0
|
117
|
+
p6.p_eql(8).should == 0.0
|
118
|
+
p10.p_eql(11).should == 0.0
|
119
|
+
pa.p_eql(2).should == 0.0
|
120
|
+
end
|
121
|
+
end # describe "#p_eql"
|
122
|
+
|
123
|
+
describe "#p_gt" do
|
124
|
+
it "should return probability of getting a number greater than target" do
|
125
|
+
p2.p_gt(1).should be_within(1.0e-9).of 0.5
|
126
|
+
p4.p_gt(3).should be_within(1.0e-9).of 0.25
|
127
|
+
p6.p_gt(2).should be_within(1.0e-9).of 4.0/6
|
128
|
+
p10.p_gt(6).should be_within(1.0e-9).of 0.4
|
129
|
+
|
130
|
+
# Trying more than one, due to unusual error seen in complex_die_spec when calculating probabilities
|
131
|
+
pa.p_gt(-2).should be_within(1.0e-9).of 1.0
|
132
|
+
pa.p_gt(-1).should be_within(1.0e-9).of 0.6
|
133
|
+
pa.p_gt(0).should be_within(1.0e-9).of 0.4
|
134
|
+
pa.p_gt(1).should be_within(1.0e-9).of 0.0
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should return 0.0 when the target number is equal or higher than maximum possible" do
|
138
|
+
p2.p_gt(2).should == 0.0
|
139
|
+
p4.p_gt(5).should == 0.0
|
140
|
+
p6.p_gt(6).should == 0.0
|
141
|
+
p10.p_gt(20).should == 0.0
|
142
|
+
pa.p_gt(3).should == 0.0
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should return 1.0 when the target number is lower than minimum" do
|
146
|
+
p2.p_gt(0).should == 1.0
|
147
|
+
p4.p_gt(-5).should == 1.0
|
148
|
+
p6.p_gt(0).should == 1.0
|
149
|
+
p10.p_gt(-200).should == 1.0
|
150
|
+
pa.p_gt(-2).should == 1.0
|
151
|
+
end
|
152
|
+
end # describe "#p_gt"
|
153
|
+
|
154
|
+
describe "#p_ge" do
|
155
|
+
it "should return probability of getting a number greater than or equal to target" do
|
156
|
+
p2.p_ge(2).should be_within(1.0e-9).of 0.5
|
157
|
+
p4.p_ge(3).should be_within(1.0e-9).of 0.5
|
158
|
+
p6.p_ge(2).should be_within(1.0e-9).of 5.0/6
|
159
|
+
p10.p_ge(6).should be_within(1.0e-9).of 0.5
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should return 0.0 when the target number is higher than maximum possible" do
|
163
|
+
p2.p_ge(6).should == 0.0
|
164
|
+
p4.p_ge(5).should == 0.0
|
165
|
+
p6.p_ge(7).should == 0.0
|
166
|
+
p10.p_ge(20).should == 0.0
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should return 1.0 when the target number is lower than or equal to minimum possible" do
|
170
|
+
p2.p_ge(1).should == 1.0
|
171
|
+
p4.p_ge(-5).should == 1.0
|
172
|
+
p6.p_ge(1).should == 1.0
|
173
|
+
p10.p_ge(-200).should == 1.0
|
174
|
+
end
|
175
|
+
end # describe "#p_ge"
|
176
|
+
|
177
|
+
describe "#p_le" do
|
178
|
+
it "should return probability of getting a number less than or equal to target" do
|
179
|
+
p2.p_le(1).should be_within(1.0e-9).of 0.5
|
180
|
+
p4.p_le(2).should be_within(1.0e-9).of 0.5
|
181
|
+
p6.p_le(2).should be_within(1.0e-9).of 2.0/6
|
182
|
+
p10.p_le(6).should be_within(1.0e-9).of 0.6
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should return 1.0 when the target number is higher than or equal to maximum possible" do
|
186
|
+
p2.p_le(6).should == 1.0
|
187
|
+
p4.p_le(4).should == 1.0
|
188
|
+
p6.p_le(7).should == 1.0
|
189
|
+
p10.p_le(10).should == 1.0
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should return 0.0 when the target number is lower than minimum possible" do
|
193
|
+
p2.p_le(0).should == 0.0
|
194
|
+
p4.p_le(-5).should == 0.0
|
195
|
+
p6.p_le(0).should == 0.0
|
196
|
+
p10.p_le(-200).should == 0.0
|
197
|
+
end
|
198
|
+
end # describe "#p_le"
|
199
|
+
|
200
|
+
describe "#p_lt" do
|
201
|
+
it "should return probability of getting a number less than target" do
|
202
|
+
p2.p_lt(2).should be_within(1.0e-9).of 0.5
|
203
|
+
p4.p_lt(3).should be_within(1.0e-9).of 0.5
|
204
|
+
p6.p_lt(2).should be_within(1.0e-9).of 1/6.0
|
205
|
+
p10.p_lt(6).should be_within(1.0e-9).of 0.5
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should return 1.0 when the target number is higher than maximum possible" do
|
209
|
+
p2.p_lt(6).should == 1.0
|
210
|
+
p4.p_lt(5).should == 1.0
|
211
|
+
p6.p_lt(7).should == 1.0
|
212
|
+
p10.p_lt(20).should == 1.0
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should return 0.0 when the target number is lower than or equal to minimum possible" do
|
216
|
+
p2.p_lt(1).should == 0.0
|
217
|
+
p4.p_lt(-5).should == 0.0
|
218
|
+
p6.p_lt(1).should == 0.0
|
219
|
+
p10.p_lt(-200).should == 0.0
|
220
|
+
end
|
221
|
+
end # describe "#p_lt"
|
222
|
+
|
223
|
+
describe "#to_h" do
|
224
|
+
# This is used loads in other tests
|
225
|
+
it "should represent a valid distribution with each integer result associated with its probability" do
|
226
|
+
p2.to_h.should be_valid_distribution
|
227
|
+
p4.to_h.should be_valid_distribution
|
228
|
+
p6.to_h.should be_valid_distribution
|
229
|
+
p10.to_h.should be_valid_distribution
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "#min" do
|
234
|
+
it "should return lowest possible result allowed by distribution" do
|
235
|
+
p2.min.should == 1
|
236
|
+
p4.min.should == 1
|
237
|
+
p6.min.should == 1
|
238
|
+
p10.min.should == 1
|
239
|
+
GamesDice::Probabilities.add_distributions( p6, p10 ).min.should == 2
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "#max" do
|
244
|
+
it "should return highest possible result allowed by distribution" do
|
245
|
+
p2.max.should == 2
|
246
|
+
p4.max.should == 4
|
247
|
+
p6.max.should == 6
|
248
|
+
p10.max.should == 10
|
249
|
+
GamesDice::Probabilities.add_distributions( p6, p10 ).max.should == 16
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe "#expected" do
|
254
|
+
it "should return the weighted mean value" do
|
255
|
+
p2.expected.should be_within(1.0e-9).of 1.5
|
256
|
+
p4.expected.should be_within(1.0e-9).of 2.5
|
257
|
+
p6.expected.should be_within(1.0e-9).of 3.5
|
258
|
+
p10.expected.should be_within(1.0e-9).of 5.5
|
259
|
+
GamesDice::Probabilities.add_distributions( p6, p10 ).expected.should be_within(1.0e-9).of 9.0
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
end # describe "instance methods"
|
264
|
+
|
265
|
+
end
|