dicey 0.17.1 → 0.18.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 530103d6f251d8f7c0b8400bde5b70c7c92bc6f79c6638aa99e0900f2af2129b
4
- data.tar.gz: 943e0dd4655eb14c1e8b67bbbdf53deadec26856a864bfa5cb25b7f7d0b624e6
3
+ metadata.gz: c11673348152c65cc6b6bb0b50a3bdf6b515479b6df9e1885c26d1e89e826dd4
4
+ data.tar.gz: 722bcec16c95bd5fbd9774aa382b3d074c2c2898fbef1308ecce9f988f2e2f55
5
5
  SHA512:
6
- metadata.gz: 747b729c073e357d7f4016d40df42a1669d166c1056e91dcf4ad07308a92f6a09c9cc2e058bbf595930766e8319197be529daa5041b280bbb6c98ab1b0f1538f
7
- data.tar.gz: c2cf10206752a9d0fff536b5d1e13291520d2af4355d468f51288d8f922de3c94dfca3cfefc20d6536c5aa957ec8d7e178724a7bdfabf544aaede4ab49cda8aa
6
+ metadata.gz: 536a1274842408a7daeb543382ffd799fbf500020a9bde67f050ce2c3ce0e95e3dd9b0b94034f115af78f0c4d95ad7eef88910271b0db131b6433ad8303b2500
7
+ data.tar.gz: 5a67e9950f0fb8b82b156dcd001623a9029a1fd666e08f6570095648ef4e57e582699ca1ffcb85d1ec9e746adb8dd1f27ad1b36630f5682629a2d5ff57272ac4
data/README.md CHANGED
@@ -70,17 +70,7 @@ gem install dicey
70
70
 
71
71
  Or, if using Bundler, add it to your `Gemfile`:
72
72
  ```rb
73
- gem "dicey", "~> 0.16"
74
- ```
75
-
76
- (Optional) If intending to work with non-numeric dice, install **vector_number** too:
77
- ```sh
78
- gem install vector_number
79
- ```
80
-
81
- or add it to your `Gemfile`:
82
- ```rb
83
- gem "vector_number"
73
+ gem "dicey", "~> 0.17"
84
74
  ```
85
75
 
86
76
  > [!TIP]
@@ -92,10 +82,8 @@ gem "vector_number"
92
82
  ### Requirements
93
83
 
94
84
  **Dicey** is tested to work on CRuby 3.0+, latest JRuby and TruffleRuby. Compatible implementations should work too.
85
+ - `vector_number` is a dependency, used for non-numeric dice, but technically not required.
95
86
  - JSON and YAML formatting require `json` and `yaml`.
96
- - Non-numeric dice require gem `vector_number` to be installed.
97
-
98
- Otherwise, there are no direct dependencies.
99
87
 
100
88
  ## Usage: CLI (command line)
101
89
 
@@ -262,13 +250,11 @@ You are a wizard and you have a spellbook with an elemental vortex spell that de
262
250
  ```sh
263
251
  $ dicey 3d❄️,🔥,⚡️,🌪,🌲 -m r
264
252
  # (❄️,🔥,⚡️,🌪,🌲)+(❄️,🔥,⚡️,🌪,🌲)+(❄️,🔥,⚡️,🌪,🌲)
265
- roll => 1⋅🌪 + 1⋅🌲 + 1⋅⚡️
253
+ roll => 1⋅"🌪" + 1⋅"🌲" + 1⋅"⚡️"
266
254
  ```
267
255
 
268
256
  Wind, wood and lightning in equal proportion it is! Your enemies will tremble!
269
257
 
270
- Regrettably, it is not possible to use elemental dice without installing **vector_number** gem first.
271
-
272
258
  ### All ways to define dice
273
259
 
274
260
  There are four *main* ways to define dice:
@@ -334,18 +320,16 @@ It is easy enough to create numeric dice or dice with distinct symbols. However,
334
320
 
335
321
  For example, a game may have a die which can roll 1-3 💚 or a ♥️. You could just use completely different strings for the different numbers of hearts, but they would not be summable (what if you need to roll two such dice and add them together?). In this case, you can directly use `VectorNumber` to create summable strings:
