games_dice 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -54,11 +54,10 @@ Or install it yourself as:
54
54
  ## Library API
55
55
 
56
56
  Although you can refer to the documentation for the contained classes, and use it if needed to
57
- build some exotic dice systems, the recommended way to use GamesDice is to create GamesDice::Dice
58
- objects via the factory methods from the GamesDice module, and then use those objects to simulate
59
- dice rolls, explain the results or calculate probabilties as required.
57
+ build some exotic dice systems, all you need to know to access the core features is described
58
+ here.
60
59
 
61
- ### GamesDice factory methods
60
+ ### GamesDice factory method
62
61
 
63
62
  #### GamesDice.create
64
63
 
@@ -217,23 +216,54 @@ A die modifier can also be a single letter plus an integer value, e.g.
217
216
 
218
217
  1d6r1
219
218
 
220
- More complex die modifiers are possible, with parameters supplied in square brackets, and multiple
221
- modifiers should combine as expected e.g.
219
+ You can add comma-seperated parameters to a modifier by using a ":" (colon) character after the
220
+ modifier letter, and a "." (full stop) to signify the end of the parameters. What parameters are
221
+ accepted, and what they mean, depends on the modifier:
222
222
 
223
- 5d10r[10,add]k2
223
+ 5d10r:>8,add.
224
+
225
+ You can use more than one modifier. Modifiers should be separated by a "." (full stop) character, although
226
+ this is optional if you use modifiers without parameters:
227
+
228
+ 5d10r:10,add.k2
229
+ 5d10xk2
230
+ 5d10x.k2
231
+
232
+ are all equivalent.
224
233
 
225
234
  #### Rerolls
226
235
 
227
236
  You can specify that dice rolling certain values should be re-rolled, and how that re-roll should be
228
237
  interpretted.
229
238
 
230
- The simple form specifies a low value that will automatically trigger a one-time replacement:
239
+ The simple form specifies a low value that will automatically trigger a re-roll and replace:
231
240
 
232
241
  1d6r1
233
242
 
234
243
  When rolled, this die will score from 1 to 6. If it rolls a 1, it will roll again automatically
235
244
  and use that result instead.
236
245
 
246
+ The full version of this modifier, allows you to specify from 1 to 3 parameters:
247
+
248
+ 1d10r:[VALUE_COMPARISON],[REROLL_TYPE],[LIMIT].
249
+
250
+ Where:
251
+
252
+ * VALUE_COMPARISON is one of >, >=, == (default), <= < plus an integer to set conditions on when the reroll should occur
253
+ * REROLL_TYPE is one of
254
+ * replace (default) - use the new value in place of existing value for the die
255
+ * add - add result of reroll to running total, and ignore any subtract rules
256
+ * subtract - subtract result of reroll from running total, and reverse sense of any further add results
257
+ * use_best - use the new value if it is higher than the existing value
258
+ * use_worst - use the new value if it is higher than the existing value
259
+ * LIMIT is an integer that sets the maximum number of times that the rule can be triggered, the default is 1000
260
+
261
+ Examples:
262
+
263
+ 1d6r:1. # Same as "1d6r1"
264
+ 1d10r:10,replace,1. #
265
+ 1d20r:<=10,use_best,1. # Roll a 20-sided die, re-roll a result if 10 or lower, and use best result
266
+
237
267
  #### Maps
238
268
 
239
269
  You can specify that the value shown on each die is converted to some other set of values. If
@@ -264,7 +294,7 @@ short codes.
264
294
 
265
295
  This is an alias for "exploding" dice:
266
296
 
267
- 5d10x
297
+ 5d10x # Same as '5d10r:10,add.'
268
298
 
269
299
  When rolled, this will score from 5 to theoretically any number, as results of 10 on any die mean that
270
300
  die rolls again and the result is added on.
@@ -3,16 +3,17 @@ require 'parslet'
3
3
  # converts string dice descriptions to data usable for the GamesDice::Dice constructor
4
4
  class GamesDice::Parser < Parslet::Parser
5
5
 
6
- # Descriptive language examples (capital letters stand in for integers)
7
- # NdXk[Z,worst] - a roll of N dice, sides X, keep worst Z results and sum them
8
- # NdXr[Z,add] - a roll of N dice, sides X, re-roll and add on a result of Z
9
- # NdXr[Y..Z,add] - a roll of N dice, sides X, re-roll and add on a result of Y..Z
10
- # NdXm[>=Z,A] - mapped dice, values greater than or equal to Z score A (unmapped values score 0 by default)
6
+ # These are the Parslet rules that define the dice grammar. It's an inefficient and over-complex
7
+ # use of Parslet, and could do with logical a clean-up.
11
8
 
12
- # These are the Parslet rules that define the dice grammar
13
9
  rule(:integer) { match('[0-9]').repeat(1) }
