Skip to content

indiesoftby/defold-splitmix64

Repository files navigation

SplixMix64 Logo

SplitMix64 PRNG for Defold

This extension wraps SplitMix64 in a simple Lua API for the Defold game engine. The SplitMix64 algo is from http://prng.di.unimi.it/splitmix64.c, and it's a very fast generator passing BigCrush.

The main idea of this extension is to get the same random numbers from the same seeds on ALL platforms supported by Defold.

Take a look at the web build 🐲 of the included example, which tests random values from the same seeds.

Splitmix64 is the default pseudo-random number generator algorithm in Java and is included / available in many other languages. It uses a fairly simple algorithm that, though it is considered to be poor for cryptographic purposes, is very fast to calculate, and is "good enough" for many random number needs. It passes several fairly rigorous PRNG "fitness" tests that some more complex algorithms fail.

Installation

You can use it in your own project by adding this project as a Defold library dependency. Open your game.project file and in the dependencies field under project add:

https://github.com/indiesoftby/defold-splitmix64/archive/main.zip

Or point to the ZIP file of a specific release.

Usage

splitmix64.randomseed(x)

Sets x as the "seed" for the pseudo-random generator: equal seeds produce equal sequences of numbers.

PARAMETERS

  • x number or string β€” Seed value. Use a string (e.g. "18446744073709551615") for full 64-bit precision; Lua number cannot represent all uint64 values.

splitmix64.state()

Returns the current internal state as a string. Use to save/restore state or pass to randomseed for full 64-bit precision.

RETURNS

  • state string β€” Current state as decimal string.

splitmix64.random([m],[n])

When called without arguments, returns a uniform pseudo-random real number in the range [0,1). When called with an integer number m, splitmix64.random returns a uniform pseudo-random integer in the range [1, m]. When called with two integer numbers m and n, splitmix64.random returns a uniform pseudo-random integer in the range [m, n].

PARAMETERS

  • m number.
  • n number.

RETURNS

  • value number - A pseudo-random number.

splitmix64.randomchoice(t)

Returns a random value from array t. If the array is empty an error is raised.

PARAMETERS

  • t table - Array of values to choose from.

RETURNS

  • value - Random element from the array.

splitmix64.weightedchoice(t)

Takes table t where keys are choices and values are weights. Returns a random key. Keys are sorted deterministically (numbers first, then strings lexicographically) before selection, so the result is reproducible for the same seed across all platforms. Weights must be β‰₯ 0. If all weights are 0 or a weight is negative, an error is raised.

PARAMETERS

  • t table - Table of key-weight pairs.

RETURNS

  • key - Random key chosen by weight.

splitmix64.toss()

Toss a coin. Returns 0 or 1.

RETURNS

  • value number - 0 or 1.

splitmix64.dice(roll, type)

Roll one or more dice of the given type. Returns a table of individual rolls and the total sum.

PARAMETERS

  • roll number - Number of dice to roll (must be > 0).
  • type number - Dice type: splitmix64.D4, splitmix64.D6, splitmix64.D8, splitmix64.D10, splitmix64.D12, splitmix64.D20, or splitmix64.D100.

RETURNS

  • rolls table - Table of individual roll results.
  • total number - Sum of all rolls.

splitmix64.shuffle(t, [inplace])

Shuffles an array using the Fisher-Yates algorithm. Returns a shuffled copy of the array, or if inplace is true, shuffles the original array in-place and returns it.

PARAMETERS

  • t table - Array to shuffle.
  • inplace boolean (optional) - If true, shuffle the array in-place instead of creating a copy. Defaults to false.

RETURNS

  • result table - The shuffled array (a new table, or the same table if inplace is true).

Dice type constants

  • splitmix64.D4, splitmix64.D6, splitmix64.D8, splitmix64.D10, splitmix64.D12, splitmix64.D20, splitmix64.D100

Example

splitmix64.randomseed(1)

print(splitmix64.random())
print(splitmix64.random(1, 10000))

-- Random choice from array
print(splitmix64.randomchoice({true, false}))

-- Weighted choice (cat twice as likely as dog)
print(splitmix64.weightedchoice({ ["cat"] = 10, ["dog"] = 5, ["frog"] = 0 }))

-- Coin toss
print(splitmix64.toss())

-- Shuffle an array (returns a new table)
local shuffled = splitmix64.shuffle({1, 2, 3, 4, 5})

-- Shuffle in-place
local t = {1, 2, 3, 4, 5}
splitmix64.shuffle(t, true)

-- Roll 2d6
local rolls, total = splitmix64.dice(2, splitmix64.D6)
print(total, rolls[1], rolls[2])

Save/Restore state

local state = splitmix64.state()
-- ... use random ...
splitmix64.randomseed(state)  -- restore exact state

splitmix64.new_instance([seed])

Creates a new independent RNG instance. Each instance has its own internal state, completely isolated from the global module state and from other instances. This allows running multiple RNG streams in parallel without interference.

PARAMETERS

  • seed number or string (optional) β€” Initial seed for the instance. Defaults to 0.

RETURNS

  • rng table β€” A table with the same set of functions as the module: random, randomseed, state, randomchoice, weightedchoice, toss, dice, shuffle.

Example

-- Create two independent generators
local rng_enemies = splitmix64.new_instance(42)
local rng_loot = splitmix64.new_instance(999)

-- Each has its own state β€” they don't affect each other or the global module
print(rng_enemies.random(1, 100))
print(rng_loot.random(1, 100))

-- The global state is unchanged
print(splitmix64.random())

-- Save/restore instance state
local saved = rng_enemies.state()
rng_enemies.randomseed(saved)

Reference Implementations

The implementations/ directory contains reference implementations of the SplitMix64 algorithm for other languages:

These implementations produce identical random number sequences for the same seeds, which makes them suitable for networked multiplayer games that rely on deterministic PRNG state. By seeding all clients with the same value, you can guarantee consistent simulation results across platforms and languages!

Advanced Usage

You can also globally substitute the built-in math.random with splitmix64:

math.randomseed = splitmix64.randomseed
math.random = splitmix64.random