336
322
  ```rb
337
- # Using Symbols is not required, but they look nicer in output.
338
- # `DieFoundry` uses Symbols for this reason.
339
- heal = VectorNumber[:"💚"]
340
- regen = VectorNumber[:"♥️"]
323
+ heal = VectorNumber["💚"]
324
+ regen = VectorNumber["♥️"]
341
325
  die = Dicey::AbstractDie.new([heal, heal * 2, heal * 3, regen])
342
- # => #<Dicey::AbstractDie:0x00007f4a7c95efe8 @current_side_index=0, @sides_list=[(1⋅💚), (2⋅💚), (3⋅💚), (1⋅♥️)], @sides_num=4>
326
+ # => #<Dicey::AbstractDie:0x00007f4a7c95efe8 @current_side_index=0, @sides_list=[(1⋅"💚"), (2⋅"💚"), (3⋅"💚"), (1⋅"♥️")], @sides_num=4>
343
327
  ```
344
328
 
345
329
  Now such dice can easily be rolled together and results summed:
346
330
  ```rb
347
331
  die.roll + die.roll
348
- # => (5⋅💚)
332
+ # => (5⋅"💚")
349
333
  ```
350
334
 
351
335
  ### Rolling
@@ -394,7 +378,7 @@ die.roll
394
378
  Distribution calculators live in `Dicey::DistributionCalculators` module. There are three main calculators currently.
395
379
  - `Dicey::DistributionCalculators::PolynomialConvolution` is the recommended calculator, able to handle all `Dicey::RegularDie` and other integer dice.
396
380
  - `Dicey::DistributionCalculators::MultinomialCoefficients` is specialized for repeated numeric dice. However, it is currently limited to dice with arithmetic sequences (this includes regular dice, however).
397
- - `Dicey::DistributionCalculators::Iterative` is the most generic, able to work with *abstract* dice. It needs gem "**vector_number**" to be installed and available to work with non-numeric dice.
381
+ - `Dicey::DistributionCalculators::Iterative` is the most generic, able to work with *abstract* dice.
398
382
 
399
383
  Additionally, there are three special calculators.
400
384
  - `Dicey::DistributionCalculators::Binomial` is a fast calculator for collections of equal two-sided dice, like coins, including *abstract* dice.
@@ -517,7 +501,7 @@ This algorithm is based on raising a univariate polynomial to a power and using
517
501
 
518
502
  This is a specialized alogorithm for coin-like dice of any kind. It is significantly faster than general algorithms.
519
503
 
520
- - Limitations: only *equal* two-sided dice are allowed, **vector_number** allows non-numeric values.
504
+ - Limitations: only *equal* two-sided dice are allowed.
521
505
  - Example: `dicey 500d2`
522
506
  - Complexity: **O(n<sup>2</sup>)**
523
507
  - Running time examples:
@@ -528,7 +512,7 @@ This is a specialized alogorithm for coin-like dice of any kind. It is significa
528
512
 
529
513
  At last, there is an iterative algorithm which goes through every possible dice roll and adds results together. Whil being inefficient, it has the largest input space, allowing to work with completely nonsensical dice, including complex numbers and altogether non-numeric values.
530
514
 
531
- - Limitations: without **vector_number** all values must be numbers, otherwise almost any values are viable.
515
+ - Limitations: practically all values are viable.
532
516
  - Example: `dicey 5 1,0.1,2 A,B,C`
533
517
  - Complexity: **O(n<sup>2</sup>⋅m<sup>2</sup>)**
534
518
  - Running time examples:
@@ -7,6 +7,7 @@ require_relative "options"
7
7
  require_relative "calculator_runner"
8
8
  require_relative "calculator_test_runner"
9
9
  require_relative "roller"
10
+ require_relative "verbose_printer"
10
11
 
11
12
  Dir["formatters/*.rb", base: __dir__].each { require_relative _1 }
12
13
 
@@ -46,7 +47,13 @@ module Dicey
46
47
  def call(argv = ARGV)
47
48
  options, arguments = get_options_and_arguments(argv)
48
49
  require_optional_libraries(options)
