simvx.graphics.playtest

Automated game playtest harness — run games headlessly with step-by-step screenshots.

Combines DemoRunner step execution with App.run_headless() to capture screenshots and scene state at each step boundary. Produces structured reports that Claude can read (JSON + PNGs + scene tree text) to diagnose bugs automatically.

Usage: from simvx.graphics.playtest import GamePlaytestHarness from simvx.core.scripted_demo import Click, Wait, Assert

harness = GamePlaytestHarness(game_root, width=1280, height=720)
report = harness.run([
    Wait(0.5),
    Click(400, 300),
    Assert(lambda g: g.clicked, "Button should be clicked"),
])
print(report.summary())

Module Contents

Classes

StepSnapshot

Captured state at a single playtest step boundary.

PlaytestReport

Full results of a playtest run.

GamePlaytestHarness

Run a game headlessly with DemoRunner steps, capturing screenshots and state.

Data

API

simvx.graphics.playtest.__all__

[‘GamePlaytestHarness’, ‘PlaytestReport’, ‘StepSnapshot’]

class simvx.graphics.playtest.StepSnapshot

Captured state at a single playtest step boundary.

step_index: int

None

step_type: str

None

step_desc: str

None

frame_index: int

None

time_seconds: float

None

screenshot_path: pathlib.Path | None

None

scene_tree: str = <Multiline-String>
ui_tree: str = <Multiline-String>
assertion_result: str | None

None

class simvx.graphics.playtest.PlaytestReport

Full results of a playtest run.

game_path: str

None

timestamp: str

None

total_frames: int

None

total_steps: int

None

completed: bool

None

passed: bool

None

failures: list[str]

None

steps: list[simvx.graphics.playtest.StepSnapshot]

None

duration_seconds: float

None

node_count: int

None

output_dir: pathlib.Path

None

save() pathlib.Path

Write report.json + screenshots/ + state/ to output_dir.

summary() str

One-paragraph text summary for Claude.

class simvx.graphics.playtest.GamePlaytestHarness(game_root: simvx.core.Node, *, width: int = 1280, height: int = 720, output_dir: str | pathlib.Path | None = None, max_frames: int = 30000, speed: float = 50.0)

Run a game headlessly with DemoRunner steps, capturing screenshots and state.

Args: game_root: The game’s root node. width: Window width. height: Window height. output_dir: Where to save screenshots and reports. Defaults to /tmp/playtest/. max_frames: Safety limit on total frames. speed: DemoRunner speed multiplier.

Initialization

run(steps: list, *, capture_every_n: int = 0, capture_on_assert: bool = True, capture_on_click: bool = True) simvx.graphics.playtest.PlaytestReport

Execute playtest steps and return a report with screenshots and state.

Args: steps: List of DemoRunner step dataclasses. capture_every_n: Also capture every N frames (0 = disabled). capture_on_assert: Screenshot before each Assert step. capture_on_click: Screenshot after each Click step.