On this page
  1. tecs.ecs.random
  2. Streams
  3. Snapshots
  4. Ranges
  5. Module contents
    1. Constructors
    2. Types
    3. Functions
  6. Constructors
    1. newRandom
  7. Types
    1. Random
  8. Functions
    1. seed
    2. stream

tecs.ecs.random

Seeded generation, in named streams a snapshot carries.

tecs.ecs.random.seed(world, 20260726)

local loot <const> = tecs.ecs.random.stream(world, "game.loot")
local spawns <const> = tecs.ecs.random.stream(world, "game.spawns")
local roll <const> = loot:integer(1, 20)

Streams

The world seed and stream name determine each Random sequence. Drawing from loot never advances spawns, so adding a stream leaves existing consumers unchanged. Use namespaced, nonempty names and call seed during world setup. Calling it later restarts every stream.

Use newRandom for tools, tests, and benchmarks that need a generator outside a world. Do not share a generator between threads.

Snapshots

The first seed or stream call installs the snapshot handler. Make that call before loading a snapshot. A load updates existing generators in place, so systems that captured a stream during setup retain the same object.

Ranges

local unit <const> = loot:next() -- [0, 1)
local d20 <const> = loot:integer(20) -- [1, 20]
local offset <const> = loot:integer(-3, 3) -- [-3, 3]
local speed <const> = loot:range(2.0, 5.0) -- [2, 5)

integer includes both ends and rejects an empty range. range excludes its upper end.

Module contents

Constructors

Constructor Description
newRandom Creates a generator outside every world and snapshot.

Types

Type Kind Description
Random record Represents a generator through four 32-bit words of xoshiro128 state and the number of draws taken from it.

Functions

Function Kind Description
seed Static Sets the world's seed and restarts every stream in it from that seed.
stream Static Returns the generator named name in world, creating it on the first call and returning the same object afterwards.

Constructors

tecs.ecs.random.newRandom Static

Creates a generator outside every world and snapshot. Tools, benchmarks, and tests use this; games usually use stream.

A seed is read as a 32-bit word, so 2^31 and -2^31 name the same one.

function tecs.ecs.random.newRandom(seed: integer): Random

Arguments

Name Type Description
seed integer Omitted takes a fixed constant, not a clock, so an unseeded generator still repeats run to run.

Returns

Type Description
Random A generator nothing else holds and nothing else knows about: two calls on one seed give two objects that draw the same sequence, each at its own pace.

Types

tecs.ecs.random.Random record

Represents a generator through four 32-bit words of xoshiro128** state and the number of draws taken from it.

Not thread-safe, and not meant to be: a generator shared across threads would produce an order that depends on which one got there first, which is the whole thing this module exists to avoid. Give each thread a stream of its own.

record tecs.ecs.random.Random
    integer: function(self, m: integer, n: integer): integer
    next: function(self): number
    range: function(self, lo: number, hi: number): number
    reseed: function(self, seed: integer)
    setState: function(self, words: {integer})
    shuffle: function<T>(self, list: {T}): {T}
    state: function(self): {integer}
end

tecs.ecs.random.Random:integer Instance

Returns the next integer in [1, m], or in [m, n] when both are given. It includes both ends and advances the generator.

Errors on an empty range, because a caller asking for a number between 5 and 3 has a bug and a silent 5 hides it.

function tecs.ecs.random.Random.integer(
    self, m: integer, n: integer
): integer
Arguments
Name Type Description
self Random
m integer The upper end when it is the only argument, and the lower end when n follows it. The one-argument form counts from 1, so integer(0) is the empty range [1, 0] and raises.
n integer The upper end, included. Nil takes the one-argument form.
Returns
Type Description
integer An integer in the range from one draw whatever the range is. The method scales and floors instead of resampling, so a width that does not divide 2^32 has a bias of about one part in 2^32.

tecs.ecs.random.Random:next Instance

Returns the next value and advances the generator by one round.