14
10
  rule(:range) { integer.as(:range_start) >> str('..') >> integer.as(:range_end) }
15
11
  rule(:dlabel) { match('[d]') }
12
+ rule(:space) { match('\s').repeat(1) }
13
+ rule(:space?) { space.maybe }
14
+ rule(:underscore) { str('_').repeat(1) }
15
+ rule(:underscore?) { space.maybe }
16
+
16
17
  rule(:bunch_start) { integer.as(:ndice) >> dlabel >> integer.as(:sides) }
17
18
 
18
19
  rule(:reroll_label) { match(['r']).as(:reroll) }
@@ -23,12 +24,33 @@ class GamesDice::Parser < Parslet::Parser
23
24
  rule(:single_modifier) { alias_label }
24
25
  rule(:modifier_label) { reroll_label | keep_label | map_label }
25
26
  rule(:simple_modifier) { modifier_label >> integer.as(:simple_value) }
26
- rule(:complex_modifier) { modifier_label >> str('[') >> str(']') } # TODO: param extraction
27
+ rule(:comparison_op) { str('>=') | str('<=') | str('==') | str('>') | str('<') }
28
+ rule(:ctl_string) { match('[a-z_]').repeat(1) }
29
+ rule(:output_string) { match('[A-Za-z0-9_]').repeat(1) }
30
+
31
+ rule(:opint_or_int) { (comparison_op.as(:comparison) >> integer.as(:compare_num)) | integer.as(:compare_num) }
32
+ rule(:comma) { str(',') }
33
+ rule(:stop) { str('.') }
34
+
35
+ rule(:condition_only) { opint_or_int.as(:condition) }
36
+
37
+ rule(:condition_and_type) { opint_or_int.as(:condition) >> comma >> ctl_string.as(:type) }
38
+ rule(:condition_and_num) { opint_or_int.as(:condition) >> comma >> integer.as(:num) }
39
+
40
+ rule(:condition_type_and_num) { opint_or_int.as(:condition) >> comma >> ctl_string.as(:type) >> comma >> integer.as(:num) }
41
+ rule(:condition_num_and_output) { opint_or_int.as(:condition) >> comma >> integer.as(:num) >> comma >> ctl_string.as(:output) }
42
+
43
+ rule(:reroll_params) { condition_type_and_num | condition_and_type | condition_only }
44
+ rule(:map_params) { condition_num_and_output | condition_and_num | condition_only }
45
+
46
+ rule(:full_reroll) { reroll_label >> str(':') >> reroll_params >> stop }
47
+ rule(:full_map) { map_label >> str(':') >> map_params >> stop }
48
+
49
+ rule(:complex_modifier) { full_reroll | full_map }
27
50
 
28
- rule(:bunch_modifier) { single_modifier | simple_modifier }
51
+ rule(:bunch_modifier) { complex_modifier | ( single_modifier >> stop.maybe ) | ( simple_modifier >> stop.maybe ) }
29
52
  rule(:bunch) { bunch_start >> bunch_modifier.repeat.as(:mods) }
30
- rule(:space) { match('\s').repeat(1) }
31
- rule(:space?) { space.maybe }
53
+
32
54
  rule(:operator) { match('[+-]').as(:op) >> space? }
33
55
  rule(:add_bunch) { operator >> bunch >> space? }
34
56
  rule(:add_constant) { operator >> integer.as(:constant) >> space? }
@@ -116,8 +138,18 @@ class GamesDice::Parser < Parslet::Parser
116
138
  out_hash[:rerolls] ||= []
117
139
  if reroll_mod[:simple_value]
118
140
  out_hash[:rerolls] << [ reroll_mod[:simple_value].to_i, :>=, :reroll_replace, 1 ]
141
+ return
142
+ end
143
+ # Typical reroll_mod: {:reroll=>"r"@5, :condition=>{:compare_num=>"10"@7}, :type=>"add"@10}
144
+ op = get_op_symbol( reroll_mod[:condition][:comparison] || '==' )
145
+ v = reroll_mod[:condition][:compare_num].to_i
146
+ type = ( 'reroll_' + ( reroll_mod[:type] || 'replace' ) ).to_sym
147
+
148
+ if reroll_mod[:num]
149
+ out_hash[:rerolls] << [ v, op, type, reroll_mod[:num].to_i ]
150
+ else
151
+ out_hash[:rerolls] << [ v, op, type ]
119
152
  end
120
- # TODO: Handle complex descriptions
121
153
  end
122
154
 
123
155
  # Called for any parsed keeper mode
@@ -137,7 +169,22 @@ class GamesDice::Parser < Parslet::Parser
137
169
  out_hash[:maps] << [ map_mod[:simple_value].to_i, :<=, 1 ]
