import { describe, expect, it } from 'vitest'; import { Rng } from '@core/util/Rng'; describe('Rng', () => { it('is deterministic for a given seed', () => { const a = Array.from({ length: 20 }, () => new Rng(42).next()); expect(new Set(a).size).toBe(1); const first = new Rng(7); const second = new Rng(7); for (let i = 0; i < 100; i++) { expect(first.next()).toBe(second.next()); } }); it('produces different streams for different seeds', () => { const a = new Rng(1); const b = new Rng(2); const differs = Array.from({ length: 10 }, () => a.next() !== b.next()); expect(differs.some(Boolean)).toBe(true); }); it('resumes from a captured state', () => { const source = new Rng(123); for (let i = 0; i < 5; i++) source.next(); const resumed = Rng.fromState(source.state); for (let i = 0; i < 20; i++) { expect(resumed.next()).toBe(source.next()); } }); it('keeps nextInt inside its bound', () => { const rng = new Rng(99); for (let i = 0; i < 10000; i++) { const v = rng.nextInt(36); expect(v).toBeGreaterThanOrEqual(0); expect(v).toBeLessThan(36); } }); it('rejects a non-positive bound and an empty pick', () => { const rng = new Rng(1); expect(() => rng.nextInt(0)).toThrow(RangeError); expect(() => rng.pick([])).toThrow(RangeError); }); it('shuffles as a permutation, not a resample', () => { const rng = new Rng(2024); const original = Array.from({ length: 36 }, (_, i) => i); const shuffled = rng.shuffle([...original]); expect(shuffled).toHaveLength(36); expect([...shuffled].sort((x, y) => x - y)).toEqual(original); expect(shuffled).not.toEqual(original); }); it('shuffles reproducibly', () => { const deck = () => Array.from({ length: 36 }, (_, i) => i); expect(new Rng(5).shuffle(deck())).toEqual(new Rng(5).shuffle(deck())); }); });