osrlib.core.rng
Named deterministic RNG streams backed by pure-Python PCG64.
This module is the determinism contract made concrete: draw sequences are part of the public compatibility guarantee, so every algorithmic choice here is frozen by golden vectors in the test suite. Do not change any of it without bumping expectations consciously — an "equivalent" reimplementation that shifts a single draw breaks replays and golden files.
The generator is PCG64 — specifically the pcg_setseq_128_xsl_rr_64 variant (128-bit
LCG state, XSL-RR output to 64 bits), the same generator behind numpy's PCG64 (not
PCG64DXSM, which is a different algorithm). Each next_uint64() advances the LCG
first, then applies XSL-RR to the new state; this is the pcg-c 128-bit convention
numpy follows, and the opposite of the widely tutorialized pcg32 pattern.
Streams are forked from a master seed by stable string keys: seed material is
SHA-256(master_seed_bytes + b":" + stream_key_utf8) with the master seed encoded as
16 bytes big-endian, so stream identity depends only on the master seed and the key
string. Adding draws to one subsystem's stream never shifts results in another.
Randomness in the library must always come from an explicitly passed
RngStream — never the stdlib random module, and never
a module-level default.
RngStream
A single PCG64 stream.
Construct via RngStreams.get in normal play;
direct construction from an (initstate, initseq) pair runs the canonical PCG64
init and exists for tests and à la carte use.
Initialize the stream with the canonical PCG64 init procedure.
The init is state = 0; inc = (initseq << 1) | 1; step; state += initstate;
step, all mod 2**128. It discards the top bit of initseq — expected
behavior, not a bug to fix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
initstate
|
int
|
The 128-bit init state, in |
required |
initseq
|
int
|
The 128-bit stream-selection constant, in |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If either argument is out of range. |
from_seed_material
classmethod
Derive and initialize the named stream for a master seed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
master_seed
|
int
|
The session's master seed, in |
required |
key
|
str
|
The stream's name. |
required |
Returns:
| Type | Description |
|---|---|
RngStream
|
A freshly initialized stream; the same arguments always produce a stream |
RngStream
|
that yields the identical draw sequence. |
restore
classmethod
restore(snapshot: RngStreamState) -> RngStream
Restore a stream from an exported snapshot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snapshot
|
RngStreamState
|
A state previously returned by
|
required |
Returns:
| Type | Description |
|---|---|
RngStream
|
A stream that continues the draw sequence exactly where the exporting |
RngStream
|
stream left off. |
export_state
export_state() -> RngStreamState
Export the stream's exact position for serialization.
Returns:
| Type | Description |
|---|---|
RngStreamState
|
A frozen snapshot of the raw PCG64 state and increment. |
next_uint64
next_uint64() -> int
Draw the next raw 64-bit output.
Advances the LCG first, then applies XSL-RR to the new state (the pcg-c 128-bit convention).
Returns:
| Type | Description |
|---|---|
int
|
A uniformly distributed integer in |
randbelow
Draw a uniformly distributed integer in [0, n).
The algorithm is frozen as top-bits rejection sampling: with
k = (n - 1).bit_length(), each candidate is next_uint64() >> (64 - k),
rejected and redrawn while candidate >= n. No masking of low bits.
Rejection means the raw-draw count per bounded draw is variable: power-of-two
bounds never reject; others (3, 6, 10, 12, 20, 100) can. randbelow(1) has
k = 0, always yields 0, and still consumes one draw.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
The exclusive upper bound. Must be positive. |
required |
Returns:
| Type | Description |
|---|---|
int
|
A uniformly distributed integer in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
RngStreamState
Bases: BaseModel
A serializable snapshot of an in-progress stream.
Captures the raw PCG64 internals — the 128-bit LCG state and the stream increment —
so saves can restore mid-sequence streams exactly via
RngStream.restore.
RngStreams
RngStreams(master_seed: int)
The named-stream container forked from a session's master seed.
Streams are created lazily on first access and cached: the same key always returns the same stream object, and stream identity depends only on the master seed and the key string.
Examples:
from osrlib.core.rng import RngStreams
streams = RngStreams(master_seed=42)
combat = streams.get("combat")
d20 = combat.randbelow(20) + 1
assert 1 <= d20 <= 20
Create the container for a master seed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
master_seed
|
int
|
The session's master seed, in |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
get
export_states
export_states() -> dict[str, RngStreamState]
Export every touched stream's exact position, keyed by stream name.
Untouched streams need no snapshot — they re-derive from the master seed on first use. Keys are sorted so serialization is deterministic.
Returns:
| Type | Description |
|---|---|
dict[str, RngStreamState]
|
The stream snapshots for saves. |
restore_states
restore_states(states: dict[str, RngStreamState]) -> None
Restore previously exported stream positions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
states
|
dict[str, RngStreamState]
|
Snapshots from
|
required |
derive_init_pair
Derive a PCG64 (initstate, initseq) pair for a named stream.
Seed material is SHA-256(master_seed_bytes + b":" + stream_key_utf8) with the
master seed encoded as fixed-width 16-byte big-endian. The 32-byte digest splits
into the init pair, both halves read big-endian: bytes 0-15 are initstate,
bytes 16-31 are initseq.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
master_seed
|
int
|
The session's master seed, in |
required |
key
|
str
|
The stream's name, e.g. |
required |
Returns:
| Type | Description |
|---|---|
tuple[int, int]
|
The |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |