games_dice 0.0.2 → 0.0.3

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.
@@ -0,0 +1,281 @@
1
+ require 'games_dice'
2
+
3
+ # Test helper class, a stub of a PRNG
4
+ class TestPRNG
5
+ def initialize
6
+ @numbers = [0.123,0.234,0.345,0.999,0.876,0.765,0.543,0.111,0.333,0.777]
7
+ end
8
+ def rand(n)
9
+ Integer( n * @numbers.pop )
10
+ end
11
+ end
12
+
13
+ describe GamesDice::ComplexDie do
14
+
15
+ before do
16
+ # Set state of default PRNG
17
+ srand(4567)
18
+ end
19
+
20
+ it "should represent a basic die as an object" do
21
+ die = GamesDice::ComplexDie.new(6)
22
+ die.min.should == 1
23
+ die.max.should == 6
24
+ die.sides.should == 6
25
+ end
26
+
27
+ it "should return results based on Ruby's internal rand() by default" do
28
+ die = GamesDice::ComplexDie.new(10)
29
+ [5,4,10,4,7,8,1,9].each do |expected|
30
+ die.roll.should == expected
31
+ die.result.should == expected
32
+ end
33
+ end
34
+
35
+ it "should use any object with a rand(Integer) method" do
36
+ prng = TestPRNG.new()
37
+ die = GamesDice::ComplexDie.new(20, :prng => prng)
38
+ [16,7,3,11,16,18,20,7].each do |expected|
39
+ die.roll.should == expected
40
+ die.result.should == expected
41
+ end
42
+ end
43
+
44
+ it "should optionally accept a rerolls param" do
45
+ GamesDice::ComplexDie.new( 10, :rerolls => [] )
46
+ GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add)] )
47
+ GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add),GamesDice::RerollRule.new(1, :>=, :reroll_subtract)] )
48
+
49
+ lambda do
50
+ GamesDice::ComplexDie.new( 10, :rerolls => 7 )
51
+ end.should raise_error( TypeError )
52
+
53
+ lambda do
54
+ GamesDice::ComplexDie.new( 10, :rerolls => ['hello'] )
55
+ end.should raise_error( TypeError )
56
+
57
+ lambda do
58
+ GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add), :reroll_add] )
59
+ end.should raise_error( TypeError )
60
+ end
61
+
62
+ it "should optionally accept a maps param" do
63
+ GamesDice::ComplexDie.new( 10, :maps => [] )
64
+ GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1)] )
65
+ GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1), GamesDice::MapRule.new(1, :>, -1) ] )
66
+
67
+ lambda do
68
+ GamesDice::ComplexDie.new( 10, :maps => 7 )
69
+ end.should raise_error( TypeError )
70
+
71
+ lambda do
72
+ GamesDice::ComplexDie.new( 10, :maps => ['hello'] )
73
+ end.should raise_error( TypeError )
74
+
75
+ lambda do
76
+ GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1),GamesDice::RerollRule.new(6, :<=, :reroll_add)] )
77
+ end.should raise_error( TypeError )
78
+ end
79
+
80
+ describe "with rerolls" do
81
+ it "should calculate correct minimum and maximum results" do
82
+ die = GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add, 3)] )
83
+ die.min.should == 1
84
+ die.max.should == 40
85
+
86
+ die = GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_subtract)] )
87
+ die.min.should == -9
88
+ die.max.should == 10
89
+
90
+ die = GamesDice::ComplexDie.new( 10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add)] )
91
+ die.min.should == 1
92
+ die.max.should == 10_010
93
+ end
94
+
95
+ it "should simulate a d10 that rerolls and adds on a result of 10" do
96
+ die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add)] )
97
+ [5,4,14,7,8,1,9].each do |expected|
98
+ die.roll.should == expected
99
+ die.result.should == expected
100
+ end
101
+ end
102
+
103
+ it "should explain how it got results outside range 1 to 10 on a d10" do
104
+ die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add),GamesDice::RerollRule.new(1, :>=, :reroll_subtract)] )
105
+ ["5","4","[10+4] 14","7","8","[1-9] -8"].each do |expected|
106
+ die.roll
107
+ die.explain_result.should == expected
108
+ end
109
+ end
110
+
111
+ it "should calculate an expected result" do
112
+ die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add),GamesDice::RerollRule.new(1, :>=, :reroll_subtract)] )
113
+ die.expected_result.should be_within(1e-10).of 5.5
114
+
115
+ die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(1, :<=, :reroll_use_best, 1)] )
116
+ die.expected_result.should be_within(1e-10).of 7.15
117
+
118
+ die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(1, :<=, :reroll_use_worst, 2)] )
119
+ die.expected_result.should be_within(1e-10).of 3.025
120
+
121
+ die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add)] )
122
+ die.expected_result.should be_within(1e-10).of 4.2
123
+
124
+ die = GamesDice::ComplexDie.new(8, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_use_best)] )
125
+ die.expected_result.should be_within(1e-10).of 5.0
126
+
127
+ die = GamesDice::ComplexDie.new(4, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_replace, 1)] )
128
+ die.expected_result.should be_within(1e-10).of 2.875
129
+ end
130
+
131
+ it "should calculate probabilities of each possible result" do
132
+ die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(7, :>, :reroll_add, 1)] )
133
+ die.probabilities[11].should be_within(1e-10).of 2/36.0
134
+ die.probabilities[8].should be_within(1e-10).of 5/36.0
135
+ die.probabilities.values.inject(:+).should be_within(1e-9).of 1.0
136
+
137
+ die = GamesDice::ComplexDie.new(10, :rerolls => [GamesDice::RerollRule.new(10, :<=, :reroll_add)] )
138
+ die.probabilities[8].should be_within(1e-10).of 0.1
139
+ die.probabilities[10].should be_false
140
+ die.probabilities[13].should be_within(1e-10).of 0.01
141
+ die.probabilities[27].should be_within(1e-10).of 0.001
142
+ die.probabilities.values.inject(:+).should be_within(1e-9).of 1.0
143
+
144
+ die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(1, :>=, :reroll_replace, 1)] )
145
+ die.probabilities[1].should be_within(1e-10).of 1/36.0
146
+ die.probabilities[2].should be_within(1e-10).of 7/36.0
147
+ die.probabilities.values.inject(:+).should be_within(1e-9).of 1.0
148
+ end
149
+
150
+ it "should calculate aggregate probabilities" do
151
+ die = GamesDice::ComplexDie.new(6, :rerolls => [GamesDice::RerollRule.new(7, :>, :reroll_add, 1)] )
152
+ die.probability_gt(7).should be_within(1e-10).of 15/36.0
153
+ die.probability_gt(-10).should == 1.0
154
+ die.probability_gt(12).should == 0.0
155
+
156
+ die.probability_ge(7).should be_within(1e-10).of 21/36.0
157
+ die.probability_ge(2).should == 1.0
158
+ die.probability_ge(15).should == 0.0
159
+
160
+ die.probability_lt(7).should be_within(1e-10).of 15/36.0
161
+ die.probability_lt(-10).should == 0.0
162
+ die.probability_lt(13).should == 1.0
163
+
164
+ die.probability_le(7).should be_within(1e-10).of 21/36.0
165
+ die.probability_le(1).should == 0.0
166
+ die.probability_le(12).should == 1.0
167
+ end
168
+ end
169
+
170
+ describe "with maps" do
171
+ it "should calculate correct minimum and maximum results" do
172
+ die = GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S')] )
173
+ die.min.should == 0
174
+ die.max.should == 1
175
+ end
176
+
177
+ it "should simulate a d10 that scores 1 for success on a value of 7 or more" do
178
+ die = GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S')] )
179
+ [0,0,1,0,1,1,0,1].each do |expected|
180
+ die.roll.should == expected
181
+ die.result.should == expected
182
+ end
183
+ end
184
+
185
+ it "should label the mappings applied with the provided names" do
186
+ die = GamesDice::ComplexDie.new( 10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
187
+ ["5", "4", "10 S", "4", "7 S", "8 S", "1 F", "9 S"].each do |expected|
188
+ die.roll
189
+ die.explain_result.should == expected
190
+ end
191
+ end
192
+
193
+ it "should calculate an expected result" do
194
+ die = GamesDice::ComplexDie.new(10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
195
+ die.expected_result.should be_within(1e-10).of 0.3
196
+ end
197
+
198
+ it "should calculate probabilities of each possible result" do
199
+ die = GamesDice::ComplexDie.new(10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
200
+ die.probabilities[1].should be_within(1e-10).of 0.4
201
+ die.probabilities[0].should be_within(1e-10).of 0.5
202
+ die.probabilities[-1].should be_within(1e-10).of 0.1
203
+ die.probabilities.values.inject(:+).should be_within(1e-9).of 1.0
204
+ end
205
+
206
+ it "should calculate aggregate probabilities" do
207
+ die = GamesDice::ComplexDie.new(10, :maps => [GamesDice::MapRule.new(7, :<=, 1, 'S'),GamesDice::MapRule.new(1, :>=, -1, 'F')] )
208
+ die.probability_gt(-1).should be_within(1e-10).of 0.9
209
+ die.probability_gt(-2).should == 1.0
210
+ die.probability_gt(1).should == 0.0
211
+
212
+ die.probability_ge(1).should be_within(1e-10).of 0.4
213
+ die.probability_ge(-1).should == 1.0
214
+ die.probability_ge(2).should == 0.0
215
+
216
+ die.probability_lt(1).should be_within(1e-10).of 0.6
217
+ die.probability_lt(-1).should == 0.0
218
+ die.probability_lt(2).should == 1.0
219
+
220
+ die.probability_le(-1).should be_within(1e-10).of 0.1
221
+ die.probability_le(-2).should == 0.0
222
+ die.probability_le(1).should == 1.0
223
+ end
224
+ end
225
+
226
+ describe "with rerolls and maps" do
227
+ before do
228
+ @die = GamesDice::ComplexDie.new( 6,
229
+ :rerolls => [GamesDice::RerollRule.new(6, :<=, :reroll_add)],
230
+ :maps => [GamesDice::MapRule.new(9, :<=, 1, 'Success')]
231
+ )
232
+ end
233
+
234
+ it "should calculate correct minimum and maximum results" do
235
+ @die.min.should == 0
236
+ @die.max.should == 1
237
+ end
238
+
239
+ it "should calculate an expected result" do
240
+ @die.expected_result.should be_within(1e-10).of 4/36.0
241
+ end
242
+
243
+ it "should calculate probabilities of each possible result" do
244
+ @die.probabilities[1].should be_within(1e-10).of 4/36.0
245
+ @die.probabilities[0].should be_within(1e-10).of 32/36.0
246
+ @die.probabilities.values.inject(:+).should be_within(1e-9).of 1.0
247
+ end
248
+
249
+ it "should calculate aggregate probabilities" do
250
+ @die.probability_gt(0).should be_within(1e-10).of 4/36.0
251
+ @die.probability_gt(-2).should == 1.0
252
+ @die.probability_gt(1).should == 0.0
253
+
254
+ @die.probability_ge(1).should be_within(1e-10).of 4/36.0
255
+ @die.probability_ge(-1).should == 1.0
256
+ @die.probability_ge(2).should == 0.0
257
+
258
+ @die.probability_lt(1).should be_within(1e-10).of 32/36.0
259
+ @die.probability_lt(0).should == 0.0
260
+ @die.probability_lt(2).should == 1.0
261
+
262
+ @die.probability_le(0).should be_within(1e-10).of 32/36.0
263
+ @die.probability_le(-1).should == 0.0
264
+ @die.probability_le(1).should == 1.0
265
+ end
266
+
267
+ it "should apply mapping to final re-rolled result" do
268
+ [0,1,0,0].each do |expected|
269
+ @die.roll.should == expected
270
+ @die.result.should == expected
271
+ end
272
+ end
273
+
274
+ it "should explain how it got each result" do
275
+ ["5", "[6+4] 10 Success", "[6+2] 8", "5"].each do |expected|
276
+ @die.roll
277
+ @die.explain_result.should == expected
278
+ end
279
+ end
280
+ end # describe "with rerolls and maps"
281
+ end # describe GamesDice::ComplexDie
@@ -0,0 +1,44 @@
1
+ require 'games_dice'
2
+
3
+ describe GamesDice::MapRule do
4
+
5
+ describe "#new" do
6
+
7
+ it "should accept self-consistent operator/value pairs as a trigger" do
8
+ GamesDice::MapRule.new( 5, :>, 1 )
9
+ GamesDice::MapRule.new( (1..5), :member?, 17 )
10
+ end
11
+
12
+ it "should reject inconsistent operator/value pairs for a trigger" do
13
+ lambda { GamesDice::MapRule.new( 5, :member?, -1 ) }.should raise_error( ArgumentError )
14
+ lambda { GamesDice::MapRule.new( (1..5), :>, 12 ) }.should raise_error( ArgumentError )
15
+ end
16
+
17
+ it "should reject non-Integer map results" do
18
+ lambda { GamesDice::MapRule.new( 5, :>, :reroll_again ) }.should raise_error( TypeError )
19
+ lambda { GamesDice::MapRule.new( (1..5), :member?, 'foo' ) }.should raise_error( TypeError )
20
+ end
21
+
22
+ end
23
+
24
+ describe '#map_from' do
25
+
26
+ it "should return the mapped value for a match" do
27
+ rule = GamesDice::MapRule.new( 5, :>, -1 )
28
+ rule.map_from(4).should == -1
29
+
30
+ rule = GamesDice::MapRule.new( (1..5), :member?, 3 )
31
+ rule.map_from(4).should == 3
32
+ end
33
+
34
+ it "should return false for no match" do
35
+ rule = GamesDice::MapRule.new( 5, :>, -1 )
36
+ rule.map_from(6).should be_false
37
+
38
+ rule = GamesDice::MapRule.new( (1..5), :member?, 3 )
39
+ rule.map_from(6).should be_false
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,44 @@
1
+ require 'games_dice'
2
+
3
+ describe GamesDice::RerollRule do
4
+
5
+ describe "#new" do
6
+
7
+ it "should accept self-consistent operator/value pairs as a trigger" do
8
+ GamesDice::RerollRule.new( 5, :>, :reroll_subtract )
9
+ GamesDice::RerollRule.new( (1..5), :member?, :reroll_replace )
10
+ end
11
+
12
+ it "should reject inconsistent operator/value pairs for a trigger" do
13
+ lambda { GamesDice::RerollRule.new( 5, :member?, :reroll_subtract ) }.should raise_error( ArgumentError )
14
+ lambda { GamesDice::RerollRule.new( (1..5), :>, :reroll_replace ) }.should raise_error( ArgumentError )
15
+ end
16
+
17
+ it "should reject bad re-roll types" do
18
+ lambda { GamesDice::RerollRule.new( 5, :>, :reroll_again ) }.should raise_error( ArgumentError )
19
+ lambda { GamesDice::RerollRule.new( (1..5), :member?, 42 ) }.should raise_error( ArgumentError )
20
+ end
21
+
22
+ end
23
+
24
+ describe '#applies?' do
25
+
26
+ it "should return true if a trigger condition is met" do
27
+ rule = GamesDice::RerollRule.new( 5, :>, :reroll_subtract )
28
+ rule.applies?(4).should be_true
29
+
30
+ rule = GamesDice::RerollRule.new( (1..5), :member?, :reroll_subtract )
31
+ rule.applies?(4).should be_true
32
+ end
33
+
34
+ it "should return false if a trigger condition is not met" do
35
+ rule = GamesDice::RerollRule.new( 5, :>, :reroll_subtract )
36
+ rule.applies?(7).should be_false
37
+
38
+ rule = GamesDice::RerollRule.new( (1..5), :member?, :reroll_subtract )
39
+ rule.applies?(6).should be_false
40
+ end
41
+
42
+ end
43
+
44
+ end
data/travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: games_dice
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
30
46
  description: ! "A simulated-dice library, with flexible rules that allow dice systems
31
47
  from\n many board and roleplay games to be built, run and
32
48
  reported."
@@ -43,12 +59,21 @@ files:
43
59
  - Rakefile
44
60
  - games_dice.gemspec
45
61
  - lib/games_dice.rb
62
+ - lib/games_dice/bunch.rb
63
+ - lib/games_dice/complex_die.rb
46
64
  - lib/games_dice/die.rb
47
65
  - lib/games_dice/die_result.rb
66
+ - lib/games_dice/map_rule.rb
67
+ - lib/games_dice/reroll_rule.rb
48
68
  - lib/games_dice/version.rb
69
+ - spec/bunch_spec.rb
70
+ - spec/complex_die_spec.rb
49
71
  - spec/die_result_spec.rb
50
72
  - spec/die_spec.rb
51
- homepage: ''
73
+ - spec/map_rule_spec.rb
74
+ - spec/reroll_rule_spec.rb
75
+ - travis.yml
76
+ homepage: https://github.com/neilslater/games_dice
52
77
  licenses: []
53
78
  post_install_message:
54
79
  rdoc_options: []
@@ -60,12 +85,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
85
  - - ! '>='
61
86
  - !ruby/object:Gem::Version
62
87
  version: '0'
88
+ segments:
89
+ - 0
90
+ hash: -106818565940437427
63
91
  required_rubygems_version: !ruby/object:Gem::Requirement
64
92
  none: false
65
93
  requirements:
66
94
  - - ! '>='
67
95
  - !ruby/object:Gem::Version
68
96
  version: '0'
97
+ segments:
98
+ - 0
99
+ hash: -106818565940437427
69
100
  requirements: []
70
101
  rubyforge_project:
71
102
  rubygems_version: 1.8.24
@@ -73,6 +104,9 @@ signing_key:
73
104
  specification_version: 3
74
105
  summary: Simulates and explains dice rolls from a variety of game systems.
75
106
  test_files:
107
+ - spec/bunch_spec.rb
108
+ - spec/complex_die_spec.rb
76
109
  - spec/die_result_spec.rb
77
110
  - spec/die_spec.rb
78
- has_rdoc:
111
+ - spec/map_rule_spec.rb
112
+ - spec/reroll_rule_spec.rb