nerd_dice 0.1.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/main.yml +67 -0
  4. data/.rubocop.yml +114 -0
  5. data/CHANGELOG.md +76 -2
  6. data/Gemfile +2 -2
  7. data/Gemfile.lock +54 -32
  8. data/README.md +372 -5
  9. data/bin/generate_checksums +13 -0
  10. data/bin/nerd_dice_benchmark +322 -0
  11. data/certs/msducheminjr.pem +26 -0
  12. data/checksum/nerd_dice-0.1.0.gem.sha256 +1 -0
  13. data/checksum/nerd_dice-0.1.0.gem.sha512 +1 -0
  14. data/checksum/nerd_dice-0.1.1.gem.sha256 +1 -0
  15. data/checksum/nerd_dice-0.1.1.gem.sha512 +1 -0
  16. data/checksum/nerd_dice-0.2.0.gem.sha256 +1 -0
  17. data/checksum/nerd_dice-0.2.0.gem.sha512 +1 -0
  18. data/checksum/nerd_dice-0.3.0.gem.sha256 +1 -0
  19. data/checksum/nerd_dice-0.3.0.gem.sha512 +1 -0
  20. data/lib/nerd_dice/class_methods/configure.rb +50 -0
  21. data/lib/nerd_dice/class_methods/execute_die_roll.rb +47 -0
  22. data/lib/nerd_dice/class_methods/harvest_totals.rb +40 -0
  23. data/lib/nerd_dice/class_methods/refresh_seed.rb +83 -0
  24. data/lib/nerd_dice/class_methods/roll_ability_scores.rb +73 -0
  25. data/lib/nerd_dice/class_methods/roll_dice.rb +45 -0
  26. data/lib/nerd_dice/class_methods/total_ability_scores.rb +52 -0
  27. data/lib/nerd_dice/class_methods/total_dice.rb +44 -0
  28. data/lib/nerd_dice/class_methods.rb +30 -0
  29. data/lib/nerd_dice/configuration.rb +91 -0
  30. data/lib/nerd_dice/convenience_methods.rb +279 -0
  31. data/lib/nerd_dice/dice_set.rb +166 -0
  32. data/lib/nerd_dice/die.rb +51 -0
  33. data/lib/nerd_dice/sets_randomization_technique.rb +19 -0
  34. data/lib/nerd_dice/version.rb +1 -1
  35. data/lib/nerd_dice.rb +15 -33
  36. data/nerd_dice.gemspec +12 -7
  37. data.tar.gz.sig +0 -0
  38. metadata +97 -21
  39. metadata.gz.sig +0 -0
  40. data/.travis.yml +0 -6
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
+ [![Coverage Status](https://coveralls.io/repos/github/statelesscode/nerd_dice/badge.svg?branch=master)](https://coveralls.io/github/statelesscode/nerd_dice?branch=master)
2
+ ![Build](https://github.com/statelesscode/nerd_dice/actions/workflows/main.yml/badge.svg)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/721f587b792d583065be/maintainability)](https://codeclimate.com/github/statelesscode/nerd_dice/maintainability)
1
4
  # NerdDice
2
5
  Nerd dice allows you to roll polyhedral dice and add bonuses as you would in a tabletop roleplaying game. You can choose to roll multiple dice and keep a specified number of dice such as rolling 4d6 and dropping the lowest for ability scores or rolling with advantage and disadvantage if those mechanics exist in your game.
3
6
 
7
+ ## Educational Videos By Stateless Code
8
+ The end-to-end process of developing this gem has been captured as [instructional videos](https://www.youtube.com/playlist?list=PL9kkbu1kLUeOnUtMpAnJOCtHdThx1Efkt). The videos are in a one-take style so that the mistakes along the way have troubleshooting and the concepts used to develop the gem are explained as they are covered.
9
+
4
10
  ## Installation
5
11
 
6
12
  Add this line to your application's Gemfile:
@@ -18,18 +24,379 @@ Or install it yourself as:
18
24
  $ gem install nerd_dice
19
25
 
20
26
  ## Usage
27
+ After the gem is installed, you can require it as you would any other gem.
28
+
29
+ ```ruby
30
+ require 'nerd_dice'
31
+ ```
32
+
33
+ ### Module methods or a dynamic method_missing DSL
34
+ There are two main patterns for using NerdDice in your project. You can invoke the module-level methods like `NerdDice.total_dice` or you can include the `NerdDice::ConvenienceMethods` module to your class \(or IRB \). Once mixed in, you can dynamically invoke methods like `roll_d20_with_advantage` or `total_3d8_plus5`. See the [Convenience Methods Mixin](#convenience-methods-mixin) section for usage details.
35
+
36
+ ### Configuration
37
+ You can customize the behavior of NerdDice via a configuration block as below or by assigning an individual property via the ```NerdDice.configuration.property = value``` syntax \(where ```property``` is the config property and ```value``` is the value you want to assign\)\. The available configuration options as well as their defaults, if applicable, are listed in the example configuration block below:
38
+
39
+ ```ruby
40
+ NerdDice.configure do | config|
41
+
42
+ # number of ability scores to place in an ability score array
43
+ config.ability_score_array_size = 6 # must duck-type to positive Integer
44
+
45
+ # number of sides for each ability score Die
46
+ config.ability_score_number_of_sides = 6 # must duck-type to positive Integer
47
+
48
+ # total number of dice rolled for each ability score
49
+ config.ability_score_dice_rolled = 4 # must duck-type to positive Integer
50
+
51
+ # highest(n) dice from the total number of dice rolled
52
+ # that are included in the ability scoretotal
53
+ #
54
+ # CANNOT EXCEED ability_score_dice_rolled see Note below
55
+ config.ability_score_dice_kept = 3 # must duck-type to positive Integer
56
+
57
+ # randomization technique options are:
58
+ # :securerandom => Uses SecureRandom.rand(). Good entropy, medium speed.
59
+ # :random_rand => Uses Random.rand(). Class method. Poor entropy, fastest speed.
60
+ # (Seed is shared with other processes. Too predictable)
61
+ # :random_object => Uses Random.new() and calls rand()
62
+ # Medium entropy, fastest speed. (Performs the best under speed benchmark)
63
+ # :randomized =>
64
+ # Uses a random choice of the :securerandom, :rand, and :random_new_interval options above
65
+ config.randomization_technique = :random_object # fast with independent seed
66
+
67
+ # Number of iterations to use on a generator before refreshing the seed
68
+ # 1 very slow and heavy pressure on processor and memory but very high entropy
69
+ # 1000 would refresh the object every 1000 times you call rand()
70
+ config.refresh_seed_interval = nil # don't refresh the seed
71
+ # Background and foreground die colors are string values.
72
+ # By default these correspond to the constants in the class
73
+ # Defaults: DEFAULT_BACKGROUND_COLOR = "#0000DD" DEFAULT_FOREGROUND_COLOR = "#DDDDDD"
74
+ # It is recommended but not enforced that these should be valid CSS color property attributes
75
+ config.die_background_color = "red"
76
+ config.die_foreground_color = "#000"
77
+ end
78
+ ```
79
+ **Note:** You cannot set `ability_score_dice_kept` greater than `ability_score_dice_rolled`. If you try to set `ability_score_dice_kept` higher than `ability_score_dice_rolled`, an error will be raised. If you set `ability_score_dice_rolled` _lower_ than the existing value of `ability_score_dice_kept`, no error will be thrown, but `ability_score_dice_kept` will be _**modified**_ to match `ability_score_dice_rolled` and a warning will be printed.
80
+
21
81
  ### Rolling a number of dice and adding a bonus
82
+ You can use two different methods to roll dice. The `total_dice` method returns an `Integer` representing the total of the dice plus any applicable bonuses. The `total_dice` method does not support chaining additional methods like `highest`, `lowest`, `with_advantage`, `with_disadvantage`. The `roll_dice` method returns a `DiceSet` collection object, and allows for chaining the methods mentioned above and iterating over the individual `Die` objects. `NerdDice.roll_dice.total` and `NerdDice.total_dice` are roughly equivalent.
83
+
22
84
  ```ruby
23
85
  # roll a single d4
24
- NerdDice.total_dice(4) # => return random Integer between 1-4
86
+ NerdDice.total_dice(4) # => Integer: between 1-4
87
+ NerdDice.roll_dice(4) # => DiceSet: with one 4-sided Die with a value between 1-4
88
+ NerdDice.roll_dice(4).total # => Integer: between 1-4
25
89
 
26
90
  # roll 3d6
27
- NerdDice.total_dice(6, 3) => return Integer total of three 6-sided dice
91
+ NerdDice.total_dice(6, 3) # => Integer: total of three 6-sided dice
92
+ NerdDice.roll_dice(6, 3) # => DiceSet: three 6-sided Die objects, each with values between 1-6
93
+ NerdDice.roll_dice(6, 3).total # => Integer: total of three 6-sided dice
28
94
 
29
95
  # roll a d20 and add 5 to the value
30
- NerdDice.total_dice(20, 1, { bonus: 5 })
96
+ NerdDice.total_dice(20, bonus: 5) # => Integer: roll a d20 and add the bonus to the total
97
+ NerdDice.roll_dice(20, bonus: 5) # => DiceSet: one 20-sided Die and bonus of 5
98
+ NerdDice.roll_dice(20, bonus: 5).total # => Integer: roll a d20 and add the bonus to the total
99
+
100
+ # without changing the config at the module level
101
+ # roll a d20 and overide the configured randomization_technique one time
102
+ NerdDice.total_dice(20, randomization_technique: :randomized) # => Integer
103
+ # roll a d20 and overide the configured randomization_technique for the DiceSet
104
+ # object will persist on the DiceSet object for subsequent rerolls
105
+ NerdDice.roll_dice(20, randomization_technique: :randomized) # => DiceSet with :randomized
106
+ ```
107
+ __NOTE:__ If provided, the bonus must respond to `:to_i` or an `ArgumentError` will be raised
108
+
109
+ ### Taking actions on the dice as objects using the DiceSet object
110
+ The `NerdDice.roll_dice` method or the `NerdDice::DiceSet.new` methods return a collection object with an array of one or more `Die` objects. There are properties on both the `DiceSet` object and the `Die` object. Applicable properties are cascaded from the `DiceSet` to the `Die` objects in the collection by default.
111
+
112
+ ```ruby
113
+ # These are equivalent. Both return a NerdDice::DiceSet
114
+ dice_set = NerdDice.roll_dice(6, 3, bonus: 2, randomization_technique: :randomized,
115
+ damage_type: 'psychic', foreground_color: '#FFF', background_color: '#0FF')
116
+
117
+ dice_set = NerdDice::DiceSet.new(6, 3, bonus: 2, randomization_technique: :randomized,
118
+ damage_type: 'psychic', foreground_color: '#FFF', background_color: '#0FF')
119
+
120
+ ```
121
+ #### Available options for NerdDice::DiceSet objects
122
+ There are a number of options that can be provided when initializing a `NerdDice::DiceSet` object after specifying the mandatory number of sides and the optional number of dice \(default: 1\). The list below provides the options and indicates whether they are cascaded to the Die objects in the collection.
123
+ * `bonus` \(Duck-type Integer, _default: 0_\): Bonus or penalty to apply to the total after all dice are rolled. _**Not applied** to Die objects_
124
+ * `randomization_technique` \(Symbol, _default: nil_\): Randomization technique override to use for the `DiceSet`. If `nil` it will use the value in `NerdDice.configuration`. _**Applied** to Die objects by default with ability to modify_
125
+ * `damage_type` \(String, _default: nil_\): Optional string indicating the damage type associated with the dice for systems where it is relevant. _**Applied** to Die objects by default with ability to modify_
126
+ * `foreground_color` \(String, _default: `NerdDice.configuration.die_foreground_color`_\): Intended foreground color to apply to the dice in the `DiceSet`. Should be a valid CSS color but is not validated or enforced and doesn\'t currently have any real functionality associated with it. _**Applied** to Die objects by default with ability to modify_
127
+ * `background_color` \(String, _default: `NerdDice.configuration.die_background_color`_\): Intended background color to apply to the dice in the `DiceSet`. Should be a valid CSS color but is not validated or enforced and doesn\'t currently have any real functionality associated with it. _**Applied** to Die objects by default with ability to modify_
128
+
129
+ #### Properties of individual Die objects
130
+ When initialized from a `DiceSet` object most of the properties of the `Die` object are inherited from the `DiceSet` object. In addition, there is an `is_included_in_total` public attribute that can be set to indicate whether the value of that particular die should be included in the total for its parent `DiceSet`. This property always starts out as true when the `Die` is initialized, but can be set to false.
131
+
132
+ ```ruby
133
+ # six sided die
134
+ die = NerdDice::Die.new(6, randomization_technique: :randomized, damage_type: 'psychic',
135
+ foreground_color: '#FFF', background_color: '#0FF')
136
+ die.is_included_in_total # => true
137
+ die.included_in_total? # => true
138
+ die.is_included_in_total = false
139
+ die.included_in_total? # => false
140
+
141
+ # value property
142
+ die.value # => Integer between 1 and number_of_sides
143
+
144
+ # Rolls/rerolls the Die, sets value to the result of the roll, and returns the new value
145
+ die.roll # => Integer.
146
+ ```
147
+ #### Iterating through dice in a DiceSet
148
+ The `DiceSet` class mixes in the `Enumerable` module and the `Die` object mixes in the `Comparable` module. This allows you to iterate over the dice in the collection. The `sort` method on the dice will return the die objects in ascending value from lowest to highest.
149
+
150
+ ```ruby
151
+ dice_set = NerdDice.roll_dice(6, 3) # => NerdDice::DiceSet
152
+ dice_set.dice => Array of Die objects
153
+ dice_set.length # => 3. (dice_set.dice.length)
154
+ dice_set[0] # => NerdDice::Die (first element of dice array)
155
+ # take actions on each die
156
+ dice_set.each do |die|
157
+ # print the current value
158
+ puts "Die value before reroll is #{die.value}"
159
+ # set the foreground_color of the die
160
+ die.foreground_color = ["gray", "#FF0000#", "#d9d9d9", "green"].shuffle.first
161
+ # reroll the die
162
+ die.roll
163
+ # print the new value
164
+ puts "Die value after reroll is #{die.value}"
165
+ # do other things
166
+ end
167
+ ```
168
+ #### Methods and method chaining on the DiceSet
169
+ Since the DiceSet is an object, you can call methods that operate on the result returned and allow for things like the 5e advantage/disadvantage mechanic, the ability to re-roll all of the dice in the `DiceSet`, or to mark them all as included in the total.
170
+
171
+ ```ruby
172
+ ##############################################
173
+ # highest/with_advantage and lowest/with_disadvantage methods
174
+ # assuming 4d6 with values of [1, 3, 4, 6]
175
+ ##############################################
176
+ dice_set = NerdDice.roll_dice(6, 4)
177
+
178
+ # the 6, 4, and 3 will have is_included_in_total true while the 1 has it false
179
+ # Returns the existing DiceSet object with the changes made to dice inclusion
180
+ dice_set.highest(3) # => DiceSet
181
+ dice_set.with_advantage(3) # => DiceSet (Alias of highest method)
182
+
183
+ # calling total after highest/with_advantage for this DiceSet
184
+ dice_set.total # => 13
185
+
186
+ # same DiceSet using lowest.
187
+ # The 1, 3, and 4 will have is_included_in_total true while the 6 has it false
188
+ dice_set.lowest(3) # => DiceSet
189
+ dice_set.with_disadvantage(3) # => DiceSet (Alias of lowest method)
190
+
191
+ # calling total after lowest/with_disadvantage for this DiceSet
192
+ dice_set.total # => 8
193
+
194
+ # you can chain these methods (assumes the same seed as the above examples)
195
+ NerdDice.roll_dice(6, 4).with_advantage(3).total # => 13
196
+ NerdDice.roll_dice(6, 4).lowest(3).total # => 8
197
+
198
+ # reroll_all! method
199
+ dice_set = NerdDice.roll_dice(6, 4)
200
+ # rerolls each of the Die objects in the collection and re-includes them in the total
201
+ dice_set.reroll_all!
202
+
203
+ # include_all_dice! method
204
+ dice_set.include_all_dice! # resets is_included_in_total to true for all Die objects
205
+ ```
206
+
207
+ ### Rolling Ability Scores
208
+ You can call `roll_ability_scores` or `total_ability_scores` to get back an array of `DiceSet` objects or `Integer` objects, respectively. The `total_ability_scores` method calls `total` on each `DiceSet` and returns those numbers with one value per ability score. The `Configuration` object defaults to 6 ability scores using a methodology of __4d6 drop the lowest__ by default.
209
+
210
+ ```ruby
211
+ # return an array of DiceSet objects including info about the discarded dice
212
+ #
213
+ NerdDice.roll_ability_scores
214
+ #=> [DiceSet0, DiceSet1, ...]
215
+ # => DiceSet0 hash representation { total: 12, dice: [
216
+ # {value: 2, is_included_in_total: true},
217
+ # {value: 6, is_included_in_total: true},
218
+ # {value: 4, is_included_in_total: true},
219
+ # {value: 1, is_included_in_total: false}
220
+ # ]}
221
+ # if you want to get back DiceSet objects that you can interact with
222
+
223
+ # just return an array of totaled ability scores
224
+ NerdDice.total_ability_scores
225
+ #=> [12, 14, 13, 15, 10, 8]
226
+ ```
227
+
228
+ Both methods can be called without arguments to use the values specified in `NerdDice.configuration` or passed a set of options.
229
+ ```ruby
230
+
231
+ # total_dice and roll_dice take the same set of options
232
+ NerdDice.roll_ability_scores(
233
+ ability_score_array_size: 7,
234
+ ability_score_number_of_sides: 8,
235
+ ability_score_dice_rolled: 5,
236
+ ability_score_dice_kept: 4,
237
+ randomization_technique: :randomized,
238
+ foreground_color: "#FF0000",
239
+ background_color: "#FFFFFF"
240
+ )
241
+ # => [DiceSet0, DiceSet1, ...] with 7 ability scores that each roll 5d8 dropping the lowest
242
+ # or if called with total_ability_scores
243
+ # => [27, 17, 21, 17, 23, 13, 27]
244
+ ```
245
+ **Note:** If you try to call this method with `ability_score_dice_kept` greater than `ability_score_dice_rolled` an error will be raised.
246
+
247
+ ### Manually setting or refreshing the random generator seed
248
+ For randomization techniques other than `:securerandom` you can manually set or refresh the generator's seed by calling the `refresh_seed!` method. This is automatically called at the interval specified in `NerdDice.configuration.refresh_seed_interval` if it is not nil.
249
+
250
+ ```ruby
251
+ # no arguments, will refresh the seed for the configured generator(s) only
252
+ NerdDice.refresh_seed! # => hash with old seed(s) or nil if :securerandom
253
+
254
+ # OPTIONS:
255
+ # randomization_technique (Symbol) => NerdDice::RANDOMIZATION_TECHNIQUES
256
+ # random_rand_seed (Integer) => Seed to set for Random
257
+ # random_object_seed (Integer) => Seed to set for new Random object
258
+ NerdDice.refresh_seed!(randomization_technique: :randomized,
259
+ random_rand_seed: 1337,
260
+ random_object_seed: 24601)
261
+ ```
262
+ __NOTE:__ Ability to specify a seed is primarily provided for testing purposes. This makes all random numbers generated _transparently deterministic_ and should not be used if you want behavior approximating randomness.
263
+
264
+ ### Utility Methods
265
+
266
+ #### Harvesting Totals from DiceSets
267
+ The `harvest_totals` method take any collection of objects where each element responds to `total` and return an array of the results of the total method.
268
+ ```ruby
269
+ ability_score_array = NerdDice.roll_ability_scores
270
+ # => Array of 6 DiceSet objects
271
+
272
+ # Arguments:
273
+ # collection (Enumerable) a collection where each element responds to total
274
+ #
275
+ # Return (Array) => Data type of each element will be whatever is returned by total method
276
+ totals_array = NerdDice.harvest_totals(totals_array)
277
+ # => [15, 14, 13, 12, 10, 8]
278
+ # yes, it just happened to be the standard array by amazing coincidence
279
+ ```
280
+ <a name="convenience-methods-mixin"></a>
281
+ ### Convenience Methods Mixin
282
+ NerdDice provides an optional mixin `NerdDice::ConvenienceMethods` that uses Ruby\'s `method_missing` metaprogramming pattern to allow you to roll any number of dice with bonuses and/or the advantage/disadvantage mechanic by dynamically responding to methods that you type that match the `roll_` or `total_` pattern.
283
+
284
+ #### Considerations for ConvenienceMethods
285
+ Before mixing in this method with a class, be aware of other `method_missing` gems that you are also mixing into your project and be sure to write robust tests. We have sought to use `method_missing` in a responsible manner that delegates back to the default implementation using `super` if the method does not match the `ConvenienceMethods` pattern, but there is no guarantee that other gems included in your project are doing the same. If you run into problems with the `ConvenienceMethods` module interacting with other `method_missing` gems, everything that the `ConvenienceMethods` module does can be replicated using the module\-level methods described above or by calling the convenience method on `NerdDice`.
286
+
287
+ Once a particular method has been called, it will define that method so that the next time it will invoke the method directly instead of traversing up the call stack for `method_missing`, which improves performance. The method will remain defined for the duration of the Ruby interpreter process.
288
+
289
+ #### Calling ConvenienceMethods as NerdDice class methods
290
+ NerdDice extends the `ConvenienceMethods` module into the top-level module as class methods, so you can call the methods on the NerdDice module without needing to worry about the implications of extending it into your own class.
291
+ ```ruby
292
+ require 'nerd_dice'
293
+ # works with all the examples and patterns below
294
+ NerdDice.roll_3d6_lowest2_minus1
295
+ NerdDice.total_d20_with_advantage_p6
296
+ ```
297
+
298
+ #### Mixing in the ConvenienceMethods module
299
+ To mix the NerdDice DSL into your class, make sure the gem is required if not already and then use `include NerdDice::ConvenienceMethods` as shown below:
300
+ ```ruby
301
+ # example of a class that mixes in NerdDice::ConvenienceMethods
302
+ require 'nerd_dice'
303
+ class Monster
304
+ include NerdDice::ConvenienceMethods
305
+
306
+ # hard-coding probably not the best solution
307
+ # but gives you an idea how to mix in to a simple class
308
+ def hits_the_monster
309
+ # using the ConvenienceMethods version
310
+ total_d20_plus5 >= @armor_class ? "hit" : "miss"
311
+ end
312
+
313
+ def initialize(armor_class=16)
314
+ @armor_class = armor_class
315
+ end
316
+ end
317
+ ```
318
+ To mix in the module as class methods, you can use `extend NerdDice::ConvenienceMethods`
319
+ ```ruby
320
+ # example of a class that mixes in NerdDice::ConvenienceMethods
321
+ require 'nerd_dice'
322
+ class OtherClass
323
+ extend NerdDice::ConvenienceMethods
324
+ end
325
+ OtherClass.roll_3d6_lowest2_minus1 # returns NerdDice::DiceSet
326
+ ```
327
+
328
+ #### ConvenienceMethods usage examples
329
+ Any invocation of `NerdDice.roll_dice` and `NerdDice.total_dice` can be duplicated using the `NerdDice::ConvenienceMethods` mixin. Here are some examples of what you can do with the return types and equivalent methods in the comments:
330
+
331
+ * `roll_dNN` and `total_dNN` roll one die
332
+ ```ruby
333
+ roll_d20 # => DiceSet: NerdDice.roll_dice(20)
334
+ roll_d8 # => DiceSet: NerdDice.roll_dice(8)
335
+ roll_d1000 # => DiceSet: NerdDice.roll_dice(1000)
336
+ total_d20 # => Integer NerdDice.total_dice(20)
337
+ total_d8 # => Integer NerdDice.total_dice(8)
338
+ total_d1000 # => Integer NerdDice.total_dice(1000)
339
+ ```
340
+ * `roll_NNdNN` and `total_NNdNN` roll specified quantity of dice
341
+ ```ruby
342
+ roll_2d20 # => DiceSet: NerdDice.roll_dice(20, 2)
343
+ roll_3d8 # => DiceSet: NerdDice.roll_dice(8, 3)
344
+ roll_22d1000 # => DiceSet: NerdDice.roll_dice(1000, 22)
345
+ total_2d20 # => Integer NerdDice.total_dice(20, 2)
346
+ total_3d8 # => Integer NerdDice.total_dice(8, 3)
347
+ total_22d1000 # => Integer NerdDice.total_dice(1000, 22)
348
+ ```
349
+ * Keyword arguments are passed on to `roll_dice`/`total_dice` method
350
+ ```ruby
351
+ roll_2d20 foreground_color: "blue" # => DiceSet: NerdDice.roll_dice(20, 2, foreground_color: "blue")
352
+
353
+ total_d12 randomization_technique: :randomized
354
+ # => Integer NerdDice.total_dice(12, randomization_technique: :randomized)
355
+ total_22d1000 randomization_technique: :random_rand
356
+ # => Integer NerdDice.total_dice(1000, 22, randomization_technique: :random_rand)
357
+
358
+ roll_4d6_with_advantage3 background_color: 'blue'
359
+ # => DiceSet: NerdDice.roll_dice(4, 3, background_color: 'blue').highest(3)
360
+ total_4d6_with_advantage3 randomization_technique: :random_rand
361
+ # => Integer: NerdDice.roll_dice(4, 3, randomization_technique: :random_rand).highest(3).total
362
+ ```
363
+ * Positive and negative bonuses can be used with `plus` (alias `p`) or `minus` (alias `m`)
364
+ ```ruby
365
+ roll_d20_plus6 # => DiceSet: NerdDice.roll_dice(20, bonus: 6)
366
+ total_3d8_p2 # => Integer: NerdDice.total_dice(8, 3, bonus: 2)
367
+ total_d20_minus5 # => Integer: NerdDice.total_dice(20, bonus: -6)
368
+ roll_3d8_m3 # => DiceSet: NerdDice.roll_dice(8, 3, bonus: -3)
369
+ ```
370
+ * `_with_advantageN` or `highestN` roll with advantage
371
+ * `_with_disadvantageN` or `lowestN` roll with disadvantage
372
+ * Calling `roll_dNN_with_advantage` \(and variants\) rolls 2 dice and keeps one
373
+ ```ruby
374
+ # equivalent
375
+ roll_3d8_with_advantage1
376
+ roll_3d8_highest1
377
+ # => DiceSet: NerdDice.roll_dice(8, 3).with_advantage(1)
378
+
379
+ # calls roll_dice and total to return an integer
380
+ total_3d8_with_advantage1
381
+ total_3d8_highest1
382
+ # => Integer: NerdDice.roll_dice(8, 3).with_advantage(1).total
383
+
384
+ # rolls two dice in this case
385
+ # equal to roll_2d20_with_advantage but more natural
386
+ roll_d20_with_advantage # => DiceSet: NerdDice.roll_dice(20, 2).with_advantage(1)
387
+ # equal to total_2d20_with_advantage but more natural
388
+ total_d20_with_advantage # => Integer: NerdDice.roll_dice(20, 2).with_advantage(1).total
389
+ ```
390
+ #### ConvenienceMethods error handling
391
+ * If you try to call with a plus and a minus, an Exception is raised
392
+ * If you call with a bonus and a keyword argument and they don't match, an Exception is raised
393
+ * Any combination not expressly allowed or matched will call `super` on `method_missing`
394
+ ```ruby
395
+ roll_3d8_plus3_m2 # will raise NameError using super method_missing
396
+ roll_3d8_plus3 bonus: 1 # will raise NerdDice::Error with message about "Bonus integrity failure"
397
+ roll_d20_with_advantage_lowest # will raise NameError using super method_missing
398
+ total_4d6_lowest3_highest2 # will raise NameError using super method_missing
31
399
  ```
32
- __NOTE:__ If provided, the bonus must be an ```Integer``` or it will be ignored
33
400
 
34
401
  ## Development
35
402
 
@@ -44,4 +411,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/statel
44
411
 
45
412
  ## Unlicense, License, and Copyright
46
413
 
47
- The document is dual-licensed under the [MIT](https://opensource.org/licenses/MIT) license and the [UNLICENSE](https://unlicense.org/) \(with strong preference toward the UNLICENSE\)\. The content is released under [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/) \(no rights reserved\). You are free to include it in its original form or modified with or without modification in your own project\.
414
+ The project is dual-licensed under the [MIT](https://opensource.org/licenses/MIT) license and the [UNLICENSE](https://unlicense.org/) \(with strong preference toward the UNLICENSE\)\. The content is released under [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/) \(no rights reserved\). You are free to include it in its original form or modified with or without additional modification in your own project\.
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "digest/sha2"
5
+
6
+ version = ARGV[0]
7
+ built_gem_path = "pkg/nerd_dice-#{version}.gem"
8
+ checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path))
9
+ checksum_path = "checksum/nerd_dice-#{version}.gem.sha512"
10
+ File.open(checksum_path, "w") { |f| f.write(checksum) }
11
+ sha256 = Digest::SHA256.new.hexdigest(File.read(built_gem_path))
12
+ checksum_256_path = "checksum/nerd_dice-#{version}.gem.sha256"
13
+ File.open(checksum_256_path, "w") { |f| f.write(sha256) }