Class: Synthra::Generator::RNG

Inherits:
Object
  • Object
show all
Defined in:
lib/synthra/generator/rng.rb

Overview

Thread-safe seeded random number generator

Provides deterministic random number generation using Ruby's Random class with mutex protection for thread safety. All type generators use this RNG to ensure reproducible output.

Examples:

Basic usage

rng = RNG.new(12345)
rng.int(1, 100)   # => 42 (always same with seed 12345)
rng.boolean(0.7)  # => true/false with 70% true probability

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(seed = nil) ⇒ RNG

Create a new RNG instance

Examples:

With seed

rng = RNG.new(12345)  # Deterministic

Without seed

rng = RNG.new  # Random seed

Parameters:

  • seed (Integer, nil) (defaults to: nil)

    seed for determinism (nil for random seed)



60
61
62
63
64
# File 'lib/synthra/generator/rng.rb', line 60

def initialize(seed = nil)
  @seed = seed || Random.new_seed
  @random = Random.new(@seed)
  @mutex = Mutex.new
end

Instance Attribute Details

#seedObject (readonly)

Returns the value of attribute seed.



46
47
48
# File 'lib/synthra/generator/rng.rb', line 46

def seed
  @seed
end

Instance Method Details

#boolean(true_probability = 0.5) ⇒ Boolean

Generate a random boolean

Examples:

50/50 chance

rng.boolean  # => true or false

70% true

rng.boolean(0.7)  # => true (70% of the time)

Parameters:

  • true_probability (Float) (defaults to: 0.5)

    probability of true (0.0 to 1.0)

Returns:

  • (Boolean)

    random boolean



174
175
176
# File 'lib/synthra/generator/rng.rb', line 174

def boolean(true_probability = 0.5)
  rand < true_probability
end

#dupRNG

Create a duplicate RNG with the same seed

Creates a new RNG that will produce the same sequence.

Examples:

rng2 = rng.dup
rng2.int(1, 100) == rng.int(1, 100)  # Both start fresh

Returns:

  • (RNG)

    new RNG with same seed



248
249
250
# File 'lib/synthra/generator/rng.rb', line 248

def dup
  self.class.new(@seed)
end

#float(min, max) ⇒ Float

Generate a random float in range

Examples:

rng.float(0.0, 1.0)  # => 0.7342...

Parameters:

  • min (Float)

    minimum value

  • max (Float)

    maximum value

Returns:

  • (Float)

    random float



232
233
234
# File 'lib/synthra/generator/rng.rb', line 232

def float(min, max)
  min + rand * (max - min)
end

#int(min, max) ⇒ Integer

Generate a random integer in inclusive range

Examples:

rng.int(1, 10)  # => 7 (between 1 and 10 inclusive)

Parameters:

  • min (Integer)

    minimum value (inclusive)

  • max (Integer)

    maximum value (inclusive)

Returns:

  • (Integer)

    random integer



217
218
219
# File 'lib/synthra/generator/rng.rb', line 217

def int(min, max)
  rand(min..max)
end

#rand(max = nil) ⇒ Integer, Float

Generate a random number

Flexible random number generation supporting multiple argument types.

Examples:

No argument (0.0 to 1.0)

rng.rand  # => 0.7342...

With max (0 to max-1)

rng.rand(100)  # => 42

With range

rng.rand(1..10)  # => 7

Parameters:

  • max (Integer, Range, nil) (defaults to: nil)

    max value, range, or nil for 0.0-1.0 float

Returns:

  • (Integer, Float)

    random number



84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/synthra/generator/rng.rb', line 84

def rand(max = nil)
  @mutex.synchronize do
    case max
    when nil
      @random.rand
    when Range
      @random.rand(max)
    else
      @random.rand(max)
    end
  end
end

#rand_range(range) ⇒ Numeric

Generate a random number in a range (alias)

Parameters:

  • range (Range)

    the range

Returns:

  • (Numeric)

    random number in range



104
105
106
# File 'lib/synthra/generator/rng.rb', line 104

def rand_range(range)
  rand(range)
end

#reset!void

This method returns an undefined value.

Reset the RNG to initial state

Re-initializes the internal random generator to start sequence over.

Examples:

rng.int(1, 100)  # => 42
rng.int(1, 100)  # => 73
rng.reset!
rng.int(1, 100)  # => 42 (back to start)


266
267
268
269
270
# File 'lib/synthra/generator/rng.rb', line 266

def reset!
  @mutex.synchronize do
    @random = Random.new(@seed)
  end
end

#sample(array) ⇒ Object?

Pick a random element from an array

Examples:

rng.sample(["a", "b", "c"])  # => "b"

Parameters:

  • array (Array)

    the array to sample from

Returns:

  • (Object, nil)

    random element or nil if empty



118
119
120
121
122
123
124
# File 'lib/synthra/generator/rng.rb', line 118

def sample(array)
  return nil if array.empty?

  @mutex.synchronize do
    array.sample(random: @random)
  end
end

#sample_many(array, count) ⇒ Array

Sample multiple elements from an array

Examples:

rng.sample_many(["a", "b", "c", "d"], 2)  # => ["c", "a"]

Parameters:

  • array (Array)

    the array to sample from

  • count (Integer)

    number of elements to sample

Returns:

  • (Array)

    sampled elements



137
138
139
140
141
# File 'lib/synthra/generator/rng.rb', line 137

def sample_many(array, count)
  @mutex.synchronize do
    array.sample(count, random: @random)
  end
end

#shuffle(array) ⇒ Array

Shuffle an array

Returns a new shuffled array; does not modify original.

Examples:

rng.shuffle([1, 2, 3, 4])  # => [3, 1, 4, 2]

Parameters:

  • array (Array)

    the array to shuffle

Returns:

  • (Array)

    shuffled copy



155
156
157
158
159
# File 'lib/synthra/generator/rng.rb', line 155

def shuffle(array)
  @mutex.synchronize do
    array.shuffle(random: @random)
  end
end

#weighted_choice(options) ⇒ Object?

Select based on weighted probabilities

Examples:

rng.weighted_choice({ "common" => 80, "rare" => 20 })
# => "common" (80% chance) or "rare" (20% chance)

Parameters:

  • options (Hash)

    mapping of values to weights (any positive numbers)

Returns:

  • (Object, nil)

    selected value based on weights



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/synthra/generator/rng.rb', line 189

def weighted_choice(options)
  return nil if options.empty?

  total = options.values.sum.to_f
  target = rand * total
  cumulative = 0.0

  options.each do |value, weight|
    cumulative += weight
    return value if target <= cumulative
  end


  # Fallback (shouldn't happen but handles float precision)
  options.keys.last
end