Chessistics/tools/automation
Samuel Bouchet 97bca7d7df Add Undo (Ctrl+Z) backed by the WorldSave checkpoint mechanism
GameSim snapshots the state before each undoable command
(PlacePiece / RemovePiece / MovePiece) into a bounded LinkedList stack
(max 32). Undo() pops the last checkpoint and emits StateRestoredEvent,
reusing the presentation rebuild path already wired for QuickLoad.

Ctrl+Z in Main triggers the engine method; the harness exposes undo()
for tests. QuickLoad clears the stack (fresh timeline). Seven unit tests
cover empty stack, place/remove/move undo, reverse-order multiple undos,
rejected commands not checkpointing, and post-simulation rewind.
2026-04-17 22:14:06 +02:00
..
harness.py Add Undo (Ctrl+Z) backed by the WorldSave checkpoint mechanism 2026-04-17 22:14:06 +02:00
README.md Add file-IPC automation harness for autonomous game testing 2026-04-16 22:34:56 +02:00
run_game.py Add file-IPC automation harness for autonomous game testing 2026-04-16 22:34:56 +02:00
smoke.py Add file-IPC automation harness for autonomous game testing 2026-04-16 22:34:56 +02:00
test_quicksave.py Add QuickSave/QuickLoad with full state restore and visual rebuild 2026-04-17 22:10:06 +02:00
test_undo.py Add Undo (Ctrl+Z) backed by the WorldSave checkpoint mechanism 2026-04-17 22:14:06 +02:00

Chessistics automation harness

Drive a running Chessistics build via file-based IPC, so an AI agent (or a scripted test) can take screenshots, inject inputs, and read game state without a human in the loop.

How it works

  • Launch Godot with --automation=<dir>. The game activates an AutomationHarness node that polls <dir>/inbox/ each frame.
  • Send commands as JSON files; the game writes results to <dir>/outbox/ and screenshots to <dir>/screens/.
  • A handshake file <dir>/ready.json is written on startup.

Without the flag, the harness is not instantiated — zero runtime overhead and zero behavior change for normal play.

Quick start

# From repo root
python tools/automation/smoke.py     # end-to-end smoke test
python tools/automation/run_game.py  # interactive REPL

Requirements:

  • Python 3.10+ (stdlib only)
  • Godot 4.6 mono build at C:\Apps\godot\Godot_v4.6.2-stable_mono_win64_console.exe (override with Harness(godot_exe=...) or edit the default in harness.py).
  • A compiled build: run dotnet build Chessistics.csproj first.

Python API

from tools.automation.harness import Harness

with Harness.launch() as h:
    h.load_mission("campaign_01", 0)
    state = h.state()                       # full snapshot as dict
    h.screenshot("before")                  # → .automation_runs/<ts>/screens/before.png
    h.place("Rook", (0, 0), (0, 3))         # place a piece
    h.step()                                # one simulation tick (auto-waits for animation)
    h.screenshot("after")
    h.set_speed(0.1); h.play()              # auto-run fast

Methods on Harness:

  • screenshot(name) -> Path
  • state() -> dict — full snapshot + animating flag
  • select(kind) — e.g. "Rook", "Knight"
  • place(kind, start, end, level=1) — returns {placed, pieceId, reason}
  • click_cell(col, row, button="left"|"right")
  • key(name)"Space" (play/pause), "Escape" (cancel)
  • play() / pause() / step() / wait_idle()
  • set_speed(interval_seconds) — auto-step interval
  • load_mission(campaign, missionIndex=0)
  • back_to_menu() / quit()

Every non-query command auto-waits for EventAnimator.IsAnimating == false before returning, so consecutive calls see a fully-settled state.

JSON protocol

Inbox: { "id": "<uuid>", "cmd": "...", "args": {...} } Outbox: { "id": "<uuid>", "ok": true, "result": {...} } or { "id": "<uuid>", "ok": false, "error": "..." }

See the cmd table in Scripts/Automation/CommandDispatcher.cs for the complete list.

Output locations

  • .automation_runs/<name>/ready.json — handshake
  • .automation_runs/<name>/inbox/, outbox/ — command queue
  • .automation_runs/<name>/screens/*.png — 1280x720 PNGs

.automation_runs/ is a good candidate for .gitignore (not added here automatically — add it manually if needed).

Troubleshooting

  • Godot exits before ready.json — build probably didn't compile, or the --path arg is wrong. Run dotnet build Chessistics.csproj and check stderr from the harness.
  • Screenshot is all black--headless was passed; the harness needs a real rendering context. Don't use --headless with automation.
  • Command times out — an animation may be stuck; check the Godot console log for errors.

Architecture notes

The harness sits behind a thin facade (AutomationFacade) that the dispatcher uses. It never reaches into engine internals — only calls existing public surfaces on GameSim, InputMapper, EventAnimator, ControlBar, PieceStockPanel. The black-box simulation separation stays intact.