Étape 0d.1 : domaine « partie » en TypeScript, scénario exécuté ; ADR 0009 accepté #11

Merged
pierre merged 11 commits from plateau into main 2026-11-02 20:14:20 +00:00
Showing only changes of commit a556c67fef - Show all commits
+75 -2
View File
@@ -2,7 +2,7 @@
- **Date** : 2026-11-02
- **Étape** : 0d.1
- **Statut** : en cours
- **Statut** : terminé (branche `plateau-domaine-travail`)
- **Exécutant** : sous-agent general-purpose, dans un worktree isolé
## Objectif
@@ -91,4 +91,77 @@ garde-fous, les difficultés, les écarts au plan.
## Résultat
À remplir à la clôture.
**Terminé.** `make lint` et `make test` passent (9/9, dont le nouveau
`tests/test_web.sh`). Les quatre critères de réussite sont tenus.
**Moteur** (`engine.h`, `engine-cli`, tests natifs dans `tests/test_engine.sh`) :
- `engine_check_half_moves` / `halfmoves POSITION_ID DÉS "8/5 6/5"` : des
demi-coups saisis un par un sont-ils le début d'un coup légal, le forment-ils
en entier, et quelle position en résulte. La légalité de chaque demi-coup
vient de `GenerateMoves` de gnubg appelé avec un seul dé ; celle du coup
entier, de la liste des coups légaux (jouer le plus de dés possible). L'ordre
des demi-coups est libre (6/5 puis 8/5 est accepté) ;
- `engine_decode_position` / `position` : plateau (pions par case, barre,
sortis, compte de points), même position vue de l'adversaire, et
`engine_game_result` : partie finie, gagnant, simple/gammon/backgammon,
points (videau et règle Jacoby compris) ;
- `engine_encode_match` / `matchid VIDEAU PROPRIÉTAIRE` ;
- les coups (`legal`, `moves`, `bestmove`) donnent leurs demi-coups ;
- `cube` donne `available` (droit de doubler), `double` (décision
DOUBLE_TAKE, DOUBLE_PASS, DOUBLE_BEAVER, REDOUBLE_TAKE ou REDOUBLE_PASS) et
`take` (`double_take <= double_pass`). La sortie de `reference` est
inchangée : les suites de contrat natives et WebAssembly passent toujours.
**Domaine** (`app/src/domain/game/`) : `Game.start(engine, { dice, ply, setup })`,
puis `roll()`, `playHalfMove({ from, to })` (false si refusé ; le coup est
enregistré dès qu'il est complet), `cancelHalfMoves()`, `double()`, `take()`,
`drop()`, `playComputerTurn()`, `undo()`, `goBackTo(id)` ; lecture par
`state` (phase, main, position vue de celui qui a la main, jet, videau,
résultat), `events`, `journal`, `displayedPosition`. Le journal est un arbre
d'événements gelés (`id`, `parent`, `version: 1`) ; `replay()` recalcule
l'état sans le moteur, chaque événement portant les positions calculées.
Niveau standard : 2 plis (`STANDARD_PLY`) ; sous Node sur le PC, une partie
entière à 2 plis prend 1,6 s, un tour de l'ordinateur quelques dizaines de ms.
**Adaptateur** (`app/src/engine/wasm-engine.ts`) : `createWasmEngine(run)`,
où `run` exécute une commande d'engine-cli ; sous Node,
`app/tests/support/node-engine.ts` le branche sur `web/engine-wasm.mjs` et
`build/wasm/`. Dans le navigateur (0d.2), `run` passera par le Web Worker
existant (`web/engine-client.mjs`).
**Scénarios** : les 7 scénarios de `jouer-une-partie.feature` passent (25
étapes) sur le vrai moteur. Deux retouches : « vous essayez de jouer 8/4 »
avec 3-1 devient « 13/12 » (8/4 se joue légalement en 8/5/4 ; la case 12 est
tenue par l'ordinateur, expliqué dans le scénario) ; le constat « Et c'est
au tour de l'ordinateur » devient « Et c'est ensuite au tour de
l'ordinateur », Cucumber ne distinguant pas une situation de départ et un
constat de même texte.
**Garde-fous** : dependency-cruiser (le domaine n'importe que le domaine,
l'interface n'importe pas le moteur, pas de cycle) ; `tests/test_web.sh`
génère des violations volontaires et vérifie que chacune est signalée.
`make lint` compile aussi le domaine seul sans types Node ni DOM. Vérifié à
la main : un import du moteur dans `side.ts` et une étape sans définition font
échouer `make test` ; un `code:` faux, une référence vers une fiche absente ou
un mauvais type font échouer `tests/test_glossary.sh`.
**Glossaire** : fiches ajoutées Demi-coup (`HalfMove`) et Plateau (`Board`),
à relire ; `code:` ajouté à Coup (`Move`), Jet (`Roll`), Pli (`Ply`), Moteur
(`Engine`), Identifiant de position (`PositionId`), Videau (`Cube`),
Événement de partie (`GameEvent`), Partie (`Game`).
**Écarts et limites** :
- `@cucumber/cucumber` 12.9.0 et non 13.x : la 13 exige Node 22.12
(`require()` de modules ES) et l'image est en 22.11. Nouvelle dépendance de
types : `@types/node` (MIT), pour compiler les tests.
- Ce que le domaine fait lui-même, faute de place dans le moteur : alterner
les tours, le jet d'ouverture (le plus fort dé commence, égalité : on
relance), doubler la valeur du videau et le donner à celui qui prend, la
perte à la valeur d'avant le double quand on passe.
- Un demi-coup utilise un seul dé : 8/4 avec 3-1 se saisit 8/5 puis 5/4.
- Le coup est enregistré dès que les demi-coups forment un coup légal ; dans
le cas théorique où il pourrait encore être prolongé en un autre coup légal,
le joueur ne le pourrait pas.
- L'ordinateur ne double pas sur un double « facultatif » (OPTIONAL_*), ne
propose pas d'abandon, et les castors ne sont pas gérés.
- `app/src/ui/` ne contient qu'un `.gitkeep`.