nerd_dice 0.3.1 → 0.4.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/main.yml +3 -3
- data/.rubocop.yml +200 -14
- data/CHANGELOG.md +78 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +44 -49
- data/README.md +131 -1
- data/bin/generate_checksums +2 -2
- data/bin/nerd_dice_benchmark +118 -0
- data/certs/msducheminjr.pem +24 -25
- data/lib/nerd_dice/convenience_methods.rb +279 -0
- data/lib/nerd_dice/version.rb +1 -1
- data/lib/nerd_dice.rb +3 -0
- data/nerd_dice.gemspec +5 -6
- data.tar.gz.sig +0 -0
- metadata +46 -47
- metadata.gz.sig +2 -5
data/bin/generate_checksums
CHANGED
@@ -7,7 +7,7 @@ version = ARGV[0]
|
|
7
7
|
built_gem_path = "pkg/nerd_dice-#{version}.gem"
|
8
8
|
checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path))
|
9
9
|
checksum_path = "checksum/nerd_dice-#{version}.gem.sha512"
|
10
|
-
File.
|
10
|
+
File.open(checksum_path, "w") { |f| f.write(checksum) }
|
11
11
|
sha256 = Digest::SHA256.new.hexdigest(File.read(built_gem_path))
|
12
12
|
checksum_256_path = "checksum/nerd_dice-#{version}.gem.sha256"
|
13
|
-
File.
|
13
|
+
File.open(checksum_256_path, "w") { |f| f.write(sha256) }
|
data/bin/nerd_dice_benchmark
CHANGED
@@ -16,14 +16,26 @@ RATIOS = {
|
|
16
16
|
total_dice_random_rand_3d6: 30.0,
|
17
17
|
total_dice_random_object_3d6: 25.5,
|
18
18
|
total_dice_randomized_3d6: 15.5,
|
19
|
+
total_magic_securerandom: 6.04,
|
20
|
+
total_magic_random_rand: 85.9,
|
21
|
+
total_magic_random_object: 89.42,
|
22
|
+
total_magic_randomized: 20.27,
|
19
23
|
roll_dice_securerandom: 4.0,
|
20
24
|
roll_dice_random_rand: 42.0,
|
21
25
|
roll_dice_random_object: 44.0,
|
22
26
|
roll_dice_randomized: 14.5,
|
27
|
+
roll_magic_securerandom: 6.04,
|
28
|
+
roll_magic_random_rand: 85.9,
|
29
|
+
roll_magic_random_object: 89.42,
|
30
|
+
roll_magic_randomized: 20.27,
|
23
31
|
roll_dice_securerandom_3d6: 13.0,
|
24
32
|
roll_dice_random_rand_3d6: 79.0,
|
25
33
|
roll_dice_random_object_3d6: 86.0,
|
26
34
|
roll_dice_randomized_3d6: 26.5,
|
35
|
+
roll_magic_securerandom_3d6: 18.38,
|
36
|
+
roll_magic_random_rand_3d6: 108.54,
|
37
|
+
roll_magic_random_object_3d6: 118.36,
|
38
|
+
roll_magic_randomized_3d6: 44.39,
|
27
39
|
roll_ability_scores_randomized: 30.5,
|
28
40
|
total_ability_scores_randomized: 30.5
|
29
41
|
}.freeze
|
@@ -52,6 +64,8 @@ random_rand_baseline = baselines[0].real
|
|
52
64
|
securerandom_baseline = baselines[1].real
|
53
65
|
|
54
66
|
puts "Roll d1000s"
|
67
|
+
|
68
|
+
puts "total_dice"
|
55
69
|
total_dice_d1000_results = Benchmark.bm do |x|
|
56
70
|
# NerdDice.total_dice securerandom
|
57
71
|
x.report("total_dice_securerandom") do
|
@@ -84,6 +98,42 @@ check_against_baseline! random_rand_baseline, total_dice_random_object
|
|
84
98
|
total_dice_randomized = total_dice_d1000_results[3]
|
85
99
|
check_against_baseline! ((random_rand_baseline * 0.75) + (securerandom_baseline * 0.25)), total_dice_randomized
|
86
100
|
|
101
|
+
puts "total_ d1000s ConvenienceMethods"
|
102
|
+
|
103
|
+
# NOTE: Due to method_missing overhead, using roll_ ratios for ConvenienceMethods total_dice
|
104
|
+
total_d1000_results = Benchmark.bm do |x|
|
105
|
+
# NerdDice.total_dice securerandom
|
106
|
+
x.report("total_magic_securerandom") do
|
107
|
+
NerdDice.configuration.randomization_technique = :securerandom
|
108
|
+
n.times { NerdDice.total_d1000 }
|
109
|
+
end
|
110
|
+
|
111
|
+
x.report("total_magic_random_rand") do
|
112
|
+
NerdDice.configuration.randomization_technique = :random_rand
|
113
|
+
n.times { NerdDice.total_d1000 }
|
114
|
+
end
|
115
|
+
|
116
|
+
x.report("total_magic_random_object") do
|
117
|
+
NerdDice.configuration.randomization_technique = :random_object
|
118
|
+
n.times { NerdDice.total_d1000 }
|
119
|
+
end
|
120
|
+
|
121
|
+
x.report("total_magic_randomized") do
|
122
|
+
NerdDice.configuration.randomization_technique = :randomized
|
123
|
+
n.times { NerdDice.total_d1000 }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
total_magic_securerandom = total_d1000_results[0]
|
128
|
+
check_against_baseline! securerandom_baseline, total_magic_securerandom
|
129
|
+
total_magic_random_rand = total_d1000_results[1]
|
130
|
+
check_against_baseline! random_rand_baseline, total_magic_random_rand
|
131
|
+
total_magic_random_object = total_d1000_results[2]
|
132
|
+
check_against_baseline! random_rand_baseline, total_magic_random_object
|
133
|
+
total_magic_randomized = total_d1000_results[3]
|
134
|
+
check_against_baseline! ((random_rand_baseline * 0.75) + (securerandom_baseline * 0.25)), total_magic_randomized
|
135
|
+
|
136
|
+
puts "roll_dice"
|
87
137
|
roll_dice_d1000_results = Benchmark.bm do |x|
|
88
138
|
# NerdDice.roll_dice securerandom
|
89
139
|
x.report("roll_dice_securerandom") do
|
@@ -116,7 +166,41 @@ check_against_baseline! random_rand_baseline, roll_dice_random_object
|
|
116
166
|
roll_dice_randomized = roll_dice_d1000_results[3]
|
117
167
|
check_against_baseline! ((random_rand_baseline * 0.75) + (securerandom_baseline * 0.25)), roll_dice_randomized
|
118
168
|
|
169
|
+
puts "roll_ d1000s ConvenienceMethods"
|
170
|
+
roll_d1000_results = Benchmark.bm do |x|
|
171
|
+
# NerdDice.roll_dice securerandom
|
172
|
+
x.report("roll_magic_securerandom") do
|
173
|
+
NerdDice.configuration.randomization_technique = :securerandom
|
174
|
+
n.times { NerdDice.roll_d1000 }
|
175
|
+
end
|
176
|
+
|
177
|
+
x.report("roll_magic_random_rand") do
|
178
|
+
NerdDice.configuration.randomization_technique = :random_rand
|
179
|
+
n.times { NerdDice.roll_d1000 }
|
180
|
+
end
|
181
|
+
|
182
|
+
x.report("roll_magic_random_object") do
|
183
|
+
NerdDice.configuration.randomization_technique = :random_object
|
184
|
+
n.times { NerdDice.roll_d1000 }
|
185
|
+
end
|
186
|
+
|
187
|
+
x.report("roll_magic_randomized") do
|
188
|
+
NerdDice.configuration.randomization_technique = :randomized
|
189
|
+
n.times { NerdDice.roll_d1000 }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
roll_magic_securerandom = roll_d1000_results[0]
|
194
|
+
check_against_baseline! securerandom_baseline, roll_magic_securerandom
|
195
|
+
roll_magic_random_rand = roll_d1000_results[1]
|
196
|
+
check_against_baseline! random_rand_baseline, roll_magic_random_rand
|
197
|
+
roll_magic_random_object = roll_d1000_results[2]
|
198
|
+
check_against_baseline! random_rand_baseline, roll_magic_random_object
|
199
|
+
roll_magic_randomized = roll_d1000_results[3]
|
200
|
+
check_against_baseline! ((random_rand_baseline * 0.75) + (securerandom_baseline * 0.25)), roll_magic_randomized
|
201
|
+
|
119
202
|
puts "Roll 3d6"
|
203
|
+
puts "total_dice 3d6"
|
120
204
|
total_dice_3d6_results = Benchmark.bm do |x|
|
121
205
|
# NerdDice.total_dice securerandom
|
122
206
|
x.report("total_dice_securerandom_3d6") do
|
@@ -149,6 +233,7 @@ check_against_baseline! random_rand_baseline, total_dice_3d6_random_object
|
|
149
233
|
total_dice_3d6_randomized = total_dice_3d6_results[3]
|
150
234
|
check_against_baseline! ((random_rand_baseline * 0.75) + (securerandom_baseline * 0.25)), total_dice_3d6_randomized
|
151
235
|
|
236
|
+
puts "roll_dice 3d6"
|
152
237
|
roll_dice_3d6_results = Benchmark.bm do |x|
|
153
238
|
# NerdDice.roll_dice securerandom
|
154
239
|
x.report("roll_dice_securerandom_3d6") do
|
@@ -181,6 +266,39 @@ check_against_baseline! random_rand_baseline, roll_dice_3d6_random_object
|
|
181
266
|
roll_dice_3d6_randomized = roll_dice_3d6_results[3]
|
182
267
|
check_against_baseline! ((random_rand_baseline * 0.75) + (securerandom_baseline * 0.25)), roll_dice_3d6_randomized
|
183
268
|
|
269
|
+
puts "roll_3d6 ConvenienceMethods"
|
270
|
+
roll_magic_3d6_results = Benchmark.bm do |x|
|
271
|
+
# NerdDice.roll_magic securerandom
|
272
|
+
x.report("roll_magic_securerandom_3d6") do
|
273
|
+
NerdDice.configuration.randomization_technique = :securerandom
|
274
|
+
n.times { NerdDice.roll_3d6 }
|
275
|
+
end
|
276
|
+
|
277
|
+
x.report("roll_magic_random_rand_3d6") do
|
278
|
+
NerdDice.configuration.randomization_technique = :random_rand
|
279
|
+
n.times { NerdDice.roll_3d6 }
|
280
|
+
end
|
281
|
+
|
282
|
+
x.report("roll_magic_random_object_3d6") do
|
283
|
+
NerdDice.configuration.randomization_technique = :random_object
|
284
|
+
n.times { NerdDice.roll_3d6 }
|
285
|
+
end
|
286
|
+
|
287
|
+
x.report("roll_magic_randomized_3d6") do
|
288
|
+
NerdDice.configuration.randomization_technique = :randomized
|
289
|
+
n.times { NerdDice.roll_3d6 }
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
roll_magic_3d6_securerandom = roll_magic_3d6_results[0]
|
294
|
+
check_against_baseline! securerandom_baseline, roll_magic_3d6_securerandom
|
295
|
+
roll_magic_3d6_random_rand = roll_magic_3d6_results[1]
|
296
|
+
check_against_baseline! random_rand_baseline, roll_magic_3d6_random_rand
|
297
|
+
roll_magic_3d6_random_object = roll_magic_3d6_results[2]
|
298
|
+
check_against_baseline! random_rand_baseline, roll_magic_3d6_random_object
|
299
|
+
roll_magic_3d6_randomized = roll_magic_3d6_results[3]
|
300
|
+
check_against_baseline! ((random_rand_baseline * 0.75) + (securerandom_baseline * 0.25)), roll_magic_3d6_randomized
|
301
|
+
|
184
302
|
puts "Setting n down to 5,000 due to more intensive methods"
|
185
303
|
n = 5_000
|
186
304
|
|
data/certs/msducheminjr.pem
CHANGED
@@ -1,27 +1,26 @@
|
|
1
1
|
-----BEGIN CERTIFICATE-----
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
t+NX7PDOWx4k
|
2
|
+
MIIETTCCArWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1zdGF0
|
3
|
+
ZWxlc3Njb2RlL0RDPWdtYWlsL0RDPWNvbTAeFw0yMDEyMDYyMzQ1NTZaFw0yMTEy
|
4
|
+
MDYyMzQ1NTZaMCgxJjAkBgNVBAMMHXN0YXRlbGVzc2NvZGUvREM9Z21haWwvREM9
|
5
|
+
Y29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyHIMJi0o3bwBZsx5
|
6
|
+
TQ35XByFsdDRsro3T+0NY7EAILtOiU04o9C2NPOp/RQE7BXQgMjGebwp6bT6QvzN
|
7
|
+
6noV4jPL7Fi5pWw08QygG7f+73YUBb+8d8o+3xGrC+UO5h1PZEtVcZwUWUG18QBE
|
8
|
+
fbDinQT6P4IDQoZwhfrPCB+aBfUyQp4Ok7oD7MEWqsq9SjrSxqxfk4+oZdXUySe7
|
9
|
+
Vi5vnzVQ5uFf56NHwWnNKCzJzmH84mBO5MzHaQpHNzKGJPoUmzLU5RBlCH6YXqBG
|
10
|
+
KhXTMUDBWKJmJ3RDry/FpGgJLKu4wzFRYjXla6IjeKozWGuPNNJ+2mesXKhsX7bo
|
11
|
+
vVCzRxPEupbEg/0FkJiWpiGlSPOdd6oJiwX8E6rlEeV605xrbOQewkbovHkYTMtG
|
12
|
+
+NH+u08x0z4Oj71kmDLwuj812uS0mtrCg2VhiYO0ZCQ4XrwBsBfK+/MtMlR+o6sG
|
13
|
+
/zvz/vHVJKaLTQxRp5oGo4QH6HfbOnwzTkXdZnt5AlN31ErJAgMBAAGjgYEwfzAJ
|
14
|
+
BgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUC7seYydsGO6O1qT4nVVD
|
15
|
+
G/LkiHYwIgYDVR0RBBswGYEXc3RhdGVsZXNzY29kZUBnbWFpbC5jb20wIgYDVR0S
|
16
|
+
BBswGYEXc3RhdGVsZXNzY29kZUBnbWFpbC5jb20wDQYJKoZIhvcNAQELBQADggGB
|
17
|
+
ADPRFRB1cjqdcE2O0jtqiDRmrR62uEYBiUbkRPVhyoEp/cK0TVhAs9mGWAyCWu0M
|
18
|
+
LewUeqNTUvQ9MgvagcKcnxa2RTjdrP3nGnwpStMr9bm3ArNJEzvWEs0Eusk9y73x
|
19
|
+
fjy0qH2pw5WPfWcKYlDehMXqOP+a4udYsz0YSNiI8qEfkDCSqTJN11d5kSjVjwGB
|
20
|
+
xkauxDT68j1JZRjPmQl3dl+DCgxkoziWX2mFTPLfGg5vZ0t6gmhdUtLvJtNIo0IX
|
21
|
+
477E5UjmE1+rULQp/fsH6n5+H+t2eCED41ST+gkKbaQBUfIuUaCmdHz9sJaIIBw2
|
22
|
+
6ordFa1nrLV4w5Uf6qYFnWVhIWX4GToyZSPO2s0DPYp3PWFJ4VtzKa2vp1TR5ZEA
|
23
|
+
dkij2eQ9M8bzWWmW+A7RNaI0CzLl967bKGBSaMVCsZGBarggWD8UwJnBhTuOPZGR
|
24
|
+
WQ4faXJSevxT+x9TgyUNJINPkz/KqreClzdL83cwxPzFFQto7zF6zMCsj0slqJjW
|
25
|
+
EQ==
|
27
26
|
-----END CERTIFICATE-----
|
@@ -0,0 +1,279 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NerdDice
|
4
|
+
# The NerdDice::ConvenienceMethods module overrides the default behavior of method_missing to
|
5
|
+
# provide the ability to dynamically roll dice with methods names that match specific patterns.
|
6
|
+
#
|
7
|
+
# Examples:
|
8
|
+
# `roll_dNN` and `total_dNN` roll one die
|
9
|
+
# <tt>
|
10
|
+
# roll_d20 # => DiceSet: NerdDice.roll_dice(20)
|
11
|
+
# roll_d8 # => DiceSet: NerdDice.roll_dice(8)
|
12
|
+
# roll_d1000 # => DiceSet: NerdDice.roll_dice(1000)
|
13
|
+
# total_d20 # => Integer NerdDice.total_dice(20)
|
14
|
+
# total_d8 # => Integer NerdDice.total_dice(8)
|
15
|
+
# total_d1000 # => Integer NerdDice.total_dice(1000)
|
16
|
+
# </tt>
|
17
|
+
#
|
18
|
+
# `roll_NNdNN` and `total_NNdNN` roll specified quantity of dice
|
19
|
+
# <tt>
|
20
|
+
# roll_2d20 # => DiceSet: NerdDice.roll_dice(20, 2)
|
21
|
+
# roll_3d8 # => DiceSet: NerdDice.roll_dice(8, 3)
|
22
|
+
# roll_22d1000 # => DiceSet: NerdDice.roll_dice(1000, 22)
|
23
|
+
# total_2d20 # => Integer NerdDice.total_dice(20, 2)
|
24
|
+
# total_3d8 # => Integer NerdDice.total_dice(8, 3)
|
25
|
+
# total_22d1000 # => Integer NerdDice.total_dice(1000, 22)
|
26
|
+
# </tt>
|
27
|
+
#
|
28
|
+
# Keyword arguments are passed on to `roll_dice`/`total_dice` method
|
29
|
+
# <tt>
|
30
|
+
# roll_2d20 foreground_color: 'blue' # => DiceSet: NerdDice.roll_dice(20, 2, foreground_color: 'blue')
|
31
|
+
# roll_2d20 foreground_color: 'blue' # => DiceSet: NerdDice.roll_dice(20, 2, foreground_color: 'blue')
|
32
|
+
# total_d12 randomization_technique: :randomized
|
33
|
+
# # => Integer NerdDice.total_dice(12, randomization_technique: :randomized)
|
34
|
+
# total_22d1000 randomization_technique: :random_rand
|
35
|
+
# # => Integer NerdDice.total_dice(1000, 22, randomization_technique: :random_rand)
|
36
|
+
# roll_4d6_with_advantage3 foreground_color: 'blue'
|
37
|
+
# # => DiceSet: NerdDice.roll_dice(4, 3, foreground_color: 'blue').highest(3)
|
38
|
+
# total_4d6_with_advantage3 randomization_technique: :random_rand
|
39
|
+
# # => Integer: NerdDice.roll_dice(4, 3, randomization_technique: :random_rand).highest(3).total
|
40
|
+
# </tt>
|
41
|
+
#
|
42
|
+
# Positive and negative bonuses can be used with `plus` (alias `p`) or `minus` (alias `m`) in DSL
|
43
|
+
# <tt>
|
44
|
+
# roll_d20_plus6 # => DiceSet: NerdDice.roll_dice(20, bonus: 6)
|
45
|
+
# total_3d8_p2 # => Integer: NerdDice.total_dice(8, 3, bonus: 2)
|
46
|
+
# total_d20_minus5 # => Integer: NerdDice.total_dice(20, bonus: -6)
|
47
|
+
# roll_3d8_m3 # => DiceSet: NerdDice.roll_dice(8, 3, bonus: -3)
|
48
|
+
# </tt>
|
49
|
+
#
|
50
|
+
# Advantage and disadvantage
|
51
|
+
# * `_with_advantageN` or `highestN` roll with advantage
|
52
|
+
# * `_with_disadvantageN` or `lowestN` roll with disadvantage
|
53
|
+
# * Calling `roll_dNN_with_advantage` \(and variants\) rolls 2 dice and keeps one
|
54
|
+
# <tt>
|
55
|
+
# # equivalent
|
56
|
+
# roll_3d8_with_advantage1
|
57
|
+
# roll_3d8_highest1
|
58
|
+
# # => DiceSet: NerdDice.roll_dice(8, 3).with_advantage(1)
|
59
|
+
# # calls roll_dice and total to return an integer
|
60
|
+
# total_3d8_with_advantage1
|
61
|
+
# total_3d8_highest1
|
62
|
+
# # => Integer: NerdDice.roll_dice(8, 3).with_advantage(1).total
|
63
|
+
# # rolls two dice in this case
|
64
|
+
# # equal to roll_2d20_with_advantage but more natural
|
65
|
+
# roll_d20_with_advantage # => DiceSet: NerdDice.roll_dice(20, 2).with_advantage(1)
|
66
|
+
# # equal to total_2d20_with_advantage but more natural
|
67
|
+
# total_d20_with_advantage # => Integer: NerdDice.roll_dice(20, 2).with_advantage(1).total
|
68
|
+
# </tt>
|
69
|
+
#
|
70
|
+
# Error Handling
|
71
|
+
# * If you try to call with a plus and a minus, an Exception is raised
|
72
|
+
# * If you call with a bonus and a keyword argument and they don't match, an Exception is raised
|
73
|
+
# * Any combination not expressly allowed or matched will call `super`
|
74
|
+
# <tt>
|
75
|
+
# roll_3d8_plus3_m2 # raise NerdDice::Error
|
76
|
+
# roll_3d8_plus3 bonus: 1 # raise NerdDice::Error
|
77
|
+
# roll_d20_with_advantage_lowest # will raise NameError using super method_missing
|
78
|
+
# total_4d6_lowest3_highest2 # will raise NameError using super method_missing
|
79
|
+
module ConvenienceMethods
|
80
|
+
DIS = /_(with_disadvantage|lowest)/.freeze
|
81
|
+
ADV = /_(with_advantage|highest)/.freeze
|
82
|
+
MOD = /(_p(lus)?\d+|_m(inus)?\d+)/.freeze
|
83
|
+
OVERALL_REGEXP = /\A(roll|total)_\d*d\d+((#{ADV}|#{DIS})\d*)?#{MOD}?\z/.freeze
|
84
|
+
|
85
|
+
# Override of method_missing
|
86
|
+
# * Attempts to match pattern to the regular expression matching the methods
|
87
|
+
# we want to intercept
|
88
|
+
# * If the method matches the pattern, it is defined and executed with send
|
89
|
+
# * Subsequent calls to the same method name will not hit method_missing
|
90
|
+
# * If the method name does not match the regular expression pattern, the default
|
91
|
+
# implementation of method_missing is called by invoking super
|
92
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
93
|
+
# returns false if no match
|
94
|
+
if match_pattern_and_delegate(method_name, *args, **kwargs, &block)
|
95
|
+
# send the method after defining it
|
96
|
+
send(method_name, *args, **kwargs, &block)
|
97
|
+
else
|
98
|
+
super
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Override :respond_to_missing? so that :respond_to? works as expected when the
|
103
|
+
# module is included.
|
104
|
+
def respond_to_missing?(symbol, include_all)
|
105
|
+
symbol.to_s.match?(OVERALL_REGEXP) || super
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# Compares the method name to the regular expression patterns for the module
|
111
|
+
# * If the pattern matches the convenience method is defined and a truthy value is returned
|
112
|
+
# * If the pattern does not match then the method returns false and method_missing invokes `super`
|
113
|
+
def match_pattern_and_delegate(method_name, *args, **kwargs, &block)
|
114
|
+
case method_name.to_s
|
115
|
+
when /\Aroll_\d*d\d+((#{ADV}|#{DIS})\d*)?#{MOD}?\z/o then define_roll_nndnn(method_name, *args, **kwargs,
|
116
|
+
&block)
|
117
|
+
when /\Atotal_\d*d\d+((#{ADV}|#{DIS})\d*)?#{MOD}?\z/o then define_total_nndnn(method_name, *args, **kwargs,
|
118
|
+
&block)
|
119
|
+
else
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# * Evaluates the method name and then uses class_eval and define_method to define the method
|
125
|
+
# * Subsequent calls to the method will bypass method_missing
|
126
|
+
#
|
127
|
+
# Defined method will return a NerdDice::DiceSet
|
128
|
+
def define_roll_nndnn(method_name, *_args, **_kwargs)
|
129
|
+
sides, number_of_dice, number_to_keep = parse_from_method_name(method_name)
|
130
|
+
# defines the method on the class mixing in the module
|
131
|
+
(class << self; self; end).class_eval do
|
132
|
+
define_method method_name do |*_args, **kwargs|
|
133
|
+
modifier = get_modifier_from_method_name!(method_name, kwargs)
|
134
|
+
kwargs[:bonus] = modifier if modifier
|
135
|
+
# NerdDice::DiceSet object
|
136
|
+
dice = NerdDice.roll_dice(sides, number_of_dice, **kwargs)
|
137
|
+
# invoke highest or lowest on the DiceSet if applicable
|
138
|
+
parse_number_to_keep(dice, method_name, number_to_keep)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# The implementation of this is different than the roll_ version because NerdDice.total_dice
|
144
|
+
# cannot deal with highest/lowest calls
|
145
|
+
# * Evaluates the method name and then uses class_eval and define_method to define the method
|
146
|
+
# * Calls :determine_total_method to allow for handling of highest/lowest mechanic
|
147
|
+
# * Subsequent calls to the method will bypass method_missing
|
148
|
+
#
|
149
|
+
# Defined method will return an Integer
|
150
|
+
def define_total_nndnn(method_name, *_args, **_kwargs)
|
151
|
+
(class << self; self; end).class_eval do
|
152
|
+
define_method method_name do |*_args, **kwargs|
|
153
|
+
# parse out bonus before calling determine_total_method
|
154
|
+
modifier = get_modifier_from_method_name!(method_name, kwargs)
|
155
|
+
kwargs[:bonus] = modifier if modifier
|
156
|
+
# parse the method and take different actions based on method_name pattern
|
157
|
+
determine_total_method(method_name, kwargs)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Determines whether to call NerdDice.total_dice or NerdDice.roll_dice.total based on RegEx pattern
|
163
|
+
# * If the method matches the ADV or DIS regular expressions, the method will call :roll_dice and :total
|
164
|
+
# * If the method does not match ADV or DIS, the method will call :total_dice (which is faster)
|
165
|
+
#
|
166
|
+
# Returns Integer irrespective of which methodology is used
|
167
|
+
def determine_total_method(method_name, kwargs)
|
168
|
+
sides, number_of_dice, number_to_keep = parse_from_method_name(method_name)
|
169
|
+
if number_to_keep
|
170
|
+
# NerdDice::DiceSet
|
171
|
+
dice = NerdDice.roll_dice(sides, number_of_dice, **kwargs)
|
172
|
+
# invoke the highest or lowest method to define dice included and then return the total
|
173
|
+
parse_number_to_keep(dice, method_name, number_to_keep).total
|
174
|
+
else
|
175
|
+
NerdDice.total_dice(sides, number_of_dice, **kwargs)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# calls a series of parse methods and then returns them as an array so the define_ methods above can
|
180
|
+
# use a one-liner to assign the variables
|
181
|
+
def parse_from_method_name(method_name)
|
182
|
+
sides = get_sides_from_method_name(method_name)
|
183
|
+
number_of_dice = get_number_of_dice_from_method_name(method_name)
|
184
|
+
number_to_keep = get_number_to_keep_from_method_name(method_name, number_of_dice)
|
185
|
+
[sides, number_of_dice, number_to_keep]
|
186
|
+
end
|
187
|
+
|
188
|
+
# parses out the Integer value of number of sides from the method name
|
189
|
+
# will only ever get called if the pattern matches so no need for guard against nil
|
190
|
+
def get_sides_from_method_name(method_name)
|
191
|
+
method_name.to_s.match(/d\d+/).to_s[1..].to_i
|
192
|
+
end
|
193
|
+
|
194
|
+
# parses the number of dice from the method name and returns the applicable Integer value
|
195
|
+
# * If number of dice are specified in the method name, that value is used
|
196
|
+
# * If number of dice are not specified and ADV or DIS match, the number is set to 2
|
197
|
+
# * If number of dice are not specified and no ADV or DIS match, the number is set to 1
|
198
|
+
def get_number_of_dice_from_method_name(method_name)
|
199
|
+
match_data = method_name.to_s.match(/_\d+d/)
|
200
|
+
default = method_name.to_s.match?(/_d\d+((#{ADV}|#{DIS})\d*)/o) ? 2 : 1
|
201
|
+
match_data ? match_data.to_s[1...-1].to_i : default
|
202
|
+
end
|
203
|
+
|
204
|
+
# parses out the modifier from the method name
|
205
|
+
# * Input must be a valid MatchData object matching the MOD regular expression
|
206
|
+
# * Does not handle nil
|
207
|
+
# * Only called from get_modifier_from_method_name!
|
208
|
+
def get_modifier_from_match_data(match_data)
|
209
|
+
if match_data.to_s.match?(/_p(lus)?\d+/)
|
210
|
+
match_data.to_s.match(/_p(lus)?\d+/).to_s.match(/\d+/).to_s.to_i
|
211
|
+
else
|
212
|
+
match_data.to_s.match(/_m(inus)?\d+/).to_s.match(/\d+/).to_s.to_i * -1
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Parses the method name to determine if the modifier pattern is present
|
217
|
+
# * Returns nil if method name does not match pattern
|
218
|
+
# * Calls get_modifier_from_match_data to get the Integer value of the modifier
|
219
|
+
# * Calls check_bonus_integrity! to ensure that the parsed modifier matches the
|
220
|
+
# keyword argument if present (which will raise an error if they don't match)
|
221
|
+
# * Returns the Integer value of the modifier
|
222
|
+
def get_modifier_from_method_name!(method_name, kwargs)
|
223
|
+
match_data = method_name.to_s.match(MOD)
|
224
|
+
|
225
|
+
return nil unless match_data
|
226
|
+
|
227
|
+
modifier = get_modifier_from_match_data(match_data)
|
228
|
+
|
229
|
+
# will raise error if mismatch
|
230
|
+
check_bonus_integrity!(kwargs, modifier)
|
231
|
+
modifier
|
232
|
+
end
|
233
|
+
|
234
|
+
# Determines whether the highest/lowest/with_advantage/with_disadvantage pattern is present in the
|
235
|
+
# method name and takes appropriate action
|
236
|
+
# * Returns nil if no match
|
237
|
+
# * Returns Integer value of number to keep otherwise (irrespective of highest/lowest)
|
238
|
+
# * Takes the number to keep from the method name if specified
|
239
|
+
# * Returns 1 if number to keep not specified and number of dice equals 1
|
240
|
+
# * Returns number_of_dice -1 if number to keep not specified and more than one die
|
241
|
+
def get_number_to_keep_from_method_name(method_name, number_of_dice)
|
242
|
+
return nil unless method_name.to_s.match?(/(#{ADV}|#{DIS})/o)
|
243
|
+
|
244
|
+
specified_number = method_name.to_s.match(/(#{ADV}|#{DIS})\d+/o)
|
245
|
+
|
246
|
+
# set the default to 1 if only one die or number minus 1 if multiple
|
247
|
+
default = number_of_dice == 1 ? 1 : number_of_dice - 1
|
248
|
+
|
249
|
+
# return pattern match if one exists or number of dice -1 if no pattern match
|
250
|
+
specified_number ? specified_number.to_s.match(/\d+/).to_s.to_i : default
|
251
|
+
end
|
252
|
+
|
253
|
+
# Checks that there is not a mismatch between the modifier specified in the method name and the
|
254
|
+
# modifier specified in the keyword arguments if one is specified
|
255
|
+
# * Raises a NerdDice::Error if there is a mismatch
|
256
|
+
# * Returns true if no keyword argument bonus
|
257
|
+
# * Returns true if keyword argument bonus and method name modifier are consistent
|
258
|
+
def check_bonus_integrity!(kwargs, bonus)
|
259
|
+
bonus_error_message = "Bonus integrity failure: "
|
260
|
+
bonus_error_message += "Modifier specified in keyword arguments was #{kwargs[:bonus]}. "
|
261
|
+
bonus_error_message += "Modifier specified in method_name was #{bonus}. "
|
262
|
+
raise NerdDice::Error, bonus_error_message if kwargs && kwargs[:bonus] && kwargs[:bonus].to_i != bonus
|
263
|
+
|
264
|
+
true
|
265
|
+
end
|
266
|
+
|
267
|
+
# Parses number to keep on a NerdDice::DiceSet
|
268
|
+
# * If number_to_keep falsey, just return the DiceSet object
|
269
|
+
# * If number_to_keep matches ADV pattern return DiceSet with highest called
|
270
|
+
# * If number_to_keep matches DIS pattern return DiceSet with lowest called
|
271
|
+
def parse_number_to_keep(dice, method_name, number_to_keep)
|
272
|
+
return dice unless number_to_keep
|
273
|
+
|
274
|
+
# use match against ADV to determine truth value of ternary expression
|
275
|
+
match_data = method_name.to_s.match(ADV)
|
276
|
+
match_data ? dice.highest(number_to_keep) : dice.lowest(number_to_keep)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
data/lib/nerd_dice/version.rb
CHANGED
data/lib/nerd_dice.rb
CHANGED
@@ -7,6 +7,7 @@ require "nerd_dice/die"
|
|
7
7
|
require "nerd_dice/dice_set"
|
8
8
|
require "nerd_dice/class_methods"
|
9
9
|
require "securerandom"
|
10
|
+
require "nerd_dice/convenience_methods"
|
10
11
|
# Nerd dice allows you to roll polyhedral dice and add bonuses as you would in
|
11
12
|
# a tabletop roleplaying game. You can choose to roll multiple dice and keep a
|
12
13
|
# specified number of dice such as rolling 4d6 and dropping the lowest for
|
@@ -23,4 +24,6 @@ module NerdDice
|
|
23
24
|
RANDOMIZATION_TECHNIQUES = %i[securerandom random_rand random_object randomized].freeze
|
24
25
|
ABILITY_SCORE_KEYS = %i[ability_score_array_size ability_score_number_of_sides ability_score_dice_rolled
|
25
26
|
ability_score_dice_kept].freeze
|
27
|
+
|
28
|
+
extend ConvenienceMethods
|
26
29
|
end
|
data/nerd_dice.gemspec
CHANGED
@@ -28,7 +28,6 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.metadata["bug_tracker_uri"] = "https://github.com/statelesscode/nerd_dice/issues"
|
29
29
|
spec.metadata["documentation_uri"] = "https://github.com/statelesscode/nerd_dice/README.md"
|
30
30
|
spec.metadata["github_repo"] = "https://github.com/statelesscode/nerd_dice"
|
31
|
-
spec.metadata["rubygems_mfa_required"] = "true"
|
32
31
|
|
33
32
|
# Specify which files should be added to the gem when it is released.
|
34
33
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -44,13 +43,13 @@ Gem::Specification.new do |spec|
|
|
44
43
|
spec.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $PROGRAM_NAME.end_with?("gem")
|
45
44
|
|
46
45
|
# Dependencies
|
47
|
-
spec.add_dependency "securerandom", "~> 0.
|
46
|
+
spec.add_dependency "securerandom", "~> 0.1", ">= 0.1.1"
|
48
47
|
|
49
48
|
# Development Dependencies
|
50
|
-
spec.add_development_dependency "coveralls_reborn", "~> 0.
|
51
|
-
spec.add_development_dependency "rubocop", "~> 1.
|
52
|
-
spec.add_development_dependency "rubocop-performance", "~> 1.
|
49
|
+
spec.add_development_dependency "coveralls_reborn", "~> 0.23.0"
|
50
|
+
spec.add_development_dependency "rubocop", "~> 1.22", ">= 1.22.2"
|
51
|
+
spec.add_development_dependency "rubocop-performance", "~> 1.11", ">= 1.11.5"
|
53
52
|
spec.add_development_dependency "rubocop-rake", "~> 0.6", ">= 0.6.0"
|
54
|
-
spec.add_development_dependency "rubocop-rspec", "~> 2.
|
53
|
+
spec.add_development_dependency "rubocop-rspec", "~> 2.5", ">= 2.5.0"
|
55
54
|
spec.add_development_dependency "simplecov-lcov", "~> 0.8.0"
|
56
55
|
end
|
data.tar.gz.sig
CHANGED
Binary file
|