import test from 'node:test'; import assert from 'node:assert/strict'; import { Game, rotateBoard, solveRotations, trace, validateLevel } from '../src/core.js'; import { createRouteBoard } from '../src/route-grammar.js'; import { createDeepLock, growDeepLevel, deepLevelStats, deadTurnAudit } from '../src/deep-lock.js'; // Seeds known to yield a chain lock; the sweep script records the full yield table. const WITNESSES = [ { family: 'orbits', radius: 3, seed: 0 }, { family: 'weave', radius: 4, seed: 0 }, { family: 'spiral', radius: 4, seed: 1 }, { family: 'switchback', radius: 4, seed: 0 }, ]; for (const { family, radius, seed } of WITNESSES) { test(`${family} r${radius}: three-turn chain is certified stage by stage and by the solver`, () => { const base = createRouteBoard(seed, { family, radius }); const snapshot = JSON.stringify(base); const lock = createDeepLock(base, { seed }); assert.ok(lock, `${family}, radius ${radius}, seed ${seed}`); assert.equal(JSON.stringify(base), snapshot, 'The helper must not alter its input route'); assert.equal(lock.minimumRotations, 3); assert.ok(lock.heroRoute.bends >= 6); const level = { ...base, tiles: lock.tiles, moveLimit: lock.tiles.length }; assert.deepEqual(validateLevel(level), []); assert.equal(solveRotations(level).rotations, 3, 'No one- or two-turn plan may clear the chain'); const [hero, gate, second, tail] = lock.tiles; const at = turn => rotateBoard(level, turn); assert.ok(lock.tiles.every(tile => trace(level, tile).status === 'blocked'), 'Everything is locked at the opening'); assert.equal(trace(at(1), gate).status, 'exit', 'Only the gate leaves at +1'); assert.ok([hero, second, tail].every(tile => trace(at(1), tile, [hero, second, tail]).status === 'blocked')); assert.equal(trace(at(2), second, [hero, second, tail]).status, 'exit', 'The second gate leaves at +2'); assert.equal(trace(at(2), hero, [hero, tail]).status, 'blocked', 'Hero and tail hold each other at +2'); assert.equal(trace(at(2), tail, [hero, tail]).status, 'blocked'); assert.equal(trace(at(1), hero, [hero, tail]).status, 'exit', 'Turning back frees the hero'); const game = new Game(level); for (const action of lock.actions) { if (action.type === 'rotate') game.rotate(action.direction); else assert.equal(game.tap(action.id).status, 'exit'); } assert.equal(game.tiles.length, 0, 'The authored three-turn witness must fully clear the board'); assert.equal(game.rotations, 3); }); test(`${family} r${radius}: growth keeps three turns and locks most of the board`, () => { const base = createRouteBoard(seed, { family, radius }); const lock = createDeepLock(base, { seed }); const target = radius === 3 ? 9 : 12; const { level, solution, placements } = growDeepLevel(base, lock, { seed, target }); assert.deepEqual(validateLevel(level), []); assert.ok(level.tiles.length >= target - 2, `grew to ${level.tiles.length} of ${target}`); assert.equal(placements.length, level.tiles.length); assert.ok(solution.solvable && !solution.exhausted); assert.ok(solution.rotations >= 3, 'Inserted pieces must not open a shorter plan'); const stats = deepLevelStats(level); assert.ok(stats.lockedShare >= .5, `locked share ${stats.lockedShare}`); assert.equal(stats.releasePerTurn.length, stats.rotations); // Every state a player can reach — any tap order, any turn sequence — must still clear. const audit = deadTurnAudit(level); assert.ok(!audit.exhausted, 'audit must enumerate every reachable state'); assert.equal(audit.unsolvable, 0, 'no reachable state may be a dead end'); const game = new Game(level); for (const action of level.experience.actions) { if (action.type === 'rotate') game.rotate(action.direction); else assert.equal(game.tap(action.id).status, 'exit'); } assert.ok(game.won, 'The solver reference must clear the grown level'); }); } test('deep lock search is deterministic and an unmet constraint returns no lock', () => { const base = createRouteBoard(0, { family: 'orbits', radius: 3 }); assert.deepEqual(createDeepLock(base, { seed: 5 }), createDeepLock(base, { seed: 5 })); assert.equal(createDeepLock(base, { minHeroBends: 999 }), null); assert.equal(createDeepLock(base, { maxChecks: 0 }), null); });