39 lines
2.8 KiB
JavaScript
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;}}
|
|
});
|