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 <[email protected]>
3.5 KiB
Module 6 scenarios: Grid, lexer and parser
Each heading is a test name. The Rust test lives in tests/rust/test_06.rs and the TypeScript test in tests/ts/test_06.test.ts; both state exactly what is written here.
Part II has its own harness object, Baba, made by newBaba(level) from a level in the format of course/schema/level.schema.json: a width, a height and a list of objects, each {kind, x, y}. x grows right and y grows down. A kind is either an object ("baba") or a word of text ("text:BABA"). place(kind, x, y) adds one more object to the level and returns its id. Several objects may share a tile.
rules() returns every rule the board currently spells, as normalised strings such as "BABA IS YOU", sorted, each distinct rule once. The base rule "TEXT IS PUSH" is always in it, although no text spells it. Duplicates are a real thing in the game (stacked text "will attempt to produce all possible rules", and a rule spelled twice can duplicate objects, see docs/research/baba-is-you.md §e), so the engine may hold the same rule more than once, but rules() is the set of distinct rules and no test counts duplicates.
Words used: the nouns BABA, ROCK, FLAG; the properties YOU, WIN, PUSH, STOP; the operators IS and AND; the meta-noun TEXT (only through the base rule). Rules read left to right in a row and top to bottom in a column. A test writes a sentence as its words with their tiles: BABA (0,0) IS (1,0) YOU (2,0) is three text objects, text:BABA at x 0, y 0, and so on.
test_06_horizontal_rule
- Setup:
newBabawith a level 5 wide and 3 high whoseobjectsare, in this order:BABA (0,0) IS (1,0) YOU (2,0); an objectbabaat (3,2);YOU (0,2) IS (1,2) FLAG (2,2). - Action: none. The rules come from the level's objects.
- Assertions:
rules()is exactly["BABA IS YOU", "TEXT IS PUSH"].- Row 2 spells nothing:
YOU IS FLAGhas a property where a noun must start the sentence, and the parser never reads right to left (FLAG IS YOU). Thebabaobject is not text and plays no part in parsing.
test_06_crossword_shared_is
- Setup:
newBabawith an empty level 3 wide and 3 high. PlaceBABA (0,1),IS (1,1),YOU (2,1), thenFLAG (1,0). - Assertions 1:
rules()is exactly["BABA IS YOU", "TEXT IS PUSH"].FLAGaboveISis half a sentence. - Action: place
WIN (1,2). - Assertions 2:
rules()is exactly["BABA IS YOU", "FLAG IS WIN", "TEXT IS PUSH"]. TheISat (1,1) is in a row sentence and a column sentence at once, and the rules were recompiled when the board changed.
test_06_and_distributes
- Setup:
newBabawith an empty level 7 wide and 1 high. PlaceBABA (0,0) AND (1,0) ROCK (2,0) IS (3,0) PUSH (4,0) AND (5,0) STOP (6,0). - Assertions:
rules()is exactly["BABA IS PUSH", "BABA IS STOP", "ROCK IS PUSH", "ROCK IS STOP", "TEXT IS PUSH"]. AND distributes over both sides: two subjects times two complements is four rules.
test_06_stacked_text_all_combinations
- Setup:
newBabawith an empty level 3 wide and 1 high. Placetext:BABAat (0,0), thentext:ROCKalso at (0,0), thenIS (1,0)andYOU (2,0). - Assertions:
rules()is exactly["BABA IS YOU", "ROCK IS YOU", "TEXT IS PUSH"]. Both stacked words start a sentence: the parser produces every rule the stack allows.
test_06_default_text_is_push
- Setup:
newBabawith a level 3 wide and 3 high whose only object is ababaat (1,1). - Assertions:
rules()is exactly["TEXT IS PUSH"]. No text spells it: it is a base rule of the game.