import test from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { createState, act, routeFor, solve } from '../labs/variants/model.js'; import { hexagon, validateLevel } from '../src/core.js'; const levels=JSON.parse(readFileSync(new URL('../labs/variants/levels.json',import.meta.url))); test('15 variant levels validate, solve, replay and use their mechanics',()=>{ assert.equal(levels.length,15); for(const level of levels){ assert.deepEqual(validateLevel(level),[]); let state=createState(level),stops=0; for(const action of level.solution){const result=act(state,action);assert.notEqual(result.state,state,level.id);if(result.status==='stop')stops++;state=result.state;} assert.equal(state.tiles.length,0,level.id); assert.equal(solve(createState(level)).solved,true,level.id); assert.ok(level.evidence.bends>0); if(level.mode==='relay')assert.ok(stops>0); if(level.mode==='switch'){assert.deepEqual(state.fired,['A']);assert.ok(level.evidence.changedRoute);assert.ok(level.evidence.openedRoute);} } }); test('six rotations restore active and alternate tracks, gates and stops remain fixed',()=>{ for(const level of levels){let state=createState(level);for(let i=0;i<6;i++)state=act(state,{type:'rotate',direction:1}).state;assert.deepEqual(state,createState(level));} }); test('wrong color rejects departure without mutating the snapshot',()=>{ const state=createState({mode:'color',cells:hexagon(2),tiles:[{id:'x',q:0,r:0,dir:0,color:0}],gates:[{q:0,r:-2,dir:0,color:1}]}); const result=act(state,{type:'tap',id:'x'});assert.equal(result.status,'wrong');assert.equal(result.state,state); state.gates[0].color=0;assert.equal(act(state,{type:'tap',id:'x'}).state.tiles.length,0); }); test('relay truncates collision checking at stop and resumes to exit',()=>{ const state=createState({mode:'relay',cells:hexagon(2),stops:[{q:0,r:0}],tiles:[{id:'x',q:0,r:2,dir:0,color:0},{id:'y',q:0,r:-2,dir:0,color:1}]}); const result=act(state,{type:'tap',id:'x'});assert.equal(result.status,'stop');assert.equal(result.state.tiles[0].r,0);assert.equal(state.tiles[0].r,2); assert.equal(routeFor(result.state,result.state.tiles[0]).status,'blocked'); const cleared=act(result.state,{type:'tap',id:'y'}).state;assert.equal(act(cleared,{type:'tap',id:'x'}).status,'exit'); }); test('occupied stop blocks; state snapshots allow exact undo of switch and parking',()=>{ const state=createState({mode:'relay',cells:hexagon(2),stops:[{q:0,r:0}],tiles:[{id:'x',q:0,r:2,dir:0,color:0},{id:'y',q:0,r:0,dir:0,color:1}]}); assert.equal(act(state,{type:'tap',id:'x'}).status,'blocked'); for(const level of levels){let current=createState(level);for(const action of level.solution){const snapshot=JSON.stringify(current);const next=act(current,action).state;assert.equal(JSON.stringify(current),snapshot);current=next;}} });