49
- return_value = RUNNERS[options.delete(:mode)].call(arguments, **options)
50
+
51
+ verbose_printer = VerbosePrinter.new(options[:verbosity])
52
+ verbose_printer.print("Selected mode: #{options[:mode]}")
53
+
54
+ return_value =
55
+ RUNNERS[options.delete(:mode)]
56
+ .call(arguments, **options, verbose_printer: verbose_printer)
50
57
  print return_value if return_value.is_a?(String)
51
58
  !!return_value
52
59
  end
@@ -13,15 +13,17 @@ module Dicey
13
13
  # @param arguments [Array<String>] die definitions
14
14
  # @param format [#call] formatter for output
15
15
  # @param result [Symbol] result type selector
16
+ # @param verbose_printer [VerbosePrinter]
16
17
  # @return [String]
17
18
  # @raise [DiceyError]
18
- def call(arguments, format:, result:, **)
19
+ def call(arguments, format:, result:, verbose_printer: nil, **)
19
20
  raise DiceyError, "no dice!" if arguments.empty?
20
21
 
21
22
  dice = arguments.flat_map { |definition| die_foundry.cast(definition) }
22
23
  calculator = DistributionCalculators::AutoSelector.call(dice)
23
24
  raise DiceyError, "no calculator could handle these dice!" unless calculator
24
25
 
26
+ verbose_printer&.print("Using calculator: #{calculator.class}")
25
27
  distribution = calculator.call(dice, result_type: result)
26
28
 
27
29
  format.call(distribution, AbstractDie.describe(dice))
@@ -72,8 +72,11 @@ module Dicey
72
72
  #
73
73
  # @param report_style [Symbol] one of: +:full+, +:quiet+;
74
74
  # +:quiet+ style does not output any text
75
+ # @param verbose_printer [VerbosePrinter]
75
76
  # @return [Boolean] whether there are no failing tests
76
- def call(*, report_style:, **)
77
+ def call(*, report_style:, verbose_printer: nil, **)
78
+ verbose_printer&.print("Available calculators:")
79
+ verbose_printer&.print(AVAILABLE_CALCULATORS.map { " - #{_1.class}" }.join("\n"))
77
80
  results = TEST_DATA.to_h { |test| run_test(test) }
78
81
  output_report(results, report_style)
79
82
  results.values.none? do |test_result|
@@ -88,9 +88,9 @@ module Dicey
88
88
  puts @parser.ver
89
89
  exit
90
90
  end
91
- @parser.on_tail("-v", "(Deprecated) Show program version and exit.") do
91
+ @parser.on_tail("-v", "--verbose", "Enable verbose output.") do
92
92
  puts @parser.ver
93
- exit
93
+ @options[:verbosity] = 1
94
94
  end
95
95
  end
96
96
 
@@ -4,7 +4,6 @@ require_relative "../die_foundry"
4
4
 
5
5
  require_relative "../mixins/rational_to_integer"
6
6
  require_relative "../mixins/vectorize_dice"
7
- require_relative "../mixins/warn_about_vector_number"
8
7
 
9
8
  module Dicey
10
9
  module CLI
@@ -14,7 +13,6 @@ module Dicey
14
13
  class Roller
15
14
  include Mixins::RationalToInteger
16
15
  include Mixins::VectorizeDice
17
- include Mixins::WarnAboutVectorNumber
18
16
 
19
17
  # @param arguments [Array<String>] die definitions
20
18
  # @param format [#call] formatter for output
@@ -28,7 +26,6 @@ module Dicey
28
26
 
29
27
  format.call({ "roll" => rational_to_integer(result) }, AbstractDie.describe(dice))
30
28
  rescue TypeError
31
- warn_about_vector_number
32
29
  raise DiceyError, "can not roll dice with non-numeric sides!"
33
30
  end
