Files
HexTrails/tests/inbound.test.js

88 lines
3.7 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { Inbound, catalog, checkLanding, reading, validateInbound } from '../labs/inbound/core.js';
import { LEVELS } from '../labs/inbound/levels.js';
test('slice levels are unambiguous and replay the standard dispatch', () => {
for (const level of LEVELS) {
assert.deepEqual(validateInbound(level), [], level.id);
const game = new Inbound(level);
for (const action of level.solution) {
const result = game.act(action);
assert.notEqual(result.status, 'blocked', `${level.id} ${action.type} ${action.id || ''}`);
assert.notEqual(result.status, 'ambiguous', level.id);
assert.notEqual(result.status, 'invalid', level.id);
}
assert.equal(game.won, true, level.id);
assert.equal(game.state.stars, level.standard, level.id);
}
});
test('a turn is countable before preview, and does not move geometry', () => {
const turn = LEVELS[1];
const before = reading(turn, 0);
assert.equal(before[0].open, false);
assert.equal(before[0].right, 1);
assert.equal(before[0].side, '右上方');
const after = reading(turn, 1);
assert.equal(after[0].open, true);
assert.equal(after[0].side, before[0].side);
assert.equal(after[0].exitSide, before[0].exitSide);
assert.equal(catalog(turn)[0].stand.q, 1);
});
test('one turn opens the near mouth and closes the far one', () => {
const level = LEVELS[2];
const start = reading(level, 0);
const right = start.find(mouth => mouth.side === '右方');
const left = start.find(mouth => mouth.side === '左方');
assert.equal(right.open, true);
assert.equal(left.open, false);
assert.equal(left.left, 1);
const turned = reading(level, 5);
assert.equal(turned.find(mouth => mouth.side === '左方').open, true);
assert.equal(turned.find(mouth => mouth.side === '右方').open, false);
const game = new Inbound(level);
assert.equal(game.act({ type: 'land', id: 'cocoa' }).status, 'no-route');
assert.equal(game.state.stars, 0);
});
test('parking the near plane blocks the path that crosses its stand', () => {
const level = LEVELS[3];
const game = new Inbound(level);
assert.equal(game.act({ type: 'land', id: 'berry' }).status, 'land');
const blocked = game.act({ type: 'land', id: 'cocoa' });
assert.equal(blocked.status, 'blocked');
assert.equal(blocked.blocker, 'berry');
assert.equal(game.state.stars, 1);
});
test('relocate exit side does not depend on the dial', () => {
const level = LEVELS[5];
const game = new Inbound(level);
assert.equal(game.act({ type: 'land', id: 'berry' }).status, 'land');
const first = game.act({ type: 'relocate', id: 'berry' });
assert.equal(first.sideName, '右下方');
assert.equal(game.state.planes.find(plane => plane.id === 'berry').side, 2);
const other = new Inbound(level);
other.act({ type: 'confirm', dial: 3, steps: 3 });
other.act({ type: 'confirm', dial: 0, steps: 3 });
other.act({ type: 'land', id: 'berry' });
assert.equal(other.act({ type: 'relocate', id: 'berry' }).sideName, '右下方');
const mouths = catalog(level);
assert.equal(mouths.find(mouth => mouth.id === 'berry-up').exitSideName, '右下方');
assert.equal(mouths.find(mouth => mouth.id === 'cocoa-right').sideName, '右方');
});
test('a closed mouth stays closed for all six dials if that side has no tick', () => {
const level = LEVELS[0];
for (let dial = 0; dial < 6; dial++) {
const open = reading(level, dial).filter(mouth => mouth.open);
if (dial === 0) assert.equal(open.length, 1);
else assert.equal(open.length, 0);
}
const game = new Inbound(level);
game.act({ type: 'confirm', dial: 1, steps: 1 });
assert.equal(checkLanding(level, game.state, 'cocoa').status, 'no-route');
});