dicebag 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,82 +1,93 @@
1
- # Encoding: UTF-8
2
-
3
- # This continues definining the DiceBag module.
1
+ # DiceBag module.
4
2
  module DiceBag
5
- # This is the Transform subclass that takes the
6
- # parsed tree and transforms it into its (almost)
7
- # final form. (It gets a normalization pass later.)
3
+ # This is the Transform subclass that takes the parsed tree and
4
+ # transforms it into its (almost) final form. (It gets a normalization
5
+ # pass later.)
8
6
  class Transform < Parslet::Transform
9
7
  def self.hashify_options(options)
10
8
  opts = {}
11
9
 
12
- options.each { |val| opts.update val } if options.respond_to? :each
10
+ options.each { |val| opts.update val } if options.respond_to?(:each)
13
11
 
14
12
  opts
15
13
  end
16
14
 
17
- # Option transforms. These are turned into an array of
18
- # 2-element arrays ('tagged arrays'), which is then
19
- # hashified later. (There is no way to update the
20
- # options when these rules are matched.)
21
- rule(drop: simple(:x)) { { drop: Integer(x) } }
22
- rule(keep: simple(:x)) { { keep: Integer(x) } }
23
- rule(reroll: simple(:x)) { { reroll: Integer(x) } }
24
- rule(target: simple(:x)) { { target: Integer(x) } }
25
-
26
- # Explode is special, in that if it is nil, then it
27
- # must remain that way.
28
- rule(explode: simple(:x)) { { explode: (x ? Integer(x) : nil) } }
29
-
30
- # Parts {:ops => (:xdx | :number)}
31
- # These are first-match, so the simple number will
32
- # be matched before the xdx subtree.
33
-
34
- # Match an operator followed by a static number.
35
- # TODO: find out why this is not matching simple
36
- # op => integers! -- 2016-04-18
37
- rule(op: simple(:o), value: simple(:v)) do
38
- [String(o), Integer(v)]
15
+ # Options.
16
+ #
17
+ # The full options hash is updated later with these sub-hashes.
18
+ rule(drop: simple(:x)) do
19
+ { drop: Integer(x) }
39
20
  end
40
21
 
41
- # Match an operator followed by an :xdx subtree.
42
- rule(op: simple(:o), value: subtree(:part)) do
43
- value = if part.is_a? Hash
44
- count = Integer(part[:xdx][:count])
45
- sides = Integer(part[:xdx][:sides])
46
- options = Transform.hashify_options(part[:options])
22
+ rule(keep: simple(:x)) do
23
+ { keep: Integer(x) }
24
+ end
47
25
 
48
- { xdx: { count: count, sides: sides }, options: options }
49
- else
50
- Integer(part)
51
- end
26
+ rule(keeplowest: simple(:x)) do
27
+ { keeplowest: Integer(x) }
28
+ end
29
+
30
+ rule(reroll: simple(:x)) do
31
+ { reroll: Integer(x) }
32
+ end
52
33
 
53
- [String(o), value]
34
+ rule(target: simple(:x)) do
35
+ { target: Integer(x) }
36
+ end
37
+
38
+ rule(failure: simple(:x)) do
39
+ { failure: Integer(x) }
40
+ end
41
+
42
+ # Explode is special, in that if it is nil, then we set it to -1 to
43
+ # reflect that.
44
+ rule(explode: simple(:x)) do
45
+ { explode: (x ? Integer(x) : nil) }
54
46
  end
55
47
 
56
48
  # Match a label by itself.
57
- rule(label: simple(:s)) { [:label, String(s)] }
49
+ rule(label: simple(:s)) { [:label, LabelPart.new(String(s))] }
58
50
 
59
51
  # Match a label followed by a :start subtree.
60
52
  rule(label: simple(:s), start: subtree(:part)) do
61
- label = String(s)
62
-
63
- [[:label, label], [:start, part]]
53
+ [
54
+ [:label, LabelPart.new(String(s))],
55
+ [:start, part]
56
+ ]
64
57
  end
65
58
 
66
59
  # Match a :start subtree, with the label not present.
67
- # Note that this returns a hash, but the final output
68
- # will still be in an array.
69
60
  rule(start: subtree(:part)) do
70
61
  [:start, part]
71
62
  end
72
63
 
64
+ # Match the xdx and options hash.
65
+ #
66
+ # TODO: Remove the .as(:xdx) in the Parser sub-class and then update
67
+ # this class to account for it. It'll make the resulting data much
68
+ # cleaner.
73
69
  rule(xdx: subtree(:xdx), options: subtree(:opts)) do
74
70
  { xdx: xdx, options: Transform.hashify_options(opts) }
75
71
  end
76
72
 
77
73
  # Convert the count and sides of an :xdx part.
78
74
  rule(count: simple(:c), sides: simple(:s)) do
