Class: GamesDice::Bunch

Inherits:
Object
  • Object
show all
Defined in:
lib/games_dice/bunch.rb,
lib/games_dice/bunch_helpers.rb

Overview

This class models a number of identical dice, which may be either GamesDice::Die or GamesDice::ComplexDie objects.

An object of this class represents a fixed number of indentical dice that may be rolled and their values summed to make a total for the bunch.

Examples:

The ubiquitous '3d6'

d = GamesDice::Bunch.new( :ndice => 3, :sides => 6 )
d.roll # => 14
d.result # => 14
d.explain_result # => "2 + 6 + 6 = 14"
d.max # => 18

Roll 5d10, and keep the best 2

d = GamesDice::Bunch.new( :ndice => 5, :sides => 10 , :keep_mode => :keep_best, :keep_number => 2 )
d.roll # => 18
d.result # => 18
d.explain_result # => "4, 9, 2, 9, 1. Keep: 9 + 9 = 18"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ GamesDice::Bunch

The constructor accepts parameters that are suitable for either GamesDice::Die or GamesDice::ComplexDie and decides which of those classes to instantiate.

Parameters:

  • options (Hash)

Options Hash (options):

  • :ndice (Integer)

    Number of dice in the bunch, mandatory

  • :sides (Integer)

    Number of sides on a single die in the bunch, mandatory

  • :name (String)

    Optional name for the bunch

  • :rerolls (Array<GamesDice::RerollRule,Array>)

    Optional rules that cause the die to roll again

  • :maps (Array<GamesDice::MapRule,Array>)

    Optional rules to convert a value into a final result for the die

  • :prng (#rand)

    Optional alternative source of randomness to Ruby's built-in #rand, passed to GamesDice::Die's constructor

  • :keep_mode (Symbol)

    Optional, either :keep_best or :keep_worst

  • :keep_number (Integer)

    Optional number of dice to keep when :keep_mode is not nil



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/games_dice/bunch.rb', line 43

def initialize(options)
  name_number_sides_from_hash(options)
  keep_mode_from_hash(options)

  raise ':prng does not support the rand() method' if options[:prng] && !options[:prng].respond_to?(:rand)

  @single_die = if options[:rerolls] || options[:maps]
                  GamesDice::ComplexDie.new(@sides, complex_die_params_from_hash(options))
                else
                  GamesDice::Die.new(@sides, options[:prng])
                end
end

Instance Attribute Details

#explain_resultString? (readonly)

Explanation of result, or nil if no call to #roll yet.

Returns:

  • (String, nil)


162
163
164
165
166
167
168
169
170
# File 'lib/games_dice/bunch.rb', line 162

def explain_result
  return nil unless @result

  # With #keep_mode, we may need to show unused and used dice separately
  used_dice = result_details
  used_dice, = find_used_dice_due_to_keep_mode(result_details) if @keep_mode && @keep_number < @ndice

  build_explanation(used_dice)
end

#keep_modeSymbol? (readonly)

Can be nil, :keep_best or :keep_worst

Returns:

  • (Symbol, nil)


70
71
72
# File 'lib/games_dice/bunch.rb', line 70

def keep_mode
  @keep_mode
end

#keep_numberInteger? (readonly)

Number of "best" or "worst" results to select when #keep_mode is not nil.

Returns:

  • (Integer, nil)


74
75
76
# File 'lib/games_dice/bunch.rb', line 74

def keep_number
  @keep_number
end

#labelString (readonly)

Description that will be used in explanations with more than one bunch

Returns:

  • (String)


83
84
85
86
87
# File 'lib/games_dice/bunch.rb', line 83

def label
  return @name if @name != ''

  "#{@ndice}d#{@sides}"
end

#mapsArray<GamesDice::MapRule>? (readonly)

Sequence of map rules, or nil if mapping is not required.

Returns:



99
100
101
# File 'lib/games_dice/bunch.rb', line 99

def maps
  @single_die.maps
end

#maxInteger (readonly)

Maximum possible result from a call to #roll

Returns:

  • (Integer)


124
125
126
127
# File 'lib/games_dice/bunch.rb', line 124

def max
  n = @keep_mode ? [@keep_number, @ndice].min : @ndice
  n * @single_die.max
end

#minInteger (readonly)

Minimum possible result from a call to #roll

Returns:

  • (Integer)


116
117
118
119
# File 'lib/games_dice/bunch.rb', line 116

def min
  n = @keep_mode ? [@keep_number, @ndice].min : @ndice
  n * @single_die.min
end

#nameString (readonly)

Name to help identify bunch

Returns:

  • (String)


58
59
60
# File 'lib/games_dice/bunch.rb', line 58

def name
  @name
end

#ndiceInteger (readonly)

Number of dice to roll

Returns:

  • (Integer)


62
63
64
# File 'lib/games_dice/bunch.rb', line 62

def ndice
  @ndice
end

#rerollsArray<GamesDice::RerollRule>? (readonly)

Sequence of re-roll rules, or nil if re-rolls are not required.

Returns:



92
93
94
# File 'lib/games_dice/bunch.rb', line 92

def rerolls
  @single_die.rerolls
end

#resultInteger? (readonly)

Result of most-recent roll, or nil if no roll made yet.

Returns:

  • (Integer, nil)


78
79
80
# File 'lib/games_dice/bunch.rb', line 78

def result
  @result
end

#result_detailsArray<GamesDice::DieResult>? (readonly)

After calling #roll, this is an array of GamesDice::DieResult objects. There is one from each #single_die rolled, allowing inspection of how the result was obtained.

Returns:



107
108
109
110
111
# File 'lib/games_dice/bunch.rb', line 107

def result_details
  return nil unless @raw_result_details

  @raw_result_details.map { |r| r.is_a?(Integer) ? GamesDice::DieResult.new(r) : r }
end

#single_dieGamesDice::Die, GamesDice::ComplexDie (readonly)

Individual die from the bunch



66
67
68
# File 'lib/games_dice/bunch.rb', line 66

def single_die
  @single_die
end

Instance Method Details

#probabilitiesGamesDice::Probabilities

Calculates the probability distribution for the bunch. When the bunch is composed of dice with open-ended re-roll rules, there are some arbitrary limits imposed to prevent large amounts of recursion.

Returns:



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/games_dice/bunch.rb', line 133

def probabilities
  return @probabilities if @probabilities

  @probabilities = if @keep_mode && @ndice > @keep_number
                     @single_die.probabilities.repeat_n_sum_k(@ndice, @keep_number, @keep_mode)
                   else
                     @single_die.probabilities.repeat_sum(@ndice)
                   end

  @probabilities
end

#rollInteger

Simulates rolling the bunch of identical dice

Returns:

  • (Integer)

    Sum of all rolled dice, or sum of all keepers



147
148
149
150
151
152
153
154
155
156
157
# File 'lib/games_dice/bunch.rb', line 147

def roll
  generate_raw_results
  return @result if !@keep_mode || @keep_number.to_i >= @ndice

  use_dice = case @keep_mode
             when :keep_best then @raw_result_details.sort[-@keep_number..]
             when :keep_worst then @raw_result_details.sort[0..(@keep_number - 1)]
             end

  @result = use_dice.sum
end