# Montrer le coup que l'ordinateur vient de jouer - **Date** : 2026-11-03 - **Étape** : 0d (retour de Pierre sur le plateau publié) - **Statut** : terminé - **Exécutant** : sous-agent general-purpose, dans un worktree isolé ## Objectif Après le coup de l'ordinateur, le joueur voit ce qui a bougé sans lire la notation. Choix de Pierre : **animation puis traces**. ## Consignes 1. **Animation** : quand l'ordinateur joue, ses pions glissent un par un, un demi-coup après l'autre (environ 0,4 s chacun), de la case de départ à la case d'arrivée. Un pion frappé part sur la barre de façon visible. Si le système demande moins d'animations (`prefers-reduced-motion`), pas d'animation : on passe directement aux traces. 2. **Traces** : une fois l'animation finie, et jusqu'à la prochaine action du joueur (lancer les dés, doubler, prendre, passer, toucher un pion) : - une flèche de chaque case de départ vers sa case d'arrivée ; - les pions déplacés entourés ; - à la case de départ d'un pion parti, un pion « fantôme » (contour seul) ; - un pion frappé signalé sur la barre. Les petits points orange actuels sont remplacés par ces traces. 3. **Bouton « Revoir »** à côté des boutons d'action : il rejoue l'animation du dernier coup de l'ordinateur, puis remet les traces. Il disparaît dès que le joueur agit. 4. **Où vit la logique** : la liste des demi-coups du dernier coup de l'ordinateur et les positions intermédiaires viennent du domaine (le journal contient déjà `MovePlayed` avec ses demi-coups ; ajouter si besoin une fonction qui donne la suite des plateaux intermédiaires, à partir du moteur, sans réimplémenter de règle : par exemple `checkHalfMoves` ou le décodage de position à chaque étape). L'interface ne fait que dessiner. Les garde-fous d'architecture existants doivent passer. 5. **Textes** dans `app/src/ui/texts.ts` ; mots du glossaire. 6. **Scénario** (ADR 0008) : ajouter à `docs/scenarios/jouer-une-partie.feature` un scénario « Voir le coup de l'ordinateur », écrit en intentions (par exemple : quand l'ordinateur a joué, alors son coup vous est montré case par case, et vous pouvez le revoir), avec ses étapes Cucumber sur le domaine. 7. **Tests** : - tests unitaires de ce qui est calculable hors navigateur (traces et plateaux intermédiaires d'un coup avec et sans pion frappé, coup depuis la barre, sortie de pions) ; - `make test-browser` (`tests/browser/play.mjs`) : après un coup de l'ordinateur, les flèches et les pions entourés sont présents ; « Revoir » rejoue puis les remet ; tout disparaît à l'action suivante ; capture d'écran `coup-adverse` ; - `make lint`, `make test` et `make test-browser` verts. Contraintes : - `CLAUDE.md`, ADR 0007, 0008, 0009 : simplicité, pas de framework ni de bibliothèque d'animation (animations CSS ou `requestAnimationFrame`), identifiants en anglais, commentaires et textes en français ; - aucune règle du jeu réimplémentée ; - l'animation ne doit pas retarder le moteur ni bloquer la saisie au-delà de sa durée ; le temps de calcul affiché reste celui du moteur ; - ne pas publier, ne pas pousser, ne pas créer de merge request ; - commits petits, en français, avec la ligne `Co-Authored-By: Claude Opus 5.5 `. Rapport attendu : ce qui a été fait, captures d'écran (chemins), résultat des tests, difficultés, écarts au plan. ## Ajout : aucun coup possible Demandé par Pierre en cours de tâche (inspiré de l'application CBG). 1. Quand le jet du joueur ne permet aucun coup, le tour ne passe plus tout seul à l'ordinateur. Les dés restent affichés, estompés ou barrés, avec un message clair (« Aucun coup possible avec 6-6. ») et un bouton « Continuer ». Le tour passe à l'ordinateur seulement quand le joueur le touche. 2. Quand l'ordinateur ne peut pas jouer son jet, ses dés et un message (« L'ordinateur ne peut pas jouer 5-5. ») restent visibles jusqu'à la prochaine action du joueur, comme les traces d'un coup. 3. Le domaine reste la seule source, sans règle réimplémentée ; textes dans `texts.ts`. 4. Scénario « Aucun coup possible avec votre jet » avec ses étapes sur le domaine et des dés imposés. Dans `play.mjs`, si le cas survient, vérifier que « Continuer » est demandé, sans forcer de cas artificiel. Les changements CSS se limitent aux nouvelles classes : un autre agent travaille sur la charte. ## Ajout : toucher les dés et le videau Demandé par Pierre en cours de tâche. 1. Toucher les dés lance les dés quand c'est au joueur de lancer (même effet que « Lancer les dés »). Sans dés à lancer affichés, la zone des dés du joueur doit être touchable (dés « en attente »). 2. Toucher le videau propose de doubler quand le joueur en a le droit (mêmes conditions, venues du domaine). Le reste du temps, toucher dés ou videau ne fait rien. 3. Les boutons restent. Dés et videau touchables : cible d'au moins 44 px, retour visuel discret, `role="button"` et `aria-label`. 4. Tests : dans `play.mjs`, lancer au moins une fois en touchant les dés et doubler au moins une fois en touchant le videau. ## Critères de réussite - Dans Chromium et Firefox sans interface, après chaque coup de l'ordinateur : animation (ou traces directes en mouvement réduit), traces visibles, « Revoir » fonctionnel, traces effacées à l'action suivante. - Le scénario « Voir le coup de l'ordinateur » passe dans `make test`. - `make lint`, `make test` et `make test-browser` passent. ## Résultat Terminé le 2026-11-03, branche `coup-adverse-travail`. **Domaine** (`app/src/domain/game/game.ts`) : `Game.lastMoveSteps()` donne le dernier coup joué demi-coup par demi-coup (`MoveSteps` : camp, demi-coups, plateaux successifs, pions frappés). Les plateaux intermédiaires viennent du moteur : `checkHalfMoves` sur les premiers demi-coups depuis la position d'avant le coup, puis `board`. Un pion frappé se lit sur le plateau du moteur (la barre adverse grossit), aucune règle n'est rejouée. Le format des événements ne change pas. **Interface** : - `board-model.ts` : `moveTrace()` calcule les traces (une flèche par demi-coup, pions arrivés, pions partis, pions frappés). Un pion qui fait étape (13/7 7/4) ne laisse rien sur la case d'étape. Testé sous Node. - `board-view.ts` : traces dessinées en laiton (`--last-move`, comme le demande la charte) : flèches avec pointe, pions arrivés entourés, pion fantôme en pointillés à la case de départ, pion frappé entouré en pointillés sur la barre. Les petits points orange ont disparu. La géométrie des piles est regroupée dans `slotCenter()`. Le pion du dessus de chaque endroit porte `data-checker`. `slideChecker()` le fait glisser avec `Element.animate` (API du navigateur, sans bibliothèque). - `app.ts` : après le tour de l'ordinateur, le plateau est redessiné à chaque étape et le pion arrivé glisse en 0,4 s depuis sa case de départ. Un pion frappé glisse en même temps vers la barre. Ensuite viennent les traces et le bouton « Revoir ». Tout s'efface à l'action suivante (lancer, doubler, prendre, passer, nouvelle partie, toucher un pion). Avec `prefers-reduced-motion: reduce`, les traces s'affichent directement. L'animation commence une fois le moteur fini : le temps affiché reste celui du moteur. Les touches sont ignorées pendant l'animation (au plus 0,4 s par demi-coup). Une partie reprise juste après le coup de l'ordinateur montre ses traces sans l'animer. - Textes `replay` et `replayTitle` dans `texts.ts`, bouton `#replay` dans `web/index.html`. **Scénario** « Voir le coup de l'ordinateur » dans `docs/scenarios/jouer-une-partie.feature`. Il part d'une position où le seul coup permis de l'ordinateur avec 3-1 frappe votre blot (13/10* 10/9), avec le videau chez vous pour qu'il ne double pas. Ses étapes vérifient les plateaux case par case, le pion frappé qui part sur la barre et un second appel identique (« revoir »). **Tests** : - `app/tests/move-steps.test.ts` (vrai moteur) : coup simple, pion frappé, coup depuis la barre qui frappe en rentrant, sortie jusqu'à la fin de partie, coup de l'ordinateur, aucun coup. - `app/tests/ui.test.ts` : traces d'un coup simple, d'un pion qui fait étape, d'une rentrée avec un pion frappé, d'une sortie. - `tests/browser/play.mjs` : après chaque action, les traces sont là si le dernier coup est celui de l'ordinateur (autant de flèches que de demi-coups, au moins un pion entouré et un fantôme, « Revoir » visible), et absentes sinon. « Revoir » est essayé deux fois. La première fois, des animations tournent et les flèches disparaissent pendant l'animation, puis les mêmes traces reviennent ; une capture `coup-adverse` est prise. La seconde fois, en mouvement réduit, aucune animation ne tourne et les traces reviennent. **Ajout : aucun coup possible** : - Domaine : `Game.roll()` ne fait plus passer le tour. `Game.cannotMove()` dit, d'après `legalMoves` du moteur, que le jet ne permet aucun coup. `Game.skipTurn()` enregistre alors le coup vide (même événement `MovePlayed` sans demi-coup qu'avant, format inchangé) ; il est refusé s'il existe un coup. Le nom `skipTurn` évite la confusion avec « passer » (le double), qui est `drop` dans le glossaire. - Interface : bouton « Continuer » (`#skip`), message « Aucun coup possible avec 6-5. », consigne « Touchez « Continuer » : le tour passe à l'ordinateur. », dés estompés et barrés (`.die-blocked`, `.die-strike`). Quand l'ordinateur ne peut pas jouer, ses dés restent barrés avec « L'ordinateur ne peut pas jouer 5-5. » jusqu'à l'action suivante. Ces deux messages se déduisent du journal : ils survivent à un rechargement. - Tests : scénario « Aucun coup possible avec votre jet » (pion sur la barre face aux six cases tenues, 6-5), tests unitaires `cannotMove`/`skipTurn` (faux moteur), partie complète adaptée. Dans le navigateur, « Continuer » a été demandé 1 fois (Chromium) et 3 fois (Firefox) : message et deux dés barrés vérifiés à chaque fois. Le jet sans coup de l'ordinateur est vérifié quand il survient (pas survenu dans ces deux parties). **Ajout : toucher les dés et le videau** : - Quand vous pouvez lancer, deux dés « en attente » (faces vides, contour en pointillés) apparaissent au milieu de la moitié gauche. La moitié droite garde le dernier jet de l'ordinateur, qui doit rester visible. Quand vous pouvez doubler (`Game.canDouble()`, comme le bouton), le videau est souligné. - Les toucher clique le bouton correspondant (`#roll`, `#double`) : mêmes conditions, même effet. Hors de ces moments, aucune zone n'est posée. - Zones à toucher : `role="button"`, `aria-label` (« Lancer les dés », « Doubler »), au moins 88 unités, soit 47 px sur le plateau de 344 px d'un téléphone de 360 px. Les boutons restent pour le clavier ; les zones n'ont pas de `tabindex`, pour ne pas doubler l'ordre de tabulation. - Navigateur : dés touchés 10 fois (Chromium) et 14 fois (Firefox), videau touché pour doubler, taille et rôle vérifiés ; aucune zone pendant la saisie d'un coup. **Résultats finaux** (dernier commit vérifié) : `make lint` sans problème ; `make test` 10/10 ; `make test-browser` vert sous Chromium et Firefox, sans violation CSP ni erreur dans la page. Le coup de l'ordinateur a été montré 21 et 30 fois. Dans Firefox, un coup revu a frappé deux pions, tous deux signalés sur la barre. **Limites** : - Un premier essai de `make test-browser` a échoué dans `docker build` (« parent snapshot … does not exist »), sans lien avec le code (probablement une autre construction Docker en parallèle). L'essai suivant est passé. - Firefox affiche des tours de l'ordinateur de 3 à 4,4 s au plus. Ce temps est mesuré avant l'animation, qui n'y est pour rien : c'est le moteur sous Firefox sur le PC de 2011. Chromium : 0,6 s au plus. - Les dés en attente sont à gauche alors que le jet lancé s'affiche à droite : c'est un choix d'interface, à revoir avec la charte si besoin. - Sur une case de plus de cinq pions, c'est le cinquième pion dessiné qui glisse (le nombre reste affiché sur la pile).