34
31
 
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dicey
4
+ module CLI
5
+ # Prints verbose output to the given IO object.
6
+ class VerbosePrinter
7
+ def initialize(verbosity, io = $stdout)
8
+ @verbosity = verbosity || 0
9
+ @io = io
10
+ end
11
+
12
+ # Prints the given message to the IO object if the verbosity level
13
+ # is greater than or equal to the minimum verbosity level.
14
+ #
15
+ # @param message [String]
16
+ # @param min_verbosity [Integer]
17
+ def print(message, min_verbosity = 1)
18
+ @io.puts message if @verbosity >= min_verbosity
19
+ end
20
+ end
21
+ end
22
+ end
@@ -47,8 +47,8 @@ module Dicey
47
47
  # - list of decimal numbers (like "0.5,0.2,0.8" or "(2.0,)"), which produces a {NumericDie},
48
48
  # but uses +Rational+ for values to maintain precise results;
49
49
  # - list of strings, possibly mixed with numbers (like "0.5,asdf" or "(👑,♠️,♥️,♣️,♦️,⚓️)"),
50
- # which produces an {AbstractDie} with strings converted to Symbols
51
- # and numbers treated the same as in previous cases.
50
+ # which produces an {AbstractDie} with numbers treated the same as in previous cases,
51
+ # and other or quoted values treated as Strings.
52
52
  #
53
53
  # Any die definition can be prefixed with a count, like "2D6" or "1d1,3,5" to create an array.
54
54
  # A plain "d" without an explicit count is ignored instead, creating a single die.
@@ -100,7 +100,7 @@ module Dicey
100
100
  when /\A#{FRACTION}\z/o
101
101
  rational_to_integer(Rational(side))
102
102
  else
103
- side.match(STRING)[:side].to_sym
103
+ side.match(STRING)[:side]
104
104
  end
105
105
  end
106
106
  build_dice(AbstractDie, definition[:count], sides)
@@ -3,7 +3,6 @@
3
3
  require_relative "base_calculator"
4
4
 
5
5
  require_relative "../mixins/vectorize_dice"
6
- require_relative "../mixins/warn_about_vector_number"
7
6
 
8
7
  module Dicey
9
8
  module DistributionCalculators
@@ -15,13 +14,12 @@ module Dicey
15
14
  # Does a number of rolls and calculates approximate probabilities from that.
16
15
  # Even if weights are requested, results are non-integer.
17
16
  #
18
- # If dice include non-numeric sides, gem +vector_number+ has to be installed.
17
+ # If dice include non-numeric sides, gem +vector_number+ has to be available.
19
18
  #
20
19
  # *Options:*
21
20
  # - *rolls* (Integer) (_defaults_ _to:_ _N_) — number of rolls to perform
22
21
  class Empirical < BaseCalculator
23
22
  include Mixins::VectorizeDice
24
- include Mixins::WarnAboutVectorNumber
25
23
 
26
24
  # Default number of rolls to perform.
27
25
  N = 10_000
@@ -29,11 +27,7 @@ module Dicey
29
27
  private
30
28
 
31
29
  def validate(dice)
32
- if defined?(VectorNumber) || dice.all?(NumericDie)
33
- true
34
- else
35
- warn_about_vector_number
36
- end
30
+ !!defined?(VectorNumber) || dice.all?(NumericDie)
37
31
  end
38
32
 
39
33
  def calculate_heuristic(dice_count, sides_count)
@@ -3,26 +3,20 @@
3
3
  require_relative "base_calculator"
4
4
 
5
5
  require_relative "../mixins/vectorize_dice"
6
- require_relative "../mixins/warn_about_vector_number"
7
6
 
8
7
  module Dicey
9
8
  module DistributionCalculators
10
9
  # Calculator for a collection of {AbstractDie} which goes through
11
10
  # every possible combination of dice (somewhat slow).
12
11
  #
13
- # If dice include non-numeric sides, gem +vector_number+ has to be installed.
12
+ # If dice include non-numeric sides, gem +vector_number+ has to be available.
14
13
  class Iterative < BaseCalculator
15
14
  include Mixins::VectorizeDice
16
- include Mixins::WarnAboutVectorNumber
17
15
 
18
16
  private
19
17
 
20
18
  def validate(dice)
21
- if defined?(VectorNumber) || dice.all?(NumericDie)
22
- true
23
- else
24
- warn_about_vector_number
25
- end
19
+ !!defined?(VectorNumber) || dice.all?(NumericDie)
26
20
  end
27
21
 
