roll-parser - v3.4.0
    Preparing search index...

    Class SeededRNG

    Seedable pseudo-random number generator using xoshiro128**. The default randomness source — roll(notation) builds one per call, and roll(notation, { seed }) builds one from your seed.

    Period 2^128 - 1. Every seed is stringified and hashed with cyrb128 into the full 128-bit state, so numeric seeds keep all 53 bits and unrelated strings are overwhelmingly unlikely to share a state; an omitted seed hashes Date.now() together with two Math.random() draws, roughly 100 bits of width rather than 32 — wide enough that concurrently created generators are very unlikely to collide, though unpredictability stays bounded by the host engine's Math.random() seeding. The first 8 draws are discarded as a run-in.

    Stringifying means 42 and '42' are the same seed — the two forms share one namespace, which matters when seeds arrive from a CLI flag or JSON.

    state snapshots the state words as a versioned RngState, and passing one to the constructor resumes that exact sequence — restore copies the words verbatim, skipping both the hash and the run-in. A snapshot from another format version throws INCOMPATIBLE_RNG_STATE; within the current version the type is the contract, and a hand-built tuple is coerced to 32 bits per word rather than rejected.

    Reproducibility guarantee: the same seed and the same notation produce the same dice for the lifetime of a major version, and RngState carries that binding. The one exception is a genuine distribution bug — bias, faulty rejection sampling — which may change the mapping in a minor release, never silently in a patch, and always with a BREAKING changelog note. The sequence is not cryptographically secure. To pin a roll beyond that, persist the RollResult rather than re-deriving it from a seed.

    import { SeededRNG, roll } from 'roll-parser';

    // Same seed = same sequence
    const a = new SeededRNG('test-seed');
    const b = new SeededRNG('test-seed');
    a.nextInt(1, 6) === b.nextInt(1, 6); // true

    // An injected instance keeps advancing across rolls; `{ seed }` restarts
    // the stream on every call.
    const rng = new SeededRNG('demo');
    roll('1d20', { rng }).total; // 1
    roll('1d20', { rng }).total; // 20 — the stream moved on
    roll('1d20', { seed: 'demo' }).total; // 1, every single time

    Implements

    Index
    • Returns a float in [0, 1), derived from one uint32 draw. Resolution is 2^-32, not the full 2^-53 a double can hold.

      Not used by the evaluator — dice go through nextInt.

      Returns number

      A float in [0, 1)

    • Returns an integer in the inclusive range [min, max], uniformly distributed — rejection sampling removes the modulo bias a plain % range would introduce.

      Bounds handling, in order:

      • min > max is normalized by swapping, so nextInt(6, 1) behaves as nextInt(1, 6). (The mock RNG throws instead; see RNG.nextInt.)
      • min === max returns that value without consuming a draw.
      • Ranges wider than 2^32 use two draws composed into a 53-bit value.
      • Ranges wider than 2^53 cannot be sampled exactly and throw a RangeError rather than silently skewing.

      Parameters

      • min: number

        Lower bound, inclusive

      • max: number

        Upper bound, inclusive

      Returns number

      An integer in [min, max]

      If max - min + 1 exceeds 2^53

      import { SeededRNG } from 'roll-parser';

      const rng = new SeededRNG('demo');
      rng.nextInt(1, 6); // 1..6
      rng.nextInt(3, 3); // 3, always
      rng.nextInt(1, Number.MAX_SAFE_INTEGER); // fine — two-draw path
    • Returns the current state as a format version followed by four unsigned 32-bit words. Feeding the snapshot back to the constructor resumes this exact sequence; the source instance is untouched, and the two then advance independently.

      The words are RngState — opaque, restorable within the major version that produced them, and rejected with INCOMPATIBLE_RNG_STATE outside it.

      Returns RngState

      A snapshot of the version and the four state words

      import { SeededRNG, roll } from 'roll-parser';

      const rng = new SeededRNG();
      const snapshot = rng.state();

      const first = roll('1d20', { rng });
      const replay = roll('1d20', { rng: new SeededRNG(snapshot) });
      first.total === replay.total; // true

      Not a fork primitive. A restored generator replays the parent's stream, so children taken at different points are the same sequence at an offset, not independent substreams — derive a seed per entity instead.

      import { SeededRNG } from 'roll-parser';

      const goblin = new SeededRNG('world:goblin');
      const orc = new SeededRNG('world:orc');