Files
systemic-game-design-course/course/07-rule-resolution/tests/ts/test_07.test.ts
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 <[email protected]>
2026-10-29 03:47:48 +01:00

110 lines
3.0 KiB
TypeScript

// Module 7: Resolving rules. 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_07_not_overrides", () => {
const b = newBaba(emptyLevel(4, 5));
write(b, [
["BABA", 0, 0],
["IS", 1, 0],
["YOU", 2, 0],
]);
const baba = b.place("baba", 0, 4);
expect(b.hasProperty(baba, "YOU")).toBe(true);
write(b, [
["BABA", 0, 2],
["IS", 1, 2],
["NOT", 2, 2],
["YOU", 3, 2],
]);
expect(b.rules()).toEqual(["BABA IS NOT YOU", "BABA IS YOU", "TEXT IS PUSH"]);
expect(b.activeRules()).toEqual(["BABA IS NOT YOU", "TEXT IS PUSH"]);
expect(b.hasProperty(baba, "YOU")).toBe(false);
});
test("test_07_x_is_x_blocks_conversion", () => {
const b = newBaba(emptyLevel(3, 5));
write(b, [
["BABA", 0, 0],
["IS", 1, 0],
["BABA", 2, 0],
["BABA", 0, 2],
["IS", 1, 2],
["ROCK", 2, 2],
["ROCK", 0, 4],
["IS", 1, 4],
["BABA", 2, 4],
]);
expect(b.rules()).toEqual(["BABA IS BABA", "BABA IS ROCK", "ROCK IS BABA", "TEXT IS PUSH"]);
expect(b.activeRules()).toEqual(["BABA IS BABA", "ROCK IS BABA", "TEXT IS PUSH"]);
});
test("test_07_x_is_not_x_deletes_and_unlocks", () => {
const b = newBaba(emptyLevel(4, 5));
write(b, [
["BABA", 0, 0],
["IS", 1, 0],
["NOT", 2, 0],
["BABA", 3, 0],
["BABA", 0, 2],
["IS", 1, 2],
["BABA", 2, 2],
["BABA", 0, 4],
["IS", 1, 4],
["ROCK", 2, 4],
]);
expect(b.rules()).toEqual(["BABA IS BABA", "BABA IS NOT BABA", "BABA IS ROCK", "TEXT IS PUSH"]);
expect(b.activeRules()).toContain("BABA IS ROCK");
expect(b.activeRules()).not.toContain("BABA IS BABA");
});
test("test_07_text_is_not_push", () => {
const b = newBaba(emptyLevel(4, 3));
const rockText = b.place("text:ROCK", 0, 2);
const rock = b.place("rock", 3, 2);
expect(b.activeRules()).toEqual(["TEXT IS PUSH"]);
expect(b.hasProperty(rockText, "PUSH")).toBe(true);
expect(b.hasProperty(rock, "PUSH")).toBe(false);
const textText = b.place("text:TEXT", 0, 0);
write(b, [
["IS", 1, 0],
["NOT", 2, 0],
["PUSH", 3, 0],
]);
expect(b.rules()).toEqual(["TEXT IS NOT PUSH", "TEXT IS PUSH"]);
expect(b.activeRules()).toEqual(["TEXT IS NOT PUSH"]);
expect(b.hasProperty(rockText, "PUSH")).toBe(false);
expect(b.hasProperty(textText, "PUSH")).toBe(false);
});
test("test_07_leading_not_breaks_sentence", () => {
const b = newBaba(emptyLevel(4, 2));
write(b, [
["NOT", 0, 0],
["BABA", 1, 0],
["IS", 2, 0],
["YOU", 3, 0],
]);
const baba = b.place("baba", 0, 1);
expect(b.rules()).toEqual(["TEXT IS PUSH"]);
expect(b.activeRules()).toEqual(["TEXT IS PUSH"]);
expect(b.hasProperty(baba, "YOU")).toBe(false);
});