Portable: Linux (getrandom + pthread) and Windows (BCryptGenRandom + CRITICAL_SECTION); thread-safe with mutex; lazy auto-seed on first call.
Introduction
The random plugin provides a fast, modern pseudo-random number generator callable from scripts. The algorithm is xoshiro256++ (Blackman & Vigna) with a 256-bit internal state and a period of 2^256-1; seeding uses SplitMix64. One global generator lives in the process behind a mutex (Linux: pthread, Windows: CRITICAL_SECTION) -- safe to call from multiple threads.
Range conventions
The integer range (random.Int) is CLOSED: [min, max] with both endpoints inclusive -- for dice rolls (1..6) or array indices (0..N-1). The floating-point output (random.Double, random.Range) is HALF-OPEN: [min, max) -- the upper bound NEVER appears, which is the standard U(0,1) convention (Java, Python, Lua, JavaScript).
Determinism and reproducibility
Given the same seed, output is platform-independent. Two functions support reproducibility: random.Seed(i64) for simple seeding (good for tests and demos), and random.SaveState / random.LoadState which serialize the FULL 256-bit internal state to a 32-byte blob (exact snapshot).
When NOT to use
This is NOT a cryptographically secure generator -- do NOT use it for key material, password-hash salt, or token generation. Use crypto.RandomBytes / crypto.RandomHex instead -- those call the OS CSPRNG (BCryptGenRandom / getrandom) on every call.
Loading the plugin
plugin "builtin:print"; plugin "../plugins/random/RandomPlugin";
A typical flow
// reproducible (for tests) random.Seed(42); i64 $d = random.Int(1, 6); // fresh sequence on every run random.AutoSeed(); double $u = random.Double();
What to know about every function
Every function is thread-safe; one global generator behind a mutex.
First call auto-seeds from OS entropy if random.Seed/AutoSeed has not been called.
Rejection sampling makes random.Int uniform across the full [min, max] (no modulo bias).
random.Double fills [0,1) with full 53-bit mantissa precision.
Runtime errors come only from wrong argument count/type or a bad LoadState blob. Reversed bounds (min > max) are swapped with a one-line notice on stderr.
Seeding and state
Seeding the generator from an integer, from OS entropy, or restoring a previous snapshot.
random.Seed
random.Seed(seed) -> intSets the global PRNG seed from an i64 value. The implementation uses a SplitMix64 recipe to expand the seed into the 4*u64 internal state words. The same seed always produces the same sequence on any platform.
| Parameter | Type | Description |
|---|---|---|
| seed | i64 | The seed value. Any 64-bit integer; 0 is valid. |
Always 0 on success. The only error cases are wrong argument count/type.
random.Seed(42); printf("%lld\n", random.Int(0, 99)); printf("%lld\n", random.Int(0, 99));
51 53
For tests and reproducible demos. If you want a different sequence on every run, call random.AutoSeed() instead.
random.AutoSeed
random.AutoSeed() -> intSeeds the generator from OS entropy (Linux: getrandom/urandom; Windows: BCryptGenRandom) and returns the i64 seed that was used. A single call guarantees a different sequence on every run.
This function takes no arguments.
The seed that was used (i64). Following random.Int / random.Double calls will produce a fresh sequence every time the program runs.
i64 $s = random.AutoSeed(); print.Line($"seed used: $s"); printf("first: %lld\n", random.Int(1, 100));
seed used: -3829416751029348176 first: 42
For real applications: games, simulations -- anywhere you want a fresh experience on every start.
random.SaveState
random.SaveState() -> blob
Snapshot of the current 256-bit PRNG state in a 32-byte blob (4 little-endian u64 words). For storage and later restoration.
This function takes no arguments.
32-byte blob; the contents are the full internal state.
random.Seed(100); random.Int(0, 1000000); blob $s[32]; $s = random.SaveState(); printf("%d bytes\n", $s.Length);
32 bytes
Reproducible replay: e.g. a demo's 'snapshot' button captures the PRNG in a moment; later restoring it makes the following draws identical.
random.LoadState
random.LoadState(state) -> intRestores a previously-saved 32-byte state. Rejects the all-zero blob (the xoshiro256++ system would collapse there).
| Parameter | Type | Description |
|---|---|---|
| state | blob | Exactly 32 bytes, output of a previous SaveState. |
0 on success. Errors on a wrong-sized blob or all-zero state.
blob $s[32]; random.Seed(100); random.Int(0, 999999); $s = random.SaveState(); int $a = random.Int(0, 999999); random.LoadState($s); int $b = random.Int(0, 999999); print.Line($"$a == $b");
862054 == 862054
Deterministic algorithm testing; pausing a long simulation and resuming it later with identical draws.
Number drawing
Integer, double, range, and fair bool -- the typical user-facing surface.
random.Int
random.Int(min, max) -> intInteger in the CLOSED interval [min, max] (BOTH endpoints inclusive). Uses rejection sampling to avoid modulo bias, so the distribution is uniform across the full range.
| Parameter | Type | Description |
|---|---|---|
| min | i64 | Lower bound (inclusive). |
| max | i64 | Upper bound (inclusive). If min > max, the bounds are swapped (notice on stderr). |
Integer in [min, max].
random.Seed(42); printf("dice: %lld\n", random.Int(1, 6)); printf("d100: %lld\n", random.Int(1, 100));
dice: 4 d100: 53
Dice rolls (1..6), array indices (0..N-1), percentages (1..100) -- any classic integer range.
random.Double
random.Double() -> float
Floating-point value in the HALF-OPEN interval [0.0, 1.0) (0.0 is possible, 1.0 is NEVER reached). The full 53-bit mantissa is filled exactly from the top 53 bits of the PRNG output.
This function takes no arguments.
Double in [0.0, 1.0).
random.Seed(1); printf("u: %f\n", random.Double());
u: 0.811612
For probabilistic decisions (if u < 0.3 -> 30%), interpolation alphas, and any U(0,1)-derived quantity.
random.Range
random.Range(min, max) -> float
Floating-point value in the half-open interval [min, max). min == max simply returns min; min > max swaps the bounds (notice on stderr).
| Parameter | Type | Description |
|---|---|---|
| min | number | Lower bound (inclusive). |
| max | number | Upper bound (NOT inclusive). If min > max, the bounds are swapped (notice on stderr). |
Double in [min, max).
random.Seed(7); printf("pos: (%f, %f)\n", random.Range(-1.0, 1.0), random.Range(-1.0, 1.0));
pos: (-0.889279, -0.477181)
For mixed positive/negative float ranges: 2D positions, durations, physical parameters (weight, speed) for random init.
random.Bool
random.Bool() -> intFair 0 or 1 value (50/50). Uses the topmost bit of the PRNG output, which is the highest-quality bit of xoshiro256++.
This function takes no arguments.
0 or 1 (DominScript has no native bool, hence i32).
random.Seed(2024); for i in 1..5 { printf("%d ", random.Bool()); } print.Line("");
1 0 1 1 0
Coin flips and fair binary decisions. For weighted bool (e.g. 70/30), use random.Double() and compare against 0.7.
Practical notes
Reproducible demos
For demos and test scripts, prefer a fixed random.Seed: this keeps the system tests deterministic, and the output can be diff-checked after each run. For live use (game, application), use random.AutoSeed() instead -- it produces a different internal state on every run.
Weighted choices with Double
For weighted bool (e.g. 70%/30%): `if (random.Double() < 0.7) { ... } else { ... }`. For more buckets, use cumulative thresholds: 60%/30%/10% -> 0.0..0.6, 0.6..0.9, 0.9..1.0.
Error handling
A wrong argument count/type, a LoadState blob with size != 32 bytes, or the all-zero state are reported by the runtime as a runtime error and the script stops. A reversed range (min > max) is not an error: the bounds are swapped and a notice is written to stderr.