Files
systemic-game-design-course/course/09-turn-pipeline/tests/ts/test_09.test.ts
T
Lucas WintherandClaude Opus 5.5 1f3359cfe2 feat: module 09 turn pipeline
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>
2026-10-29 04:28:17 +01:00

251 lines
5.6 KiB
TypeScript

// 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]);
});