nerd_dice 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|