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>
251 lines
5.6 KiB
TypeScript
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]);
|
|
});
|