import { describe, expect, it } from 'vitest'; import { SeededRNG } from '../src/core/SeededRNG'; describe('SeededRNG', () => { it('same seed + stream name → identical sequence', () => { const a = new SeededRNG(42).stream('patrons'); const b = new SeededRNG(42).stream('patrons'); for (let i = 0; i < 100; i++) expect(a.next()).toBe(b.next()); }); it('different seeds diverge', () => { const a = new SeededRNG(1).stream('x'); const b = new SeededRNG(2).stream('x'); const seqA = Array.from({ length: 10 }, () => a.next()); const seqB = Array.from({ length: 10 }, () => b.next()); expect(seqA).not.toEqual(seqB); }); it('streams are independent — draining one does not shift another', () => { const rng1 = new SeededRNG(7); const rng2 = new SeededRNG(7); rng1.stream('a'); for (let i = 0; i < 500; i++) rng1.stream('a').next(); // drain a const fromDrained = rng1.stream('b').next(); const fromFresh = rng2.stream('b').next(); expect(fromDrained).toBe(fromFresh); }); it('stream() returns the same stream instance per name (stateful)', () => { const rng = new SeededRNG(9); const first = rng.stream('q').next(); const second = rng.stream('q').next(); expect(first).not.toBe(second); // continued, not restarted }); it('int() covers bounds inclusively and stays in range', () => { const s = new SeededRNG(3).stream('ints'); const seen = new Set(); for (let i = 0; i < 2000; i++) { const v = s.int(1, 6); expect(v).toBeGreaterThanOrEqual(1); expect(v).toBeLessThanOrEqual(6); seen.add(v); } expect(seen.size).toBe(6); }); it('chance() distribution sanity', () => { const s = new SeededRNG(11).stream('coin'); let hits = 0; for (let i = 0; i < 10_000; i++) if (s.chance(0.3)) hits++; expect(hits / 10_000).toBeGreaterThan(0.27); expect(hits / 10_000).toBeLessThan(0.33); }); it('weighted() respects weights and never returns zero-weight entries', () => { const s = new SeededRNG(13).stream('w'); const counts = { a: 0, b: 0, c: 0 }; for (let i = 0; i < 10_000; i++) { counts[s.weighted([['a', 9], ['b', 1], ['c', 0]] as const)]++; } expect(counts.c).toBe(0); expect(counts.a).toBeGreaterThan(counts.b * 5); }); it('pick/weighted throw on empty/invalid input', () => { const s = new SeededRNG(1).stream('e'); expect(() => s.pick([])).toThrow(); expect(() => s.weighted([])).toThrow(); }); });