138
170
  return
139
171
  end
140
- # TODO: Handle complex descriptions
172
+
173
+ # Typical
174
+ end
175
+
176
+ # The dice description language uses (r).op.x, whilst GamesDice::RerollRule uses x.op.(r), so
177
+ # as well as converting to a symbol, we must reverse sense of input to constructor
178
+ OP_CONVERSION = {
179
+ '==' => :==,
180
+ '>=' => :<=,
181
+ '>' => :<,
182
+ '<' => :>,
183
+ '<=' => :>=,
184
+ }
185
+
186
+ def get_op_symbol parsed_op_string
187
+ OP_CONVERSION[ parsed_op_string.to_s ]
141
188
  end
142
189
 
143
190
  end # class Parser
@@ -1,3 +1,3 @@
1
1
  module GamesDice
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
data/spec/readme_spec.rb CHANGED
@@ -146,3 +146,74 @@ describe GamesDice::Probabilities do
146
146
  end
147
147
 
148
148
  end # describe GamesDice::Probabilities
149
+
150
+ describe 'String Dice Description' do
151
+
152
+ before :each do
153
+ srand(35241)
154
+ end
155
+
156
+ describe "'1d6'" do
157
+ it "returns expected results from rolling" do
158
+ d = GamesDice.create '1d6'
159
+ (1..20).map { |n| d.roll }.should == [6, 3, 2, 3, 4, 6, 4, 2, 6, 3, 3, 5, 6, 6, 3, 6, 5, 2, 1, 4]
160
+ end
161
+ end
162
+
163
+ describe "'2d6 + 1d4'" do
164
+ it "returns expected results from rolling" do
165
+ d = GamesDice.create '2d6 + 1d4'
166
+ (1..5).map { |n| d.roll }.should == [11, 10, 12, 12, 14]
167
+ end
168
+ end
169
+
170
+ describe "'1d100 + 1d20 - 5'" do
171
+ it "returns expected results from rolling" do
172
+ d = GamesDice.create '1d100 + 1d20 - 5'
173
+ (1..5).map { |n| d.roll }.should == [75, 78, 24, 102, 32]
174
+ end
175
+ end
176
+
177
+ describe "'1d10x'" do
178
+ it "returns expected results from rolling" do
179
+ d = GamesDice.create '1d10x'
180
+ (1..20).map { |n| d.roll }.should == [2, 3, 4, 7, 6, 7, 4, 2, 6, 3, 7, 5, 6, 7, 6, 6, 5, 19, 4, 19]
181
+ end
182
+ end
183
+
184
+ describe "'1d6r1'" do
185
+ it "returns expected results from rolling" do
186
+ d = GamesDice.create '1d6r1'
187
+ (1..20).map { |n| d.roll }.should == [6, 3, 2, 3, 4, 6, 4, 2, 6, 3, 3, 5, 6, 6, 3, 6, 5, 2, 4, 2]
188
+ end
189
+ end
190
+
191
+ describe "'5d10r:10,add.k2'" do
192
+ it "returns expected results from rolling" do
193
+ d = GamesDice.create '5d10r:10,add.k2'
194
+ (1..5).map { |n| d.roll }.should == [13, 13, 14, 38, 15]
195
+ end
196
+ end
197
+
198
+ describe "'3d10m6'" do
199
+ it "returns expected results from rolling" do
200
+ d = GamesDice.create '3d10m6'
201
+ (1..6).map { |n| d.roll }.should == [0, 3, 1, 1, 3, 2]
202
+ end
203
+ end
204
+
205
+ describe "'5d10k2'" do
206
+ it "returns expected results from rolling" do
207
+ d = GamesDice.create '5d10k2'
208
+ (1..5).map { |n| d.roll }.should == [13, 13, 14, 19, 19]
209
+ end
210
+ end
211
+
212
+ describe "'5d10x'" do
213
+ it "returns expected results from rolling" do
214
+ d = GamesDice.create '5d10x'
215
+ (1..5).map { |n| d.roll }.should == [22, 22, 31, 53, 25]
216
+ end
217
+ end
218
+
219
+ end
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.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-21 00:00:00.000000000 Z
12
+ date: 2013-05-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -112,7 +112,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
112
112
  version: '0'
113
113
  segments:
114
114
  - 0
115
- hash: -2496597161129373974
115
+ hash: -4383036419295474875
116
116
  required_rubygems_version: !ruby/object:Gem::Requirement
117
117
  none: false
118
118
  requirements:
@@ -121,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
121
  version: '0'
122
122
  segments:
123
123
  - 0
124
- hash: -2496597161129373974
124
+ hash: -4383036419295474875
125
125
  requirements: []
126
126
  rubyforge_project:
127
127
  rubygems_version: 1.8.24