# Chessistics Jeu de logistique sur echiquier en Godot 4 / C#. Le joueur place des pieces d'echecs sur un plateau ; elles se deplacent automatiquement et transportent des ressources entre des productions et des demandes. ## Architecture : Black-Box Simulation Ref: https://samuel-bouchet.fr/posts/2026-04-08-black-box-sim/ Le moteur de jeu (`chessistics-engine/`) est une boite noire sans aucune dependance vers Godot. Il recoit des **Commands**, mute son etat interne, et retourne des **Events**. Le code Godot (`Scripts/`) ne fait que traduire l'input en commands et les events en visuels/animations. ``` Input → Command → GameSim (state + rules) → Events → Presentation ``` - **Commands** (`PlacePieceCommand`, `StartSimulationCommand`, …) : seul moyen de modifier l'etat. - **Events** (`PiecePlacedEvent`, `CargoDeliveredEvent`, …) : seul output du moteur. Le presenteur les consomme pour animer. - **GameSim** : point d'entree unique. `ProcessCommand()` retourne la liste d'events. - **Tests** : `chessistics-tests/` teste le moteur en headless, sans Godot. ## Pieges Godot a eviter ### MouseFilter sur les Controls enfants de Node2D Tout `Control` (ColorRect, Label…) a `MouseFilter = Stop` par defaut. Quand un Control est enfant d'un Node2D (ex: les ColorRect dans CellView, les Labels dans PieceView), **il participe quand meme au systeme GUI et consomme les clics**, empechant `_UnhandledInput` de recevoir l'evenement. **Regle** : toujours mettre `MouseFilter = Control.MouseFilterEnum.Ignore` sur les Controls purement visuels enfants de Node2D. ## Conventions Claude ### Plans Les fichiers de plan doivent etre rediges a la racine du workspace (ex: `/workspace/PLAN_juice.md`), **pas** dans `.claude/plans/` car ce dossier a une taille limitee. ## Harnais d'automatisation (Claude peut jouer tout seul) Le jeu peut etre pilote de maniere autonome via le flag `--automation=`. Un `AutomationHarness` (`Scripts/Automation/`) s'active alors comme noeud au root de la scene, lit des commandes JSON dans `/inbox/`, ecrit les resultats dans `/outbox/`, et place les captures d'ecran dans `/screens/`. Sans le flag, comportement normal — overhead zero. Cote agent, un wrapper Python stdlib (`tools/automation/harness.py`) expose une API simple. Godot attendu a `C:\Apps\godot\Godot_v4.6.2-stable_mono_win64_console.exe`. ### Build + utilisation ```bash dotnet build Chessistics.csproj # compiler avant tout lancement python tools/automation/smoke.py # smoke test end-to-end python tools/automation/run_game.py # REPL interactif ``` ### API Python ```python from tools.automation.harness import Harness with Harness.launch() as h: h.load_mission("campaign_01", 0) # charge la campagne + mission 0 state = h.state() # snapshot complet (dict) h.screenshot("before") # -> .automation_runs//screens/before.png h.place("Pawn", (0, 0), (0, 1)) # pose une piece h.step() # un tour (auto-wait animation) h.screenshot("after") h.set_speed(0.1); h.play() # auto-play rapide ``` Methodes : `screenshot`, `state`, `select`, `place`, `click_cell`, `key`, `play`, `pause`, `step`, `wait_idle`, `set_speed`, `load_mission`, `back_to_menu`, `quit`. Toutes les commandes non-query attendent `EventAnimator.IsAnimating == false` avant de retourner -> appels en serie toujours vus par le prochain `state()`. ### Validation visuelle par Claude Les PNG 1280x720 ecrites dans `.automation_runs//screens/` peuvent etre lues directement par l'outil `Read` de Claude. Workflow type pour valider l'UI : 1. `h.load_mission("campaign_01", N)` + `h.screenshot("mission_N_start")` 2. Lire le PNG -> verifier titre, flavor banner, board, panneau objectifs, stock 3. Placer des pieces via `h.place(...)` et re-screenshot 4. `h.step()` en boucle + screenshot a chaque etape 5. Attendre `phase == "MissionComplete"` dans le snapshot Cette boucle permet de valider que : - Les demandes affichent les bons compteurs - Les pieces bougent comme prevu - Le stock se met a jour - L'ecran `MissionComplete` apparait quand attendu ### Details importants - `Place` passe par le signal `PlacementRequested` (meme chemin qu'un vrai clic) -- ne pas appeler `GameSim.ProcessCommand(PlacePieceCommand)` directement dans le dispatcher, ca mute deux fois. - Les captures d'ecran sont prises apres `RenderingServer.frame_post_draw` -> le frame reflete l'etat final, animations incluses. - La facade (`AutomationFacade`) est la **seule** surface exposee au dispatcher. Elle ne touche que des methodes/signals publics de `GameSim`, `InputMapper`, `EventAnimator`, `ControlBar`, `PieceStockPanel`. La separation black-box tient. - Les fichiers IPC sont ecrits `.tmp` puis renommes (atomique sur Windows). - La campagne se charge via `load_mission("campaign_01", 0)`. Passer a une mission > 0 n'est pas supporte directement (il faut passer par `MissionComplete` reel).