1import test from 'node:test';2import assert from 'node:assert/strict';3import { createGame } from '../src/engine.js';4import { levels } from '../src/levels.js';56const move = (game, direction, count = 1) => {7 const delta = { up: [0, -1], down: [0, 1], left: [-1, 0], right: [1, 0] }[direction];8 for (let index = 0; index < count; index++) game.step(...delta);9};1011test('built-in levels have compact rectangular boards and typed objects', () => {12 assert.equal(levels.length, 3);13 for (const level of levels) {14 assert.equal(level.width, 12);15 assert.equal(level.height, 9);16 assert.ok(level.objects.length > 0);17 for (const object of level.objects) {18 assert.ok(['word', 'object'].includes(object.kind));19 assert.ok(Number.isInteger(object.x) && Number.isInteger(object.y));20 assert.ok(object.x >= 0 && object.x < level.width);21 assert.ok(object.y >= 0 && object.y < level.height);22 }23 }24});2526test('level 1 teaches that breaking STOP changes movement', () => {27 const game = createGame(levels[0]);28 const rules = game.getState().rules.map(rule => rule.text);29 assert.ok(rules.includes('MIAU IS YOU'));30 assert.ok(rules.includes('FLAG IS WIN'));31 assert.ok(rules.includes('ROCK IS STOP'));3233 move(game, 'right', 2);34 move(game, 'up', 2); // Push STOP out of the sentence.35 assert.ok(!game.has('ROCK', 'STOP'));36 move(game, 'down', 4);37 move(game, 'right', 7); // Walk through ROCK and onto FLAG.38 assert.equal(game.getState().won, true);39});4041test('level 2 jams a pushed rock at the flag until its PUSH rule changes', () => {42 const game = createGame(levels[1]);43 assert.equal(game.has('ROCK', 'PUSH'), true);44 assert.equal(game.has('WALL', 'STOP'), true);45 move(game, 'right', 10);46 const rock = game.getState().objects.find(object => object.kind === 'object' && object.noun === 'ROCK');47 assert.equal(rock.x, 11);48 assert.equal(game.getState().won, false);4950 const solved = createGame(levels[1]);51 const steps = { U: [0, -1], D: [0, 1], L: [-1, 0], R: [1, 0] };52 const route = 'UURRRRRUDLLLLLDDRRRRRRRRRR';53 for (const [index, direction] of [...route].entries()) {54 solved.step(...steps[direction]);55 if (index === 7) assert.equal(solved.has('ROCK', 'PUSH'), false);56 }57 assert.equal(solved.getState().won, true);58});5960test('level 3 teaches an AND subject list', () => {61 const game = createGame(levels[2]);62 assert.equal(game.has('ROCK', 'WIN'), true);63 assert.equal(game.has('FLAG', 'WIN'), true);64 move(game, 'right', 6);65 assert.equal(game.getState().won, true);66});