diff --git a/README.md b/README.md index eb68376..23aa2e8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A web platform for comparing fun, cool-looking AI-generated games. Play each ver | 🔮 Marble Madness | Roll a glowing marble across neon platforms suspended in space. Master physics, dodge hazards, and race the clock. | 6 | | 🧭 3D Maze | Navigate a procedurally generated labyrinth in first-person 3D. Find the glowing exit before time runs out. | 8 | | ⛳ Mini Golf 3D | Aim, pull back, and sink impossible banks across neon-drenched 3D mini golf courses, including a nine-hole solar eclipse odyssey. | 5 | -| 💎 Tile Matching | Swap, match, and chain your way to the top. Line up three or more gems to trigger cascading combos and dazzling special tiles. | 7 | +| 💎 Tile Matching | Swap, match, and chain your way to the top. Line up three or more gems to trigger cascading combos and dazzling special tiles. | 8 | | 👾 Space Invaders | Defend Earth from waves of descending alien invaders. Blast through formations, grab power-ups, and survive the onslaught. | 5 | | 🔢 Sudoku | Solve a glowing logic grid under pressure with notes, streaks, and satisfying neon feedback for every sharp deduction. | 2 | | 🏎️ Neon Horizon Racer | Drift through a neon synthwave highway, dodge traffic, chain near misses, and chase checkpoints into the glowing horizon. | 6 | @@ -41,7 +41,7 @@ A web platform for comparing fun, cool-looking AI-generated games. Play each ver | OpenAI GPT 5.4 | 1,763 lines | 1,875 lines | 2,278 lines | 1,482 lines | 1,419 lines | 779 lines | 1,960 lines | 2,279 lines | 1,837 lines | 2,401 lines | 2,852 lines | 3,116 lines | 2,135 lines | 3,371 lines | N/A | 3,093 lines | N/A | N/A | N/A | | OpenAI GPT 5.5 | N/A | N/A | N/A | N/A | N/A | 2,031 lines | 2,369 lines | N/A | N/A | N/A | 2,413 lines | 1,643 lines | 2,058 lines | N/A | 1,398 lines | 2,155 lines | N/A | N/A | N/A | | OpenAI GPT 5.6 Terra | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 930 lines | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 147 lines | N/A | N/A | -| OpenAI GPT 5.6 Luna | N/A | N/A | 1,444 lines | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 289 lines | N/A | N/A | +| OpenAI GPT 5.6 Luna | N/A | N/A | 1,444 lines | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 893 lines | N/A | N/A | N/A | N/A | N/A | 289 lines | N/A | N/A | | OpenAI GPT 5.6 Sol | N/A | N/A | N/A | 1,736 lines | N/A | N/A | N/A | 304 lines | 2,421 lines | 2,134 lines | 3,263 lines | N/A | N/A | N/A | 278 lines | 1,542 lines | 2,447 lines | 715 lines | N/A | | OpenAI GPT 5.4 Mini | 1,309 lines | 1,223 lines | 1,267 lines | 1,389 lines | 570 lines | 541 lines | 1,309 lines | 1,687 lines | 1,486 lines | 1,345 lines | 2,093 lines | 1,035 lines | N/A | N/A | 2,734 lines | 3,965 lines | N/A | N/A | N/A | | Google Gemini 3.1 Pro | 60 lines | 184 lines | 394 lines | 230 lines | 63 lines | 58 lines | 44 lines | 46 lines | 65 lines | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | diff --git a/games-metadata.json b/games-metadata.json index 04c485a..11eeb7d 100644 --- a/games-metadata.json +++ b/games-metadata.json @@ -4746,6 +4746,19 @@ ] } ] + }, + { + "model": "GPT 5.6 Luna", + "modelId": "gpt-5-6-luna", + "date": "2026-07-31", + "linesOfCode": 893, + "path": "/games/tile-matching/gpt-5-6-luna/index.html", + "features": [ + "sound", + "music", + "particles", + "powerups" + ] } ] }, diff --git a/public/games/tile-matching/gpt-5-6-luna/index.html b/public/games/tile-matching/gpt-5-6-luna/index.html new file mode 100644 index 0000000..f620ada --- /dev/null +++ b/public/games/tile-matching/gpt-5-6-luna/index.html @@ -0,0 +1,892 @@ + + + + + +LUNAR ARRAY // GPT 5.6 Luna + + + +
+ + + + +
+ +
+
+
OpenAI // GPT 5.6 Luna // Observatory Deck
+

