Module 9 lesson, scenarios, tests (8, both tracks), reference engines (TURN phase list, conversion, content-registered hazards, win/lost), WEAK Expansion Drop, harness.md M9 semantics, research notes (WEAK page, baba-is-auto Game::MovePlayer, X IS NOT X placement reading). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7.1 KiB
7.1 KiB
Module 9 scenarios: The turn pipeline
Each heading is a test name. The Rust test lives in tests/rust/test_09.rs and the TypeScript test in tests/ts/test_09.test.ts; both state exactly what is written here.
The level format, place, rules(), activeRules(), hasProperty, posOf, kindsAt 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 to 8. This module adds three harness functions:
step(dir)(Rust:step(Dir::Right)) plays one whole turn, in the course's simplified order: Movement → Parse → Conversion → Parse → Hazards → Win, and a last parse so thatrules()matches the board. Movement is Module 8'smovewithout its reparse.won()istrueonce a turn's win check has found aYOUobject that isWIN, or that shares its tile with aWINobject on the same float level. It staystrue.lost()istruewhen no object isYOU.
What the turn does, in this module:
- Conversion. Every object named by a rule
X IS Y, whereYis a noun, becomes aYwhere it stands: same id, same tile.X IS Xblocks it. An object named byX IS NOT Xis deleted. All objects are converted at once, from the board as it was after movement. - Hazards, each against the objects that share a tile on the same float level (both
FLOATor both not), and each destroying everything it finds at once before the next runs:SINKdestroys every object it shares a tile with, and itself;HOTdestroysMELTobjects (an object that is both destroys itself);DEFEATdestroysYOUobjects (an object that is both destroys itself). - Win runs after every hazard, so a
YOUobject destroyed byDEFEATcan't win. - Every phase uses the rules from the parse before it.
Words used: the nouns BABA, ROCK, FLAG, LAVA, KEY, SKULL, WATER; the properties YOU, WIN, PUSH, HOT, MELT, SINK, DEFEAT, FLOAT; the operators IS, AND, NOT; the base rule TEXT IS PUSH.
test_09_lava_saved_by_reparse
- Setup:
newBabawith an empty level 6 wide and 5 high. PlaceBABA (0,0) IS (1,0) YOU (2,0)andLAVA (0,1) IS (1,1) HOT (2,1). PlaceBABA (4,2) IS (4,3) MELT (4,4), reading down column 4. Place an objectlavaat (4,4), under the wordMELT, and an objectbabaat (3,4). - Assertions 1:
hasProperty(baba, "MELT")is true. - Action:
step("right"). - Assertions 2:
posOf(baba)is (4,4), on the lava, andposOf(meltText)is (5,4): Baba pushedMELTout of its sentence as it stepped.posOf(lava)is (4,4), andkindsAt(4,4)is["baba", "lava"]: Baba survived. The rules were reparsed after movement, so when the hazards ran,BABA IS MELTno longer existed.rules()doesn't contain"BABA IS MELT", andlost()is false.- That Baba survives is inferred from the wiki's order of operations, not stated by a source (see the lesson).
test_09_mid_turn_conversion
- Setup:
newBabawith an empty level 5 wide and 5 high. PlaceBABA (0,0) IS (1,0) YOU (2,0)andBABA (0,2) IS (1,2), with (2,2) empty. Place a texttext:ROCKat (2,3). Place an objectbabaat (2,4) (the pusher) and an objectbabaat (4,4) (the other Baba). - Action:
step("up"). - Assertions:
posOf(rockText)is (2,2): the pusher pushed it up, andBABA IS ROCKnow reads along row 2.rules()contains"BABA IS ROCK".posOf(pusher)is (2,3) andposOf(other)is (4,3): both moved first, then converted where they stood.kindsAt(2,3)is["rock"]andkindsAt(4,3)is["rock"]: the rule the move completed converted both Babas in the same turn.kindsAt(2,4)andkindsAt(4,4)are[].
test_09_identity_lock_blocks_conversion
- Setup:
newBabawith an empty level 5 wide and 5 high. PlaceBABA (0,0) IS (1,0) YOU (2,0),BABA (0,1) IS (1,1) BABA (2,1),BABA (0,2) IS (1,2) ROCK (2,2)andKEY (0,3) IS (1,3) ROCK (2,3). Place an objectbabaat (0,4) and an objectkeyat (3,4). - Action:
step("right"). - Assertions:
posOf(baba)is (1,4) andkindsAt(1,4)is["baba"]:BABA IS BABAblockedBABA IS ROCK.kindsAt(3,4)is["rock"]andposOf(key)is (3,4):KEY IS ROCKconverted the key, in place, with the same id.activeRules()doesn't contain"BABA IS ROCK"; it contains"BABA IS BABA"and"KEY IS ROCK".
test_09_self_negation_deletes
- 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) NOT (2,1) KEY (3,1)andKEY (0,2) IS (1,2) ROCK (2,2). Place an objectbabaat (0,4) and an objectkeyat (3,4). - Assertions 1:
posOf(key)is (3,4): placing text doesn't delete anything; the turn does. - Action:
step("right"). - Assertions 2:
posOf(key)isnull:KEY IS NOT KEYdeleted the key in the conversion step.kindsAt(3,4)is["rock"]:KEY IS NOT KEYdoesn't lock the key, soKEY IS ROCKstill applies, and a rock appears where the key was, as a new object.posOf(baba)is (1,4).
test_09_float_separates_hazards
- Setup:
newBabawith an empty level 4 wide and 6 high. PlaceBABA (0,0) IS (1,0) YOU (2,0),KEY (0,1) IS (1,1) YOU (2,1),SKULL (0,2) IS (1,2) DEFEAT (2,2)andBABA (0,3) IS (1,3) FLOAT (2,3). Place an objectbabaat (0,4) and an objectskullat (1,4); an objectkeyat (0,5) and an objectskullat (1,5). - Action:
step("right"). - Assertions:
posOf(baba)is (1,4) andkindsAt(1,4)is["baba", "skull"]: Baba isFLOATand the skull isn't, so they are on different float levels andDEFEATdoesn't reach Baba.posOf(key)isnullandkindsAt(1,5)is["skull"]: the key is on the skull's float level and is destroyed.lost()is false: Baba is stillYOU.
test_09_sink_destroys_both
- Setup:
newBabawith an empty level 5 wide and 5 high. PlaceBABA (0,0) IS (1,0) YOU (2,0),ROCK (0,1) IS (1,1) PUSH (2,1)andWATER (0,2) IS (1,2) SINK (2,2). Place an objectbabaat (0,4), an objectrockat (1,4) and an objectwaterat (2,4). - Action:
step("right"). - Assertions:
posOf(rock)andposOf(water)are bothnull: the rock was pushed onto the water, andSINKdestroyed both.kindsAt(2,4)is[].posOf(baba)is (1,4): Baba didn't share the water's tile and survives.
test_09_you_and_win
- Setup:
newBabawith an empty level 4 wide and 4 high. PlaceBABA (0,0) IS (1,0) YOU (2,0)andBABA (0,1) IS (1,1) WIN (2,1). Place an objectbabaat (0,3). - Assertions 1:
won()is false: the win is checked by the turn, not when text is placed. - Action:
step("right"). - Assertions 2:
won()is true: an object that is bothYOUandWINwins.lost()is false.
test_09_defeat_beats_win
- Setup:
newBabawith an empty level 5 wide and 4 high. PlaceBABA (0,0) IS (1,0) YOU (2,0)andFLAG (0,1) IS (1,1) WIN (2,1) AND (3,1) DEFEAT (4,1). Place an objectbabaat (0,3) and an objectflagat (1,3). - Action:
step("right"). - Assertions:
posOf(baba)isnull: Baba stepped onto the flag, which isDEFEAT.won()is false:DEFEATruns before the win check, so noYOUobject is left to win.lost()is true, andposOf(flag)is (1,3).