// Module 9: The turn pipeline. 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_09_lava_saved_by_reparse", () => { const b = newBaba(emptyLevel(6, 5)); write(b, [ ["BABA", 0, 0], ["IS", 1, 0], ["YOU", 2, 0], ]); write(b, [ ["LAVA", 0, 1], ["IS", 1, 1], ["HOT", 2, 1], ]); write(b, [ ["BABA", 4, 2], ["IS", 4, 3], ]); const meltText = b.place("text:MELT", 4, 4); const lava = b.place("lava", 4, 4); const baba = b.place("baba", 3, 4); expect(b.hasProperty(baba, "MELT")).toBe(true); b.step("right"); expect(b.posOf(baba)).toEqual([4, 4]); expect(b.posOf(meltText)).toEqual([5, 4]); // Inferred from the order of operations, not stated by a source: Baba survives. expect(b.posOf(lava)).toEqual([4, 4]); expect(b.kindsAt(4, 4)).toEqual(["baba", "lava"]); expect(b.rules()).not.toContain("BABA IS MELT"); expect(b.lost()).toBe(false); }); test("test_09_mid_turn_conversion", () => { const b = newBaba(emptyLevel(5, 5)); write(b, [ ["BABA", 0, 0], ["IS", 1, 0], ["YOU", 2, 0], ]); write(b, [ ["BABA", 0, 2], ["IS", 1, 2], ]); const rockText = b.place("text:ROCK", 2, 3); const pusher = b.place("baba", 2, 4); const other = b.place("baba", 4, 4); b.step("up"); expect(b.posOf(rockText)).toEqual([2, 2]); expect(b.rules()).toContain("BABA IS ROCK"); expect(b.posOf(pusher)).toEqual([2, 3]); expect(b.posOf(other)).toEqual([4, 3]); expect(b.kindsAt(2, 3)).toEqual(["rock"]); expect(b.kindsAt(4, 3)).toEqual(["rock"]); expect(b.kindsAt(2, 4)).toEqual([]); expect(b.kindsAt(4, 4)).toEqual([]); }); test("test_09_identity_lock_blocks_conversion", () => { const b = newBaba(emptyLevel(5, 5)); write(b, [ ["BABA", 0, 0], ["IS", 1, 0], ["YOU", 2, 0], ]); write(b, [ ["BABA", 0, 1], ["IS", 1, 1], ["BABA", 2, 1], ]); write(b, [ ["BABA", 0, 2], ["IS", 1, 2], ["ROCK", 2, 2], ]); write(b, [ ["KEY", 0, 3], ["IS", 1, 3], ["ROCK", 2, 3], ]); const baba = b.place("baba", 0, 4); const key = b.place("key", 3, 4); b.step("right"); expect(b.posOf(baba)).toEqual([1, 4]); expect(b.kindsAt(1, 4)).toEqual(["baba"]); expect(b.kindsAt(3, 4)).toEqual(["rock"]); expect(b.posOf(key)).toEqual([3, 4]); const active = b.activeRules(); expect(active).not.toContain("BABA IS ROCK"); expect(active).toContain("BABA IS BABA"); expect(active).toContain("KEY IS ROCK"); }); test("test_09_self_negation_deletes", () => { const b = newBaba(emptyLevel(5, 5)); write(b, [ ["BABA", 0, 0], ["IS", 1, 0], ["YOU", 2, 0], ]); write(b, [ ["KEY", 0, 1], ["IS", 1, 1], ["NOT", 2, 1], ["KEY", 3, 1], ]); write(b, [ ["KEY", 0, 2], ["IS", 1, 2], ["ROCK", 2, 2], ]); const baba = b.place("baba", 0, 4); const key = b.place("key", 3, 4); expect(b.posOf(key)).toEqual([3, 4]); b.step("right"); expect(b.posOf(key)).toBeNull(); expect(b.kindsAt(3, 4)).toEqual(["rock"]); expect(b.posOf(baba)).toEqual([1, 4]); }); test("test_09_float_separates_hazards", () => { const b = newBaba(emptyLevel(4, 6)); write(b, [ ["BABA", 0, 0], ["IS", 1, 0], ["YOU", 2, 0], ]); write(b, [ ["KEY", 0, 1], ["IS", 1, 1], ["YOU", 2, 1], ]); write(b, [ ["SKULL", 0, 2], ["IS", 1, 2], ["DEFEAT", 2, 2], ]); write(b, [ ["BABA", 0, 3], ["IS", 1, 3], ["FLOAT", 2, 3], ]); const baba = b.place("baba", 0, 4); b.place("skull", 1, 4); const key = b.place("key", 0, 5); b.place("skull", 1, 5); b.step("right"); expect(b.posOf(baba)).toEqual([1, 4]); expect(b.kindsAt(1, 4)).toEqual(["baba", "skull"]); expect(b.posOf(key)).toBeNull(); expect(b.kindsAt(1, 5)).toEqual(["skull"]); expect(b.lost()).toBe(false); }); test("test_09_sink_destroys_both", () => { const b = newBaba(emptyLevel(5, 5)); write(b, [ ["BABA", 0, 0], ["IS", 1, 0], ["YOU", 2, 0], ]); write(b, [ ["ROCK", 0, 1], ["IS", 1, 1], ["PUSH", 2, 1], ]); write(b, [ ["WATER", 0, 2], ["IS", 1, 2], ["SINK", 2, 2], ]); const baba = b.place("baba", 0, 4); const rock = b.place("rock", 1, 4); const water = b.place("water", 2, 4); b.step("right"); expect(b.posOf(rock)).toBeNull(); expect(b.posOf(water)).toBeNull(); expect(b.kindsAt(2, 4)).toEqual([]); expect(b.posOf(baba)).toEqual([1, 4]); }); test("test_09_you_and_win", () => { const b = newBaba(emptyLevel(4, 4)); write(b, [ ["BABA", 0, 0], ["IS", 1, 0], ["YOU", 2, 0], ]); write(b, [ ["BABA", 0, 1], ["IS", 1, 1], ["WIN", 2, 1], ]); b.place("baba", 0, 3); expect(b.won()).toBe(false); b.step("right"); expect(b.won()).toBe(true); expect(b.lost()).toBe(false); }); test("test_09_defeat_beats_win", () => { const b = newBaba(emptyLevel(5, 4)); write(b, [ ["BABA", 0, 0], ["IS", 1, 0], ["YOU", 2, 0], ]); write(b, [ ["FLAG", 0, 1], ["IS", 1, 1], ["WIN", 2, 1], ["AND", 3, 1], ["DEFEAT", 4, 1], ]); const baba = b.place("baba", 0, 3); const flag = b.place("flag", 1, 3); b.step("right"); expect(b.posOf(baba)).toBeNull(); expect(b.won()).toBe(false); expect(b.lost()).toBe(true); expect(b.posOf(flag)).toEqual([1, 3]); });