79
- { count: Integer(c), sides: Integer(s) }
75
+ {
76
+ count: (c ? Integer(c) : 1),
77
+ sides: Integer(s)
78
+ }
79
+ end
80
+
81
+ # Match an operator followed by an :xdx subtree.
82
+ rule(op: simple(:o), value: subtree(:part)) do
83
+ part[:options] = Transform.hashify_options(part[:options])
84
+
85
+ [String(o), part]
86
+ end
87
+
88
+ # Match an operator followed by a immediate value.
89
+ rule(op: simple(:o), value: simple(:v)) do
90
+ [String(o), Integer(v)]
80
91
  end
81
92
  end
82
93
  end
data/lib/dicebag.rb CHANGED
@@ -1,183 +1,33 @@
1
- # Copyright (c) 2012 Randy Carnahan <syn at dragonsbait dot com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining
4
- # a copy of this software and associated documentation files (the "Software"),
5
- # to deal in the Software without restriction, including without limitation
6
- # the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
- # and/or sell copies of the Software, and to permit persons to whom the
8
- # Software is furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included
11
- # in all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
16
- # NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
- # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18
- # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
19
- # USE OR OTHER DEALINGS IN THE SOFTWARE.
20
- #
21
- # dicelib.rb -- version: 3.2.0
22
-
23
1
  require 'parslet'
24
2
 
25
3
  # This defined the main DiceBag module.
26
4
  module DiceBag
27
- DEFAULT_ROLL = '1d6'.freeze
28
-
29
- # This is our generic DiceBagError
30
- # exception subclass.
31
- class DiceBagError < Exception; end
32
-
33
- ###
34
- # Module Methods
35
- ###
36
-
37
- # This takes the parsed tree, AFTER it has
38
- # been through the Transform class, and massages
39
- # the data a bit more, to ease the iteration that
40
- # happens in the Roll class. It will convert all
41
- # values into the correct *Part class.
42
- def self.normalize_tree(tree)
43
- tree = [tree] unless tree.first.is_a? Array
44
-
45
- tree.map { |part| normalize part }
46
- end
47
-
48
- def self.normalize(part)
49
- [
50
- normalize_op(part.first),
51
- normalize_value(part.last)
52
- ]
53
- end
54
-
55
- def self.normalize_op(op)
56
- # We swap out the strings for symbols.
57
- # If the op is not one of the arithimetic
58
- # operators, then the op itself is returned.
59
- # (This should only happen on :start arrays.)
60
- case op
61
- when '+' then :add
62
- when '-' then :sub
63
- when '*' then :mul
64
- when '/' then :div
65
- else
66
- op
67
- end
68
- end
69
-
70
- def self.normalize_value(val)
71
- case val
72
- when String
73
- LabelPart.new val
74
- when Hash
75
- RollPart.new normalize_xdx(val)
76
- else
77
- StaticPart.new val
78
- end
79
- end
80
-
81
- # This further massages the xDx hashes.
82
- def self.normalize_xdx(hash)
83
- count = hash[:xdx][:count]
84
- sides = hash[:xdx][:sides]
85
-
86
- # Delete the no longer needed :xdx key.
87
- hash.delete(:xdx)
88
-
89
- # Default to at least 1 die.
90
- count = 1 if count.zero? || count.nil?
91
-
92
- # Set the :count and :sides keys directly
93
- # and set the notes array.
94
- hash[:count] = count
95
- hash[:sides] = sides
96
- hash[:notes] = []
97
-
98
- normalize_options hash
99
- end
100
-
101
- def self.normalize_options(hash)
102
- if hash[:options].empty?
103
- hash.delete(:options)
104
- else
105
- normalize_explode hash
106
- normalize_reroll hash
107
- normalize_drop_keep hash
108
- normalize_target hash
109
- end
110
-
111
- hash
112
- end
5
+ # This is our generic DiceBagError exception subclass.
6
+ class DiceBagError < StandardError; end
113
7
 
114
- # Prevent Explosion abuse.
115
- def self.normalize_explode(xdx)
116
- return unless xdx[:options].key? :explode
117
-
118
- explode = xdx[:options][:explode]
119
-
120
- if explode.nil? || explode.zero? || explode == 1
121
- xdx[:options][:explode] = xdx[:sides]
122
-
123
- xdx[:notes].push("Explode set to #{xdx[:sides]}")
124
- end
125
- end
126
-
127
- # Prevent Reroll abuse.
128
- def self.normalize_reroll(xdx)
129
- return unless xdx[:options].key? :reroll
130
-
131
- if xdx[:options][:reroll] >= xdx[:sides]
132
- xdx[:options][:reroll] = 0
133
-
134
- xdx[:notes].push 'Reroll reset to 0.'
135
- end
136
- end
137
-
138
- # Make sure there are enough dice to
139
- # handle both Drop and Keep values.
140
- # If not, both are reset to 0. Harsh.
141
- def self.normalize_drop_keep(xdx)
142
- drop = xdx[:options].fetch(:drop, 0)
143
- keep = xdx[:options].fetch(:keep, 0)
144
-
145
- if (drop + keep) >= xdx[:count]
146
- xdx[:options][:drop] = 0
147
- xdx[:options][:keep] = 0
8
+ # This is the wrapper for the parse, transform, and normalize calls.
9
+ # This is called by the Roll class, but may be called to get the raw
10
+ # returned array of parsed parts for other purposes.
11
+ def self.parse(dstr = '')
12
+ tree = Parser.new.parse(dstr)
13
+ ast = Transform.new.apply(tree)
148
14
 