LUNAR ARRAYMatch the light. Bend the gravity.

+

A tactile match-three instrument for building constellations, breaking moon-dust, and spending one perfectly timed Lunar Pulse.

+
+ + +
+
+
Drag or click two neighbors · Arrows + Enter move · H hint · S shuffle · P pause · M mute
+
+
+ +
+
+
The array is holding
+

Pause / telemetry frozen

+

Your board is waiting exactly where you left it.

+
+
+
+ +
+
+
Orbit solution confirmed
+

Perilune complete

+
★☆☆
+
Score0
Best chain0
Pulse uses0
Stars1
+
+
+
+ +
+
+
Telemetry lost
+

The orbit slipped

+

The array still has a route. Try a different line.

+
+
+
+ +
+
+
Orbit run complete
+

The array goes dark

+
Score0
Best chain0
Pulse uses0
Session best0
+
+
+
+
+ + + + diff --git a/tests/model-identity.spec.ts b/tests/model-identity.spec.ts index 991e325..3aaaf52 100644 --- a/tests/model-identity.spec.ts +++ b/tests/model-identity.spec.ts @@ -34,7 +34,7 @@ test.describe("Model identity system", { await page.goto("/games/tile-matching"); const cards = page.locator('[data-model-id][data-model-color]'); - await expect(cards).toHaveCount(7); + await expect(cards).toHaveCount(8); await expect(cards.locator("h3")).toHaveText([ "Claude Opus 4.6", "Claude Opus 5", @@ -43,6 +43,7 @@ test.describe("Model identity system", { "OpenAI GPT 5.4 Mini", "OpenAI GPT 5.5", "OpenAI GPT 5.6 Sol", + "OpenAI GPT 5.6 Luna", ]); await expect(page.locator('[data-model-id="gpt-5-4"]')).toHaveAttribute( diff --git a/tests/tile-matching-luna.spec.ts b/tests/tile-matching-luna.spec.ts new file mode 100644 index 0000000..8715660 --- /dev/null +++ b/tests/tile-matching-luna.spec.ts @@ -0,0 +1,416 @@ +import { expect, test, type Page } from "@playwright/test"; + +type Cell = { r: number; c: number }; +type Tile = { type: number; special: number; id: number } | null; +type Snapshot = { + grid: number; + board: Tile[][]; + mask: number[][]; + blockers: number[][]; + score: number; + moves: number; + timeLeft: number; + level: number; + levelName: string; + mode: string; + objective: { kind: string; target: number; progress: number; complete: boolean; scoreTarget?: number; pulseTarget?: number; collected: number[] }; + chain: number; + bestChain: number; + blockerCleared: number; + pulse: number; + pulseUses: number; + pulseArmed: boolean; + pulseDirection: string; + pendingGravity: string | null; + gravity: string; + cursor: Cell; + selection: Cell | null; + hasMove: boolean; + matchCount: number; + active: boolean; + locked: boolean; + paused: boolean; + screen: string; + layout: { width: number; height: number; boardX: number; boardY: number; boardSize: number; cell: number; mode: string }; +}; + +type LunaApi = { + constants: { GRID: number; BEAM_ROW: number; BEAM_COL: number; BURST: number; CORE: number; FUSION: Record }; + snapshot(): Snapshot; + setSeed(seed: number): boolean; + startMission(index: number): boolean; + startEndless(): boolean; + setSpeed(value: number): void; + setBoard(board: number[][], specials?: Array): boolean; + setMask(mask: number[][]): boolean; + setBlockers(entries: Array): boolean; + setMoves(value: number): void; + setTime(value: number): void; + setScore(value: number): void; + setPulse(value: number): void; + setObjective(spec: { kind: string; target?: number; colors?: number[]; perColor?: number; scoreTarget?: number; pulseTarget?: number }): void; + swap(r1: number, c1: number, r2: number, c2: number): Promise; + detonate(r: number, c: number): Promise; + resolve(): Promise; + collapse(): Snapshot; + shuffle(manual?: boolean): boolean; + hint(force?: boolean): { a: Cell; b: Cell; score: number; reason: string } | null; + bestMove(): { a: Cell; b: Cell; score: number; reason: string } | null; + pulse(direction: string): Promise; + finishTurn(): Promise; + advance(ms: number): Snapshot; + audioEvents(): string[]; + clearAudioEvents(): void; + cellRect(r: number, c: number): { x: number; y: number; w: number; h: number }; +}; + +declare global { + interface Window { __lunarArrayTest: LunaApi; __lunaAudioStarts?: number } +} + +const GAME = "/games/tile-matching/gpt-5-6-luna/index.html"; +const GRID = 8; +const stableBoard = Array.from({ length: GRID }, (_, r) => + Array.from({ length: GRID }, (_, c) => (r + c) % 6), +); +const clone = (board: number[][]) => board.map((row) => row.slice()); + +function fourMatchBoard() { + const board = clone(stableBoard); + board[3] = [1, 1, 2, 1, 4, 5, 0, 2]; + board[2][2] = 1; + return board; +} + +function lMatchBoard() { + const board = clone(stableBoard); + board[4][4] = 5; + board[4][3] = 1; + board[4][5] = 1; + board[3][4] = 1; + board[2][4] = 1; + board[5][4] = 1; + return board; +} + +function fiveMatchBoard() { + const board = clone(stableBoard); + board[6] = [2, 2, 5, 2, 2, 4, 5, 0]; + board[5][2] = 2; + return board; +} + +async function openGame(page: Page, viewport = { width: 1280, height: 720 }) { + await page.setViewportSize(viewport); + await page.goto(`${GAME}?test=1`); + await expect(page).toHaveTitle("LUNAR ARRAY // GPT 5.6 Luna"); + await expect(page.locator("#titleScreen")).toHaveClass(/active/); + await expect.poll(() => page.evaluate(() => Boolean(window.__lunarArrayTest))).toBe(true); +} + +const snapshot = (page: Page) => page.evaluate(() => window.__lunarArrayTest.snapshot()); + +async function startMission(page: Page, index = 0) { + await page.evaluate((mission) => { + window.__lunarArrayTest.setSeed(7); + window.__lunarArrayTest.setSpeed(50); + window.__lunarArrayTest.startMission(mission); + }, index); +} + +async function settle(page: Page) { + await expect.poll(async () => (await snapshot(page)).locked).toBe(false); +} + +test.describe("GPT 5.6 Luna Tile Matching — Lunar Array", { + tag: ["@spec:tile-matching-luna", "@game:tile-matching/gpt-5-6-luna"], +}, () => { + test("boots an 8x8 legal board with procedural audio", async ({ page }) => { + await page.addInitScript(() => { + window.__lunaAudioStarts = 0; + if (!("OscillatorNode" in window)) return; + const original = window.OscillatorNode.prototype.start; + window.OscillatorNode.prototype.start = function (...args) { + window.__lunaAudioStarts = (window.__lunaAudioStarts ?? 0) + 1; + return original.apply(this, args); + }; + }); + await openGame(page); + await startMission(page); + const state = await snapshot(page); + expect(state.grid).toBe(8); + expect(state.levelName).toBe("Perilune"); + expect(state.moves).toBe(22); + expect(state.objective.kind).toBe("score"); + expect(state.objective.target).toBe(3000); + expect(state.matchCount).toBe(0); + expect(state.hasMove).toBe(true); + expect(state.board.flat().filter(Boolean)).toHaveLength(64); + await expect.poll(() => page.evaluate(() => window.__lunaAudioStarts ?? 0)).toBeGreaterThan(2); + await expect(page.locator("#missionName")).toHaveText("Perilune"); + await expect(page.locator("#hud")).not.toHaveAttribute("aria-live"); + }); + + test("charges only valid swaps and cascades into a full board", async ({ page }) => { + await openGame(page); + await startMission(page); + await page.evaluate((board) => { + window.__lunarArrayTest.setBoard(board); + window.__lunarArrayTest.setMoves(12); + window.__lunarArrayTest.setObjective({ kind: "score", target: 999999 }); + }, stableBoard); + expect(await page.evaluate(() => window.__lunarArrayTest.swap(0, 0, 0, 1))).toBe(false); + expect((await snapshot(page)).moves).toBe(12); + await page.evaluate((board) => window.__lunarArrayTest.setBoard(board), fourMatchBoard()); + expect(await page.evaluate(() => window.__lunarArrayTest.swap(2, 2, 3, 2))).toBe(true); + await settle(page); + const state = await snapshot(page); + expect(state.moves).toBe(11); + expect(state.score).toBeGreaterThan(0); + expect(state.board.flat().filter(Boolean).length).toBeGreaterThanOrEqual(60); + expect(state.hasMove).toBe(true); + }); + + test("forges beam, moonburst, and eclipse core specials", async ({ page }) => { + await openGame(page); + await startMission(page); + const constants = await page.evaluate(() => window.__lunarArrayTest.constants); + const cases = [ + { board: fourMatchBoard(), swap: [2, 2, 3, 2] as const, specials: [constants.BEAM_ROW, constants.BEAM_COL] }, + { board: lMatchBoard(), swap: [5, 4, 4, 4] as const, specials: [constants.BURST] }, + { board: fiveMatchBoard(), swap: [5, 2, 6, 2] as const, specials: [constants.CORE] }, + ]; + for (const entry of cases) { + await page.evaluate((board) => { + window.__lunarArrayTest.setSeed(11); + window.__lunarArrayTest.setBoard(board); + window.__lunarArrayTest.setMoves(20); + window.__lunarArrayTest.setObjective({ kind: "score", target: 999999 }); + }, entry.board); + expect(await page.evaluate(([r1, c1, r2, c2]) => window.__lunarArrayTest.swap(r1, c1, r2, c2), entry.swap)).toBe(true); + await settle(page); + const specials = (await snapshot(page)).board.flat().filter((tile) => tile && entry.specials.includes(tile.special)); + expect(specials.length).toBeGreaterThan(0); + } + }); + + test("runs each special fusion and records its audio cue", async ({ page }) => { + await openGame(page); + await startMission(page); + const constants = await page.evaluate(() => window.__lunarArrayTest.constants); + const cases = [ + [constants.BEAM_ROW, constants.BEAM_COL, "fusion:beam_beam"], + [constants.BEAM_ROW, constants.BURST, "fusion:beam_burst"], + [constants.BURST, constants.BURST, "fusion:burst_burst"], + [constants.CORE, constants.BEAM_ROW, "fusion:core_beam"], + [constants.CORE, constants.BURST, "fusion:core_burst"], + [constants.CORE, constants.CORE, "fusion:core_core"], + ] as const; + for (const [first, second, event] of cases) { + await page.evaluate(([board, a, b]) => { + window.__lunarArrayTest.setBoard(board, [{ r: 3, c: 3, special: a }, { r: 3, c: 4, special: b }]); + window.__lunarArrayTest.setMoves(20); + window.__lunarArrayTest.setObjective({ kind: "score", target: 999999 }); + window.__lunarArrayTest.clearAudioEvents(); + }, [stableBoard, first, second]); + expect(await page.evaluate(() => window.__lunarArrayTest.swap(3, 3, 3, 4))).toBe(true); + await settle(page); + expect(await page.evaluate(() => window.__lunarArrayTest.audioEvents())).toContain(event); + expect((await snapshot(page)).score).toBeGreaterThan(0); + } + }); + + test("keeps Eclipse Gate's blocker target attainable", async ({ page }) => { + await openGame(page); + await startMission(page, 6); + const state = await snapshot(page); + expect(state.objective.target).toBe(4); + expect(state.blockers.flat().filter((layers) => layers > 0)).toHaveLength(4); + }); + + test("uses Lunar Pulse to chip edge blockers and redirect the next refill", async ({ page }) => { + await openGame(page); + await startMission(page, 4); + await page.evaluate((board) => { + window.__lunarArrayTest.setBoard(board); + window.__lunarArrayTest.setBlockers([{ r: 0, c: 0, layers: 2 }, { r: 0, c: 1, layers: 1 }]); + window.__lunarArrayTest.setMoves(12); + window.__lunarArrayTest.setPulse(100); + window.__lunarArrayTest.setObjective({ kind: "blockers", target: 99, pulseTarget: 1 }); + }, fourMatchBoard()); + const before = await snapshot(page); + expect(await page.evaluate(() => window.__lunarArrayTest.pulse("up"))).toBe(true); + const after = await snapshot(page); + expect(after.moves).toBe(before.moves); + expect(after.pulse).toBe(0); + expect(after.pulseUses).toBe(1); + expect(after.gravity).toBe("up"); + expect(after.blockers[0][0]).toBe(1); + expect(after.blockers[0][1]).toBe(0); + await page.evaluate(() => window.__lunarArrayTest.collapse()); + expect((await snapshot(page)).gravity).toBe("down"); + }); + + test("completes a mission when the final condition is supplied by Pulse", async ({ page }) => { + await openGame(page); + await startMission(page, 4); + await page.evaluate((board) => { + window.__lunarArrayTest.setBoard(board); + window.__lunarArrayTest.setBlockers([{ r: 0, c: 0, layers: 1 }]); + window.__lunarArrayTest.setPulse(100); + window.__lunarArrayTest.setObjective({ kind: "blockers", target: 1, pulseTarget: 1 }); + }, stableBoard); + expect(await page.evaluate(() => window.__lunarArrayTest.pulse("up"))).toBe(true); + await expect.poll(async () => (await snapshot(page)).screen).toBe("complete"); + expect((await snapshot(page)).active).toBe(false); + }); + + test("keeps hints free, charges manual shuffles, and recovers dead boards", async ({ page }) => { + await openGame(page); + await startMission(page); + await page.evaluate((board) => { + window.__lunarArrayTest.setBoard(board); + window.__lunarArrayTest.setMoves(12); + window.__lunarArrayTest.setObjective({ kind: "score", target: 999999 }); + }, fourMatchBoard()); + expect(await page.evaluate(() => window.__lunarArrayTest.hint(true))).not.toBeNull(); + expect((await snapshot(page)).moves).toBe(12); + expect(await page.evaluate(() => window.__lunarArrayTest.shuffle(true))).toBe(true); + const state = await snapshot(page); + expect(state.moves).toBe(11); + expect(state.matchCount).toBe(0); + expect(state.hasMove).toBe(true); + }); + + test("ends a campaign when the final move is spent on a shuffle", async ({ page }) => { + await openGame(page); + await startMission(page); + await page.evaluate((board) => { + window.__lunarArrayTest.setBoard(board); + window.__lunarArrayTest.setMoves(1); + window.__lunarArrayTest.setObjective({ kind: "score", target: 999999 }); + }, fourMatchBoard()); + expect(await page.evaluate(() => window.__lunarArrayTest.shuffle(true))).toBe(true); + await expect.poll(async () => (await snapshot(page)).screen).toBe("failed"); + const state = await snapshot(page); + expect(state.moves).toBe(0); + expect(state.active).toBe(false); + }); + + test("pauses Orbit Run time and ends at 120 seconds", async ({ page }) => { + await openGame(page); + await page.evaluate(() => { + window.__lunarArrayTest.setSpeed(50); + window.__lunarArrayTest.startEndless(); + window.__lunarArrayTest.setTime(120); + }); + expect((await snapshot(page)).mode).toBe("endless"); + await page.evaluate(() => window.__lunarArrayTest.advance(3000)); + expect((await snapshot(page)).timeLeft).toBeCloseTo(117, 0); + await page.keyboard.press("p"); + expect((await snapshot(page)).paused).toBe(true); + await page.evaluate(() => window.__lunarArrayTest.advance(5000)); + expect((await snapshot(page)).timeLeft).toBeCloseTo(117, 0); + await page.keyboard.press("p"); + await page.evaluate(() => window.__lunarArrayTest.advance(117000)); + await expect.poll(async () => (await snapshot(page)).screen).toBe("endless"); + expect((await snapshot(page)).active).toBe(false); + }); + + test("supports pointer swapping and keeps the active layout inside 1280x720", async ({ page }) => { + await openGame(page); + await startMission(page); + await page.evaluate((board) => { + window.__lunarArrayTest.setBoard(board); + window.__lunarArrayTest.setMoves(10); + window.__lunarArrayTest.setObjective({ kind: "score", target: 999999 }); + }, fourMatchBoard()); + const from = await page.evaluate(() => window.__lunarArrayTest.cellRect(2, 2)); + const to = await page.evaluate(() => window.__lunarArrayTest.cellRect(3, 2)); + await page.mouse.click(from.x + from.w / 2, from.y + from.h / 2); + await page.mouse.click(to.x + to.w / 2, to.y + to.h / 2); + await settle(page); + expect((await snapshot(page)).score).toBeGreaterThan(0); + for (const selector of ["#board", "#missionName", "#objectiveValue", "#movesValue", "#scoreValue", "#pulseButton"]) { + const box = await page.locator(selector).boundingBox(); + expect(box, selector).not.toBeNull(); + expect(box!.x + box!.width).toBeLessThanOrEqual(1280); + expect(box!.y + box!.height).toBeLessThanOrEqual(720); + } + }); + + test("supports keyboard cursor swaps and touch drags", async ({ page }) => { + await openGame(page); + await startMission(page); + await page.evaluate((board) => { + window.__lunarArrayTest.setBoard(board); + window.__lunarArrayTest.setMoves(10); + window.__lunarArrayTest.setObjective({ kind: "score", target: 999999 }); + }, fourMatchBoard()); + await page.locator("#board").focus(); + const best = await page.evaluate(() => window.__lunarArrayTest.bestMove()); + expect(best).not.toBeNull(); + if (!best) return; + const moveCursor = async (target: Cell) => { + let cursor = (await snapshot(page)).cursor; + while (cursor.r > target.r) { await page.keyboard.press("ArrowUp"); cursor = (await snapshot(page)).cursor; } + while (cursor.r < target.r) { await page.keyboard.press("ArrowDown"); cursor = (await snapshot(page)).cursor; } + while (cursor.c > target.c) { await page.keyboard.press("ArrowLeft"); cursor = (await snapshot(page)).cursor; } + while (cursor.c < target.c) { await page.keyboard.press("ArrowRight"); cursor = (await snapshot(page)).cursor; } + }; + await moveCursor(best.a); + await page.keyboard.press("Enter"); + expect((await snapshot(page)).selection).toEqual(best.a); + await moveCursor(best.b); + expect((await snapshot(page)).selection).toEqual(best.a); + await page.keyboard.press("Enter"); + await settle(page); + expect((await snapshot(page)).score).toBeGreaterThan(0); + + await page.evaluate((board) => window.__lunarArrayTest.setBoard(board), fourMatchBoard()); + const [from, to] = await page.evaluate(() => [ + window.__lunarArrayTest.cellRect(2, 2), + window.__lunarArrayTest.cellRect(3, 2), + ]); + await page.evaluate(([start, end]) => { + const canvas = document.querySelector("#board"); + if (!canvas) throw new Error("board canvas missing"); + const emit = (type: string, point: { x: number; y: number; w: number; h: number }) => canvas.dispatchEvent(new PointerEvent(type, { + bubbles: true, + clientX: point.x + point.w / 2, + clientY: point.y + point.h / 2, + pointerId: 1, + pointerType: "touch", + isPrimary: true, + })); + emit("pointerdown", start); + emit("pointermove", end); + emit("pointerup", end); + }, [from, to]); + await settle(page); + expect((await snapshot(page)).score).toBeGreaterThan(0); + }); + + test("stacks the HUD rails cleanly on a phone viewport", async ({ page }) => { + await openGame(page, { width: 390, height: 844 }); + await startMission(page); + const leftRail = await page.locator("#leftRail").boundingBox(); + const rightRail = await page.locator("#rightRail").boundingBox(); + const board = await page.locator("#board").boundingBox(); + expect(leftRail).not.toBeNull(); + expect(rightRail).not.toBeNull(); + expect(board).not.toBeNull(); + if (leftRail && rightRail && board) { + expect(leftRail.x + leftRail.width).toBeLessThan(rightRail.x); + expect(board.x).toBeGreaterThanOrEqual(0); + expect(board.x + board.width).toBeLessThanOrEqual(390); + expect(board.y + board.height).toBeLessThanOrEqual(844); + } + }); + + test("does not expose the deterministic API outside test mode", async ({ page }) => { + await page.goto(GAME); + await expect(page.locator("#titleScreen")).toHaveClass(/active/); + expect(await page.evaluate(() => "__lunarArrayTest" in window)).toBe(false); + }); +});