Étape 0d.1 : avant le plateau jouable (0d.2), il faut la logique d'une partie contre l'ordinateur, sans interface. Elle doit être testable et organisée selon les règles d'architecture : domaine pur, journal d'événements, moteur derrière une interface (ADR 0007). La documentation utilisateur doit aussi être exécutée comme test (ADR 0008). Les mesures sur le Fairphone 4 ont confirmé le socle PWA en TypeScript (ADR 0009), qui est accepté dans cette MR avec ces mesures.
Quoi
ADR 0009 accepté, avec les mesures du Fairphone 4 (Chrome 156, 3 essais) : meilleur coup à 2 plis en 348 ms au pire pour un seuil de 3 s, videau en 148 ms, 7,3 Mo à télécharger. Socle PWA, TypeScript, Biome, @cucumber/cucumber, dependency-cruiser. L'étape 0c (Android natif) est abandonnée. ADR 0004 amendé avec ces outils, validés par Pierre.
actions du joueur : roll(), playHalfMove({from, to}), cancelHalfMoves(), double(), take(), drop() ;
tour de l'ordinateur : playComputerTurn(). Il double si le moteur le recommande, sinon il joue le meilleur coup à 2 plis (STANDARD_PLY) ;
retour en arrière : undo() et goBackTo(id). Une action faite après un retour ouvre une branche.
Journal d'événements (events.ts) : GameStarted, DiceRolled, MovePlayed, CubeOffered, CubeTaken, CubeDropped, GameEnded. Les événements sont gelés et forment un arbre ; chacun a un id, un parent et une version. L'état se recalcule sans le moteur (state.ts), et les dés sont injectés.
Interface Engine définie par le domaine, et son adaptateur WebAssembly app/src/engine/wasm-engine.ts.
Moteur complété, avec tests natifs :
validation demi-coup par demi-coup : un dé à la fois, avec GenerateMoves de gnubg, et le coup complet doit être un coup légal de la liste ;
décodage d'une position en plateau ;
résultat de fin de partie : simple, gammon ou backgammon, videau, Jacoby ;
encodage du Match ID ;
droit de doubler.
Tests :
11 tests unitaires sur un faux moteur ;
deux parties complètes jusqu'à GameEnded sur le vrai moteur WebAssembly, à 0 puis 2 plis. Une partie à 2 plis prend 1,6 s sous Node ;
le scénario docs/scenarios/jouer-une-partie.feature est exécuté par Cucumber sur le domaine et le vrai moteur : 7 scénarios, 25 étapes ;
garde-fous dependency-cruiser : le domaine n'importe que le domaine, ui/ n'importe pas engine/, pas de cycle. tests/test_web.sh vérifie qu'une violation volontaire de chaque règle est bien détectée.
Glossaire relié au code :
deux fiches ajoutées, demi-coup.md (HalfMove) et plateau.md (Board), à relire par Pierre ;
code: renseigné sur 8 fiches ;
tests/test_glossary.sh vérifie le lien dans les deux sens.
Outillage : image ustagammon/web:1 (docker/web/) ; make web ; make lint lance Biome et tsc --noEmit ; make build compile le moteur WebAssembly et l'application.
Comment
Travail délégué à un sous-agent selon un plan écrit avant la délégation, supprimé à la clôture ; son résultat est repris ici.
Le moteur reste la seule autorité pour les règles. Le domaine ne fait que ce que le moteur ne gère pas, faute d'état de partie : alterner les tours, gérer le jet d'ouverture (relance en cas d'égalité), doubler la valeur du videau et le donner à celui qui prend, faire perdre à la valeur d'avant le double celui qui passe.
Deux retouches du scénario, sans changer son sens pour le joueur :
l'exemple de coup interdit « 8/4 » avec 3-1 était en fait un coup permis (8/5/4). Il devient « 13/12 », case tenue par l'ordinateur ;
un constat portait le même texte qu'une situation de départ d'un autre scénario. Il devient « c'est ensuite au tour de l'ordinateur ».
Vérification par la session principale, après fusion de main dans la branche (MR !9 et !10) :
make setup && make build, puis make lint (Biome et TypeScript inclus) ;
make test : 10/10. Comparaisons native et WebAssembly aux références, 7 scénarios Cucumber, garde-fous sans violation, test du site de production ;
relecture du domaine : fichiers courts (313 lignes au plus) et commentés en français. Le journal d'événements est documenté comme contrat durable.
Choix et compromis
Cucumber 12.9 plutôt que 13, qui exige Node 22.12 alors que l'image Node imposée est la 22.11.
Un dé par demi-coup : 8/4 avec 3-1 se saisit 8/5 puis 5/4. C'est simple et sans ambiguïté ; l'interface pourra proposer plus tard le déplacement direct.
Coup enregistré dès qu'il est complet et légal : dans le cas théorique d'un coup encore prolongeable en un autre coup légal, le joueur ne pourrait pas le prolonger.
Videau : l'ordinateur ne double pas sur un double « facultatif » ; ni l'abandon ni les castors ne sont gérés à ce stade.
@types/node (MIT) ajouté pour compiler les tests ; les autres outils sont ceux validés.
Risques et déploiement
make setup construit une image de plus (ustagammon/web:1).
Le format du journal d'événements devient un contrat durable (version 1) : le changer demandera une migration et un ADR.
L'interface engine.h s'enrichit sans casser la sortie de reference : les suites de contrat native et WebAssembly passent toujours.
Rien n'est publié par cette MR : le site en ligne ne change pas tant que make deploy-prod n'est pas relancé.
Retour arrière : annuler le commit de fusion.
Comment vérifier
git switch main && git pull
make setup && make build
make lint && make test
./build/engine-cli halfmoves 4HPwATDgc/ABMA MAEAAAAAAAAA 31 "8/5 6/5"
Hors périmètre
Le plateau jouable, la PWA hors ligne et la vérification sur le téléphone avec Pierre : étape 0d.2.
La relecture des nouvelles fiches de glossaire par Pierre.
## Pourquoi
Étape 0d.1 : avant le plateau jouable (0d.2), il faut la logique d'une partie contre l'ordinateur, sans interface. Elle doit être testable et organisée selon les règles d'architecture : domaine pur, journal d'événements, moteur derrière une interface (ADR 0007). La documentation utilisateur doit aussi être exécutée comme test (ADR 0008). Les mesures sur le Fairphone 4 ont confirmé le socle PWA en TypeScript (ADR 0009), qui est accepté dans cette MR avec ces mesures.
## Quoi
- **ADR 0009 accepté**, avec les mesures du Fairphone 4 (Chrome 156, 3 essais) : meilleur coup à 2 plis en 348 ms au pire pour un seuil de 3 s, videau en 148 ms, 7,3 Mo à télécharger. Socle PWA, TypeScript, Biome, `@cucumber/cucumber`, `dependency-cruiser`. L'étape 0c (Android natif) est abandonnée. ADR 0004 amendé avec ces outils, validés par Pierre.
- **Domaine `app/src/domain/game/`** (environ 640 lignes) :
- `Game.start(engine, { dice, ply, setup })` ;
- actions du joueur : `roll()`, `playHalfMove({from, to})`, `cancelHalfMoves()`, `double()`, `take()`, `drop()` ;
- tour de l'ordinateur : `playComputerTurn()`. Il double si le moteur le recommande, sinon il joue le meilleur coup à 2 plis (`STANDARD_PLY`) ;
- retour en arrière : `undo()` et `goBackTo(id)`. Une action faite après un retour ouvre une branche.
- **Journal d'événements** (`events.ts`) : `GameStarted`, `DiceRolled`, `MovePlayed`, `CubeOffered`, `CubeTaken`, `CubeDropped`, `GameEnded`. Les événements sont gelés et forment un arbre ; chacun a un `id`, un `parent` et une `version`. L'état se recalcule sans le moteur (`state.ts`), et les dés sont injectés.
- **Interface `Engine`** définie par le domaine, et son adaptateur WebAssembly `app/src/engine/wasm-engine.ts`.
- **Moteur complété**, avec tests natifs :
- validation demi-coup par demi-coup : un dé à la fois, avec `GenerateMoves` de gnubg, et le coup complet doit être un coup légal de la liste ;
- décodage d'une position en plateau ;
- résultat de fin de partie : simple, gammon ou backgammon, videau, Jacoby ;
- encodage du Match ID ;
- droit de doubler.
- **Tests** :
- 11 tests unitaires sur un faux moteur ;
- deux parties complètes jusqu'à `GameEnded` sur le vrai moteur WebAssembly, à 0 puis 2 plis. Une partie à 2 plis prend 1,6 s sous Node ;
- **le scénario `docs/scenarios/jouer-une-partie.feature` est exécuté par Cucumber** sur le domaine et le vrai moteur : 7 scénarios, 25 étapes ;
- garde-fous `dependency-cruiser` : le domaine n'importe que le domaine, `ui/` n'importe pas `engine/`, pas de cycle. `tests/test_web.sh` vérifie qu'une violation volontaire de chaque règle est bien détectée.
- **Glossaire relié au code** :
- deux fiches ajoutées, `demi-coup.md` (`HalfMove`) et `plateau.md` (`Board`), à relire par Pierre ;
- `code:` renseigné sur 8 fiches ;
- `tests/test_glossary.sh` vérifie le lien dans les deux sens.
- **Outillage** : image `ustagammon/web:1` (`docker/web/`) ; `make web` ; `make lint` lance Biome et `tsc --noEmit` ; `make build` compile le moteur WebAssembly et l'application.
## Comment
Travail délégué à un sous-agent selon un plan écrit avant la délégation, supprimé à la clôture ; son résultat est repris ici.
Le moteur reste la seule autorité pour les règles. Le domaine ne fait que ce que le moteur ne gère pas, faute d'état de partie : alterner les tours, gérer le jet d'ouverture (relance en cas d'égalité), doubler la valeur du videau et le donner à celui qui prend, faire perdre à la valeur d'avant le double celui qui passe.
Deux retouches du scénario, sans changer son sens pour le joueur :
- l'exemple de coup interdit « 8/4 » avec 3-1 était en fait un coup permis (8/5/4). Il devient « 13/12 », case tenue par l'ordinateur ;
- un constat portait le même texte qu'une situation de départ d'un autre scénario. Il devient « c'est ensuite au tour de l'ordinateur ».
Vérification par la session principale, après fusion de `main` dans la branche (MR !9 et !10) :
- `make setup && make build`, puis `make lint` (Biome et TypeScript inclus) ;
- `make test` : 10/10. Comparaisons native et WebAssembly aux références, 7 scénarios Cucumber, garde-fous sans violation, test du site de production ;
- relecture du domaine : fichiers courts (313 lignes au plus) et commentés en français. Le journal d'événements est documenté comme contrat durable.
## Choix et compromis
- **Cucumber 12.9** plutôt que 13, qui exige Node 22.12 alors que l'image Node imposée est la 22.11.
- **Un dé par demi-coup** : 8/4 avec 3-1 se saisit 8/5 puis 5/4. C'est simple et sans ambiguïté ; l'interface pourra proposer plus tard le déplacement direct.
- **Coup enregistré dès qu'il est complet et légal** : dans le cas théorique d'un coup encore prolongeable en un autre coup légal, le joueur ne pourrait pas le prolonger.
- **Videau** : l'ordinateur ne double pas sur un double « facultatif » ; ni l'abandon ni les castors ne sont gérés à ce stade.
- **`@types/node` (MIT)** ajouté pour compiler les tests ; les autres outils sont ceux validés.
## Risques et déploiement
- `make setup` construit une image de plus (`ustagammon/web:1`).
- **Le format du journal d'événements devient un contrat durable** (version 1) : le changer demandera une migration et un ADR.
- **L'interface `engine.h` s'enrichit** sans casser la sortie de `reference` : les suites de contrat native et WebAssembly passent toujours.
- Rien n'est publié par cette MR : le site en ligne ne change pas tant que `make deploy-prod` n'est pas relancé.
Retour arrière : annuler le commit de fusion.
## Comment vérifier
```
git switch main && git pull
make setup && make build
make lint && make test
./build/engine-cli halfmoves 4HPwATDgc/ABMA MAEAAAAAAAAA 31 "8/5 6/5"
```
## Hors périmètre
- Le plateau jouable, la PWA hors ligne et la vérification sur le téléphone avec Pierre : étape 0d.2.
- La relecture des nouvelles fiches de glossaire par Pierre.
Generated with Claude Code https://claude.com/claude-code
pierre
added 11 commits 2026-11-02 20:14:19 +00:00
Ce dont le domaine d'une partie a besoin, sans réimplémenter de règle :
décodage d'un Position ID en plateau et position vue de l'adversaire,
résultat d'une partie finie (gammon, règle Jacoby), codage d'un Match ID,
vérification de demi-coups saisis un par un (la légalité vient de
GenerateMoves de gnubg), demi-coups de chaque coup légal, droit de doubler
et recommandations de double et de prise. Nouvelles commandes d'engine-cli
position, halfmoves et matchid, avec leurs tests natifs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Image docker/web/ (Node 22.11) avec TypeScript 6.0.3, @types/node,
Biome 2.5.15, @cucumber/cucumber 12.9.0 et dependency-cruiser 18.6.0,
versions figées (ADR 0009). make web compile app/ dans build/web/ ;
make lint passe Biome et tsc --noEmit, et vérifie à part que le domaine
compile sans les types de Node ni du navigateur. make setup construit
l'image ; CREDITS.md à jour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
app/src/domain/game/ : une partie libre contre l'ordinateur, enregistrée
comme un arbre d'événements immuables et versionnés (GameStarted,
DiceRolled, MovePlayed, CubeOffered, CubeTaken, CubeDropped, GameEnded) ;
l'état se recalcule en les rejouant, annuler repart du parent, une action
après un retour ouvre une branche. Dés injectés. Le joueur saisit son coup
demi-coup par demi-coup ; l'ordinateur joue et décide du videau à 2 plis.
Toutes les règles viennent du moteur, derrière l'interface Engine que le
domaine définit.
app/src/engine/wasm-engine.ts : l'adaptateur Engine sur les commandes
d'engine-cli du moteur WebAssembly.
Tests : unitaires avec un faux moteur, et deux parties complètes jusqu'à
GameEnded sur le vrai moteur, avec des dés imposés.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Les étapes de docs/scenarios/jouer-une-partie.feature pilotent le domaine
sur le vrai moteur WebAssembly, avec des dés imposés (Cucumber, sous Node).
Deux retouches du scénario : 8/4 avec 3-1 est un coup permis (8/5/4),
remplacé par 13/12 (case tenue par l'ordinateur) ; « c'est ensuite au tour
de l'ordinateur » distingue le constat de la situation de départ du même nom.
dependency-cruiser vérifie que le domaine n'importe que le domaine, que
l'interface n'appelle pas le moteur et qu'il n'y a pas de cycle ;
tests/test_web.sh prouve aussi que des violations volontaires sont
détectées.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fiches Demi-coup (HalfMove) et Plateau (Board) ajoutées ; champ code des
fiches Coup, Jet, Pli, Moteur, Identifiant de position, Videau, Événement de
partie et Partie. tests/test_glossary.sh vérifie que chaque référence
« Glossaire : » du code mène à une fiche qui nomme le type, et que chaque
champ code désigne un type qui renvoie à sa fiche.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fusion du travail du sous-agent (branche plateau-domaine-travail) ; plan
supprimé à la clôture, son résultat est repris dans la merge request.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pierre
merged commit b4421dcea5 into main2026-11-02 20:14:20 +00:00
Elle répond "valid": true, "complete": true et la position obtenue 4HPwATCwZ/ABMA.
Correction de la section « Comment vérifier » : la commande `halfmoves` ne prend pas de Match ID. La bonne syntaxe est :
```
./build/engine-cli halfmoves 4HPwATDgc/ABMA 31 "8/5 6/5"
```
Elle répond `"valid": true, "complete": true` et la position obtenue `4HPwATCwZ/ABMA`.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Pourquoi
Étape 0d.1 : avant le plateau jouable (0d.2), il faut la logique d'une partie contre l'ordinateur, sans interface. Elle doit être testable et organisée selon les règles d'architecture : domaine pur, journal d'événements, moteur derrière une interface (ADR 0007). La documentation utilisateur doit aussi être exécutée comme test (ADR 0008). Les mesures sur le Fairphone 4 ont confirmé le socle PWA en TypeScript (ADR 0009), qui est accepté dans cette MR avec ces mesures.
Quoi
@cucumber/cucumber,dependency-cruiser. L'étape 0c (Android natif) est abandonnée. ADR 0004 amendé avec ces outils, validés par Pierre.app/src/domain/game/(environ 640 lignes) :Game.start(engine, { dice, ply, setup });roll(),playHalfMove({from, to}),cancelHalfMoves(),double(),take(),drop();playComputerTurn(). Il double si le moteur le recommande, sinon il joue le meilleur coup à 2 plis (STANDARD_PLY) ;undo()etgoBackTo(id). Une action faite après un retour ouvre une branche.events.ts) :GameStarted,DiceRolled,MovePlayed,CubeOffered,CubeTaken,CubeDropped,GameEnded. Les événements sont gelés et forment un arbre ; chacun a unid, unparentet uneversion. L'état se recalcule sans le moteur (state.ts), et les dés sont injectés.Enginedéfinie par le domaine, et son adaptateur WebAssemblyapp/src/engine/wasm-engine.ts.GenerateMovesde gnubg, et le coup complet doit être un coup légal de la liste ;GameEndedsur le vrai moteur WebAssembly, à 0 puis 2 plis. Une partie à 2 plis prend 1,6 s sous Node ;docs/scenarios/jouer-une-partie.featureest exécuté par Cucumber sur le domaine et le vrai moteur : 7 scénarios, 25 étapes ;dependency-cruiser: le domaine n'importe que le domaine,ui/n'importe pasengine/, pas de cycle.tests/test_web.shvérifie qu'une violation volontaire de chaque règle est bien détectée.demi-coup.md(HalfMove) etplateau.md(Board), à relire par Pierre ;code:renseigné sur 8 fiches ;tests/test_glossary.shvérifie le lien dans les deux sens.ustagammon/web:1(docker/web/) ;make web;make lintlance Biome ettsc --noEmit;make buildcompile le moteur WebAssembly et l'application.Comment
Travail délégué à un sous-agent selon un plan écrit avant la délégation, supprimé à la clôture ; son résultat est repris ici.
Le moteur reste la seule autorité pour les règles. Le domaine ne fait que ce que le moteur ne gère pas, faute d'état de partie : alterner les tours, gérer le jet d'ouverture (relance en cas d'égalité), doubler la valeur du videau et le donner à celui qui prend, faire perdre à la valeur d'avant le double celui qui passe.
Deux retouches du scénario, sans changer son sens pour le joueur :
Vérification par la session principale, après fusion de
maindans la branche (MR !9 et !10) :make setup && make build, puismake lint(Biome et TypeScript inclus) ;make test: 10/10. Comparaisons native et WebAssembly aux références, 7 scénarios Cucumber, garde-fous sans violation, test du site de production ;Choix et compromis
@types/node(MIT) ajouté pour compiler les tests ; les autres outils sont ceux validés.Risques et déploiement
make setupconstruit une image de plus (ustagammon/web:1).engine.hs'enrichit sans casser la sortie dereference: les suites de contrat native et WebAssembly passent toujours.make deploy-prodn'est pas relancé.Retour arrière : annuler le commit de fusion.
Comment vérifier
Hors périmètre
Generated with Claude Code https://claude.com/claude-code
Correction de la section « Comment vérifier » : la commande
halfmovesne prend pas de Match ID. La bonne syntaxe est :Elle répond
"valid": true, "complete": trueet la position obtenue4HPwATCwZ/ABMA.