Files
Ustagammon/docs/plans/2026-11-02-plateau-pwa.md
T

9.1 KiB

Plateau minimal jouable, en PWA hors ligne

  • Date : 2026-11-02
  • Étape : 0d.2
  • Statut : terminé (2026-11-03)
  • Exécutant : sous-agent general-purpose, dans un worktree isolé

Objectif

Sur https://ustagammon.sans.pub, un joueur joue une partie complète contre l'ordinateur sur son téléphone, au toucher. L'application s'installe (PWA) et fonctionne hors ligne. C'est la fin de l'étape 0 : critère « 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 ». Pas de design soigné : l'objectif est de valider la boucle de jeu complète, proprement.

Consignes

  1. Interface app/src/ui/ (TypeScript, DOM ou SVG, sans framework) :
    • un plateau lisible sur un écran de téléphone en portrait (24 points, barre, pions sortis, dés, videau) ;
    • saisie au toucher : toucher une case de départ, puis une case d'arrivée ; les cases d'arrivée possibles sont mises en évidence (le domaine expose ce qui est possible à partir des coups légaux du moteur, sans réimplémenter de règle) ; annuler les demi-coups en cours ;
    • boutons : lancer les dés, doubler, prendre, passer, nouvelle partie ;
    • pendant le tour de l'ordinateur : un indicateur discret ; le coup de l'ordinateur s'affiche (sans animation obligatoire) ;
    • fin de partie : gagnant, simple, gammon ou backgammon, points ;
    • reprise automatique de la partie en cours après fermeture de la page (le journal d'événements sérialisé dans localStorage suffit pour l'étape 0 ; stockage derrière une petite interface, hors du domaine).
  2. Architecture (ADR 0007, garde-fous existants) : l'assemblage (domaine, adaptateur moteur dans un Web Worker, interface, stockage) se fait dans un point d'entrée dédié (par exemple app/src/main.ts), seul autorisé à tout importer ; ui/ n'importe toujours pas engine/. Le moteur tourne dans un Web Worker, jamais sur le fil d'affichage. Étendre les règles dependency-cruiser si besoin.
  3. Textes : en français, dans un fichier de textes séparé du code (ADR 0007, point 5) ; les mots du jeu sont ceux du glossaire.
  4. Identité minimale : couleurs et tailles en variables CSS, thème clair et sombre (prefers-color-scheme), contrastes lisibles (pions clairs et foncés distincts du plateau). Palette provisoire et sobre inspirée du brief (bois, ivoire, un accent) : l'identité définitive viendra plus tard.
  5. Aide dans l'application (ADR 0008, source unique) : le build copie docs/glossaire/ et docs/scenarios/ dans le site ; une page d'aide simple affiche le scénario « Jouer une partie contre l'ordinateur » et les fiches du glossaire (au moins le terme et sa phrase « En bref »), lus à l'exécution depuis ces copies, jamais recopiés à la main.
  6. PWA : manifeste, icônes provisoires simples (pas de logo : un symbole neutre), service worker qui met en cache tous les fichiers du site (dont les 8,7 Mo de données du moteur) pour le hors ligne, et se met à jour proprement à chaque publication (le commit de version.json peut servir de clé de cache).
  7. Site publié (make site, docs/deploiement.md) : l'application à la racine ; les pages de prototype (test du moteur et mesure) restent accessibles, par exemple sous /moteur/ et /moteur/bench.html, et la doc le dit. Ajouter une politique de sécurité du contenu (CSP) dans deploy/Caddyfile, compatible WebAssembly ('wasm-unsafe-eval'), et la vérifier. tests/test_site.sh à jour (manifeste, service worker, aide).
  8. Tests :
    • tests unitaires de ce qui est testable hors navigateur (par exemple la transformation de l'état du domaine en ce que dessine le plateau, les destinations possibles) ;
    • vérification dans un vrai navigateur : une partie jouée par l'interface, au moins jusqu'à plusieurs coups de chaque joueur, dans Chromium et Firefox sans interface (image mcr.microsoft.com/playwright:v1.48.2-noble, déjà présente). Cette vérification est faite et rapportée ; ne l'ajouter à make test que si c'est simple et rapide, en le signalant comme nouvelle dépendance de test (Playwright, Apache-2.0) ;
    • les tests existants restent verts.
  9. Guide de test : compléter docs/guide-test-etape-0.md d'une section « Étape 0d : jouer une partie sur le téléphone » : ouvrir https://ustagammon.sans.pub, installer l'application, jouer une partie complète en suivant docs/scenarios/jouer-une-partie.feature, vérifier le hors ligne (mode avion), et ce qu'il faut rapporter (temps des coups de l'ordinateur ressentis, problèmes rencontrés, captures si besoin).

