Movement planned in full, then committed: a Planner builds a plan (moving and destroyed ids) by running content's movement responses (OPEN/SHUT, PUSH, STOP) for every step, rolling back failed branches. Edge of level blocks; YOU objects move furthest-ahead first; rules are reparsed once after movement. Harness: move/move_dir(Dir), posOf, kindsAt. Expansion Drop: PULL as one word plus one content response. Co-Authored-By: Claude Opus 5.5 <[email protected]>
5.0 KiB
5.0 KiB
Module 8 scenarios: Push chains (validate, then commit)
Each heading is a test name. The Rust test lives in tests/rust/test_08.rs and the TypeScript test in tests/ts/test_08.test.ts; both state exactly what is written here.
The level format, place, rules(), activeRules(), hasProperty and the way a test writes a sentence (BABA (0,0) IS (1,0) YOU (2,0) is three text objects) are those of Modules 6 and 7. This module adds three harness functions:
move(dir)(Rust:move_dir(Dir::Right)) moves every object that isYOUone tile indir,"up","down","left"or"right", with everything it pushes, and then reparses the rules. Nothing else happens: no conversion, no win, no hazard. That is the turn of Module 9.posOf(id)is the object's tile[x, y](Rust:Some((x, y))), ornull(Rust:None) once the object has been destroyed.kindsAt(x, y)is the kinds of the objects on a tile, sorted, written as they are placed:"baba"for a thing,"text:ROCK"for text. An empty tile gives[].
What moving means, in this module:
- An object that is
PUSHon the tile a mover enters is pushed one tile the same way, and may push the next one: a push chain. If any object in the chain can't move, nothing in the chain moves, theYOUobject included. - An object that is
STOP, and isn't pushed out of the way, blocks the move. - The edge of the level blocks like
STOP: no object leaves the level by moving. - When an
OPENobject moves onto a tile with aSHUTobject (or the reverse), both are destroyed, and theSHUTobject doesn't block. A pushed object counts as moving. - Every rule a move uses is the rule in force before the move. The rules are reparsed once, after all movement.
Words used: the nouns BABA, ROCK, WALL, KEY, DOOR; the properties YOU, PUSH, STOP, OPEN, SHUT; the operators IS, AND; the base rule TEXT IS PUSH.
test_08_chain_moves_together
- Setup:
newBabawith an empty level 6 wide and 4 high. PlaceBABA (0,0) IS (1,0) YOU (2,0)andROCK (0,1) IS (1,1) PUSH (2,1). Place an objectbabaat (0,3) and three objectsrockat (1,3), (2,3) and (3,3). - Action:
move("right"). - Assertions:
posOf(baba)is (1,3).posOfof the three rocks, in the order placed, is (2,3), (3,3) and (4,3): the whole chain shifted one tile.kindsAt(0,3)is[]andkindsAt(4,3)is["rock"].
test_08_chain_into_stop_nothing_moves
- Setup:
newBabawith an empty level 6 wide and 5 high. PlaceBABA (0,0) IS (1,0) YOU (2,0),ROCK (0,1) IS (1,1) PUSH (2,1)andWALL (0,2) IS (1,2) STOP (2,2). Place an objectbabaat (0,4), three objectsrockat (1,4), (2,4) and (3,4), and an objectwallat (4,4). The tile (5,4), beyond the wall, is empty. - Action:
move("right"). - Assertions:
posOf(baba)is still (0,4).- The rocks are still at (1,4), (2,4) and (3,4), and the wall at (4,4).
- The chain is checked before anything moves: the first rocks don't move because the last one can't, and Baba doesn't move because the chain can't.
test_08_open_shut_during_push
- Setup:
newBabawith an empty level 5 wide and 5 high. PlaceBABA (0,0) IS (1,0) YOU (2,0),KEY (0,1) IS (1,1) PUSH (2,1) AND (3,1) OPEN (4,1)andDOOR (0,2) IS (1,2) STOP (2,2) AND (3,2) SHUT (4,2). Place an objectbabaat (0,4), an objectkeyat (1,4) and an objectdoorat (2,4). - Action:
move("right"). - Assertions:
posOf(key)andposOf(door)are bothnull: the pushed key is OPEN, the door is SHUT, and both are destroyed during the move. The door's STOP doesn't block the key.posOf(baba)is (1,4): the push succeeded, so Baba moves one tile, into the key's former tile. It does not reach the door's tile.kindsAt(2,4)is[]andkindsAt(1,4)is["baba"].- Baba's landing tile is inferred from the sources, not stated by them (see the lesson).
test_08_text_pushed
- Setup:
newBabawith an empty level 4 wide and 5 high. PlaceBABA (0,0) IS (1,0) YOU (2,0). Place an objectbabaat (0,2) and a texttext:ROCKat (1,2). Place a texttext:ISat (2,3) and a texttext:PUSHat (2,4). - Assertions 1:
rules()is exactly["BABA IS YOU", "TEXT IS PUSH"]. - Action:
move("right"). - Assertions 2:
posOf(baba)is (1,2) andposOf(rockText)is (2,2): text is pushed by the base ruleTEXT IS PUSH, with no rule written for it.rules()is exactly["BABA IS YOU", "ROCK IS PUSH", "TEXT IS PUSH"]: the pushedROCKnow reads down the column asROCK IS PUSH, andmovereparsed the rules.
test_08_level_edge_is_stop
- Setup:
newBabawith an empty level 3 wide and 3 high. PlaceBABA (0,0) IS (1,0) YOU (2,0). Place an objectbabaat (1,2) and a texttext:FLAGat (2,2), on the right edge. - Action 1:
move("right"). - Assertions 1:
posOf(baba)is (1,2) andposOf(flagText)is (2,2). The text would have to leave the level, so the chain can't move. - Action 2:
move("down"). - Assertions 2:
posOf(baba)is still (1,2): Baba is on the bottom edge. Nothing was destroyed:posOf(flagText)is still (2,2).