TrajectView now draws an arrow at each endpoint (since pieces oscillate
between start and end, both directions are relevant) and runs a looped
width/alpha tween that breathes between 0.3 and 0.75 over ~2.2s. The
pulse makes stationary relays visually distinct from static board art
without competing with active cargo/particle animations.
OnMissionAdvanced now plays a three-track cinematic when the auto-advance
fires between missions:
- Every cell in the rebuilt board pops in with a scale+fade tween
(0.45s, back ease) so terrain expansion reads as "appearing".
- The camera smooth-pans + zooms back to the new board center over
0.7s, undoing any collision zoom that was active.
- A full-size "Mission N\nName" overlay fades in, holds for ~1.4s,
then fades out and frees itself.
The per-cell animation doubles as the expansion cue; a dedicated
new-cells-only tween can be layered later if needed. No new engine
surface — hooks onto the existing MissionStartedEvent path.
DetailPanel exposes the currently-shown piece id; Main's key handler
delegates Delete or Backspace to the same path as the Retirer button
when a piece is selected. Harness gains "delete" under key() so UI
tests can exercise the shortcut directly.
EventAnimator now emits CollisionOccurred at the end of the collision
phase, carrying the struck cell and victim/destroyer identity. Main pans
and zooms the camera onto the cell over 0.45s and shows a fading toast
("Pion détruit par Tour — retourné au stock", or "collision mutuelle"
for same-status ties). The toast fades out after 3s and leaves the
camera framing the collision so the player can inspect the aftermath
before resuming.
InputMapper tracks a mouse-down over a placed piece and promotes it to
drag mode once the cursor travels past an 8px threshold. Legal drop
cells (those where the piece's start→end vector still fits a legal
placement) are highlighted in green. Releasing on a legal cell emits a
RelocateRequested signal; Main feeds it to MovePieceCommand, which is
already undoable via the existing history stack.
Escape or releasing on an invalid cell cancels. The harness gains a
relocate() helper so UI tests can script drag-and-drop moves without
synthesizing motion events.
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.
BoardState.CaptureSave/RestoreFromSave deep-copy every mutable field
(grid, pieces, demands, transformers, buffers, stock, campaign progress)
into a WorldSave slot. GameSim.QuickSave/QuickLoad expose slotted saves
and emit StateSavedEvent / StateRestoredEvent — the latter carries a
fresh BoardSnapshot so the presentation can rebuild board, pieces,
trajectories, objectives, stock, camera, and control bar in one pass.
F5/F9 trigger it in Main; harness gains quick_save/quick_load commands so
UI tests can checkpoint a scenario and resume without replaying from
scratch. Seven xUnit tests cover the roundtrip (including independence
from post-save mutations, campaign state, and multi-slot isolation).
Delete autonomous_plan.md (fully shipped), PLAN_playtest.md (all P1-P7
done), PLAN_missions.md and PLAN_leveldesign.md (partial — engine done,
UI polish + 3 final missions + recurring demands + transformer visuals
remain). The surviving TODO list lives in docs/PLAN.md.
Also add the 6-step dev loop to CLAUDE.md (take next topic → implement →
tests → UI test → docs → commit).
If DNS resolution transiently fails for one domain, the whole postStart
hook used to exit 1 and leave the container without a firewall. Warn and
continue so the remaining rules still get installed.
Explains why YOLO is reasonable inside this specific container (bind-mount
scope, firewall allow-list, non-root user, no host secrets mounted) and
flags the residual risks worth knowing: uncommitted work in /workspace,
exfiltration via GitHub / Anthropic, no CPU/RAM limits. Key advice: commit
before long autonomous runs.
Claude Code running inside the project's dev container can now build the
game, launch a real Godot instance under Xvfb, and drive the automation
harness end-to-end — no Windows dependency.
Dockerfile adds (as root, before USER node):
- X11 / Mesa software GL / audio runtime deps + python3
- .NET SDK 9.0 via upstream dot.net install script -> /usr/local/dotnet
- Godot 4.6.2-stable mono Linux x86_64 -> /opt/godot/godot
- /usr/local/bin/godot-xvfb wrapper: auto-wraps invocations in
xvfb-run -a --server-args="-screen 0 1280x720x24 ..."
harness.py picks GODOT_BIN from env, defaults to /opt/godot/godot on
Linux, and auto-wraps the subprocess in xvfb-run when DISPLAY is unset.
Windows code path unchanged.
init-firewall.sh adds api.nuget.org to the allowlist so dotnet restore
works post-boot. Godot + .NET SDK are fetched at image build time, before
the firewall exists.
New docs:
- autonomous_plan.md: design rationale, alternatives considered
- README.md: launch instructions for Windows terminal / Docker Desktop /
VS Code Dev Containers / WSL2 natif
- CLAUDE.md already documents the harness (done in previous commit)
Validation: docker build succeeds; inside the container, dotnet --version
=9.0.313, godot --version=4.6.2.stable.mono, dotnet test=102/102,
python3 tools/automation/smoke.py passes end-to-end with 14 non-black
1280x720 PNGs. Mission 1 screenshot is visually identical to the Windows
build, and Xvfb determinism is a bonus (det_a.png ≡ det_b.png bytewise).
Launching Godot with --automation=<dir> activates an AutomationHarness
node that polls <dir>/inbox/ for JSON command files, executes them via
a thin facade over existing public surfaces (GameSim, InputMapper,
EventAnimator, ControlBar, PieceStockPanel), and writes results plus
screenshots back to disk. The black-box simulation boundary is not
crossed — every command routes through the same signals/methods a real
player would trigger.
A stdlib-only Python helper (tools/automation/harness.py) wraps the
protocol for test scripts and interactive REPLs. Smoke test passes
end-to-end: load mission, place a piece, step 10 turns, capture 14
1280x720 PNGs, handle rejections, quit cleanly. Existing 102 engine
unit tests still green.
Bundles in-flight work on the campaign/missions system (CampaignDef,
MissionDef, TerrainPatch, TransformerDef, MissionChecker, CampaignLoader,
FlavorBanner, transformer rules), plan files, and matching tests. Baseline
commit so the upcoming automation testing harness lands on a clean tree.
- Mouse wheel up/down zooms camera in/out
- Zoom clamped between 0.3x and 3x to prevent getting lost
- Updated PLAN.md: marked phases 5-6 as done, added phase 7-8
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dame (Queen):
- Moves 1-2 cells in all 8 directions (orthogonal + diagonal)
- Social status 7 (highest — priority over all other pieces)
- Deep burgundy color, letter "D"
- Rare and powerful, forces strategic placement choices
Levels:
- Level 7 "La Dame Blanche": 10x10, walled arena with central fortress,
1 queen available as a logistics superweapon
- Level 8 "Le Grand Reseau": 12x10, 4 productions + 4 demands,
two vertical wall corridors, 2 queens, full network challenge
GDD updated with Dame section and status hierarchy.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bugfixes:
- Stop now fully rebuilds piece visuals (fixes destroyed pieces not
reappearing and lingering cargo indicators)
- Piece stock buttons use explicit selected/unselected styling instead
of ambiguous toggle behavior (teal bg + bright border when selected)
- Color dots next to stock buttons match piece colors
Features:
- Clicking a placed piece highlights its start/end cells with piece color
- Back-to-menu button ("← Menu") in top-left returns to level selector
with fade transition
- DetailPanel shows "Pion" name and has styled remove button
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Pion (Pawn): orthogonal range 1, social status 1 (lowest), green color
- All existing levels get surplus stock including pawns for player choice
- Level 4 "Le Carrefour": 8x8, dual cargo diagonal routes, center wall block
- Level 5 "Le Labyrinthe": 8x6, wall corridors, knights essential for shortcuts
- Level 6 "Trois Royaumes": 10x8, 3 productions + 3 demands, full network puzzle
- Production interval concept removed (all produce every turn)
- GDD updated with Pion section and 6 level descriptions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New turn order: produce -> transfer -> move -> collision resolution
- Collisions now destroy weaker pieces (status > level > mutual destruction)
instead of halting the simulation. SimPhase.Collision removed.
- Add piece Level property (all level 1 in proto, prepared for future)
- Production fires every turn (interval concept removed), buffer = Amount
(default 1, future 2-4), leftovers overwritten each turn
- Transfer tiebreaker: status > level > clockwise direction (alternating
even/odd turns in y-up coords), replaces distance-to-production
- Demands always accept matching cargo even when already satisfied
- TurnNumber added to all turn events for animation grouping
- Simultaneous animations: produce flash, cargo slide, parallel piece moves
- Camera centering fix + middle-click pan
- GDD updated with new rules + lore section added
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CargoFilter property to PieceState, auto-assigned at placement
by tracing relay chain back to production. TransferResolver now
enforces cargo-type filtering and uses forward-direction sorting
with cargo-aware distance calculations. Prevents cross-route
contamination on multi-cargo boards.
Level 3 restored to dual-cargo (Wood+Stone) with correct 10R+2K stock.
Two new solvability tests validate filter auto-assignment and chain
propagation. All 60 tests green.
- Fix cargo bouncing in relay chains: piece-to-piece transfers now prefer
receivers farther from production (forward flow) instead of closer (backward)
- Level 2 stock corrected: 4R+1B → 6R+1B to match required solution
- Level 3 simplified to single cargo type (6R+1K) — dual-cargo on 6x6
requires engine support for cargo-type filtering (Phase 2)
- Add PLAN.md with prototype roadmap (phases 2-6)
- 57 tests passing
Black box sim engine (commands in, events out) with 3 piece types
(Rook, Bishop, Knight), cargo transfer system with social status
priority, collision detection, and victory/defeat conditions.
57 tests covering rules, simulation, loading, and solvability.
Godot 4 presentation layer scaffolding.