dicebag 3.2.1 → 3.3.0

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.
@@ -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: []