nerd_dice 0.1.0 → 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.
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) }