feat: module 06 parser
First Part II module. The cumulative reference adds a second engine (src/engine/puzzle: grid, lexer, parser) beside Part I's; nouns and properties are data in src/content/words.json, and the engine names only IS/AND/NOT/TEXT and the base rule TEXT IS PUSH. Rules are recompiled from the board on every change; rules() is a sorted set. Adds the M6 scenarios and tests (rows, columns, shared IS, AND distribution, stacked text, base rule), the HOT word as a data-only Expansion Drop, the lesson with the Part II bridge, and the Rust LevelDef shape in harness.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e04dc1a11f
commit
68fe9834c3
55 files changed
+5679
-1
No files matched your search
@@ -0,0 +1,78 @@
|
||||
// Module 6: Grid, lexer and parser. Each test matches a heading in scenarios.md.
|
||||
import { expect, test } from "vitest";
|
||||
import { newBaba, type Baba } from "../src/harness";
|
||||
|
||||
/** An empty level `width` wide and `height` high. */
|
||||
const emptyLevel = (width: number, height: number) => ({ width, height, objects: [] });
|
||||
|
||||
/** Place one text object per word: `[word, x, y]`. */
|
||||
function write(b: Baba, words: [string, number, number][]): void {
|
||||
for (const [word, x, y] of words) b.place(`text:${word}`, x, y);
|
||||
}
|
||||
|
||||
test("test_06_horizontal_rule", () => {
|
||||
const b = newBaba({
|
||||
width: 5,
|
||||
height: 3,
|
||||
objects: [
|
||||
{ kind: "text:BABA", x: 0, y: 0 },
|
||||
{ kind: "text:IS", x: 1, y: 0 },
|
||||
{ kind: "text:YOU", x: 2, y: 0 },
|
||||
{ kind: "baba", x: 3, y: 2 },
|
||||
{ kind: "text:YOU", x: 0, y: 2 },
|
||||
{ kind: "text:IS", x: 1, y: 2 },
|
||||
{ kind: "text:FLAG", x: 2, y: 2 },
|
||||
],
|
||||
});
|
||||
|
||||
expect(b.rules()).toEqual(["BABA IS YOU", "TEXT IS PUSH"]);
|
||||
});
|
||||
|
||||
test("test_06_crossword_shared_is", () => {
|
||||
const b = newBaba(emptyLevel(3, 3));
|
||||
write(b, [
|
||||
["BABA", 0, 1],
|
||||
["IS", 1, 1],
|
||||
["YOU", 2, 1],
|
||||
["FLAG", 1, 0],
|
||||
]);
|
||||
|
||||
expect(b.rules()).toEqual(["BABA IS YOU", "TEXT IS PUSH"]);
|
||||
|
||||
b.place("text:WIN", 1, 2);
|
||||
|
||||
expect(b.rules()).toEqual(["BABA IS YOU", "FLAG IS WIN", "TEXT IS PUSH"]);
|
||||
});
|
||||
|
||||
test("test_06_and_distributes", () => {
|
||||
const b = newBaba(emptyLevel(7, 1));
|
||||
write(b, [
|
||||
["BABA", 0, 0],
|
||||
["AND", 1, 0],
|
||||
["ROCK", 2, 0],
|
||||
["IS", 3, 0],
|
||||
["PUSH", 4, 0],
|
||||
["AND", 5, 0],
|
||||
["STOP", 6, 0],
|
||||
]);
|
||||
|
||||
expect(b.rules()).toEqual(["BABA IS PUSH", "BABA IS STOP", "ROCK IS PUSH", "ROCK IS STOP", "TEXT IS PUSH"]);
|
||||
});
|
||||
|
||||
test("test_06_stacked_text_all_combinations", () => {
|
||||
const b = newBaba(emptyLevel(3, 1));
|
||||
write(b, [
|
||||
["BABA", 0, 0],
|
||||
["ROCK", 0, 0],
|
||||
["IS", 1, 0],
|
||||
["YOU", 2, 0],
|
||||
]);
|
||||
|
||||
expect(b.rules()).toEqual(["BABA IS YOU", "ROCK IS YOU", "TEXT IS PUSH"]);
|
||||
});
|
||||
|
||||
test("test_06_default_text_is_push", () => {
|
||||
const b = newBaba({ width: 3, height: 3, objects: [{ kind: "baba", x: 1, y: 1 }] });
|
||||
|
||||
expect(b.rules()).toEqual(["TEXT IS PUSH"]);
|
||||
});
|
||||
Reference in new issue
Block a user