diff --git a/docs/adr/0004-outils-de-developpement-dans-docker.md b/docs/adr/0004-outils-de-developpement-dans-docker.md index 4c89128..3b1208d 100644 --- a/docs/adr/0004-outils-de-developpement-dans-docker.md +++ b/docs/adr/0004-outils-de-developpement-dans-docker.md @@ -46,3 +46,9 @@ construire ou tester ; rien n'est embarqué dans l'application. | Python 3 | PSF | `ustagammon/build-c` | Comparateur des références | | jq | MIT | `ustagammon/build-c` | Lecture du JSON dans les tests | | Emscripten | MIT / NCSA | `emscripten/emsdk:3.1.64` | Compilation du moteur en WebAssembly (étape 0b) | + +### 2026-11-02 — Outils de l'application web (étape 0d) + +Validés par Pierre avec l'ADR 0009, dans Docker, jamais embarqués : +TypeScript (`tsc`, Apache-2.0), Biome (MIT ou Apache-2.0), +`@cucumber/cucumber` (MIT), `dependency-cruiser` (MIT). diff --git a/docs/adr/0009-socle-pwa-typescript.md b/docs/adr/0009-socle-pwa-typescript.md new file mode 100644 index 0000000..e6a100a --- /dev/null +++ b/docs/adr/0009-socle-pwa-typescript.md @@ -0,0 +1,53 @@ +# 0009. Socle : PWA en TypeScript, moteur en WebAssembly + +- **Statut** : proposé +- **Date** : 2026-11-02 +- **Décideurs** : Pierre (langage, outils), sur proposition de Claude Code ; + le socle sera accepté après les mesures sur le Fairphone 4 + +## Contexte + +L'étape 0 doit trancher entre une PWA (moteur en WebAssembly) et une +application Android native (`docs/etape-0.md`). L'étape 0b a produit le moteur +en WebAssembly (MR !9) : même suite de contrat qu'en natif (écart maximal +9,5e-4 pour une tolérance de 0,002), 7,3 Mo compressés à télécharger (seuil +40 Mo). Sur le PC de développement (i5-2450M, 2011), le meilleur coup à 2 plis +prend au plus 0,3 s sous Chromium et 2,3 s sous Firefox (seuil sur le +téléphone : 3 s). Les mesures sur le Fairphone 4 restent à faire. + +L'ADR 0007 renvoyait au choix du socle le langage de l'interface et l'outil de +vérification des dépendances. + +## Décision + +Sous réserve des mesures sur le Fairphone 4 : + +- **Socle : PWA**, installable et utilisable hors ligne, moteur en WebAssembly + dans un Web Worker. L'étape 0c (Android natif) n'est pas faite. +- **Langage : TypeScript**, compilé par `tsc` en modules JavaScript standard, + sans framework d'interface ni empaqueteur. +- **Lint et formatage : Biome.** +- **Scénarios exécutés** (ADR 0008) par `@cucumber/cucumber`, sous Node, sur le + domaine branché au vrai moteur WebAssembly. +- **Règles d'architecture vérifiées** (ADR 0007) par `dependency-cruiser` : + le domaine n'importe ni l'interface, ni le stockage, ni le moteur ; aucun + écran n'importe le moteur. +- Tous ces outils tournent dans Docker et ne sont jamais embarqués dans + l'application. + +## Alternatives écartées + +- **Android natif (Kotlin, NDK)** : un seul système, une chaîne de build lourde + (Gradle, NDK) sur une machine de 2011, et aucun gain mesuré à ce stade sur + les temps de calcul. Reste le plan B si les mesures du téléphone échouent. +- **JavaScript annoté (JSDoc) vérifié par TypeScript** : pas d'étape de build, + mais des types moins lisibles ; Pierre a choisi TypeScript. +- **Un framework d'interface** (React, Vue…) : inutile pour un plateau et + quelques écrans, et contraire à la règle de simplicité (ADR 0007). + +## Conséquences + +- La distribution sur F-Droid et le Play Store demandera plus tard une + enveloppe Android minimale autour de la PWA (à décider à l'étape 1). +- Si les mesures du Fairphone 4 dépassent les seuils, cet ADR passe au statut + « obsolète » et l'étape 0c est lancée. diff --git a/docs/adr/README.md b/docs/adr/README.md index 6be6423..55949a4 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -15,3 +15,4 @@ l'ancien passe au statut « remplacé par NNNN ». Voir l'ADR 0001. | [0006](0006-merge-requests-systematiques.md) | Merge requests systématiques, fusionnées par Claude Code | accepté | | [0007](0007-architecture-et-conventions-de-code.md) | Architecture et conventions de code | accepté | | [0008](0008-glossaire-et-scenarios-source-unique.md) | Glossaire et scénarios : une source unique pour le code, l'aide et les tests | accepté | +| [0009](0009-socle-pwa-typescript.md) | Socle : PWA en TypeScript, moteur en WebAssembly | proposé | diff --git a/docs/feuille-de-route.md b/docs/feuille-de-route.md index ca23cbc..acd4dbf 100644 --- a/docs/feuille-de-route.md +++ b/docs/feuille-de-route.md @@ -18,9 +18,10 @@ page de test, page de mesure et guide de test (0b). | Sous-étape | Contenu | État | Plan | | --- | --- | --- | --- | | Mesures | Pierre relève les mesures sur le Fairphone 4, en suivant `docs/guide-test-etape-0.md` | 🔄 | | -| Décision | Socle PWA ou Android natif, consigné dans un ADR — validation de Pierre | ⏳ | | +| Décision | Socle PWA ou Android natif : ADR 0009 proposé (PWA, TypeScript), à accepter après les mesures | 🔄 | | | 0c | Android natif (NDK, Gradle en ligne de commande, `make deploy` via adb) | ⏸ si 0b échoue | | -| 0d | Plateau minimal jouable sur le socle retenu ; vérifié avec Pierre en suivant `docs/scenarios/jouer-une-partie.feature` | ⏳ | | +| 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 par coup de l'IA en niveau standard, tous les tests au vert. diff --git a/docs/plans/2026-11-02-domaine-partie.md b/docs/plans/2026-11-02-domaine-partie.md new file mode 100644 index 0000000..3c6281c --- /dev/null +++ b/docs/plans/2026-11-02-domaine-partie.md @@ -0,0 +1,94 @@ +# 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/.md` en commentaire, et la + fiche reçoit `code: `. 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 ` ; +- 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.