28
22
  def calculate_heuristic(dice_count, sides_count)
@@ -3,6 +3,8 @@
3
3
  require_relative "mixins/rational_to_integer"
4
4
 
5
5
  module Dicey
6
+ # @note This class is considered experimental. It may be changed at any point.
7
+ #
6
8
  # Calculates distribution properties,
7
9
  # also known as descriptive statistics when applied to a population sample.
8
10
  #
data/lib/dicey/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dicey
4
- VERSION = "0.17.1"
4
+ VERSION = "0.18.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dicey
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.1
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Bulancov
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-12-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: vector_number
@@ -17,13 +16,19 @@ dependencies:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
18
  version: 0.4.3
20
- type: :development
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: 2.0.0
22
+ type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
23
25
  requirements:
24
26
  - - ">="
25
27
  - !ruby/object:Gem::Version
26
28
  version: 0.4.3
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: 2.0.0
27
32
  description: |
28
33
  Dicey provides a CLI executable and a Ruby API for fast calculation of
29
34
  distribution of weights or probabilities of dice rolls,
@@ -33,7 +38,6 @@ description: |
33
38
  It can also be used to roll dice. While not the primary focus,
34
39
  rolling is well supported, including ability to seed random source
35
40
  for reproducible results.
36
- email:
37
41
  executables:
38
42
  - dicey
39
43
  - dicey-to-gnuplot
@@ -59,6 +63,7 @@ files:
59
63
  - lib/dicey/cli/formatters/yaml_formatter.rb
60
64
  - lib/dicey/cli/options.rb
61
65
  - lib/dicey/cli/roller.rb
66
+ - lib/dicey/cli/verbose_printer.rb
62
67
  - lib/dicey/die_foundry.rb
63
68
  - lib/dicey/distribution_calculators/auto_selector.rb
64
69
  - lib/dicey/distribution_calculators/base_calculator.rb
@@ -72,7 +77,6 @@ files:
72
77
  - lib/dicey/mixins/missing_math.rb
73
78
  - lib/dicey/mixins/rational_to_integer.rb
74
79
  - lib/dicey/mixins/vectorize_dice.rb
75
- - lib/dicey/mixins/warn_about_vector_number.rb
76
80
  - lib/dicey/numeric_die.rb
77
81
  - lib/dicey/regular_die.rb
78
82
  - lib/dicey/version.rb
@@ -82,11 +86,10 @@ licenses:
82
86
  metadata:
83
87
  homepage_uri: https://github.com/trinistr/dicey
84
88
  bug_tracker_uri: https://github.com/trinistr/dicey/issues
85
- documentation_uri: https://rubydoc.info/gems/dicey/0.17.1
86
- source_code_uri: https://github.com/trinistr/dicey/tree/v0.17.1
87
- changelog_uri: https://github.com/trinistr/dicey/blob/v0.17.1/CHANGELOG.md
89
+ documentation_uri: https://rubydoc.info/gems/dicey/0.18.0
90
+ source_code_uri: https://github.com/trinistr/dicey/tree/v0.18.0
91
+ changelog_uri: https://github.com/trinistr/dicey/blob/v0.18.0/CHANGELOG.md
88
92
  rubygems_mfa_required: 'true'
89
- post_install_message:
90
93
  rdoc_options:
91
94
  - "--main"
92
95
  - README.md
@@ -103,8 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
106
  - !ruby/object:Gem::Version
104
107
  version: '0'
105
108
  requirements: []
106
- rubygems_version: 3.5.22
107
- signing_key:
109
+ rubygems_version: 3.6.9
108
110
  specification_version: 4
109
111
  summary: Dice roll weights/probabilities distribution calculator. Also rolls dice.
110
112
  test_files: []
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Dicey
4
- module Mixins
5
- # @api private
6
- # Mix-in for warning about missing VectorNumber gem.
7
- module WarnAboutVectorNumber
8
- private
9
-
10
- def warn_about_vector_number
11
- warn <<~TEXT
12
- Dice with non-numeric sides need gem "vector_number" to be present and available.
13
- If this is intended, please install the gem.
14
- TEXT
15
- false
16
- end
17
- end
18
- end
19
- end