games_dice 0.1.2 → 0.1.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.
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