Files

39 lines
2.8 KiB
JavaScript

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;}}
});