Compare commits

..
Author SHA1 Message Date
Pierre MARTINandClaude Opus 5.5 1eeb6c95ae Décisions de Pierre du 2026-11-09 ; journal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 19:12:54 +01:00
Pierre MARTINandClaude Opus 5.5 65a4638475 Fusion de la MR !43 : une adresse pour chaque écran, et un lien pour chaque position (0e.4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 19:03:28 +01:00
Pierre MARTINandClaude Opus 5.5 2b8fa1a0b0 Recette design des adresses : barre de l'aide collée, boutons sur une ligne, position en paysage
Barre haute de l'aide en position sticky ; question ou terme ouvert par
son adresse arrêté 12 px sous elle ; surlignage du terme effacé après 2 s.
Boutons en white-space: nowrap ; actions de la position empilées
(Jouer, Copier, Retour). En paysage, plateau à gauche sur toute la
hauteur avec la géométrie du jeu (videau entier, sans menu ni annuler),
colonne de 288 px à droite. Test navigateur : aucun bouton sur deux
lignes, barre de l'aide en vue, actions visibles sans défiler, videau
entier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 18:52:50 +01:00
pierre 8e5be8e339 Fusion de la MR !46 : tutoriel, Jacoby 2026-11-09 17:38:55 +00:00
Pierre MARTINandClaude Opus 5.5 e724368f09 Charte : tutoriel, étape 7 avec la règle de Jacoby, lien « Quitter »
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 18:38:53 +01:00
Pierre MARTINandClaude Opus 5.5 2352a5317f Fusion de la MR !45 : « Grosse erreur » plutôt que « gaffe »
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 18:37:41 +01:00
Pierre MARTINandClaude Opus 5.5 0a88e835d2 Vocabulaire : « Grosse erreur » plutôt que « gaffe » (choix de Pierre)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 18:37:29 +01:00
Pierre MARTINandClaude Opus 5.5 d1db2b33c8 Adresses des écrans : plan clos
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:41:06 +01:00
Pierre MARTIN f74b03fa0d Fusion de main dans adresses-ecrans 2026-11-09 14:41:03 +01:00
Pierre MARTINandClaude Opus 5.5 d26033b7bb Plan 0e.4 : résultat
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:40:16 +01:00
Pierre MARTINandClaude Opus 5.5 2f1d36413e Test navigateur : le coup joué a 10 s pour arriver dans IndexedDB
L'application enregistre sans attendre ; sur le disque dur de la machine
de développement, l'écriture de Firefox peut suivre de loin. En cas
d'échec, le message dit ce que montre l'écran.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:16:48 +01:00
Pierre MARTINandClaude Opus 5.5 9a5a0bb677 Fin de partie : la vibration attend la fin de l'animation du dernier coup
Le dessin lancé au début de l'animation annonçait la fin ; une prise de
l'ordinateur pendant ce dernier coup vibrait ensuite et la remplaçait (vu
dans Firefox par make test-browser).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:16:48 +01:00
pierre dd5954a113 Fusion de la MR !42 : écrans 1e à 1i 2026-11-09 13:01:54 +00:00
Pierre MARTINandClaude Opus 5.5 4cdef612fd Charte : écrans de l'étape 1, lots 1e à 1i (bilan, analyse, IA, leçons, progression, gestes)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:01:51 +01:00
pierre 2eaa8ef304 Fusion de la MR !41 : écrans 1d 2026-11-09 12:53:29 +00:00
Pierre MARTINandClaude Opus 5.5 71a30cb2b6 Charte : écrans de l'étape 1, lot 1d (ligne et feuille du maître, phrases)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 13:53:27 +01:00
pierre 76fd72bbb5 Fusion de la MR !40 : écrans 1c 2026-11-09 12:47:21 +00:00
Pierre MARTINandClaude Opus 5.5 0e816564f9 Charte : écrans de l'étape 1, lot 1c (nouvelle partie, score de match)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 13:47:19 +01:00
Pierre MARTINandClaude Opus 5.5 b40b28d25b Fusion de la MR !39 : plan 0e.5, mise à jour de l'application en un toucher
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 13:46:28 +01:00
Pierre MARTINandClaude Opus 5.5 9a6e82a2db Plan 0e.5 : mise à jour de l'application en un toucher
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 13:45:27 +01:00
Pierre MARTINandClaude Opus 5.5 edcbcea131 Fusion de la MR !38 : plans 1e à 1i
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 21:03:51 +01:00
Pierre MARTINandClaude Opus 5.5 5c75f29f32 Plans 1e à 1i : bilan et analyse, prompts, leçons, progression, plateau soigné
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 21:03:46 +01:00
19 changed files with 756 additions and 112 deletions
+15 -3
View File
@@ -646,7 +646,9 @@ export function startApp(options: AppOptions): void {
/** Une seule vibration par fin de partie. */
function announceEnd(g: Game): void {
const head = g.journal.head;
if (g.state.phase !== "over" || head === announcedEnd) {
// Pendant l'animation du dernier coup, une prise vibrerait après
// la fin et la remplacerait : on l'annonce au dessin qui suit.
if (g.state.phase !== "over" || head === announcedEnd || animating) {
return;
}
announcedEnd = head;
@@ -1262,6 +1264,7 @@ export function startApp(options: AppOptions): void {
? TEXTS.unreadablePosition
: "";
ui.positionBoard.toggleAttribute("hidden", !shown);
ui.position.classList.toggle("with-board", shown);
ui.positionDetail.hidden = !shown;
ui.positionCopy.hidden = !shown;
ui.positionPlay.hidden = state.kind !== "shown" || state.shared.result !== null;
@@ -1269,11 +1272,20 @@ export function startApp(options: AppOptions): void {
return;
}
const shared = state.shared;
const width = ui.positionBoard.clientWidth || Math.min(window.innerWidth, 640);
// En paysage, le plateau occupe toute la hauteur, à gauche de la
// colonne du titre et des actions (app.css), avec la géométrie du
// jeu : la colonne gauche du cadre garde le videau, sans menu ni
// bouton annuler (il ne se joue pas).
const landscape = layout() === "landscape";
const box = ui.positionBoard.getBoundingClientRect();
const width = box.width || Math.min(window.innerWidth, 640);
const height = box.height || window.innerHeight;
const model = boardModel(shared.sides);
renderBoard(
ui.positionBoard,
boardGeometry("portrait", PORTRAIT_ASPECT, width / 640),
landscape
? boardGeometry("landscape", width / height, height / 520)
: boardGeometry("portrait", PORTRAIT_ASPECT, width / 640),
model,
{
dice: [],
+2
View File
@@ -231,6 +231,8 @@ export function createHelpView(container: HTMLElement, tabs: HTMLElement): HelpV
const opened = questions.find((q) => q.id === question) ?? null;
const closed = route.screen === "help" && opened === null ? lastOpen : null;
lastOpen = opened;
// La barre haute de l'aide reste en vue (app.css) : ce qu'on
// amène en haut s'arrête 12 px sous elle (scroll-margin-top).
const target = opened?.section ?? (term === null ? undefined : terms.get(term));
if (target !== undefined) {
target.scrollIntoView({ block: "start" });
+21 -12
View File
@@ -20,7 +20,7 @@ Repris du cadrage (modules, écrans clés, IA générative en V1) et des ADR 001
| 1a. Moteur pour l'analyse (fait) | Niveaux de jeu (plis et bruit de GNU Backgammon : débutant à expert), classement des coups d'un jet avec équités, analyse d'un coup joué et d'une décision de videau, chance d'un jet ; exposés par `engine.h`, `engine-cli` et le Worker, avec positions de référence comparées à gnubg de bureau | — |
| 1b. Parties enregistrées (fait) | Stockage IndexedDB derrière l'interface de stockage existante : plusieurs parties, reprise automatique, branches (« Rejouer d'ici » crée une branche, l'originale reste intacte et rechargeable), export SGF avec variantes | — |
| 1c. Match et réglages | Partie libre ou match en N points, règle Crawford, règle Jacoby, choix du niveau de l'ordinateur, conseil du maître activable ou non ; écran de nouvelle partie | 1a, 1b |
| 1d. Conseil du maître | Après chaque coup du joueur, jamais avant (ADR 0011) : appréciation du moteur (bon coup, imprécision, erreur, gaffe) avec mot, icône et couleur, phrase du maître fondée sur les chiffres du moteur, coup recommandé montré sur demande ; aussi pour les décisions de videau ; calcul en arrière-plan sans retarder le jeu | 1a |
| 1d. Conseil du maître | Après chaque coup du joueur, jamais avant (ADR 0011) : appréciation du moteur (bon coup, imprécision, erreur, grosse erreur) avec mot, icône et couleur, phrase du maître fondée sur les chiffres du moteur, coup recommandé montré sur demande ; aussi pour les décisions de videau ; calcul en arrière-plan sans retarder le jeu | 1a |
| 1e. Fin de partie et analyse | Bilan (ADR 0011) : résultat, coups par appréciation, taux d'erreur comparé à la chance, « le coup à revoir » ; écran d'analyse : graphe d'équité (toucher un point ouvre la position), liste des erreurs filtrable, visualiseur pas à pas coup joué contre meilleur coup, « Rejouer d'ici » | 1a, 1b, 1d |
| 1f. Prompts à copier | Gabarits versionnés et testés (`docs/prompts/` ou équivalent, source unique), bouton « Demander à une IA » sur le plateau, le conseil du maître, l'analyse et la fin de partie ; le moteur décide, l'IA explique | 1d, 1e |
| 1g. Accueil, tutoriel et leçons | Accueil (reprendre, nouvelle partie, prochaine leçon, progression résumée), tutoriel des règles interactif et illustré (0e.5), cinq premières leçons numérotées (ADR 0011) avec positions et « à vous de jouer », progression locale ; contenus CC BY-SA 4.0 en brouillon, à relire par un joueur fort | 0e.5, 1a |
@@ -36,21 +36,30 @@ Repris du cadrage (modules, écrans clés, IA générative en V1) et des ADR 001
ton des phrases du maître est encourageant ; l'espace qu'il occupe est
réservé dans la mise en page (revue UX 0e.2), pas en fenêtre surgissante.
## Questions pour Pierre (ne bloquent pas le démarrage)
## Décisions de Pierre (2026-11-09)
- **Indice** : le cadrage le liste dans le module Jeu, l'ADR 0011 l'a laissé
« à concevoir séparément ». Il n'est pas dans l'étape 1 sans décision.
- **Questionnaire de premier lancement** (choix du parcours) : le parcours 1
seul existe en V1 ; proposé : un simple choix « Je découvre » / « Je connais
les règles » qui oriente vers le tutoriel ou la partie.
- **Indice avant le coup** : pas en V1 (« l'idée est bonne »), à reprendre en
V1.5 ou V2. Le conseil du maître après le coup reste le seul mécanisme de
l'étape 1.
- **Premier lancement** : un choix « Je découvre » / « Je connais les règles »
(fait en 0e.5).
- **Seuils des appréciations** : ceux de GNU Backgammon (douteux 0,03,
mauvais 0,06, très mauvais 0,12 d'équité, `arSkillLevel`), exposés par le
moteur depuis 1a ; à calibrer sur de vraies parties.
- **Relecture des leçons** par un joueur fort de l'Atelier du Huit.
mauvais 0,06, très mauvais 0,12, `arSkillLevel`), exposés par le moteur
depuis 1a ; à recalibrer après les retours de Pierre sur de vraies parties.
Le niveau le plus grave se dit « Grosse erreur », pas « gaffe ».
- **Leçons** : pas de relecture préalable par un joueur fort ; elles sont
livrées telles quelles et améliorées sur retours d'experts. Garde-fous
maintenus : chaque réponse attendue est le meilleur coup du moteur, vérifié
par un test, et le texte n'affirme rien que le moteur ne confirme.
- **Plein écran par défaut dans l'app installée** : non (pas de
`display_override` fullscreen) ; la barre d'état reste visible en portrait.
- **Validés** : le texte du tutoriel et toutes les maquettes du canevas
(accueil, installation, tutoriel, aide, écrans 1c à 1i), ainsi que le cadre
des phrases du maître (section « Les phrases du maître » de
`docs/identite/charte/ecrans-etape-1.md`).
## Ordre de réalisation
1a et 1b d'abord (fondations, sans conflit avec l'interface), en parallèle des
sous-étapes 0e. Puis 1c, 1d, 1e, 1f dans cet ordre ; 1g après la validation des
maquettes d'accueil et de tutoriel par Pierre ; 1h et 1i ; 1j en dernier. Chaque
sous-étapes 0e. Puis 1c, 1d, 1e, 1f dans cet ordre ; 1g (maquettes validées par Pierre) ; 1h et 1i ; 1j en dernier. Chaque
sous-étape a son plan dans `docs/plans/` et sa merge request.
+2 -3
View File
@@ -23,15 +23,14 @@ Demandes de Pierre du 2026-11-03, traitées avant d'ouvrir l'étape 1.
| Sous-étape | Contenu | État | Plan |
| --- | --- | --- | --- |
| 0e.4 | Une adresse par écran (aide, glossaire, menu, mes parties…) et par position, par de vrais liens (ADR 0013 amendé) | ⏳ | `plans/2026-11-03-position-dans-l-adresse.md` |
| 0e.5 | Page d'accueil, installation en un toucher (« Ajouter à l'écran d'accueil », proposée par l'app), tutoriel des règles très didactique avec plateaux illustrés, aide réécrite sur un ton plus humain ; textes à valider par Pierre | ⏳ | `plans/2026-11-03-accueil-tutoriel-aide.md` |
| 0e.5 | Page d'accueil, mise à jour de l'app en un toucher, installation en un toucher (« Ajouter à l'écran d'accueil », proposée par l'app), tutoriel des règles très didactique avec plateaux illustrés, aide réécrite sur un ton plus humain ; textes à valider par Pierre | ⏳ | `plans/2026-11-03-accueil-tutoriel-aide.md` |
## Étapes suivantes
| Étape | Contenu | État |
| --- | --- | --- |
| 1. V1 Jouer et comprendre | Détail et ordre dans `docs/etape-1.md` : moteur pour l'analyse, parties enregistrées et branches, match et réglages, conseil du maître, fin de partie et analyse, prompts, accueil, tutoriel et leçons, progression, plateau soigné, plan de test | 🔄 |
| 2. V1.5 Apprendre | Parcours 2, exercices, progression | ⏳ |
| 2. V1.5 Apprendre | Parcours 2, exercices, progression ; idée retenue par Pierre : un indice avant le coup (sans la réponse) | ⏳ |
| 3. V2 Perfectionnement et IA | Parcours 3 et 4, rollouts, révision espacée | ⏳ |
Chaque étape reçoit son fichier `docs/etape-N.md` avant de démarrer.
+1
View File
@@ -14,6 +14,7 @@ Décisions : ADR 0010. Fichiers du logo et des icônes : `../logo/`.
| Fichier | Contenu |
| --- | --- |
| `guide-de-marque.md` | Règles d'usage : ton, couleurs, typographie, formes, mouvement, icônes, composants |
| `ecrans-etape-1.md` | Écrans de l'étape 1, lot par lot : nouvelle partie et match (1c), conseil du maître (1d), bilan et analyse (1e), IA (1f), leçons (1g), progression (1h), gestes (1i) |
| `accueil-tutoriel-aide.md` | Accueil, installation sans intrusion, tutoriel des règles (forme et première version du texte), aide en questions |
| `recette-ux.md` | Critères de recette design de l'écran de jeu, à cocher sur les captures et sur le téléphone |
| `interaction.md` | Comportement : réponse immédiate, annuler plutôt que confirmer, mouvement, vibrations, clavier, mises en page de l'écran de jeu (portrait, paysage, ordinateur), pictogrammes |
@@ -67,7 +67,8 @@ interrompre.
deux ou trois phrases. Une étape sur deux finit par un écran **« à vous »**
où le joueur fait le geste sur le vrai plateau, contrôlé par le moteur.
- En haut : une barre de progression en huit segments (`accent` pour les
étapes faites) et « Passer » à droite. En bas : « Retour » et
étapes faites) et « Quitter » à droite (pas « Passer » : ce mot appartient au
videau, et le tutoriel enseigne les mots exacts du jeu). En bas : « Retour » et
« Continuer ».
- Les positions sont données par leurs identifiants gnubg (ADR 0013) ; le
plateau est le composant de l'app, non jouable sauf sur les écrans
@@ -113,9 +114,10 @@ interrompre.
flèche 6, un 1 un pion de la flèche 1. Touchez le pion, puis la sortie à
droite. » *À vous* : sortir deux pions avec 6-3.
7. **Gammon et backgammon.** « Une partie gagnée vaut 1 point. Si
l'adversaire n'a sorti aucun pion quand vous finissez, c'est un gammon :
2 points. S'il lui reste en plus un pion sur la barre ou dans votre camp,
c'est un backgammon : 3 points. »
l'adversaire n'a sorti aucun pion, c'est un gammon : 2 points ; s'il lui
reste en plus un pion sur la barre ou dans votre camp, c'est un
backgammon : 3 points. En partie libre, gammon et backgammon ne comptent
que si le videau a été doublé : c'est la règle de Jacoby. »
8. **Le videau.** « Le cube aux faces 2, 4, 8, 16, 32 et 64 sert à miser.
Avant de lancer vos dés, vous pouvez proposer de doubler la valeur de la
partie. L'ordinateur prend, et la partie continue en valant le double, ou il
+253
View File
@@ -0,0 +1,253 @@
# Écrans de l'étape 1
Règles de design des écrans de l'étape 1 (`docs/etape-1.md`), lot par lot.
Maquettes : canevas https://claude.ai/artifact/CQw6vbDpi6XQqVWQa4sANe, rangées
« Étape 1 ». Comportement général : `interaction.md` ; liens : ADR 0013.
## 1c. Nouvelle partie et match
### Écran « Nouvelle partie » (`#/nouvelle-partie`)
Ouvert depuis le menu, l'accueil et la fin de partie. Une seule page, sans
étapes, de haut en bas :
1. **Type de partie** : un sélecteur à deux segments, « Partie libre » et
« Match » (fond `surface-sunken`, segment choisi en `surface` avec
`shadow-raised`).
- **Match** : « Le premier à [3] [5] [7] [11] points », quatre boutons
carrés de 44 px, le choisi en brique. Dessous, une phrase sur la règle de
Crawford, avec le lien vers sa fiche du glossaire.
- **Partie libre** : une phrase sur la règle de Jacoby, avec le lien vers
sa fiche. Pas d'interrupteur pour Crawford ni Jacoby : ce sont les règles
usuelles, et le joueur débutant n'a pas à les choisir.
2. **Niveau de l'ordinateur** : six barres de hauteur croissante, à toucher
(Débutant, Amateur, Intermédiaire, Avancé, Expert, Champion) ; les barres
jusqu'au niveau choisi sont en brique, le niveau choisi porte l'anneau de
focus. Dessous, le nom en `heading` et une phrase qui le décrit en mots de
joueur, jamais en plis ni en bruit :
- Débutant : « Joue simplement et laisse passer des occasions. Idéal pour
vos premières parties. »
- Amateur : « Un joueur qui connaît les règles et commet encore des
erreurs. »
- Intermédiaire : « Un habitué de la table : solide, sans être
implacable. »
- Avancé : « Un bon joueur de club. Peu d'erreurs de pions, un videau
sérieux. »
- Expert : « Très peu d'erreurs. Chaque point se gagne. »
- Champion : « GNU Backgammon à son meilleur niveau sur ce téléphone. »
3. **Pendant la partie** : interrupteur « Conseil du maître », sous-titre
« Une ligne après chacun de vos coups ».
4. **Barre d'action collée en bas** : un seul bouton principal, qui dit ce
qu'il lance (« Commencer la partie » ou « Commencer le match en 5
points »), et la mention « Vos choix sont gardés pour la prochaine fois. ».
Par défaut : partie libre, Intermédiaire, conseil activé. Fermer (croix en
haut à gauche, ou retour du téléphone) ne change rien à la partie en cours.
Commencer une nouvelle partie alors qu'une partie est en cours suit la règle
« annuler plutôt que confirmer » (bandeau « Reprendre l'ancienne »).
### Score de match sur l'écran de jeu
- **Portrait** : une ligne `overline` centrée sous la barre haute, « Match en
5 points », complétée par « · partie Crawford » pendant la partie Crawford ;
dans chaque bandeau, une pastille carrée (contour `border-strong`, chiffre
gras) avec le score de ce joueur, juste avant son compte de points.
- **Paysage** : dans la colonne de commandes, sous le menu, une case qui
empile le score de l'ordinateur, « sur 5 », puis le vôtre (même ordre que
les bandeaux : l'ordinateur en haut, vous en bas).
- Le score se lit aussi au lecteur d'écran : « Match en 5 points :
l'ordinateur 4, vous 2 ».
- Pendant la partie Crawford, le videau n'est pas affiché : il n'existe pas
pour cette partie.
## 1d. Conseil du maître
### La ligne du maître
Maquette « Portrait révisé : conseil du maître après le coup ». Une ligne de
44 px, réservée sous le bandeau du joueur (elle ne pousse rien) :
- icône d'appréciation, puis le **mot en couleur** suivi d'un point
(« Bon coup. »), puis la **phrase du maître en italique**, sur une ligne,
tronquée par des points de suspension si elle est trop longue, et un
chevron ;
- elle apparaît **après** la validation du coup du joueur (ou sa décision de
videau), dès que l'analyse est prête, en fondu de `duration-fast` ; elle
s'efface à l'action suivante du joueur (lancer les dés, toucher un pion) ;
- **pendant que le joueur réfléchit, rien n'apparaît** ; si l'analyse arrive
trop tard (le joueur a déjà relancé), la ligne n'apparaît pas, et le coup
reste commenté dans le bilan ;
- conseil désactivé : la ligne reste réservée et vide (la mise en page ne
bouge pas).
### Les mots
| Seuil gnubg (écart d'équité) | Mot | Couleur | Icône |
| --- | --- | --- | --- |
| meilleur coup, ou moins de 0,03 | Bon coup | `verdict-good` | coche |
| 0,03 à 0,06 (doubtful) | Imprécision | `verdict-doubtful` | point d'interrogation |
| 0,06 à 0,12 (bad) | Erreur | `verdict-bad` | croix |
| plus de 0,12 (very bad) | Grosse erreur | `verdict-bad` | croix |
Pour le videau : « Bon choix » et « Bonne décision » remplacent « Bon coup ».
« Grosse erreur » est la recommandation de design ; `docs/etape-1.md` écrit
« gaffe », et le choix final revient à Pierre (ton).
### La feuille d'explication
Maquette « Feuille d'explication du maître ». Toucher la ligne ouvre une
feuille qui monte du bas (`duration-moderate`), au-dessus du plateau voilé :
1. le mot d'appréciation et le contexte (« Erreur · votre 3-1 d'ouverture ») ;
2. le titre : le coup du maître, à la première personne (« J'aurais joué
8/5 6/5 ») ; pour un bon coup : « C'est aussi le coup que j'aurais joué » ;
3. la phrase du maître, en italique ;
4. un sélecteur à deux segments, « Votre coup » et « Le coup du maître », au-
dessus d'un plateau qui montre la position d'avant le coup ; le coup choisi
se joue pion par pion (`duration-fast`), avec un tracé du départ à
l'arrivée : `text` pour votre coup, `verdict-good` pour celui du maître ;
5. « Voir les chiffres du moteur » (repliée par défaut) : le classement des
coups avec leur écart d'équité et le niveau d'évaluation ; votre coup est en
gras ;
6. « Demander à une IA » (1f) et « Fermer ».
Le lien vers la fiche d'un concept du glossaire n'apparaît que si le moteur
permet de le déterminer, sinon rien.
### Les phrases du maître
- **Le maître parle à la première personne et vouvoie** : « J'aurais joué… »,
jamais « Vous auriez dû… », jamais « Il fallait… ».
- **Il ne cite que des faits** : le coup recommandé, le rang de votre coup,
les pions frappés, les points faits ou laissés (lisibles dans le coup
lui-même), l'écart d'équité sur demande. Aucune explication stratégique
inventée (« la case la plus précieuse », « trop risqué ») : celles-là
viendront des leçons, relues par un joueur fort.
- **Trois parties au plus** : ce que vous avez fait, ce que le maître préfère,
un fait qui les distingue. Moins de 140 caractères pour la ligne ; la
feuille peut aller jusqu'à deux phrases.
- **Ton** : encourageant et sobre. Pas de point d'exclamation, pas d'émoji, pas
de superlatif. Les bons coups ont plusieurs formules qui tournent
(« C'est le coup du maître. », « Exactement ce que j'aurais joué. »,
« Bien vu. »), pour ne pas lasser.
- **Exemples** (gabarits à remplir avec les données du moteur) :
- Bon coup : « Bien vu. 8/5 6/5 fait votre point 5. »
- Imprécision : « J'aurais joué {meilleur}. Votre coup était le
{rang}e choix, tout près. »
- Erreur : « J'aurais joué {meilleur}. Votre {joué} était le {rang}e
choix. »
- Videau, prise juste : « Bonne décision : prendre était le bon choix. »
- Videau, double manqué : « Ici, j'aurais doublé. »
## 1e. Bilan et analyse
### Bilan (`#/parties/<id>/bilan`)
Maquette « Bilan de fin de partie ». Il remplace le panneau de fin de partie
quand l'analyse est prête ; avant, le panneau actuel s'affiche et le bilan le
remplace en place, sans saut.
1. `overline` « Fin de partie », titre `display` (« Vous gagnez », « L'ordinateur
gagne un gammon »), puis une phrase : le résultat en points et une
lecture simple du jeu et des dés (« Votre jeu a fait la différence, avec
des dés un peu contre vous. »), construite à partir du taux d'erreur et de
la chance donnés par le moteur, jamais inventée.
2. Trois cases côte à côte : le nombre de bons coups, d'imprécisions et
d'erreurs (grosses erreurs comprises), chiffre en 28 px, mot et icône en
couleur d'appréciation.
3. Deux jauges sur une ligne chacune :
- « Votre jeu » : le taux d'erreur traduit en niveau (correspondance avec
les classements de gnubg documentée, mêmes noms que les niveaux de
l'ordinateur), jauge en `verdict-good` ;
- « Les dés » : la chance, jauge centrée (à droite si elle vous a aidé, à
gauche sinon), en `border-strong`, avec trois mots possibles : « pour
vous », « équilibrés », « contre vous » (« un peu » ou « nettement »
selon l'écart).
Lien « Comment lire ces mesures » vers les fiches du glossaire.
4. « Le coup à revoir » : la plus grosse erreur, mini-plateau, mot
d'appréciation, coup joué et coup du maître ; « Revoir ce coup » (ouvre
l'analyse sur ce coup) et « Demander à une IA ». Sans erreur ni
imprécision : la carte dit « Aucun coup à revoir : vous avez joué comme le
maître. »
5. Barre d'actions : « Voir l'analyse » et « Nouvelle partie » (principale).
### Analyse (`#/parties/<id>/analyse/<coup>`)
Maquette interactive « Analyse ». Une page qui défile, de haut en bas :
1. **Courbe de vos chances de gagner**, coup après coup, en pourcentage (pas
en équité : un débutant lit « 41 % », pas « −0,18 »). Tracé `text` de 2 px,
aire `accent-soft`, ligne pointillée à 50 %. Les coups commentés sont des
points de couleur d'appréciation, les autres de petits points `text`.
**On parcourt la courbe du doigt** : chaque coup possède une bande verticale
sur toute la hauteur du graphique, qui sert de cible tactile ; le coup
choisi porte un trait vertical brique et un point cerclé.
2. **Visualiseur** : « ‹ Coup 7 sur 24 · 6-1 › », le plateau de la position
d'avant le coup, la phrase du maître, le sélecteur « Votre coup » / « Le
coup du maître » de la feuille 1d, puis « Rejouer d'ici » (crée une
branche, ADR 0011 et 1b) et « Demander à une IA ».
3. **Les coups à revoir** : filtres en pastilles de 44 px (« Tout »,
« Erreurs », « Imprécisions », avec leurs nombres), puis une ligne par
coup : numéro, dés, coup joué, mot d'appréciation. Toucher une ligne place
le visualiseur et la courbe sur ce coup.
Sur ordinateur, `←` et `→` passent d'un coup à l'autre.
## 1f. Demander à une IA
Le bouton est secondaire partout, jamais l'action principale, avec l'icône
de copie : dans la feuille du maître (1d), dans la carte « Le coup à
revoir » du bilan, dans le visualiseur de l'analyse, et dans une fiche du
glossaire. Après la copie, le bandeau « Prompt copié » (`verdict-good`, 2 s),
puis le texte du prompt reste lisible dans une zone `surface-sunken`
repliable, pour qui veut le relire avant de le coller (composant `AskAI`).
## 1g. Leçons (`#/lecons`, `#/lecons/<n>/<écran>`)
Maquette « Leçons ». En tête, un rappel discret « Les règles du jeu ·
revoir » (le tutoriel de 0e.5). Puis les cinq leçons numérotées, dans
l'ordre, chacune avec une pastille ronde à son numéro :
- terminée : pastille `verdict-good`, « Terminée » ;
- en cours : carte bordée de brique, pastille brique, « 2 exercices sur 4 »,
barre de progression, bouton « Continuer » (seule action principale de
l'écran) ;
- à commencer : pastille au contour `border-strong`.
Toutes les leçons restent ouvertes : l'ordre est conseillé, jamais imposé.
Les écrans d'une leçon suivent la forme du tutoriel (barre de progression,
« Passer », « Retour », « Continuer », écrans « à vous » contrôlés par le
moteur).
## 1h. Progression (`#/progression`)
Maquette « Progression ».
- Une phrase de synthèse, factuelle (« Sur vos 8 dernières parties, vous
faites moins d'erreurs : votre taux d'erreur est passé de 14 à 7. »). Si la
tendance monte, la phrase reste encourageante et sans reproche (« Votre
taux d'erreur remonte un peu ces derniers jours. Les leçons sont là
pour reprendre un point. »).
- La courbe du taux d'erreur par partie terminée, avec le rappel « plus bas,
c'est mieux » dans son titre, le dernier point en `verdict-good`, et un lien
vers la fiche « taux d'erreur ».
- La liste des parties (résultat, date, niveau de l'ordinateur, taux d'erreur
en chiffre gras aligné à droite), chacune ouvrant son bilan.
- Moins de trois parties analysées : pas de courbe, une phrase « Votre
courbe apparaîtra après trois parties. ».
## 1i. Gestes sur le plateau
- **Glisser-déposer** : le pion suit le doigt, agrandi à 115 % et décalé de
12 px vers le haut pour rester visible sous le doigt ; à son départ, un pion
fantôme en pointillés ; les cases d'arrivée permises gardent leur voile et
leur anneau (révision UX). Lâché sur une case permise : il s'y pose
(`duration-fast`, vibration « pion posé ») ; lâché ailleurs : il revient à
sa place en `duration-fast`, sans vibration ni message.
- **Double toucher** : joue le coup évident quand il existe (un seul coup légal
complet commence par ce pion, ou le moteur n'en propose qu'un) ; sinon, il
vaut un simple toucher, sans message d'erreur.
- **Toucher simple** : inchangé (pion puis case, ou avance directe s'il n'a
qu'une case d'arrivée).
- Les trois gestes coexistent ; aucun n'est expliqué par une fenêtre : le
tutoriel et l'aide en parlent.
+35
View File
@@ -163,3 +163,38 @@ Une entrée datée par session : ce qui a été fait, décidé, et la suite.
Playwright (412×915, 915×412, 844×320, clair et sombre) avant l'essai de
Pierre sur le téléphone.
- Réglage en main des vibrations sur le Fairphone 4.
## 2026-11-03 → 2026-11-09 : retours 0e et début de l'étape 1
**Fait**
- Retours du premier test sur téléphone, traités et publiés :
- saisie du coup (MR !28) ;
- révision UX arbitrée par la session « identité » (MR !35) ;
- parties enregistrées avec branches et export SGF, sous-étape 1b (MR !37).
- Fusionnés :
- adresses par écran et liens de position, sous-étape 0e.4 (MR !43). L'ADR 0013 est amendé : une adresse par écran, de vrais liens ;
- moteur pour l'analyse et les niveaux, sous-étape 1a (MR !32) : 9 niveaux de gnubg, analyse de coup et de videau, chance d'un jet, concordance avec gnubg de bureau à 1,64e-3 près.
- Décisions techniques :
- le cache d'évaluation de gnubg est vidé à chaque calcul, pour que les résultats ne dépendent que de leurs arguments ;
- le schéma IndexedDB est un format durable (amendement de l'ADR 0007) ;
- l'image Docker de test est nommée par worktree, ce qui corrige l'instabilité des tests concurrents.
- Plans écrits pour toute l'étape 1, de 1c à 1i. La session « identité » a maquetté tous les écrans (MR !40 à !42).
- Décisions et validations de Pierre (2026-11-09), consignées dans `docs/etape-1.md` :
- « Grosse erreur » plutôt que « gaffe » ;
- pas d'indice en V1 ;
- pas de relecture préalable des leçons ;
- pas de plein écran par défaut ;
- seuils de gnubg ;
- validés : le texte du tutoriel, les maquettes et le cadre des phrases du maître.
- Nouvelles demandes de Pierre, intégrées aux plans :
- une adresse pour chaque écran ;
- la mise à jour forcée de l'app (0e.5) ;
- un coach bienveillant, mis en avant mais discret.
**Reste à faire**
- 0e.5 (MR !44) : appliquer les corrections de recette T1 à T5, puis fusionner et publier.
- Publication bloquée : `RESTART.sh`, un fichier local non suivi par git, fait échouer `make lint`, et donc `make deploy-prod`.
- Étape 1 :
- 1c en cours ;
- puis 1d, 1e, 1f, 1g, 1h et 1i ;
- enfin le plan de test 1j.
+17 -2
View File
@@ -41,9 +41,24 @@ Traiter les points 2 et 3 du message de Pierre du 2026-11-03 et sa demande d'ins
- « Les mots du jeu » : les cartes du glossaire, avec une recherche ;
- « À propos » : licence, crédits, version ;
- le tutoriel est proposé en tête de l'aide.
5. **Scénarios Gherkin** : premier lancement, installation, suivre le tutoriel.
6. **`make test-browser`** :
5. **Mise à jour de l'application** (demande de Pierre du 2026-11-09). Quand
le service worker trouve une nouvelle version, l'app affiche un bandeau
discret « Nouvelle version disponible » avec « Mettre à jour », jamais
pendant un coup en cours. Toucher le bouton active la nouvelle version
(`skipWaiting`, puis rechargement au `controllerchange`), et la partie
reprend où elle en était. Le menu propose aussi « Rechercher une mise à
jour » (`registration.update()`), qui dit « Vous avez la dernière
version » sinon. L'onglet « À propos » affiche la version publiée (le
commit de `version.json`). Aujourd'hui, `web/sw.js` appelle
`skipWaiting()` dès l'installation et efface l'ancien cache : une page
ouverte garde son ancien code et peut ne plus trouver les anciens
fichiers. Le nouveau service worker doit attendre l'accord du joueur, sauf
à la toute première installation.
6. **Scénarios Gherkin** : premier lancement, installation, suivre le tutoriel.
7. **`make test-browser`** :
- premier lancement, tutoriel complet avec ses écrans « à vous », aide ;
- mise à jour : publier deux versions du site de test l'une après
l'autre, voir le bandeau, mettre à jour, retrouver la partie ;
- `beforeinstallprompt` simulé dans Chromium ;
- captures dans `build/browser-shots/accueil-tutoriel/` pour la recette design.
+46
View File
@@ -0,0 +1,46 @@
# Fin de partie et analyse (1e)
- **Date** : 2026-11-03
- **Étape** : 1e (`docs/etape-1.md`)
- **Statut** : prévu (après 1d)
- **Exécutant** : sous-agent general-purpose, dans un worktree
## Objectif
Que le joueur comprenne sa partie : le bilan de fin de partie (ADR 0011), puis un écran d'analyse avec graphe d'équité, liste des erreurs, visualiseur et « Rejouer d'ici ».
## Consignes
Respecter les ADR 0007, 0008, 0011 et 0013. Chaque écran a son adresse : `#/parties/<id>/bilan` et `#/parties/<id>/analyse/<coup>`, à ajouter au tableau de l'ADR 0013 par un amendement. Pour la charte, suivre les maquettes « Fin de partie » du canevas. Aucune nouvelle dépendance : le graphe est dessiné en SVG à la main.
1. **Analyse de la partie.** Elle réutilise les analyses rangées par 1d (coups et videau) et complète celles qui manquent en arrière-plan, de façon progressive et annulable. Elle donne :
- le taux d'erreur à la manière de gnubg ;
- la chance aux dés (`luck`) pour chaque camp ;
- le décompte des coups par appréciation.
2. **Bilan**, dans le panneau de fin de partie existant :
- le résultat ;
- les coups par appréciation ;
- le taux d'erreur comparé à la chance, en phrases simples ;
- « le coup à revoir » : la plus grosse erreur, avec sa position, le coup joué et le meilleur coup ;
- un emplacement pour le bouton « Demander à une IA » (1f) ;
- « Voir l'analyse ».
3. **Écran d'analyse** :
- un graphe d'équité où toucher un point ouvre la position ;
- la liste des erreurs, filtrable par appréciation ;
- un visualiseur pas à pas qui montre sur le plateau le coup joué contre le meilleur coup, avec des pions animés ;
- « Rejouer d'ici », qui crée une branche (domaine de 1b) sans toucher l'original.
4. **Tests** :
- scénarios Gherkin : bilan, coup à revoir, rejouer d'ici, chacun avec des dés imposés ;
- fiches du glossaire : taux d'erreur, graphe d'équité si absentes ;
- `make lint`, `make test`, `make test-browser`, avec des captures pour la recette design.
## Critères de réussite
- Après une partie, le bilan nomme le coup à revoir, conforme à l'analyse du moteur.
- L'analyse s'ouvre par son adresse.
- « Rejouer d'ici » crée une branche.
- Les trois commandes passent.
## Résultat
À remplir à la clôture.
+9 -5
View File
@@ -29,17 +29,21 @@ du moteur.
(événement d'analyse rétrocompatible, ou cache séparé justifié) pour servir
au bilan (1e) sans recalcul.
2. **Ligne du maître** : icône d'appréciation, mot en couleur (« Bon coup. »,
« Imprécision. », « Erreur. », « Gaffe. » : correspondance avec les seuils
« Imprécision. », « Erreur. », « Grosse erreur. » (choix de Pierre du 2026-11-09, plutôt que « gaffe ») : correspondance avec les seuils
de gnubg documentée), phrase courte en italique, chevron. Toucher ouvre la
feuille d'explication : coup joué et meilleur coup montrés sur le plateau
(pions animés, ADR 0011 « comparaison visuelle »), écart d'équité seulement
sur demande, lien vers la fiche du glossaire du concept en jeu si le moteur
permet de le déterminer, sinon rien.
3. **Phrases du maître** : gabarits versionnés en français dans une source
unique sous `docs/` (CC BY-SA 4.0), ton encourageant, jamais culpabilisant ;
elles ne citent que des faits du moteur (coup recommandé, rang, pions
frappés, écart) : aucune explication inventée. Première version à faire
valider par Pierre (ton).
unique sous `docs/` (CC BY-SA 4.0). Référence de ton, approuvée par Pierre
le 2026-11-09 : la section « Les phrases du maître » de
`docs/identite/charte/ecrans-etape-1.md` (1re personne et vouvoiement,
faits du moteur uniquement, moins de 140 caractères, ton sobre sans
exclamation, formules qui tournent pour les bons coups, exemples). Un test
vérifie la longueur et l'absence de « ! » sur chaque gabarit rendu. Les
phrases livrées passent par la session « identité » puis par Pierre pour
la relecture finale.
4. **Discret** : rien n'apparaît pendant que le joueur réfléchit ; la ligne
s'efface à l'action suivante ; désactivable (réglage de 1c et menu).
5. **Videau** : même traitement pour doubler, ne pas doubler, prendre, passer.
+36
View File
@@ -0,0 +1,36 @@
# Cinq premières leçons et accueil complet (1g)
- **Date** : 2026-11-03
- **Étape** : 1g (`docs/etape-1.md`) ; le tutoriel des règles est traité en 0e.5
- **Statut** : prévu (après 0e.5 et 1d)
- **Exécutant** : sous-agent general-purpose, dans un worktree
## Objectif
Mettre en place les cinq premières leçons numérotées de l'ADR 0011. Chaque leçon est faite d'écrans courts : une explication, une position animée, puis « à vous de jouer ». La progression est stockée localement, et l'accueil propose « Continuer la leçon ».
## Consignes
1. **Contenus.** Ils vivent dans une source unique sous `docs/lecons/`, sous licence CC BY-SA 4.0, au même format que le tutoriel de 0e.5 (réutiliser son moteur d'écrans). Cinq leçons du parcours 2 du cadrage, au niveau débutant :
1. Les ouvertures : bien jouer ses premiers dés.
2. Construire ses cases et faire des points.
3. Frapper ou ne pas frapper : sécurité et risque.
4. La course et le compte de points.
5. Sortir ses pions sans se faire frapper.
Chaque leçon compte deux à quatre exercices « à vous ». Leurs positions sont données par identifiants gnubg. La réponse attendue est le meilleur coup du moteur à 2 plis, vérifié par un test. Le texte n'affirme rien que le moteur ne confirme. Décision de Pierre du 2026-11-09 : pas de relecture préalable par un joueur fort ; les leçons sont livrées telles quelles et améliorées sur retours d'experts. Les garde-fous restent : chaque réponse attendue est le meilleur coup du moteur, vérifié par un test, et le texte n'affirme rien que le moteur ne confirme.
2. **Progression.** Elle est enregistrée dans le stockage de 1b, dans le magasin `settings` ou dans un nouveau magasin avec montée de version (ADR 0007 amendé). Chaque leçon a son état : à commencer, « 2 exercices sur 4 », terminée.
3. **Adresses** : `#/lecons` et `#/lecons/<n>/<écran>`, à ajouter au tableau de l'ADR 0013.
4. **Accueil.** Il reçoit la carte « prochaine leçon » prévue par la charte.
5. **Tests.**
- Pour chaque exercice, la position est valide et la réponse attendue est bien le meilleur coup du moteur.
- Scénarios Gherkin.
- `make lint`, `make test`, `make test-browser`, avec captures.
## Critères de réussite
Les cinq leçons se suivent jusqu'au bout. Chaque exercice accepte le coup du moteur et commente les autres avec bienveillance. Les trois commandes passent.
## Résultat
À remplir à la clôture.
+32
View File
@@ -0,0 +1,32 @@
# Plateau soigné : gestes et finitions (1i)
- **Date** : 2026-11-03
- **Étape** : 1i (`docs/etape-1.md`)
- **Statut** : prévu
- **Exécutant** : sous-agent general-purpose, dans un worktree
## Objectif
Terminer le plateau soigné du cadrage, une fois la révision UX en place :
- les gestes naturels (principe d'expérience 2) ;
- les écarts restants avec `docs/identite/charte/board.js`.
## Consignes
1. **Glisser-déposer.** On peut glisser un pion en plus du toucher pion puis destination. Le pion suit le doigt, et le lâcher hors d'une case permise le ramène à sa place. Les cibles restent celles du moteur.
2. **Double toucher.** Un double toucher sur un pion joue le seul coup évident (cadrage). Un « coup évident » est défini ainsi : un seul coup légal complet commence par ce pion, ou le moteur ne propose qu'un coup. Sinon, rien ne se passe.
3. **Écarts restants avec board.js.** Les relever par comparaison visuelle (filet des pions, numéros sur le cadre, videau…) et les corriger.
4. **Clavier.** Faire ce que la révision UX a laissé :
- le choix des pions aux flèches ;
- l'affichage des raccourcis en `kbd` quand un clavier est détecté (interaction.md).
5. **Tests.**
- Scénarios Gherkin si un nouveau comportement est visible.
- `make lint`, `make test`, `make test-browser` (glisser-déposer simulé, double toucher), avec captures pour la recette design.
## Critères de réussite
Un coup se joue entièrement au glisser-déposer. Le double toucher joue le coup évident. Les trois commandes passent.
## Résultat
À remplir à la clôture.
@@ -1,50 +0,0 @@
# Une adresse pour chaque écran, et pour chaque position (0e.4)
- **Date** : 2026-11-03
- **Étape** : 0e.4 (feuille de route)
- **Statut** : prévu
- **Exécutant** : sous-agent general-purpose, dans un worktree
## Objectif
Appliquer l'ADR 0013 et son amendement du 2026-11-03 : chaque écran (aide,
glossaire, menu, mes parties…) a son adresse et s'ouvre par un vrai lien ;
chaque position du plateau a un lien direct.
## Consignes
ADR 0007, 0008 et 0013. Aucune nouvelle dépendance.
0. **Routeur** : un petit module pur (sans dépendance) qui traduit le
fragment en écran et inversement, testé unitairement ; l'app dessine
l'écran demandé par l'adresse, sur `hashchange`. Tous les écrans existants
(aide et ses questions, glossaire et ses termes, à propos, menu, mes
parties de 1b) passent par lui, avec de vrais liens `<a href>` ; le bouton
retour du navigateur ramène à l'écran précédent. Les actions de jeu restent
des boutons.
1. Lecture de `#/position/<Position ID>/<Match ID>` ; validation par le
moteur ; message clair si illisible.
2. Vue de la position, non jouable, avec « Jouer à partir d'ici » (nouvelle
partie depuis la position, la partie en cours restant reprenable) et
« Retour à ma partie ».
3. Domaine : démarrer une partie depuis une position et un Match ID donnés
(événement GameStarted étendu de façon rétrocompatible, ou équivalent
justifié), sans règle réimplémentée.
4. « Copier le lien de la position » depuis le menu de la partie et depuis la
vue du coup de l'ordinateur ; le lien copié rouvre exactement la même
position (test aller-retour).
5. Scénarios Gherkin (« Ouvrir un lien vers une position », « Partager la
position »), fiches du glossaire (Position ID, Match ID si absentes).
6. `make lint`, `make test`, `make test-browser` : chaque adresse du tableau
de l'ADR ouverte directement dans un nouvel onglet montre le bon écran ;
« retour » du navigateur ; liens reconnus comme liens (rôle `link`) ;
position : ouvrir un lien, jouer à partir d'ici, revenir, lien invalide.
## Critères de réussite
Un lien copié depuis l'app, ouvert dans un nouvel onglet, montre la même
position ; les trois commandes passent.
## Résultat
À remplir à la clôture.
+30
View File
@@ -0,0 +1,30 @@
# Progression (1h)
- **Date** : 2026-11-03
- **Étape** : 1h (`docs/etape-1.md`)
- **Statut** : prévu (après 1e)
- **Exécutant** : sous-agent general-purpose, dans un worktree
## Objectif
Mettre en place la progression du cadrage pour la V1 : l'historique des parties et la courbe du taux d'erreur.
## Consignes
1. **Écran `#/progression`.** Il est conforme à la charte et s'ajoute au tableau de l'ADR 0013. Il montre :
- la courbe du taux d'erreur par partie terminée, en SVG à la main, sans dépendance ;
- la liste des parties, reliée à leur bilan et à leur analyse (1e) ;
- un résumé en une phrase.
2. **Accueil.** Il reçoit la « ligne de progression » de la charte.
3. **Données.** Elles proviennent des analyses enregistrées : rien n'est recalculé si l'analyse existe. Une partie non analysée est analysée en arrière-plan.
4. **Tests.**
- Scénarios Gherkin.
- `make lint`, `make test`, `make test-browser` avec trois parties jouées, et captures.
## Critères de réussite
Après trois parties, la courbe a trois points égaux aux taux d'erreur des bilans. Les trois commandes passent.
## Résultat
À remplir à la clôture.
+42
View File
@@ -0,0 +1,42 @@
# Prompts à copier (1f)
- **Date** : 2026-11-03
- **Étape** : 1f (`docs/etape-1.md`)
- **Statut** : prévu (après 1e)
- **Exécutant** : sous-agent general-purpose, dans un worktree
## Objectif
Mettre en place la section « IA générative en V1 » du cadrage. Un bouton « Demander à une IA » copie un prompt rempli avec le contexte exact. Le moteur décide, l'IA explique. L'app n'appelle aucune IA.
## Consignes
1. **Gabarits.** Les cinq gabarits du cadrage sont versionnés dans une source unique sous `docs/prompts/`, sous licence CC BY-SA 4.0 :
- « pourquoi ce coup est meilleur » ;
- « expliquer cette décision de videau » ;
- « bilan de ma partie » ;
- « expliquer ce concept » ;
- « créer des exercices », à garder pour la progression.
Chaque gabarit contient :
- le rôle et le niveau du joueur ;
- le Position ID et le Match ID ;
- un diagramme texte de la position, produit à partir du plateau décodé par le moteur ;
- les chiffres du moteur, avec le niveau d'évaluation ;
- la consigne de ne pas contredire ces chiffres ;
- la langue.
Ajouter aussi le lien `#/position/...` de l'ADR 0013, avec l'adresse publique du site.
2. **Remplissage.** Une fonction pure remplit les gabarits. Elle est testée sur des analyses réelles du moteur : aucun champ vide ni `{inconnu}`, et les chiffres sont identiques à ceux du moteur.
3. **Boutons.** Ils sont placés sur le conseil du maître (feuille d'explication), le bilan, l'analyse et la fiche d'un terme du glossaire. La copie passe par `navigator.clipboard`, avec un repli de sélection du texte. Un bandeau « Prompt copié » l'accompagne, et le texte reste visible pour qui veut le relire.
4. **Tests.**
- Scénarios Gherkin : copier le prompt d'une erreur, et celui d'un bilan.
- `make lint`, `make test` et `make test-browser`, avec lecture du presse-papiers dans Chromium et captures.
## Critères de réussite
Le prompt copié après une erreur contient la position, les identifiants gnubg et le top 3 du moteur avec les mêmes chiffres que l'app. Les trois commandes passent.
## Résultat
À remplir à la clôture.
+111 -4
View File
@@ -300,10 +300,49 @@ async function checkStable(when) {
stableChecks++;
}
/** Les boutons visibles d'une page dont le texte passe à la ligne : un
* bouton tient sur une ligne et fait 48 px de haut (charte). Un bouton plus
* haut que 48 px n'est refusé que si son texte occupe plusieurs lignes ;
* un texte coupé par le bord (white-space: nowrap) est refusé aussi. */
async function wrappedButtons(target) {
return target.evaluate(() => {
const wrapped = [];
for (const el of document.querySelectorAll("button, a.button")) {
const r = el.getBoundingClientRect();
if (el.closest("[hidden]") || r.width === 0 || r.height === 0) {
continue;
}
const name = el.id ? `#${el.id}` : el.textContent.trim();
const tops = new Set();
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
if (node.textContent.trim() === "") {
continue;
}
const range = document.createRange();
range.selectNodeContents(node);
for (const line of range.getClientRects()) {
if (line.width > 0 && ![...tops].some((top) => Math.abs(top - line.top) < 4)) {
tops.add(line.top);
}
}
}
if (r.height > 48.5 && tops.size > 1) {
wrapped.push(`${name} sur ${tops.size} lignes (${Math.round(r.height)} px)`);
} else if (el.scrollWidth > el.clientWidth + 1) {
wrapped.push(`${name} coupé (${el.scrollWidth} px pour ${el.clientWidth})`);
}
}
return wrapped;
});
}
/** Les critères de la recette vérifiables sur la vue courante. Renvoie des
* mesures pour le compte rendu. */
async function checkView(width, height, when) {
const where = `${width}x${height} (${when})`;
const wrapped = await wrappedButtons(page);
check(wrapped.length === 0, `${where} : boutons sur plusieurs lignes : ${wrapped.join(", ")}`);
const seen = await page.evaluate(() => {
const visible = (el) => {
if (el.closest("[hidden]")) {
@@ -762,6 +801,65 @@ async function checkAddresses() {
await expectScreen(tab, `capture ${name}`, expected);
await sleep(300);
await tab.screenshot({ path: `${dir}/${browserName}-${name}-${width}x${height}.png` });
const where = `${name} ${width}x${height}`;
const wrapped = await wrappedButtons(tab);
check(wrapped.length === 0, `${where} : boutons sur plusieurs lignes : ${wrapped.join(", ")}`);
if (name === "question" || name === "terme") {
// La barre haute de l'aide reste en vue, et ce qu'on a
// ouvert s'arrête juste sous elle.
const seen = await tab.evaluate(() => {
const bar = document.getElementById("help-bar").getBoundingClientRect();
const target = document.querySelector(
"#help-play .question.open, #help-glossary dt.current",
).getBoundingClientRect();
return { scrolled: window.scrollY, barTop: bar.top, barBottom: bar.bottom, top: target.top };
});
check(seen.scrolled > 0 && Math.abs(seen.barTop) <= 1,
`${where} : barre de l'aide hors de la vue (${JSON.stringify(seen)})`);
check(Math.abs(seen.top - (seen.barBottom + 12)) <= 2,
`${where} : élément à ${seen.top} px, barre jusqu'à ${seen.barBottom} px`);
}
if (name === "position") {
// Les trois actions, dans l'ordre, visibles sans défiler.
const seen = await tab.evaluate(() => ({
scrolled: window.scrollY,
order: [...document.querySelectorAll(".position-actions > *")].map((el) => el.id),
boxes: ["position-play", "position-copy", "position-back"].map((id) => {
const r = document.getElementById(id).getBoundingClientRect();
return [r.left, r.top, r.right, r.bottom];
}),
board: (() => {
const r = document.getElementById("position-board").getBoundingClientRect();
return [r.left, r.top, r.width, r.height];
})(),
cube: (() => {
const r = document.querySelector("#position-board .cube-face")?.getBoundingClientRect();
return r === undefined ? null : [r.left, r.top, r.right, r.bottom];
})(),
controls: document.querySelectorAll("#position-board [data-action]").length,
}));
check(seen.order.join() === "position-play,position-copy,position-back",
`${where} : actions dans l'ordre ${seen.order}`);
const inView = seen.boxes.every(([l, t, r, b]) => l >= 0 && t >= 0 && r <= width && b <= height);
if (width > height) {
check(seen.scrolled === 0 && inView,
`${where} : actions hors de la vue sans défiler : ${JSON.stringify(seen.boxes)}`);
check(seen.board[0] === 0 && seen.board[1] === 0 && Math.abs(seen.board[3] - height) <= 1,
`${where} : plateau pas sur toute la hauteur à gauche : ${seen.board}`);
// Le videau, dans la colonne gauche du cadre, entier à
// l'écran ; ni menu ni annuler : la position ne se joue pas.
const [cl, ct, cr, cb] = seen.cube ?? [-1, -1, -1, -1];
check(seen.cube !== null && cl >= 0 && ct >= 0 && cr <= seen.board[2] && cb <= height,
`${where} : videau coupé : ${JSON.stringify(seen.cube)}`);
check(seen.controls === 0, `${where} : ${seen.controls} commandes sur le plateau`);
} else {
// Empilées sur toute la largeur.
const [first] = seen.boxes;
check(seen.boxes.every(([l, , r], i) => Math.abs(l - first[0]) <= 1 &&
Math.abs(r - first[2]) <= 1 && (i === 0 || seen.boxes[i - 1][3] <= seen.boxes[i][1])),
`${where} : actions pas empilées : ${JSON.stringify(seen.boxes)}`);
}
}
await tab.close();
}
}
@@ -1223,10 +1321,19 @@ for (let actions = 0; ; actions++) {
} else {
await (await touchTarget("confirm", "Jouer ce coup", 2, confirms % 2)).click();
}
await waitForPlayer();
const played = await savedJournal();
check(countMoves(played, "player") === countMoves(journal, "player") + 1,
"le coup joué n'est pas dans le journal");
const next = await waitForPlayer();
// L'application enregistre sans attendre ; sur un disque lent,
// l'écriture d'IndexedDB peut suivre de loin : on lui laisse 10 s.
const expected = countMoves(journal, "player") + 1;
let played = await savedJournal();
for (let i = 0; i < 100 && countMoves(played, "player") < expected; i++) {
await sleep(100);
played = await savedJournal();
}
check(countMoves(played, "player") === expected,
`le coup joué n'est pas dans le journal : ${countMoves(played, "player")} coups ` +
`enregistrés, ${expected} attendus ; à l'écran : ${next}, ` +
`« ${await page.textContent("#info")} »`);
halfMoves = 0;
touched = false;
} else {
+85 -20
View File
@@ -363,6 +363,8 @@ button {
border: 2px solid var(--accent);
border-radius: var(--radius-pill);
cursor: pointer;
/* Un bouton tient sur une ligne (charte). */
white-space: nowrap;
/* Pas de délai ni de zoom sur un double toucher. */
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
@@ -1317,16 +1319,36 @@ html.rotated .banner {
-webkit-user-select: text;
}
#help-back {
margin-top: var(--space-4);
/* La barre haute de l'aide (« Retour » et onglets) reste en vue quand la
* page défile. Sa hauteur est fixe (12 + 48 + 8 + 44 + 1 px de filet) : une
* question ou un terme ouvert par son adresse s'arrête 12 px sous elle. */
.help {
--help-bar-height: 113px;
}
/* Les onglets de l'aide : des liens, l'onglet courant souligné en brique. */
.help-bar {
position: sticky;
top: env(safe-area-inset-top);
z-index: 3;
height: var(--help-bar-height);
margin-inline: calc(-1 * var(--space-4));
padding: var(--space-3) var(--space-4) 0;
background: var(--background);
}
.help .question,
.glossary dt {
scroll-margin-top: calc(var(--help-bar-height) + 12px + env(safe-area-inset-top));
}
/* Les onglets de l'aide : des liens, l'onglet courant souligné en brique ;
* sur une ligne, qui défile si l'écran est trop étroit. */
.help-tabs {
display: flex;
flex-wrap: wrap;
gap: var(--space-1);
margin-top: var(--space-4);
overflow-x: auto;
white-space: nowrap;
margin-top: var(--space-2);
border-bottom: 1px solid var(--border);
}
@@ -1402,6 +1424,12 @@ html.rotated .banner {
margin-inline: calc(-1 * var(--space-4));
padding-inline: var(--space-4);
background: var(--accent-soft);
/* Le surlignage montre où l'on arrive, puis s'efface après 2 s. */
animation: term-fade var(--duration-moderate) var(--easing-exit) 2s forwards;
}
@keyframes term-fade {
to { background-color: transparent; }
}
/* -------------------------------------------------- Liens et boutons */
@@ -1420,6 +1448,7 @@ a.button {
background: var(--accent);
border: 2px solid var(--accent);
border-radius: var(--radius-pill);
white-space: nowrap;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
@@ -1483,14 +1512,6 @@ a.link-button {
margin: var(--space-2) 0;
}
/* En paysage, le plateau tient dans la hauteur de l'écran. */
html.landscape .position-board {
width: min(100%, calc(80vh * 640 / 520));
height: auto;
margin-inline: auto;
aspect-ratio: 640 / 520;
}
.position-detail {
margin: var(--space-2) 0 var(--space-4);
font-size: 15px;
@@ -1498,26 +1519,70 @@ html.landscape .position-board {
color: var(--muted);
}
/* Les actions empilées sur toute la largeur, la principale en premier. */
.position-actions {
display: grid;
grid-template-columns: 1fr 1.6fr;
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.position-actions > * {
min-width: 0;
width: 100%;
}
.position-copy {
#position-copy {
display: inline-flex;
gap: 6px;
align-items: center;
justify-content: center;
width: 100%;
margin-top: var(--space-3);
}
.position-copy .icon {
#position-copy .icon {
width: 20px;
height: 20px;
}
/* En paysage, comme l'écran de jeu : le plateau à gauche sur toute la
* hauteur (colonne gauche du cadre avec le videau seul), le titre, le contexte et les actions
* dans une colonne à droite. */
html.landscape .position-view.with-board {
position: fixed;
inset: 0;
display: grid;
grid-template-columns: minmax(0, 1fr) 288px;
grid-template-rows: auto auto auto auto 1fr;
max-width: none;
margin: 0;
padding: 0;
overflow-y: auto;
background: var(--background);
}
html.landscape .position-view.with-board > * {
grid-column: 2;
margin-inline: 0;
padding-inline: var(--space-4) calc(var(--space-4) + env(safe-area-inset-right));
}
html.landscape .position-view.with-board .position-title {
margin-top: var(--space-4);
}
html.landscape .position-view.with-board .position-board {
grid-row: 1 / -1;
grid-column: 1;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
aspect-ratio: auto;
background: var(--frame);
}
html.landscape .position-view.with-board .position-actions {
padding-bottom: var(--space-4);
}
html.landscape .position-view.with-board .position-actions > * {
padding-inline: 16px;
}
+13 -9
View File
@@ -84,27 +84,31 @@
<!-- L'aide : un onglet par adresse (#/aide, #/glossaire, #/a-propos). -->
<section id="help" class="help page" hidden>
<a id="help-back" href="#/" class="button secondary"></a>
<nav id="help-tabs" class="help-tabs">
<a href="#/aide" data-screen="help"></a>
<a href="#/glossaire" data-screen="glossary"></a>
<a href="#/a-propos" data-screen="about"></a>
</nav>
<!-- La barre haute de l'aide reste en vue quand la page défile. -->
<div id="help-bar" class="help-bar">
<a id="help-back" href="#/" class="button secondary"></a>
<nav id="help-tabs" class="help-tabs">
<a href="#/aide" data-screen="help"></a>
<a href="#/glossaire" data-screen="glossary"></a>
<a href="#/a-propos" data-screen="about"></a>
</nav>
</div>
<div id="help-content"></div>
</section>
<!-- Une position partagée (#/position/<Position ID>/<Match ID>), non
jouable. -->
jouable. En portrait, de haut en bas ; en paysage, le plateau à gauche
sur toute la hauteur, le reste dans une colonne à droite. -->
<section id="position-view" class="position-view page" aria-labelledby="position-title" hidden>
<h2 id="position-title" class="position-title"></h2>
<p id="position-message" class="position-message" role="status"></p>
<svg id="position-board" class="board position-board" role="img" xmlns="http://www.w3.org/2000/svg"></svg>
<p id="position-detail" class="position-detail"></p>
<div class="position-actions">
<a id="position-back" href="#/" class="button secondary"></a>
<button id="position-play" type="button"></button>
<button id="position-copy" type="button" class="secondary"></button>
<a id="position-back" href="#/" class="button secondary"></a>
</div>
<button id="position-copy" type="button" class="secondary position-copy"></button>
</section>
<!-- Menu : une feuille qui monte du bas. -->