import { describe, it, expect } from 'vitest'; import { createRNG, Mulberry32RNG } from '@/utils/rng'; describe('createRNG', () => { it('should create RNG with default seed', () => { const rng = createRNG(); expect(rng.getSeed()).toBe(1); }); it('should create RNG with custom seed', () => { const rng = createRNG(12345); expect(rng.getSeed()).toBe(12345); }); it('should generate numbers in range [0, 1)', () => { const rng = createRNG(42); for (let i = 0; i < 100; i++) { const num = rng.next(); expect(num).toBeGreaterThanOrEqual(0); expect(num).toBeLessThan(1); } }); it('should generate numbers with max parameter', () => { const rng = createRNG(42); for (let i = 0; i < 100; i++) { const num = rng.next(100); expect(num).toBeGreaterThanOrEqual(0); expect(num).toBeLessThan(100); } }); it('should generate integers in range [0, max)', () => { const rng = createRNG(42); for (let i = 0; i < 100; i++) { const num = rng.nextInt(10); expect(Number.isInteger(num)).toBe(true); expect(num).toBeGreaterThanOrEqual(0); expect(num).toBeLessThan(10); } }); it('should be deterministic with same seed', () => { const rng1 = createRNG(12345); const rng2 = createRNG(12345); const sequence1 = Array.from({ length: 10 }, () => rng1.next()); const sequence2 = Array.from({ length: 10 }, () => rng2.next()); expect(sequence1).toEqual(sequence2); }); it('should produce different sequences with different seeds', () => { const rng1 = createRNG(12345); const rng2 = createRNG(54321); const sequence1 = Array.from({ length: 10 }, () => rng1.next()); const sequence2 = Array.from({ length: 10 }, () => rng2.next()); expect(sequence1).not.toEqual(sequence2); }); it('should reset seed with setSeed', () => { const rng = createRNG(42); const firstSequence = Array.from({ length: 5 }, () => rng.next()); rng.setSeed(42); const secondSequence = Array.from({ length: 5 }, () => rng.next()); expect(firstSequence).toEqual(secondSequence); }); it('should generate uniformly distributed integers', () => { const rng = createRNG(42); const buckets = new Array(10).fill(0); const iterations = 10000; for (let i = 0; i < iterations; i++) { const num = rng.nextInt(10); buckets[num]++; } // 每个桶应该大约有 10% 的值 const expected = iterations / 10; const tolerance = expected * 0.3; // 30% 容差 buckets.forEach((count, index) => { expect(count).toBeGreaterThan(expected - tolerance); expect(count).toBeLessThan(expected + tolerance); }); }); }); describe('Mulberry32RNG', () => { it('should instantiate with default seed', () => { const rng = new Mulberry32RNG(); expect(rng.getSeed()).toBe(1); }); it('should instantiate with custom seed', () => { const rng = new Mulberry32RNG(99999); expect(rng.getSeed()).toBe(99999); }); it('should implement RNG interface', () => { const rng = new Mulberry32RNG(42); // Should have all RNG methods expect(typeof rng.next).toBe('function'); expect(typeof rng.nextInt).toBe('function'); expect(typeof rng.setSeed).toBe('function'); expect(typeof rng.getSeed).toBe('function'); }); it('should produce same results as createRNG with same seed', () => { const factoryRng = createRNG(12345); const directRng = new Mulberry32RNG(12345); for (let i = 0; i < 10; i++) { expect(factoryRng.next()).toBe(directRng.next()); expect(factoryRng.nextInt(100)).toBe(directRng.nextInt(100)); } }); it('should allow seed changes after instantiation', () => { const rng = new Mulberry32RNG(100); expect(rng.getSeed()).toBe(100); rng.setSeed(200); expect(rng.getSeed()).toBe(200); const value = rng.next(); expect(value).toBeGreaterThanOrEqual(0); expect(value).toBeLessThan(1); }); });