From 0a88e835d2fba0776a5e8c3af3b6e4e1e51cac91 Mon Sep 17 00:00:00 2001 From: Pierre MARTIN Date: Mon, 9 Nov 2026 18:37:29 +0100 Subject: [PATCH 1/4] =?UTF-8?q?Vocabulaire=20:=20=C2=AB=20Grosse=20erreur?= =?UTF-8?q?=20=C2=BB=20plut=C3=B4t=20que=20=C2=AB=20gaffe=20=C2=BB=20(choi?= =?UTF-8?q?x=20de=20Pierre)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs/etape-1.md | 2 +- docs/plans/2026-11-03-conseil-du-maitre.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/etape-1.md b/docs/etape-1.md index 8f669f3..c619913 100644 --- a/docs/etape-1.md +++ b/docs/etape-1.md @@ -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 | diff --git a/docs/plans/2026-11-03-conseil-du-maitre.md b/docs/plans/2026-11-03-conseil-du-maitre.md index 64dc46c..81fde3d 100644 --- a/docs/plans/2026-11-03-conseil-du-maitre.md +++ b/docs/plans/2026-11-03-conseil-du-maitre.md @@ -29,7 +29,7 @@ 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 From e724368f0922874cea385cc892c0d3d8cd2ae0c2 Mon Sep 17 00:00:00 2001 From: Pierre MARTIN Date: Mon, 9 Nov 2026 18:38:53 +0100 Subject: [PATCH 2/4] =?UTF-8?q?Charte=20:=20tutoriel,=20=C3=A9tape=207=20a?= =?UTF-8?q?vec=20la=20r=C3=A8gle=20de=20Jacoby,=20lien=20=C2=AB=20Quitter?= =?UTF-8?q?=20=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs/identite/charte/accueil-tutoriel-aide.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/identite/charte/accueil-tutoriel-aide.md b/docs/identite/charte/accueil-tutoriel-aide.md index e45e5fe..5fe146d 100644 --- a/docs/identite/charte/accueil-tutoriel-aide.md +++ b/docs/identite/charte/accueil-tutoriel-aide.md @@ -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 From 2b8fa1a0b05ed15a6d80dc894eee47559b29437e Mon Sep 17 00:00:00 2001 From: Pierre MARTIN Date: Mon, 9 Nov 2026 18:52:50 +0100 Subject: [PATCH 3/4] =?UTF-8?q?Recette=20design=20des=20adresses=20:=20bar?= =?UTF-8?q?re=20de=20l'aide=20coll=C3=A9e,=20boutons=20sur=20une=20ligne,?= =?UTF-8?q?=20position=20en=20paysage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- app/src/ui/app.ts | 14 +++++- app/src/ui/help.ts | 2 + tests/browser/play.mjs | 98 ++++++++++++++++++++++++++++++++++++++ web/app.css | 105 +++++++++++++++++++++++++++++++++-------- web/index.html | 22 +++++---- 5 files changed, 210 insertions(+), 31 deletions(-) diff --git a/app/src/ui/app.ts b/app/src/ui/app.ts index b71bf6f..869c329 100644 --- a/app/src/ui/app.ts +++ b/app/src/ui/app.ts @@ -1264,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; @@ -1271,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: [], diff --git a/app/src/ui/help.ts b/app/src/ui/help.ts index bbca105..6a3ca07 100644 --- a/app/src/ui/help.ts +++ b/app/src/ui/help.ts @@ -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" }); diff --git a/tests/browser/play.mjs b/tests/browser/play.mjs index 3b9d3c7..c43392b 100644 --- a/tests/browser/play.mjs +++ b/tests/browser/play.mjs @@ -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(); } } diff --git a/web/app.css b/web/app.css index 11a94cd..bebd591 100644 --- a/web/app.css +++ b/web/app.css @@ -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; +} diff --git a/web/index.html b/web/index.html index 43dae8d..90d97db 100644 --- a/web/index.html +++ b/web/index.html @@ -84,27 +84,31 @@ + jouable. En portrait, de haut en bas ; en paysage, le plateau à gauche + sur toute la hauteur, le reste dans une colonne à droite. --> From 1eeb6c95aed0a49cec5298355f10f72584454d5c Mon Sep 17 00:00:00 2001 From: Pierre MARTIN Date: Mon, 9 Nov 2026 19:12:54 +0100 Subject: [PATCH 4/4] =?UTF-8?q?D=C3=A9cisions=20de=20Pierre=20du=202026-11?= =?UTF-8?q?-09=20;=20journal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs/etape-1.md | 31 ++++++++++++------- docs/feuille-de-route.md | 2 +- docs/journal.md | 35 ++++++++++++++++++++++ docs/plans/2026-11-03-conseil-du-maitre.md | 12 +++++--- docs/plans/2026-11-03-lecons.md | 2 +- 5 files changed, 65 insertions(+), 17 deletions(-) diff --git a/docs/etape-1.md b/docs/etape-1.md index c619913..715c358 100644 --- a/docs/etape-1.md +++ b/docs/etape-1.md @@ -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. diff --git a/docs/feuille-de-route.md b/docs/feuille-de-route.md index 182d163..fc94b57 100644 --- a/docs/feuille-de-route.md +++ b/docs/feuille-de-route.md @@ -30,7 +30,7 @@ Demandes de Pierre du 2026-11-03, traitées avant d'ouvrir l'étape 1. | É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. diff --git a/docs/journal.md b/docs/journal.md index 7cfcc1d..5c5abf2 100644 --- a/docs/journal.md +++ b/docs/journal.md @@ -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. diff --git a/docs/plans/2026-11-03-conseil-du-maitre.md b/docs/plans/2026-11-03-conseil-du-maitre.md index 81fde3d..87f121b 100644 --- a/docs/plans/2026-11-03-conseil-du-maitre.md +++ b/docs/plans/2026-11-03-conseil-du-maitre.md @@ -36,10 +36,14 @@ du moteur. 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. diff --git a/docs/plans/2026-11-03-lecons.md b/docs/plans/2026-11-03-lecons.md index 75296b2..59873ad 100644 --- a/docs/plans/2026-11-03-lecons.md +++ b/docs/plans/2026-11-03-lecons.md @@ -18,7 +18,7 @@ Mettre en place les cinq premières leçons numérotées de l'ADR 0011. Chaque l 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. Les contenus sont livrés en brouillon, marqués « à relire par un joueur fort » (cadrage, risque « contenus »), et Pierre les valide. + 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//<écran>`, à ajouter au tableau de l'ADR 0013. 4. **Accueil.** Il reçoit la carte « prochaine leçon » prévue par la charte.