Class: Dicey::AbstractDie

Inherits:
Object
  • Object
show all
Defined in:
lib/dicey/abstract_die.rb

Overview

Asbtract die which may have an arbitrary list of sides, not even neccessarily numbers but strings or other objects.

As the base class for all dice, defines their API.

Dice can be created through several methods:

Rolling a die is done through #roll. #current returns the current side of the die.

AbstractDie.srand can be used to (re)set the internal randomizer's state for all dice, allowing to reproduce the same sequence of rolls (if it was done with a known state).

Direct Known Subclasses

NumericDie, StaticDie

Constant Summary collapse

STRING_TO_QUOTE =

Matcher to check whether string needs quoting.

/["',()+−-]/
@@random =

Shared randomness source, accessed through rand and srand.

Random.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sides_list) ⇒ AbstractDie

Returns a new instance of AbstractDie.

Parameters:

  • sides_list (Enumerable<Any>)

Raises:



95
96
97
98
99
100
101
102
103
# File 'lib/dicey/abstract_die.rb', line 95

def initialize(sides_list)
  @sides_list = sides_list.to_a
  @sides_list = @sides_list.dup if @sides_list.equal?(sides_list) && !@sides_list.frozen?
  raise DiceyError, "dice must have at least one side!" if @sides_list.empty?

  @sides_list.freeze
  @sides_num = @sides_list.size
  @current_side_index = 0
end

Instance Attribute Details

#sides_list ⇒ Array<Any> (readonly)

Die's list of sides.

Returns:

  • (Array<Any>)


86
87
88
# File 'lib/dicey/abstract_die.rb', line 86

def sides_list
  @sides_list
end

#sides_num ⇒ Integer (readonly)

Number of sides of the die.

Returns:

  • (Integer)


91
92
93
# File 'lib/dicey/abstract_die.rb', line 91

def sides_num
  @sides_num
end

Class Method Details

.describe(dice) ⇒ String

Get a text representation of a list of dice.

Parameters:

Returns:

  • (String)


53
54
55
56
57
58
59
60
61
62
# File 'lib/dicey/abstract_die.rb', line 53

def self.describe(dice)
  return dice.to_s if AbstractDie === dice

  dice.map(&:to_s).reduce { |string, die|
    die_string = die.to_s
    string << "+" unless die_string.match?(/\A[+-]/)
    string << die_string
    string
  }.to_s
end

.from_count(count, definition) ⇒ Array<AbstractDie>

Create a number of equal dice from one definition.

Parameters:

  • count (Integer) —

    number of dice to create

  • definition (Enumerable<Any>, Any) —

    definition suitable for the dice class

Returns:



79
80
81
# File 'lib/dicey/abstract_die.rb', line 79

def self.from_count(count, definition)
  Array.new(count) { new(definition) }
end

.from_list(*definitions) ⇒ Array<AbstractDie>

Create a bunch of different dice at once from a list of definitions.

Parameters:

  • definitions (Array<Enumerable<Any>>, Array<Any>) —

    list of definitions suitable for the dice class

Returns:



69
70
71
# File 'lib/dicey/abstract_die.rb', line 69

def self.from_list(*definitions)
  definitions.map { new(_1) }
end

.rand ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get a random value using a private instance of Random.

Do not use this method directly. Reproducible rolls depend on it being called only internally.

See Also:

  • Random#rand


34
35
36
# File 'lib/dicey/abstract_die.rb', line 34

def self.rand(...)
  @@random.rand(...)
end

.srand ⇒ Object

Reset internal randomizer using a new seed.

See Also:

  • Random.new


40
41
42
# File 'lib/dicey/abstract_die.rb', line 40

def self.srand(...)
  @@random = Random.new(...)
end

Instance Method Details

#==(other) ⇒ Boolean

Determine if this die and the other one have the same list of sides. Be aware that differently ordered sides are not considered equal.

Parameters:

Returns:

  • (Boolean)

See Also:



148
149
150
# File 'lib/dicey/abstract_die.rb', line 148

def ==(other)
  AbstractDie === other && same_sides?(other)
end

#current ⇒ Any

Get current side of the die.

Returns:

  • (Any) —

    current side



108
109
110
# File 'lib/dicey/abstract_die.rb', line 108

def current
  @sides_list[@current_side_index]
end

#eql?(other) ⇒ Boolean

Determine if this die and the other one are of the same class and have the same list of sides. Be aware that differently ordered sides are not considered equal.

die_1.eql?(die_2) implies die_1.hash == die_2.hash.

Parameters:

Returns:

  • (Boolean)

See Also:



162
163
164
# File 'lib/dicey/abstract_die.rb', line 162

def eql?(other)
  self.class === other && same_sides?(other)
end

#freeze ⇒ Object

Freezes self (if not already frozen); returns self.

Performs computations that memoize results before freezing.



185
186
187
188
# File 'lib/dicey/abstract_die.rb', line 185

def freeze
  numeric? unless frozen?
  super
end

#hash ⇒ Integer

Generates an Integer hash value for this object.

Returns:

  • (Integer)


169
170
171
# File 'lib/dicey/abstract_die.rb', line 169

def hash
  [self.class, @sides_list].hash
end

#next ⇒ Any

Get next side of the die, advancing internal state. Starts from first side, wraps from last to first side.

Returns:

  • (Any) —

    next side



116
117
118
119
120
# File 'lib/dicey/abstract_die.rb', line 116

def next
  ret = current
  @current_side_index = (@current_side_index + 1) % @sides_num
  ret
end

#numeric? ⇒ Boolean

Whether all sides of this die are Numeric.

Returns:

  • (Boolean)


176
177
178
179
180
# File 'lib/dicey/abstract_die.rb', line 176

def numeric?
  return @numeric if defined?(@numeric)

  @numeric = @sides_list.all?(Numeric)
end

#roll ⇒ Any

Move internal state to a random side.

Returns:

  • (Any) —

    rolled side



125
126
127
128
# File 'lib/dicey/abstract_die.rb', line 125

def roll
  @current_side_index = self.class.rand(@sides_num)
  current
end

#to_s ⇒ String

Return a string representing the die.

Default representation is a list of sides in round brackets. Strings are quoted.

Returns:

  • (String)


136
137
138
139
# File 'lib/dicey/abstract_die.rb', line 136

def to_s
  sides = @sides_list.map { |side| side_to_s(side) }
  (@sides_list.size > 1) ? "(#{sides.join(",")})" : "(#{sides.first},)"
end