Étape 0d.1 : domaine « partie » en TypeScript, scénario exécuté, garde-fous

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>
This commit is contained in:
Pierre MARTIN
2026-11-02 21:13:44 +01:00
co-authored by Claude Opus 5.5
44 changed files with 2512 additions and 112 deletions
+3 -2
View File
@@ -14,11 +14,12 @@ Acquis : socle du dépôt, organisation (ADR 0001, 0003, 0006), source du moteur
de référence (0a.2), moteur embarqué et `engine-cli` (0a.3), comparaison
automatique aux références, tolérance 0,002 (0a.4), moteur en WebAssembly avec
page de test, page de mesure et guide de test (0b), mesures sur le Fairphone 4 et
socle PWA en TypeScript (ADR 0009 ; 0c Android natif abandonnée).
socle PWA en TypeScript (ADR 0009 ; 0c Android natif abandonnée), domaine
« partie » en journal d'événements avec scénario exécuté sur le vrai moteur et
garde-fous d'architecture (0d.1), déploiement sur https://ustagammon.sans.pub.
| Sous-étape | Contenu | État | Plan |
| --- | --- | --- | --- |
| 0d.1 | Domaine « partie » en TypeScript (journal d'événements), scénario exécuté sur le vrai moteur, garde-fous d'architecture | 🔄 | `plans/2026-11-02-domaine-partie.md` |
| 0d.2 | Plateau minimal jouable (PWA hors ligne) ; vérifié avec Pierre en suivant `docs/scenarios/jouer-une-partie.feature` | ⏳ | |
**Critère de fin** : une partie complète jouée sur le Fairphone 4, moins de 2 s
+6 -1
View File
@@ -26,7 +26,10 @@ Voir aussi : [Doubler](doubler.md)
`code` est facultatif : il désigne le type du domaine qui implémente le terme,
dès qu'il existe. Dans le code, ce type porte en commentaire le chemin de sa
fiche (`Glossaire : docs/glossaire/videau.md`).
fiche (`Glossaire : docs/glossaire/videau.md`), juste avant sa déclaration
exportée. `make test` vérifie le lien dans les deux sens : chaque fiche avec
`code` est reliée à ce type, chaque référence du code mène à une fiche dont le
`code` est le type qui la porte.
## Termes
@@ -38,6 +41,7 @@ fiche (`Glossaire : docs/glossaire/videau.md`).
| [Branche](branche.md) | Branch |
| [Compte de points](compte-de-points.md) | Pip count |
| [Coup](coup.md) | Move |
| [Demi-coup](demi-coup.md) | Half-move |
| [Course](course.md) | Race |
| [Doubler](doubler.md) | Double |
| [Équité](equite.md) | Equity |
@@ -51,6 +55,7 @@ fiche (`Glossaire : docs/glossaire/videau.md`).
| [Partie](partie.md) | Game |
| [Passer](passer.md) | Drop |
| [Pion](pion.md) | Checker |
| [Plateau](plateau.md) | Board |
| [Pli](pli.md) | Ply |
| [Point](point.md) | Point |
| [Prendre](prendre.md) | Take |
+1
View File
@@ -1,6 +1,7 @@
---
term: Coup
english: Move
code: Move
---
# Coup
+15
View File
@@ -0,0 +1,15 @@
---
term: Demi-coup
english: Half-move
code: HalfMove
---
# Demi-coup
**En bref** : le déplacement d'un pion avec un seul dé, par exemple 8/5 avec un 3 ; un coup en compte deux, ou quatre avec un double.
On saisit son coup demi-coup par demi-coup : l'application n'accepte que ceux
qui mènent à un coup permis. Pour un pion qui sort, la case d'arrivée se note
« off » (6/off) ; pour un pion qui rentre depuis la barre, la case de départ se
note « bar » (bar/22).
Voir aussi : [Coup](coup.md), [Jet](jet.md), [Barre](barre.md), [Sortie](sortie.md)
+1
View File
@@ -1,6 +1,7 @@
---
term: Événement de partie
english: Game event
code: GameEvent
---
# Événement de partie
@@ -1,6 +1,7 @@
---
term: Identifiant de position
english: Position ID
code: PositionId
---
# Identifiant de position
+1
View File
@@ -1,6 +1,7 @@
---
term: Jet
english: Roll
code: Roll
---
# Jet
+1
View File
@@ -1,6 +1,7 @@
---
term: Moteur
english: Engine
code: Engine
---
# Moteur
+1
View File
@@ -1,6 +1,7 @@
---
term: Partie
english: Game
code: Game
---
# Partie
+14
View File
@@ -0,0 +1,14 @@
---
term: Plateau
english: Board
code: Board
---
# Plateau
**En bref** : la surface de jeu, avec ses 24 cases (les points), la barre au milieu, et les pions de chaque joueur.
Chaque joueur numérote les cases de son point de vue : sa case 1 est la plus
proche de sa sortie, sa case 24 la plus éloignée, qui est la case 1 de
l'adversaire.
Voir aussi : [Point](point.md), [Pion](pion.md), [Barre](barre.md), [Identifiant de position](identifiant-de-position.md)
+1
View File
@@ -1,6 +1,7 @@
---
term: Pli
english: Ply
code: Ply
---
# Pli
+1
View File
@@ -1,6 +1,7 @@
---
term: Videau
english: Cube
code: Cube
---
# Videau
-94
View File
@@ -1,94 +0,0 @@
# Domaine « partie » en TypeScript, scénarios exécutés, garde-fous
- **Date** : 2026-11-02
- **Étape** : 0d.1
- **Statut** : en cours
- **Exécutant** : sous-agent general-purpose, dans un worktree isolé
## Objectif
Le domaine d'une partie contre l'ordinateur existe en TypeScript, sous forme de
journal d'événements (ADR 0007), branché au moteur WebAssembly par une
interface `Engine`. Le scénario `docs/scenarios/jouer-une-partie.feature`
s'exécute sous Node, sur le vrai moteur, dans `make test`. Les règles
d'architecture et le lien glossaire ↔ code sont vérifiés par les tests.
L'interface graphique et la PWA sont l'étape suivante (0d.2), pas celle-ci.
## Consignes
1. **Outillage** (ADR 0009, ADR 0004) : une image `docker/web/` (Node
`node:22.11.0-alpine3.20` déjà présent, versions npm figées et vérifiées) avec
TypeScript, Biome, `@cucumber/cucumber` et `dependency-cruiser`. Cibles
`make web` (compile `app/` vers `build/web/` avec `tsc`), et intégration à
`make lint` (Biome et `tsc --noEmit`) et `make test`. Étiquette d'image dans
le `Makefile`, `make setup` la construit. `CREDITS.md` à jour.
2. **Arborescence** (à ajuster si plus simple) :
- `app/src/domain/` : le domaine pur, sous-dossier `game/` ;
- `app/src/engine/` : l'adaptateur qui implémente `Engine` avec le moteur
WebAssembly (réutiliser `web/engine-wasm.mjs` et `build/wasm/`) ;
- `app/src/ui/` : vide pour l'instant (étape 0d.2) ;
- `app/tests/` : tests unitaires (runner intégré de Node, `node:test`) et
définitions des étapes Cucumber.
Les pages de prototype existantes (`web/index.html`, `web/bench.html`)
doivent continuer de fonctionner.
3. **Interface `Engine`** définie par le domaine (coups légaux avec position
résultante, meilleur coup pour un niveau, décision de videau, décodage d'une
position en plateau pour l'affichage). Compléter `engine.h` et `engine-cli`
si une information manque (par exemple décoder un Position ID en nombre de
pions par case, savoir si la partie est finie et combien elle rapporte), en
restant simple, avec tests natifs.
4. **Journal d'événements** (ADR 0007, point 3) : événements immuables avec
identifiant, parent et version de format (`GameStarted`, `DiceRolled`,
`MovePlayed`, `CubeOffered`, `CubeTaken`, `CubeDropped`, `GameEnded`). L'état
se calcule en rejouant le journal. Une branche est un nouvel événement
accroché à un ancien ; annuler revient à repartir du parent. Le hasard des
dés est injecté (source de dés passée au domaine).
5. **Règles de la partie de 0d** : partie libre contre l'ordinateur (Jacoby
selon les réglages par défaut de gnubg) ; l'ordinateur joue le meilleur coup
au niveau « standard » (choisir un niveau qui tienne les 2 s sur téléphone :
2 plis d'après les mesures 0b, à confirmer) ; il décide du videau avec le
moteur ; le joueur peut prendre ou passer, et peut lui-même doubler (le
moteur décide alors si l'ordinateur prend ou passe). Le joueur saisit son
coup demi-coup par demi-coup (case de départ, case d'arrivée) ; le domaine
n'accepte que ce qui mène à un coup légal complet. Aucune règle du jeu n'est
réimplémentée : tout vient du moteur.
6. **Glossaire ↔ code** (ADR 0008) : chaque type du domaine qui correspond à
un terme porte `Glossaire : docs/glossaire/<fiche>.md` en commentaire, et la
fiche reçoit `code: <Type>`. Ajouter les fiches manquantes. Étendre
`tests/test_glossary.sh` pour vérifier le lien dans les deux sens.
7. **Scénarios exécutés** : définitions des étapes en français pour
`docs/scenarios/jouer-une-partie.feature`, branchées sur le domaine et le
vrai moteur WebAssembly (sous Node), avec des dés imposés pour être
reproductibles. Si une étape du scénario est impossible à exécuter telle
quelle, ajuster le scénario en gardant son sens pour le joueur, et le dire.
8. **Garde-fous d'architecture** (dependency-cruiser) : le domaine n'importe
ni `engine/`, ni `ui/`, ni rien de spécifique au navigateur ; `ui/`
n'importe pas `engine/` ; pas de dépendance circulaire. Vérifier qu'une
violation volontaire fait échouer `make test`.
Contraintes :
- `CLAUDE.md`, ADR 0004, 0007, 0008, 0009 : simplicité avant tout, pas de
patron de conception sans besoin, identifiants en anglais, commentaires et
docs en français, tout outil dans Docker, `-j2` ;
- ne pas modifier `engine/gnubg/` ni les références ;
- `make lint` et `make test` doivent passer ; commits petits, en français, avec
la ligne `Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>` ;
- ne pas pousser ni créer de merge request ; ne rien simuler.
Rapport attendu : ce qui a été fait, l'API du domaine en quelques lignes, les
fiches de glossaire ajoutées, le résultat des scénarios, les vérifications des
garde-fous, les difficultés, les écarts au plan.
## Critères de réussite
- `make test` exécute le scénario « Jouer une partie contre l'ordinateur » sur
le vrai moteur et passe.
- Une partie complète (jusqu'à `GameEnded`) se joue dans un test, l'ordinateur
contre une stratégie simple du joueur, avec des dés imposés.
- Les garde-fous d'architecture et le lien glossaire ↔ code échouent sur une
violation volontaire.
- `make lint` et `make test` passent.
## Résultat
À remplir à la clôture.
+4 -2
View File
@@ -5,8 +5,10 @@ joueur, écrit pour lui. Ces fichiers sont la seule source (ADR 0008) :
l'application les met en forme comme pages d'aide, et ils sont exécutés comme
tests d'intégration sur le domaine et le moteur réel.
Tant que le domaine n'existe pas, les scénarios se déroulent à la main : ils
servent alors de guide de test.
`make test` les exécute avec Cucumber : les étapes, définies dans
`app/tests/steps/`, pilotent le domaine branché sur le vrai moteur
WebAssembly, avec des dés imposés pour que chaque exécution soit identique. Une
étape sans définition fait échouer les tests.
## Format
+5 -2
View File
@@ -14,11 +14,14 @@ Fonctionnalité: Jouer une partie contre l'ordinateur
Étant donné que c'est à vous de jouer avec le jet 3-1
Quand vous jouez 8/5 6/5
Alors votre coup est accepté
Et c'est au tour de l'ordinateur
Et c'est ensuite au tour de l'ordinateur
Scénario: Un coup interdit est refusé
La case 12 est tenue par l'ordinateur, qui y a cinq pions : vous ne
pouvez pas vous y poser.
Étant donné que c'est à vous de jouer avec le jet 3-1
Quand vous essayez de jouer 8/4
Quand vous essayez de jouer 13/12
Alors votre coup est refusé
Et la position n'a pas changé