function tecs.ecs.random.Random.next(self): number
Arguments
Name Type Description
self Random
Returns
Type Description
number A multiple of 2^-32 in [0, 1). Zero is a value it takes; one is not.

tecs.ecs.random.Random:range Instance

Returns the next value in [lo, hi) and advances the generator.

function tecs.ecs.random.Random.range(
    self, lo: number, hi: number
): number
Arguments
Name Type Description
self Random
lo number The included end, whether or not it is the smaller of the two.
hi number The excluded end. A reversed pair is not an error and is not swapped either: the draw is lo + next() * (hi - lo) and next never reaches one, so range(7, 3) draws from (3, 7] rather than [3, 7).
Returns
Type Description
number A value between the two ends, and lo exactly when the two are equal.

tecs.ecs.random.Random:reseed Instance

Restarts the sequence from seed in place. The generator retains its identity, so existing holders draw from the restart.

function tecs.ecs.random.Random.reseed(self, seed: integer)
Arguments
Name Type Description
self Random
seed integer Read as a 32-bit word, as newRandom reads one, so 2^31 and -2^31 restart the same sequence. Zero is a seed like any other; the generator expands it instead of rejecting it.
Returns

None.

tecs.ecs.random.Random:setState Instance

Puts four state words back, in place, so anything already holding this generator draws from what was put back.

function tecs.ecs.random.Random.setState(self, words: {integer})
Arguments
Name Type Description
self Random
words {integer} Exactly four, in the order state gives them, and each read as a 32-bit word, so a state result round-trips unchanged. Any other length raises, as does nil, as does all four being zero.
Returns

None.

tecs.ecs.random.Random:shuffle Instance

Shuffles a table in place with Fisher-Yates and returns the same table. It advances the generator once per element past the first.

function tecs.ecs.random.Random.shuffle<T>(self, list: {T}): {T}
Type Parameters
Name Constraint Description
T
Arguments
Name Type Description
self Random The generator advances once per element after the first.
list {T} The method reorders this list over 1..#list. A list of one or none stays unchanged and draws nothing, so its length determines how far a shared generator advances.
Returns
Type Description
{T} list itself rather than a copy, so the caller's own reference is already shuffled and the return is a convenience.

tecs.ecs.random.Random:state Instance

Returns the four state words as plain integers that a snapshot, log line, or bug report can carry.

function tecs.ecs.random.Random.state(self): {integer}
Arguments
Name Type Description
self Random
Returns
Type Description
{integer} A fresh four-element array each call, and a copy: writing to it does not move the generator, and the generator moving does not change it.

Functions

tecs.ecs.random.seed Static

Sets the world's seed and restarts every stream in it from that seed.

Call it during setup. Calling it mid-run is well defined and rewinds everything, which is rarely what a caller means.

function tecs.ecs.random.seed(world: types.World, seed: integer)

Arguments

Name Type Description
world types.World The call reseeds each existing stream in place, so a holder keeps the object. It also installs the snapshot handler, which is why games that load snapshots call it during setup.
seed integer Read as a 32-bit word, as newRandom reads one. There is no way to ask a world for its seed back; take it from a snapshot instead.

Returns

None.

tecs.ecs.random.stream Static

Returns the generator named name in world, creating it on the first call and returning the same object afterwards.

Names use dot namespaces like snapshot data keys. The engine uses tecs.audio and tecs.runif, so game names should carry their own prefix.

function tecs.ecs.random.stream(
    world: types.World, name: string
): Random

Arguments

Name Type Description
world types.World The first ask installs a snapshot handler named tecs.random on it. A world that loads a snapshot before anything has asked for a stream has no handler yet and drops the saved seed, which random.seed during setup avoids.
name string Nil or empty raises. The module compares names byte for byte, so two spellings of the same idea create independent streams.

Returns

Type Description
Random The world's generator for this name, not a copy: two callers asking for one name draw from the same sequence, in the order they happen to ask.