import { describe, expect, it } from 'vitest' import { generateInitialCondition, getInitialConditionRuntime, listInitialConditionRuntimes, registerInitialConditionRuntime } from '../admin/src/caInitialConditionGenerators.js' describe('CA initial condition runtimes', () => { it('registers the built-in random soup generator', () => { expect(listInitialConditionRuntimes().map((runtime) => runtime.id)).toEqual( expect.arrayContaining(['random-soup', 'naga-markov']) ) expect(getInitialConditionRuntime('random-soup')?.label).toBe('Random soup') expect(getInitialConditionRuntime('naga-markov')?.label).toBe('Naga Markov chains') }) it('generates deterministic cell coordinates for the same seed and density', () => { const input = { caClass: { dimensions: 2, states: 2 }, settings: { simulation: { grid: { size: [5, 5, 1] } } }, params: { density: 0.35, seed: 'repeatable' } } expect(generateInitialCondition('random-soup', input)).toEqual(generateInitialCondition('random-soup', input)) }) it('clamps density and records generator provenance in the persisted initial condition', () => { const generated = generateInitialCondition('random-soup', { caClass: { dimensions: 2, states: 2 }, settings: { simulation: { grid: { size: [3, 3, 1] } } }, params: { density: 2, seed: 'full' } }) expect(generated.cells).toHaveLength(9) expect(generated.generator).toEqual({ kind: 'random-soup', density: 1, seed: 'full' }) }) it('lets custom initial condition runtimes plug into the same contract', () => { registerInitialConditionRuntime({ id: 'test-single-cell', label: 'Test single cell', generate: () => ({ type: 'cells', cells: [[1, 1, 0]], generator: { kind: 'test-single-cell' } }) }) expect(generateInitialCondition('test-single-cell', { caClass: { dimensions: 2, states: 2 }, settings: {}, params: {} }).cells).toEqual([[1, 1, 0]]) }) it('generates collision-free Naga Markov chains with directional continuity', () => { const generated = generateInitialCondition('naga-markov', { caClass: { dimensions: 3, states: 7 }, settings: { simulation: { grid: { size: [12, 12, 12] } } }, params: { count: 2, length: 5, sameTypeProbability: 1, seed: 'straight-nagas' } }) expect(generated.cells).toHaveLength(10) expect(new Set(generated.cells.map(([x, y, z]) => `${x}_${y}_${z}`)).size).toBe(10) expect(generated.generator).toMatchObject({ kind: 'naga-markov', count: 2, length: 5, sameTypeProbability: 1, seed: 'straight-nagas', created: 2 }) const occupied = new Set(generated.cells.map(([x, y, z]) => `${x}_${y}_${z}`)) for (let start = 0; start < generated.cells.length; start += 5) { for (let index = start; index < start + 4; index += 1) { const [x, y, z, arrow] = generated.cells[index] const [nextX, nextY, nextZ, nextArrow] = generated.cells[index + 1] expect(nextArrow).toBe(arrow) if (arrow === 1) expect([nextX, nextY, nextZ]).toEqual([x + 1, y, z]) if (arrow === 2) expect([nextX, nextY, nextZ]).toEqual([x, y + 1, z]) if (arrow === 3) expect([nextX, nextY, nextZ]).toEqual([x, y, z + 1]) if (arrow === 4) expect([nextX, nextY, nextZ]).toEqual([x - 1, y, z]) if (arrow === 5) expect([nextX, nextY, nextZ]).toEqual([x, y - 1, z]) if (arrow === 6) expect([nextX, nextY, nextZ]).toEqual([x, y, z - 1]) } const [x, y, z, arrow] = generated.cells[start + 4] const terminalTarget = arrow === 1 ? [x + 1, y, z] : arrow === 2 ? [x, y + 1, z] : arrow === 3 ? [x, y, z + 1] : arrow === 4 ? [x - 1, y, z] : arrow === 5 ? [x, y - 1, z] : [x, y, z - 1] expect(occupied.has(terminalTarget.join('_'))).toBe(false) } }) it('rejects duplicate runtime ids and missing implementations', () => { expect(() => registerInitialConditionRuntime({ id: 'random-soup', label: 'Duplicate random soup', generate: () => ({ type: 'cells', cells: [], generator: { kind: 'random-soup' } }) }) ).toThrow('already registered') expect(() => generateInitialCondition('missing-generator', { caClass: { dimensions: 2, states: 2 }, settings: {}, params: {} }) ).toThrow('unavailable') }) })