Contraintes :

  • CLAUDE.md, ADR 0004, 0007, 0008, 0009 : simplicité avant tout, pas de framework, identifiants en anglais, commentaires, docs et textes en français, tout outil dans Docker, -j2 ;
  • aucune règle du jeu réimplémentée : tout vient du moteur, via le domaine ;
  • ne pas modifier engine/gnubg/ ni les références ;
  • ne pas publier (make deploy-prod) : la session principale s'en charge ;
  • 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, captures d'écran du plateau (fichiers dans le dossier de travail temporaire, chemins dans le rapport), résultat de la partie jouée dans les navigateurs sans interface, taille du site, difficultés, écarts au plan.

Critères de réussite

  • Dans Chromium et Firefox sans interface, une partie se joue par l'interface (dés, coups au toucher, coups de l'ordinateur, videau) sans erreur.
  • Le site fonctionne hors ligne après un premier chargement (service worker).
  • make lint et make test passent.

Résultat

Fait en deux sessions de sous-agent (la première interrompue par la fin d'un quota, la seconde l'a reprise), sur la branche plateau-ui-travail.

  • Domaine (app/src/domain/game/game.ts) : reprise d'une partie depuis son journal, plateau vu de chaque camp, demi-coups possibles à la suite de ceux déjà saisis, droit de doubler ; tout vient du moteur.
  • Interface (app/src/ui/) : plateau en SVG (24 cases, barre, pions sortis, dés, videau), saisie au toucher avec cases d'arrivée en surbrillance, annulation, boutons du jet, du videau et de nouvelle partie, coup de l'ordinateur affiché avec son temps, fin de partie (simple, gammon, backgammon, points), reprise depuis localStorage (app/src/storage/), textes dans texts.ts, thème clair et sombre en variables CSS (web/app.css).
  • Assemblage : app/src/main.ts, seul point d'entrée autorisé à tout importer (règles dependency-cruiser) ; moteur dans le Web Worker.
  • Aide (app/src/ui/help.ts) : scénario « Jouer une partie contre l'ordinateur » et fiches du glossaire (terme et « En bref »), lus à l'exécution dans les copies de docs/ faites par make site.
  • PWA : manifeste, icônes provisoires, service worker qui met en cache les 70 fichiers du site (empreinte du contenu comme version), enregistré une fois le moteur chargé.
  • Site : application à la racine, pages de test sous /moteur/ ; CSP dans deploy/Caddyfile, vérifiée par tests/test_site.sh (en-tête) et dans les navigateurs (aucune violation, script en ligne bloqué). Site de 9,1 Mo (70 fichiers, dont 8,6 Mo pour le moteur), archive de 8,8 Mo.
  • Tests : tests unitaires de l'interface et du domaine ; make test-browser (Playwright, Apache-2.0, hors de make test : plusieurs minutes et une image de 3 Go) joue une partie complète dans Chromium et Firefox sans écran : videau proposé des deux côtés, reprise après rechargement, aide, thème sombre, hors ligne, CSP. Les deux passent.
  • Guide de test : section « Étape 0d » de docs/guide-test-etape-0.md.

Deux défauts trouvés par la vérification dans les navigateurs et corrigés :

  1. Pile du moteur WebAssembly : callMain d'Emscripten laissait ses arguments sur la pile (64 Ko) ; vers la 540e commande d'une partie, elle débordait (moteur muet dans Firefox, onglet planté dans Chromium, « memory access out of bounds » sous Node). La pile est remise en place après chaque commande (web/engine-wasm.mjs) ; test de 3000 commandes.
  2. Installation du service worker dans Chromium : elle échouait quand le Worker téléchargeait les données du moteur au même moment ; le service worker est désormais enregistré une fois le moteur chargé.

Écarts et limites :

  • le hors ligne n'est vraiment simulé que dans Chromium (Firefox sous Playwright refuse toute navigation hors ligne) : à confirmer en mode avion sur le téléphone ;
  • temps des coups de l'ordinateur en niveau standard, sur ce PC chargé : 0,6 s au plus dans Chromium, jusqu'à 4,6 s dans Firefox ; le critère de 2 s est à mesurer sur le Fairphone 4 ;
  • thème sombre : les pions foncés sur les cases foncées restent lisibles grâce à leur contour, mais le contraste est le point faible de la palette provisoire.