149
- xdx[:notes].push 'Drop and Keep Conflict. Both reset to 0.'
150
- end
15
+ Normalize.call ast
151
16
  end
152
17
 
153
- # Finally, if we have a target number, make sure it is equal
154
- # to or less than the dice sides and greater than 0, otherwise,
155
- # set it to 0 (aka no target number) and add a note.
156
- def self.normalize_target(xdx)
157
- return unless xdx[:options].key? :target
158
-
159
- target = xdx[:options][:target]
160
-
161
- return if target >= 0 && target <= xdx[:sides]
162
-
163
- xdx[:options][:target] = 0
164
-
165
- xdx[:notes].push 'Target number too large or is negative; reset to 0.'
18
+ ### Main Syntatic Sugar Interface
19
+ def self.roll(dstr)
20
+ DiceBag::Roll.new(dstr).roll
166
21
  end
167
22
 
168
- # This is the wrapper for the parse, transform,
169
- # and normalize calls. This is called by the Roll
170
- # class, but may be called to get the raw returned
171
- # array of parsed bits for other purposes.
172
- def self.parse(dstr = '')
173
- tree = Parser.new.parse(dstr)
174
- ast = Transform.new.apply(tree)
175
-
176
- normalize_tree ast
23
+ # The default roll if one is not given.
24
+ def self.default_roll
25
+ '1d6'
177
26
  end
178
27
  end
179
28
 
180
29
  # Our sub-modules.
30
+ require_relative './dicebag/normalize'
181
31
  require_relative './dicebag/roll_string'
182
32
  require_relative './dicebag/roll_part_string'
183
33
  require_relative './dicebag/parser'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dicebag
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SynTruth
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-18 00:00:00.000000000 Z
11
+ date: 2016-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -32,12 +32,15 @@ dependencies:
32
32
  version: 1.4.0
33
33
  description: A very flexible dice rolling library for Ruby.
34
34
  email: syntruth@gmail.com
35
- executables: []
35
+ executables:
36
+ - dicebag
36
37
  extensions: []
37
38
  extra_rdoc_files: []
38
39
  files:
40
+ - bin/dicebag
39
41
  - lib/dicebag.rb
40
42
  - lib/dicebag/label_part.rb
43
+ - lib/dicebag/normalize.rb
41
44
  - lib/dicebag/parser.rb
42
45
  - lib/dicebag/result.rb
43
46
  - lib/dicebag/roll.rb
@@ -46,29 +49,35 @@ files:
46
49
  - lib/dicebag/roll_string.rb
47
50
  - lib/dicebag/simple_part.rb
48
51
  - lib/dicebag/static_part.rb
52
+ - lib/dicebag/systems/dnd.rb
53
+ - lib/dicebag/systems/fudge.rb
54
+ - lib/dicebag/systems/gurps.rb
55
+ - lib/dicebag/systems/savage_worlds.rb
56
+ - lib/dicebag/systems/standard.rb
57
+ - lib/dicebag/systems/storyteller.rb
49
58
  - lib/dicebag/transform.rb
50
59
  homepage: https://github.com/syntruth/Dice-Bag
51
60
  licenses:
52
61
  - MIT
53
62
  metadata: {}
54
- post_install_message:
63
+ post_install_message:
55
64
  rdoc_options: []
56
65
  require_paths:
57
66
  - lib
67
+ - lib/systems
58
68
  required_ruby_version: !ruby/object:Gem::Requirement
59
69
  requirements:
60
70
  - - ">="
61
71
  - !ruby/object:Gem::Version
62
- version: '0'
72
+ version: 2.7.1
63
73
  required_rubygems_version: !ruby/object:Gem::Requirement
64
74
  requirements:
65
75
  - - ">="
66
76
  - !ruby/object:Gem::Version
67
77
  version: '0'
68
78
  requirements: []
69
- rubyforge_project:
70
- rubygems_version: 2.4.5.1
71
- signing_key:
79
+ rubygems_version: 3.1.2
80
+ signing_key:
72
81
  specification_version: 4
73
82
  summary: 'Dice Bag: Ruby Dice Rolling Library'
74
83
  test_files: []