Étape 0d.1 : domaine « partie » en TypeScript, scénario exécuté ; ADR 0009 accepté #11
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user