dicey 0.16.2 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +50 -34
- data/exe/dicey +2 -2
- data/lib/dicey/abstract_die.rb +24 -5
- data/lib/dicey/cli/blender.rb +19 -14
- data/lib/dicey/{sum_frequency_calculators/runner.rb → cli/calculator_runner.rb} +9 -15
- data/lib/dicey/{sum_frequency_calculators/test_runner.rb → cli/calculator_test_runner.rb} +37 -13
- data/lib/dicey/cli/formatters/base_list_formatter.rb +36 -0
- data/lib/dicey/cli/formatters/base_map_formatter.rb +38 -0
- data/lib/dicey/cli/formatters/gnuplot_formatter.rb +28 -0
- data/lib/dicey/cli/formatters/json_formatter.rb +14 -0
- data/lib/dicey/cli/formatters/list_formatter.rb +14 -0
- data/lib/dicey/cli/formatters/null_formatter.rb +17 -0
- data/lib/dicey/cli/formatters/yaml_formatter.rb +14 -0
- data/lib/dicey/cli/options.rb +15 -11
- data/lib/dicey/cli/roller.rb +47 -0
- data/lib/dicey/cli.rb +23 -0
- data/lib/dicey/die_foundry.rb +3 -2
- data/lib/dicey/distribution_calculators/auto_selector.rb +73 -0
- data/lib/dicey/{sum_frequency_calculators → distribution_calculators}/base_calculator.rb +44 -37
- data/lib/dicey/distribution_calculators/binomial.rb +62 -0
- data/lib/dicey/{sum_frequency_calculators → distribution_calculators}/empirical.rb +9 -10
- data/lib/dicey/distribution_calculators/iterative.rb +51 -0
- data/lib/dicey/{sum_frequency_calculators → distribution_calculators}/multinomial_coefficients.rb +21 -12
- data/lib/dicey/{sum_frequency_calculators/kronecker_substitution.rb → distribution_calculators/polynomial_convolution.rb} +15 -8
- data/lib/dicey/distribution_calculators/trivial.rb +56 -0
- data/lib/dicey/distribution_properties_calculator.rb +5 -2
- data/lib/dicey/mixins/missing_math.rb +44 -0
- data/lib/dicey/mixins/rational_to_integer.rb +1 -0
- data/lib/dicey/mixins/vectorize_dice.rb +17 -12
- data/lib/dicey/mixins/warn_about_vector_number.rb +19 -0
- data/lib/dicey/numeric_die.rb +1 -1
- data/lib/dicey/version.rb +1 -1
- data/lib/dicey.rb +26 -5
- metadata +30 -26
- data/lib/dicey/output_formatters/gnuplot_formatter.rb +0 -24
- data/lib/dicey/output_formatters/hash_formatter.rb +0 -36
- data/lib/dicey/output_formatters/json_formatter.rb +0 -12
- data/lib/dicey/output_formatters/key_value_formatter.rb +0 -34
- data/lib/dicey/output_formatters/list_formatter.rb +0 -12
- data/lib/dicey/output_formatters/null_formatter.rb +0 -15
- data/lib/dicey/output_formatters/yaml_formatter.rb +0 -12
- data/lib/dicey/roller.rb +0 -46
- data/lib/dicey/sum_frequency_calculators/auto_selector.rb +0 -41
- data/lib/dicey/sum_frequency_calculators/brute_force.rb +0 -41
|
@@ -3,17 +3,24 @@
|
|
|
3
3
|
require_relative "base_calculator"
|
|
4
4
|
|
|
5
5
|
module Dicey
|
|
6
|
-
module
|
|
7
|
-
# Calculator for lists of dice with integer sides (fast)
|
|
6
|
+
module DistributionCalculators
|
|
7
|
+
# Calculator for lists of dice with integer sides (fast),
|
|
8
|
+
# using polynomial convolution.
|
|
8
9
|
#
|
|
9
10
|
# Example dice: (1,2,3,4), (0,1,-5,6), (5,4,5,4,5).
|
|
10
11
|
#
|
|
11
|
-
#
|
|
12
|
+
# Discrete random variables can be represented as polynomials,
|
|
13
|
+
# while probability mass function of a sum of independent random variables
|
|
14
|
+
# is a convolution of their probability mass functions,
|
|
15
|
+
# which corresponds to multiplication of polynomials.
|
|
16
|
+
#
|
|
17
|
+
# Uses Kronecker substitution method for polynomial multiplication.
|
|
18
|
+
# @see https://en.wikipedia.org/wiki/Convolution#Discrete_convolution
|
|
12
19
|
# @see https://en.wikipedia.org/wiki/Kronecker_substitution
|
|
13
20
|
# @see https://arxiv.org/pdf/0712.4046v1.pdf
|
|
14
21
|
# David Harvey, Faster polynomial multiplication via multi-point Kronecker substitution
|
|
15
22
|
# (in particular section 3)
|
|
16
|
-
class
|
|
23
|
+
class PolynomialConvolution < BaseCalculator
|
|
17
24
|
private
|
|
18
25
|
|
|
19
26
|
def validate(dice)
|
|
@@ -21,7 +28,7 @@ module Dicey
|
|
|
21
28
|
end
|
|
22
29
|
|
|
23
30
|
def calculate_heuristic(dice_count, sides_count)
|
|
24
|
-
(dice_count**3
|
|
31
|
+
(2 * dice_count**3 - 4_250_000) + (26 * sides_count**2 + 230_000)
|
|
25
32
|
end
|
|
26
33
|
|
|
27
34
|
def calculate(dice, **nil)
|
|
@@ -32,9 +39,9 @@ module Dicey
|
|
|
32
39
|
extract_coefficients(product, evaluation_point, offset, polynomials.count)
|
|
33
40
|
end
|
|
34
41
|
|
|
35
|
-
# Turn dice into hashes where keys are side values and values are
|
|
42
|
+
# Turn dice into hashes where keys are side values and values are counts of those sides,
|
|
36
43
|
# representing corresponding polynomials where
|
|
37
|
-
# side values are powers and
|
|
44
|
+
# side values are powers and counts are coefficients.
|
|
38
45
|
#
|
|
39
46
|
# @param dice [Enumerable<NumericDie>]
|
|
40
47
|
# @return [Array<Hash{Integer => Integer}>]
|
|
@@ -56,7 +63,7 @@ module Dicey
|
|
|
56
63
|
1 << coefficient_magnitude
|
|
57
64
|
end
|
|
58
65
|
|
|
59
|
-
#
|
|
66
|
+
# Calculate values of polynomials at +evaluation_point+.
|
|
60
67
|
#
|
|
61
68
|
# @param polynomials [Array<Hash{Integer => Integer}>]
|
|
62
69
|
# @param evaluation_point [Integer]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base_calculator"
|
|
4
|
+
|
|
5
|
+
require_relative "../mixins/vectorize_dice"
|
|
6
|
+
|
|
7
|
+
module Dicey
|
|
8
|
+
module DistributionCalculators
|
|
9
|
+
# Distribution calculator with fast paths for some trivial cases (very fast).
|
|
10
|
+
#
|
|
11
|
+
# Currently included cases:
|
|
12
|
+
# - single {AbstractDie}, even without +VectorNumber+ (categorical distribution),
|
|
13
|
+
# - two of the same {RegularDie} (simple multinomial distribution).
|
|
14
|
+
#
|
|
15
|
+
# You probably shouldn't use this one manually, it's mostly there for {AutoSelector}.
|
|
16
|
+
class Trivial < BaseCalculator
|
|
17
|
+
include Mixins::VectorizeDice
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def validate(dice)
|
|
22
|
+
dice.size == 1 || two_regular_dice?(dice)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def two_regular_dice?(dice)
|
|
26
|
+
dice.size == 2 && RegularDie === dice.first && dice.first == dice.last
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def calculate_heuristic(_dice_count, sides_count)
|
|
30
|
+
-5_000_000 + (328 * sides_count - 89800)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def calculate(dice, **nil)
|
|
34
|
+
die = dice.first
|
|
35
|
+
if dice.size == 1
|
|
36
|
+
categorical(die)
|
|
37
|
+
else
|
|
38
|
+
bimultinomial(die)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def categorical(die)
|
|
43
|
+
die = vectorize_dice(die)
|
|
44
|
+
die.sides_list.tally
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Simplest multinomial distribution: two regular dice.
|
|
48
|
+
def bimultinomial(die)
|
|
49
|
+
middle = die.sides_num
|
|
50
|
+
(1...(die.sides_num * 2)).each_with_object({}) do |i, hash|
|
|
51
|
+
hash[i + 1] = middle - (middle - i).abs
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -11,8 +11,11 @@ module Dicey
|
|
|
11
11
|
# - mode(s), median, arithmetic mean;
|
|
12
12
|
# - important moments (expected value, variance, skewness, kurtosis).
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
14
|
+
# Distributions are assumed to be complete populations,
|
|
15
|
+
# i.e. this class is unsuitable for samples.
|
|
16
|
+
#
|
|
17
|
+
# It is notable that common dice create symmetric distributions,
|
|
18
|
+
# which means that skewness is 0, while measures of central tendency
|
|
16
19
|
# (median, mean, ...) are all equal.
|
|
17
20
|
# Mode is often not unique, but includes this center.
|
|
18
21
|
class DistributionPropertiesCalculator
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dicey
|
|
4
|
+
module Mixins
|
|
5
|
+
# @api private
|
|
6
|
+
# Some math functions missing from Math, though without argument range checks.
|
|
7
|
+
module MissingMath
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Calculate number of combinations of +n+ elements taken +k+ at a time.
|
|
11
|
+
#
|
|
12
|
+
# Unlike plain binomial coefficients, combinations are defined as 0 for +k > n+.
|
|
13
|
+
#
|
|
14
|
+
# @param n [Integer] natural integer
|
|
15
|
+
# @param k [Integer] natural integer
|
|
16
|
+
# @return [Integer]
|
|
17
|
+
def combinations(n, k) # rubocop:disable Naming/MethodParameterName
|
|
18
|
+
return 0 if k > n
|
|
19
|
+
return 1 if k.zero? || k == n
|
|
20
|
+
|
|
21
|
+
factorial_quo(n, k) / factorial(n - k)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Calculate factorial of a natural number.
|
|
25
|
+
#
|
|
26
|
+
# @param n [Integer] natural integer
|
|
27
|
+
# @return [Integer]
|
|
28
|
+
def factorial(n) # rubocop:disable Naming/MethodParameterName
|
|
29
|
+
(n < 23) ? Math.gamma(n + 1).to_i : (1..n).reduce(:*)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Calculate +n! / k!+ where +n >= k+.
|
|
33
|
+
#
|
|
34
|
+
# @param n [Integer] natural integer
|
|
35
|
+
# @param k [Integer] natural integer
|
|
36
|
+
# @return [Integer]
|
|
37
|
+
def factorial_quo(n, k) # rubocop:disable Naming/MethodParameterName
|
|
38
|
+
return Math.gamma(n + 1).to_i / Math.gamma(k + 1).to_i if n < 23 && k < 23
|
|
39
|
+
|
|
40
|
+
((k + 1)..n).reduce(:*)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Dicey
|
|
4
|
-
# @api private
|
|
5
4
|
module Mixins
|
|
5
|
+
# @api private
|
|
6
6
|
# Mix-in for converting dice with non-numeric sides into dice with +VectorNumber+ sides.
|
|
7
7
|
module VectorizeDice
|
|
8
8
|
private
|
|
@@ -10,20 +10,25 @@ module Dicey
|
|
|
10
10
|
# Vectorize non-numeric sides for AbstractDie instances,
|
|
11
11
|
# leaving NumericDie instances unchanged.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
13
|
+
# If +VectorNumber+ is not available, returns the original dice.
|
|
14
14
|
#
|
|
15
|
-
# @param dice [Array<AbstractDie
|
|
16
|
-
# @return [Array<AbstractDie
|
|
15
|
+
# @param dice [Array<AbstractDie>, AbstractDie]
|
|
16
|
+
# @return [Array<AbstractDie>, AbstractDie] dice with vectorized sides
|
|
17
17
|
def vectorize_dice(dice)
|
|
18
|
-
dice
|
|
19
|
-
|
|
18
|
+
return dice unless defined?(VectorNumber)
|
|
19
|
+
return vectorize_die_sides(dice) if AbstractDie === dice
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
dice.map { vectorize_die_sides(_1) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def vectorize_die_sides(die)
|
|
25
|
+
return die if NumericDie === die
|
|
26
|
+
|
|
27
|
+
die.class.new(
|
|
28
|
+
die.sides_list.map do |side|
|
|
29
|
+
(Numeric === side || VectorNumber === side) ? side : VectorNumber.new([side])
|
|
30
|
+
end
|
|
31
|
+
)
|
|
27
32
|
end
|
|
28
33
|
end
|
|
29
34
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
data/lib/dicey/numeric_die.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Dicey
|
|
|
7
7
|
#
|
|
8
8
|
# The only inherent difference in behavior compared to {AbstractDie} is
|
|
9
9
|
# that this class checks values for sides on initialization.
|
|
10
|
-
#
|
|
10
|
+
# {AbstractDie} may be rejected where only numeric dice are expected.
|
|
11
11
|
class NumericDie < AbstractDie
|
|
12
12
|
# @param sides_list [Array<Numeric>, Range<Numeric>, Enumerable<Numeric>]
|
|
13
13
|
# @raise [DiceyError] if +sides_list+ contains non-numerical values or is empty
|
data/lib/dicey/version.rb
CHANGED
data/lib/dicey.rb
CHANGED
|
@@ -7,13 +7,34 @@ rescue LoadError
|
|
|
7
7
|
# VectorNumber not available, sad
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
# A library for
|
|
10
|
+
# A library for calculating roll distributions and rolling dice.
|
|
11
|
+
#
|
|
12
|
+
# Includes several classes of dice:
|
|
13
|
+
# - {AbstractDie}, the base and most generic class;
|
|
14
|
+
# - {NumericDie}, a subclass for strictly numeric dice;
|
|
15
|
+
# - {RegularDie}, for the most common dice.
|
|
16
|
+
#
|
|
17
|
+
# See {AbstractDie} for API and more information.
|
|
18
|
+
#
|
|
19
|
+
# Roll distributions can be calculated via one of several algorithms
|
|
20
|
+
# in {DistributionCalculators},
|
|
21
|
+
# with automatic selection available via {DistributionCalculators::AutoSelector}.
|
|
22
|
+
#
|
|
23
|
+
# There are also a couple of utility classes:
|
|
24
|
+
# - {DistributionPropertiesCalculator} for analyzing a distribution;
|
|
25
|
+
# - {DieFoundry} for creating dice from strings.
|
|
11
26
|
module Dicey
|
|
12
27
|
# General error for Dicey.
|
|
13
28
|
class DiceyError < StandardError; end
|
|
14
29
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
30
|
+
require_relative "dicey/abstract_die"
|
|
31
|
+
require_relative "dicey/numeric_die"
|
|
32
|
+
require_relative "dicey/regular_die"
|
|
33
|
+
|
|
34
|
+
require_relative "dicey/die_foundry"
|
|
35
|
+
|
|
36
|
+
require_relative "dicey/distribution_properties_calculator"
|
|
37
|
+
Dir["dicey/distribution_calculators/*.rb", base: __dir__].each { require_relative _1 }
|
|
38
|
+
|
|
39
|
+
require_relative "dicey/version"
|
|
19
40
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dicey
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Alexander Bulancov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: vector_number
|
|
@@ -26,8 +26,8 @@ dependencies:
|
|
|
26
26
|
version: 0.4.3
|
|
27
27
|
description: |
|
|
28
28
|
Dicey provides a CLI executable and a Ruby API for fast calculation of
|
|
29
|
-
|
|
30
|
-
with support for all kinds of numeric dice,
|
|
29
|
+
distribution of weights or probabilities of dice rolls,
|
|
30
|
+
with support for all kinds of numeric dice, and non-numeric ones too!
|
|
31
31
|
Results can be exported as JSON, YAML or a gnuplot data file.
|
|
32
32
|
|
|
33
33
|
It can also be used to roll dice. While not the primary focus,
|
|
@@ -46,30 +46,35 @@ files:
|
|
|
46
46
|
- exe/dicey-to-gnuplot
|
|
47
47
|
- lib/dicey.rb
|
|
48
48
|
- lib/dicey/abstract_die.rb
|
|
49
|
+
- lib/dicey/cli.rb
|
|
49
50
|
- lib/dicey/cli/blender.rb
|
|
51
|
+
- lib/dicey/cli/calculator_runner.rb
|
|
52
|
+
- lib/dicey/cli/calculator_test_runner.rb
|
|
53
|
+
- lib/dicey/cli/formatters/base_list_formatter.rb
|
|
54
|
+
- lib/dicey/cli/formatters/base_map_formatter.rb
|
|
55
|
+
- lib/dicey/cli/formatters/gnuplot_formatter.rb
|
|
56
|
+
- lib/dicey/cli/formatters/json_formatter.rb
|
|
57
|
+
- lib/dicey/cli/formatters/list_formatter.rb
|
|
58
|
+
- lib/dicey/cli/formatters/null_formatter.rb
|
|
59
|
+
- lib/dicey/cli/formatters/yaml_formatter.rb
|
|
50
60
|
- lib/dicey/cli/options.rb
|
|
61
|
+
- lib/dicey/cli/roller.rb
|
|
51
62
|
- lib/dicey/die_foundry.rb
|
|
63
|
+
- lib/dicey/distribution_calculators/auto_selector.rb
|
|
64
|
+
- lib/dicey/distribution_calculators/base_calculator.rb
|
|
65
|
+
- lib/dicey/distribution_calculators/binomial.rb
|
|
66
|
+
- lib/dicey/distribution_calculators/empirical.rb
|
|
67
|
+
- lib/dicey/distribution_calculators/iterative.rb
|
|
68
|
+
- lib/dicey/distribution_calculators/multinomial_coefficients.rb
|
|
69
|
+
- lib/dicey/distribution_calculators/polynomial_convolution.rb
|
|
70
|
+
- lib/dicey/distribution_calculators/trivial.rb
|
|
52
71
|
- lib/dicey/distribution_properties_calculator.rb
|
|
72
|
+
- lib/dicey/mixins/missing_math.rb
|
|
53
73
|
- lib/dicey/mixins/rational_to_integer.rb
|
|
54
74
|
- lib/dicey/mixins/vectorize_dice.rb
|
|
75
|
+
- lib/dicey/mixins/warn_about_vector_number.rb
|
|
55
76
|
- lib/dicey/numeric_die.rb
|
|
56
|
-
- lib/dicey/output_formatters/gnuplot_formatter.rb
|
|
57
|
-
- lib/dicey/output_formatters/hash_formatter.rb
|
|
58
|
-
- lib/dicey/output_formatters/json_formatter.rb
|
|
59
|
-
- lib/dicey/output_formatters/key_value_formatter.rb
|
|
60
|
-
- lib/dicey/output_formatters/list_formatter.rb
|
|
61
|
-
- lib/dicey/output_formatters/null_formatter.rb
|
|
62
|
-
- lib/dicey/output_formatters/yaml_formatter.rb
|
|
63
77
|
- lib/dicey/regular_die.rb
|
|
64
|
-
- lib/dicey/roller.rb
|
|
65
|
-
- lib/dicey/sum_frequency_calculators/auto_selector.rb
|
|
66
|
-
- lib/dicey/sum_frequency_calculators/base_calculator.rb
|
|
67
|
-
- lib/dicey/sum_frequency_calculators/brute_force.rb
|
|
68
|
-
- lib/dicey/sum_frequency_calculators/empirical.rb
|
|
69
|
-
- lib/dicey/sum_frequency_calculators/kronecker_substitution.rb
|
|
70
|
-
- lib/dicey/sum_frequency_calculators/multinomial_coefficients.rb
|
|
71
|
-
- lib/dicey/sum_frequency_calculators/runner.rb
|
|
72
|
-
- lib/dicey/sum_frequency_calculators/test_runner.rb
|
|
73
78
|
- lib/dicey/version.rb
|
|
74
79
|
homepage: https://github.com/trinistr/dicey
|
|
75
80
|
licenses:
|
|
@@ -77,9 +82,9 @@ licenses:
|
|
|
77
82
|
metadata:
|
|
78
83
|
homepage_uri: https://github.com/trinistr/dicey
|
|
79
84
|
bug_tracker_uri: https://github.com/trinistr/dicey/issues
|
|
80
|
-
documentation_uri: https://rubydoc.info/gems/dicey/0.
|
|
81
|
-
source_code_uri: https://github.com/trinistr/dicey/tree/v0.
|
|
82
|
-
changelog_uri: https://github.com/trinistr/dicey/blob/v0.
|
|
85
|
+
documentation_uri: https://rubydoc.info/gems/dicey/0.17.0
|
|
86
|
+
source_code_uri: https://github.com/trinistr/dicey/tree/v0.17.0
|
|
87
|
+
changelog_uri: https://github.com/trinistr/dicey/blob/v0.17.0/CHANGELOG.md
|
|
83
88
|
rubygems_mfa_required: 'true'
|
|
84
89
|
post_install_message:
|
|
85
90
|
rdoc_options:
|
|
@@ -101,6 +106,5 @@ requirements: []
|
|
|
101
106
|
rubygems_version: 3.5.22
|
|
102
107
|
signing_key:
|
|
103
108
|
specification_version: 4
|
|
104
|
-
summary:
|
|
105
|
-
dice.
|
|
109
|
+
summary: Dice roll weights/probabilities distribution calculator. Also rolls dice.
|
|
106
110
|
test_files: []
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "key_value_formatter"
|
|
4
|
-
|
|
5
|
-
module Dicey
|
|
6
|
-
module OutputFormatters
|
|
7
|
-
# Formats a hash as a text file suitable for consumption by Gnuplot.
|
|
8
|
-
#
|
|
9
|
-
# Will transform Rational probabilities to Floats.
|
|
10
|
-
class GnuplotFormatter < KeyValueFormatter
|
|
11
|
-
SEPARATOR = " "
|
|
12
|
-
|
|
13
|
-
private
|
|
14
|
-
|
|
15
|
-
def transform(key, value)
|
|
16
|
-
[derationalize(key), derationalize(value)]
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def derationalize(value)
|
|
20
|
-
value.is_a?(Rational) ? value.to_f : value
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Dicey
|
|
4
|
-
# Processors which turn data to text.
|
|
5
|
-
module OutputFormatters
|
|
6
|
-
# Base formatter for outputting in formats which can be converted from a Hash directly.
|
|
7
|
-
# Can add an optional description into the result.
|
|
8
|
-
# @abstract
|
|
9
|
-
class HashFormatter
|
|
10
|
-
# @param hash [Hash{Object => Object}]
|
|
11
|
-
# @param description [String] text to add to result as an extra key
|
|
12
|
-
# @return [String]
|
|
13
|
-
def call(hash, description = nil)
|
|
14
|
-
hash = hash.transform_keys { to_primitive(_1) }
|
|
15
|
-
hash.transform_values! { to_primitive(_1) }
|
|
16
|
-
output = {}
|
|
17
|
-
output["description"] = description if description
|
|
18
|
-
output["results"] = hash
|
|
19
|
-
output.public_send(self.class::METHOD)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
def to_primitive(value)
|
|
25
|
-
return value if primitive?(value)
|
|
26
|
-
return value.to_f if Numeric === value
|
|
27
|
-
|
|
28
|
-
value.to_s
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def primitive?(value)
|
|
32
|
-
value.is_a?(Integer) || value.is_a?(Float) || value.is_a?(String)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "hash_formatter"
|
|
4
|
-
|
|
5
|
-
module Dicey
|
|
6
|
-
module OutputFormatters
|
|
7
|
-
# Formats a hash as a JSON document under +results+ key, with optional +description+ key.
|
|
8
|
-
class JSONFormatter < HashFormatter
|
|
9
|
-
METHOD = :to_json
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "../mixins/rational_to_integer"
|
|
4
|
-
|
|
5
|
-
module Dicey
|
|
6
|
-
module OutputFormatters
|
|
7
|
-
# Base formatter for outputting lists of key-value pairs separated by newlines.
|
|
8
|
-
# Can add an optional description into the result.
|
|
9
|
-
# @abstract
|
|
10
|
-
class KeyValueFormatter
|
|
11
|
-
include Mixins::RationalToInteger
|
|
12
|
-
|
|
13
|
-
# @param hash [Hash{Object => Object}]
|
|
14
|
-
# @param description [String] text to add as a comment.
|
|
15
|
-
# @return [String]
|
|
16
|
-
def call(hash, description = nil)
|
|
17
|
-
initial_string = description ? "# #{description}\n" : +""
|
|
18
|
-
hash.each_with_object(initial_string) do |(key, value), output|
|
|
19
|
-
output << line(transform(key, value)) << "\n"
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
def transform(key, value)
|
|
26
|
-
[rational_to_integer(key), rational_to_integer(value)]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def line((key, value))
|
|
30
|
-
"#{key}#{self.class::SEPARATOR}#{value}"
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "key_value_formatter"
|
|
4
|
-
|
|
5
|
-
module Dicey
|
|
6
|
-
module OutputFormatters
|
|
7
|
-
# Formats a hash as list of key => value pairs, similar to a Ruby Hash.
|
|
8
|
-
class ListFormatter < KeyValueFormatter
|
|
9
|
-
SEPARATOR = " => "
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Dicey
|
|
4
|
-
module OutputFormatters
|
|
5
|
-
# Formatter that doesn't format anything and always returns an empty string.
|
|
6
|
-
class NullFormatter
|
|
7
|
-
# @param hash [Hash{Object => Object}]
|
|
8
|
-
# @param description [String] text to add as a comment.
|
|
9
|
-
# @return [String] always an empty string
|
|
10
|
-
def call(hash, description = nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
11
|
-
""
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "hash_formatter"
|
|
4
|
-
|
|
5
|
-
module Dicey
|
|
6
|
-
module OutputFormatters
|
|
7
|
-
# Formats a hash as a YAML document under +results+ key, with optional +description+ key.
|
|
8
|
-
class YAMLFormatter < HashFormatter
|
|
9
|
-
METHOD = :to_yaml
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
data/lib/dicey/roller.rb
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "die_foundry"
|
|
4
|
-
|
|
5
|
-
require_relative "mixins/rational_to_integer"
|
|
6
|
-
require_relative "mixins/vectorize_dice"
|
|
7
|
-
|
|
8
|
-
module Dicey
|
|
9
|
-
# Let the dice roll!
|
|
10
|
-
#
|
|
11
|
-
# This is the implementation of roll mode for the CLI.
|
|
12
|
-
class Roller
|
|
13
|
-
include Mixins::RationalToInteger
|
|
14
|
-
include Mixins::VectorizeDice
|
|
15
|
-
|
|
16
|
-
# @param arguments [Array<String>] die definitions
|
|
17
|
-
# @param format [#call] formatter for output
|
|
18
|
-
# @return [nil]
|
|
19
|
-
# @raise [DiceyError]
|
|
20
|
-
def call(arguments, format:, **)
|
|
21
|
-
raise DiceyError, "no dice!" if arguments.empty?
|
|
22
|
-
|
|
23
|
-
dice = arguments.flat_map { |definition| die_foundry.cast(definition) }
|
|
24
|
-
result = roll_dice(dice)
|
|
25
|
-
|
|
26
|
-
format.call({ "roll" => rational_to_integer(result) }, AbstractDie.describe(dice))
|
|
27
|
-
rescue TypeError
|
|
28
|
-
warn <<~TEXT
|
|
29
|
-
Dice with non-numeric sides need gem "vector_number" to be present and available.
|
|
30
|
-
If this is intended, please install the gem.
|
|
31
|
-
TEXT
|
|
32
|
-
raise DiceyError, "can not roll dice with non-numeric sides!"
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def die_foundry
|
|
38
|
-
@die_foundry ||= DieFoundry.new
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def roll_dice(dice)
|
|
42
|
-
dice = vectorize_dice(dice) if defined?(VectorNumber)
|
|
43
|
-
dice.sum(&:roll)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "brute_force"
|
|
4
|
-
require_relative "kronecker_substitution"
|
|
5
|
-
require_relative "multinomial_coefficients"
|
|
6
|
-
|
|
7
|
-
module Dicey
|
|
8
|
-
module SumFrequencyCalculators
|
|
9
|
-
# Tool to automatically select a calculator for a given set of dice.
|
|
10
|
-
#
|
|
11
|
-
# Calculator is guaranteed to be compatible, with a strong chance of being the most performant.
|
|
12
|
-
#
|
|
13
|
-
# @see BaseCalculator#heuristic_complexity
|
|
14
|
-
class AutoSelector
|
|
15
|
-
# Calculators to consider when selecting a match.
|
|
16
|
-
AVAILABLE_CALCULATORS = [
|
|
17
|
-
KroneckerSubstitution.new,
|
|
18
|
-
MultinomialCoefficients.new,
|
|
19
|
-
BruteForce.new,
|
|
20
|
-
].freeze
|
|
21
|
-
|
|
22
|
-
# @param calculators [Array<BaseCalculator>]
|
|
23
|
-
# calculators which this instance will consider
|
|
24
|
-
def initialize(calculators = AVAILABLE_CALCULATORS)
|
|
25
|
-
@calculators = calculators
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Determine best (or adequate) calculator for a given set of dice
|
|
29
|
-
# based on heuristics from the list of available calculators.
|
|
30
|
-
#
|
|
31
|
-
# @param dice [Enumerable<NumericDie>]
|
|
32
|
-
# @return [BaseCalculator, nil] +nil+ if no calculator is compatible
|
|
33
|
-
def call(dice)
|
|
34
|
-
compatible = @calculators.select { _1.valid_for?(dice) }
|
|
35
|
-
return if compatible.empty?
|
|
36
|
-
|
|
37
|
-
compatible.min_by { _1.heuristic_complexity(dice) }
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|