feat(themes): whitelist governance, synergy cap, docs + tests; feat(random): laid roadwork for random implementation, testing in headless confirmed

This commit is contained in:
matt 2025-09-17 13:23:27 -07:00
parent 03e839fb87
commit 16261bbf09
34 changed files with 12594 additions and 23 deletions

69
code/random_util.py Normal file
View file

@ -0,0 +1,69 @@
from __future__ import annotations
import hashlib
import secrets
import random
from typing import Union
"""
Seeded RNG utilities for deterministic behavior.
Contract (minimal):
- derive_seed_from_string(s): produce a stable, platform-independent int seed from a string or int.
- set_seed(seed): return a new random.Random instance seeded deterministically.
- generate_seed(): return a high-entropy, non-negative int suitable for seeding.
- get_random(seed=None): convenience to obtain a new Random instance (seeded when provided).
No globals/state: each call returns an independent Random instance.
"""
SeedLike = Union[int, str]
def _to_bytes(s: str) -> bytes:
try:
return s.encode("utf-8", errors="strict")
except Exception:
# Best-effort fallback
return s.encode("utf-8", errors="ignore")
def derive_seed_from_string(seed: SeedLike) -> int:
"""Derive a stable positive integer seed from a string or int.
- int inputs are normalized to a non-negative 63-bit value.
- str inputs use SHA-256 to generate a deterministic 63-bit value.
"""
if isinstance(seed, int):
# Normalize to 63-bit positive
return abs(int(seed)) & ((1 << 63) - 1)
# String path: deterministic, platform-independent
data = _to_bytes(str(seed))
h = hashlib.sha256(data).digest()
# Use first 8 bytes (64 bits) and mask to 63 bits to avoid sign issues
n = int.from_bytes(h[:8], byteorder="big", signed=False)
return n & ((1 << 63) - 1)
def set_seed(seed: SeedLike) -> random.Random:
"""Return a new Random instance seeded deterministically from the given seed."""
r = random.Random()
r.seed(derive_seed_from_string(seed))
return r
def get_random(seed: SeedLike | None = None) -> random.Random:
"""Return a new Random instance; seed when provided.
This avoids mutating the module-global PRNG and keeps streams isolated.
"""
if seed is None:
return random.Random()
return set_seed(seed)
def generate_seed() -> int:
"""Return a high-entropy positive 63-bit integer suitable for seeding."""
# secrets is preferred for entropy here; mask to 63 bits for consistency
return secrets.randbits(63)