Files
systemic-game-design-course/course/07-rule-resolution/scenarios.md
T
Lucas WintherandClaude Opus 5.5 d993457772 feat: module 07 rule resolution
Adds IS NOT to the Part II parser and a resolution pass (negation, then
identity lock) that turns parsed rules into the rules in force; harness
gains activeRules() and hasProperty(). A leading NOT now breaks the
sentence instead of being ignored (carried from M6 review). Nouns say
which objects they name ("selects"), so the ALL Expansion Drop is one
line of data. Corrects lesson 06's NOT wording; harness.md M7 details.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 03:47:48 +01:00

5.1 KiB

Module 7 scenarios: Resolving rules (NOT, identity, meta-nouns)

Each heading is a test name. The Rust test lives in tests/rust/test_07.rs and the TypeScript test in tests/ts/test_07.test.ts; both state exactly what is written here.

The level format, place, rules() and the way a test writes a sentence (BABA (0,0) IS (1,0) YOU (2,0) is three text objects) are those of Module 6. This module adds the operator NOT to the grammar, and two harness functions:

  • rules() is still everything the text spells, before resolution, and now includes negated rules, written "BABA IS NOT YOU".
  • activeRules() is the rule set after the resolution pass: sorted, normalised strings, each distinct rule once. A rule X IS NOT Y cancels every X IS Y, wherever each is written; the negated rule itself stays in the set, because it keeps overriding. A rule X IS X that survives cancels every X IS Y whose Y is a noun other than X (Baba can't convert). The base rule TEXT IS PUSH is in it unless TEXT IS NOT PUSH cancels it.
  • hasProperty(id, prop) asks whether one object has a property under the active rules. A thing is named by its noun (a baba object by BABA); a text object is named by TEXT. The object has the property if an active rule gives it to one of its names and no active negated rule takes it away from one of its names.

X IS NOT X is decided here and acted on later. It cancels X IS X (it is the negation of that rule, like any other), so other X IS Y conversions work again, and it stays in activeRules() as the record that every X is to vanish. Removing the objects is part of the turn, in Module 9. The tests check the cancellation and the unlocked conversion, which the sources state; they do not check that "BABA IS NOT BABA" is listed in activeRules(), which is this course's way of recording the deletion, not something the sources describe.

NOT in front of a complement negates that complement only: BABA IS NOT YOU AND WIN is BABA IS NOT YOU and BABA IS WIN. NOT in front of a subject is not part of the course's grammar: the sentence it stands in spells nothing.

Words used: the nouns BABA, ROCK; the properties YOU, PUSH; the operators IS, NOT; the meta-noun TEXT.

test_07_not_overrides

  • Setup: newBaba with an empty level 4 wide and 5 high. Place BABA (0,0) IS (1,0) YOU (2,0) and an object baba at (0,4).
  • Assertions 1: hasProperty(baba, "YOU") is true.
  • Action: place BABA (0,2) IS (1,2) NOT (2,2) YOU (3,2).
  • Assertions 2:
    • rules() is exactly ["BABA IS NOT YOU", "BABA IS YOU", "TEXT IS PUSH"]: both sentences are still on the board.
    • activeRules() is exactly ["BABA IS NOT YOU", "TEXT IS PUSH"]: the negation overrides BABA IS YOU, although that rule is written above it.
    • hasProperty(baba, "YOU") is false.

test_07_x_is_x_blocks_conversion

  • Setup: newBaba with an empty level 3 wide and 5 high. Place BABA (0,0) IS (1,0) BABA (2,0), BABA (0,2) IS (1,2) ROCK (2,2) and ROCK (0,4) IS (1,4) BABA (2,4).
  • Assertions:
    • rules() is exactly ["BABA IS BABA", "BABA IS ROCK", "ROCK IS BABA", "TEXT IS PUSH"].
    • activeRules() is exactly ["BABA IS BABA", "ROCK IS BABA", "TEXT IS PUSH"]. BABA IS BABA blocks Baba from converting into anything, so BABA IS ROCK is not active. It doesn't block other things from converting into Baba, so ROCK IS BABA is.

test_07_x_is_not_x_deletes_and_unlocks

  • Setup: newBaba with an empty level 4 wide and 5 high. Place BABA (0,0) IS (1,0) NOT (2,0) BABA (3,0), BABA (0,2) IS (1,2) BABA (2,2) and BABA (0,4) IS (1,4) ROCK (2,4).
  • Assertions:
    • rules() is exactly ["BABA IS BABA", "BABA IS NOT BABA", "BABA IS ROCK", "TEXT IS PUSH"].
    • activeRules() contains "BABA IS ROCK" and does not contain "BABA IS BABA". BABA IS NOT BABA cancels the lock, so the conversion is active again. (Baba is also flagged to vanish; see the note above on what the test leaves open.)

test_07_text_is_not_push

  • Setup: newBaba with an empty level 4 wide and 3 high. Place a text text:ROCK at (0,2) and an object rock at (3,2).
  • Assertions 1:
    • activeRules() is exactly ["TEXT IS PUSH"].
    • hasProperty(rockText, "PUSH") is true: the base rule applies to every text object.
    • hasProperty(rock, "PUSH") is false: TEXT names text, not the rock the text names.
  • Action: place TEXT (0,0) IS (1,0) NOT (2,0) PUSH (3,0).
  • Assertions 2:
    • rules() is exactly ["TEXT IS NOT PUSH", "TEXT IS PUSH"].
    • activeRules() is exactly ["TEXT IS NOT PUSH"]: the base rule is a rule like any other and NOT cancels it.
    • hasProperty(rockText, "PUSH") is false, and so is hasProperty of the TEXT text at (0,0).

test_07_leading_not_breaks_sentence

  • Setup: newBaba with an empty level 4 wide and 2 high. Place NOT (0,0) BABA (1,0) IS (2,0) YOU (3,0) and an object baba at (0,1).
  • Assertions:
    • rules() and activeRules() are both exactly ["TEXT IS PUSH"]. A NOT in front of the subject is not in the course's grammar, and the parser must not read the rest of the sentence as BABA IS YOU: the NOT is there, and dropping it would change the meaning.
    • hasProperty(baba, "YOU") is false.