Moteur pour l'analyse et les niveaux (étape 1a) #32

Manually merged
pierre merged 8 commits from moteur-analyse into main 2026-11-03 14:09:42 +00:00
Owner

Pourquoi

L'étape 1 (docs/etape-1.md) a besoin de plus que le meilleur coup et la décision de videau :

  • l'ordinateur doit jouer à plusieurs niveaux, du débutant au plus fort (1c) ;
  • le conseil du maître (1d) et le bilan de fin de partie (1e) doivent dire, après chaque coup, ce qu'il valait, quel était le meilleur coup et si les dés ont aidé.

Le cadrage fait de GNU Backgammon la seule autorité. Niveaux, analyses et seuils viennent donc de gnubg et sont vérifiés contre gnubg de bureau, comme le reste du moteur.

Quoi

Ce que le moteur sait faire désormais :

  • jouer aux 9 niveaux prédéfinis de gnubg 1.08 :
    • débutant, amateur, intermédiaire et avancé jouent à 0 pli avec un bruit de 0,06 / 0,05 / 0,04 / 0,015. Ce bruit est déterministe : même position et même jet donnent toujours le même coup ;
    • les niveaux 0 à 4 plis jouent sans bruit ;
  • analyser un coup joué : son rang, son équité et celle du meilleur coup, la perte, l'appréciation ;
  • analyser une décision de videau : ne pas doubler, doubler, prendre, passer, avec la perte et l'appréciation ;
  • donner la chance d'un jet et sa catégorie, de très malchanceux à très chanceux.

Où c'est exposé :

  • engine.h ;
  • engine-cli, avec l'option --level et les commandes levels, analysemove, analysecube et luck ;
  • l'interface Engine du domaine (levelMove, levelCubeDecision, analyseMove, analyseCube, luck), par simple ajout ;
  • les seuils de gnubg, en constantes : appréciation 0,03 / 0,06 / 0,12 (arSkillLevel) et chance 0,3 / 0,6 (arLuckLevel). Un test vérifie que les valeurs TypeScript sont égales à celles du moteur.

Autres changements :

  • trois fiches de glossaire : Niveau de jeu, Appréciation, Chance ;
  • make bench mesure aussi « Analyse du coup joué, 2 plis ».

Comment

Le travail a été délégué à un sous-agent selon le plan docs/plans/2026-11-03-moteur-analyse.md, supprimé dans cette MR. L'agent a été interrompu une fois par la limite d'usage, puis a repris.

Chaque fonction reproduit le code de gnubg qu'elle remplace, avec le fichier et la ligne d'origine cités en commentaire :

  • niveaux : aecSettings (eval.c l. 318-328) et le choix du coup de ComputerTurn (play.c) ;
  • analyse : AnalyzeMove (analysis.c) ;
  • chance : portage de LuckNormal et LuckFirst. analysis.c n'est pas dans la copie, car il dépend de toute la gestion de partie de gnubg.

engine/gnubg/ n'est pas modifié.

Les références viennent de la vraie analyse de partie de gnubg de bureau. Pour chaque cas, generate.py rejoue une courte partie, puis lance « analyse game ». Pour les niveaux, la commande « play » donne le coup réellement joué. compare.py compare ces valeurs aux moteurs natif et WebAssembly :

  • 2 890 nombres comparés (32 positions, 64 niveaux, 37 analyses) ;
  • écart maximal de 1,64e-3, sur move-jacoby à 2 plis (une bascule de la recherche). Tous les autres sont sous 1e-3 ;
  • tous les coups choisis aux niveaux sont identiques à ceux de gnubg de bureau.

Le moteur vide désormais le cache d'évaluation de gnubg au début de chaque calcul. C'est un changement de comportement :

  • Le défaut. Une recherche à 2 plis avec élagage laisse dans le cache des évaluations à 0 pli faites avec les réseaux d'élagage. Elles sont rangées sous la même clé que les évaluations ordinaires, car EvalKey (eval.c l. 2302) n'inclut l'élagage qu'à partir d'1 pli.
  • Ses effets. Un calcul à 0 pli qui suit reprend ces valeurs. Le défaut existe aussi dans gnubg de bureau : la chance d'un jet y varie jusqu'à 2,5e-2 selon ce qui a été analysé avant.
  • Pourquoi le corriger ici. Notre Worker garde le moteur chargé : un test échouait selon l'ordre des calculs. Un résultat ne dépend plus que de ses arguments, et un test de régression le vérifie.

Fusion avec main. La session principale a fusionné main dans la branche. Les pages /moteur/, retirées par la MR !28, restent supprimées ; la mesure de l'analyse est passée dans engine-wasm/benchmark.mjs.

Seuils dans docs/etape-1.md. Le document proposait 0,04 / 0,08 / 0,16. Il reprend maintenant les vrais seuils de gnubg.

Choix et compromis

  • Vider le cache coûte environ 3 ms par appel, et le cache n'est plus réutilisé d'un appel à l'autre. Sur le PC, le meilleur coup à 2 plis passe d'une médiane de 80 à 92 ms (maximum de 321 à 447 ms), loin du seuil de 3 s. Garder le cache rendait les résultats dépendants de l'historique des calculs.
  • Les identifiants de niveaux sont ceux de gnubg (beginner… 4ply). Les noms montrés au joueur relèvent du produit.
  • La décision de videau aux niveaux bruités est l'évaluation de gnubg avec bruit. Le pré-filtre « fenêtre de marché » et le double facultatif à 0 pli de l'ordinateur de gnubg sont des règles de déroulement : elles sont laissées au domaine (1c).

Risques et déploiement

  • Tolérance : elle reste à 0,002. L'écart maximal mesuré (1,64e-3) en laisse peu si d'autres bascules apparaissent.
  • Interface Engine : ses méthodes existantes sont inchangées. Aucun format de données persistant n'est touché.
  • Rien de visible dans l'application tant que 1c et 1d n'utilisent pas ces fonctions.
  • Retour arrière : annuler la fusion.

Comment vérifier

make lint
make test        # 10/10 ; « 37 analyses ... écart maximal 1.64e-03 »
make reference && git diff --exit-code tests/reference
build/engine-cli levels
build/engine-cli --ply 2 analysemove 4HPwATDgc/ABMA cIkFAAAAAAAA 31 4HPwATDgc+IBKA   # rang 2, very_bad
build/engine-cli --level beginner bestmove mM/gATCw5+ABMA cAkTAAAAAAAA 64            # 24/20 24/18
build/engine-cli luck 4HPwATDgc/ABMA cIkFAAAAAAAA 66                                 # lucky
make bench       # médiane 75,7 ms, max 451 ms pour l'analyse à 2 plis sur le PC

Hors périmètre

  • Déroulement de la partie : la règle de doubler de l'ordinateur et l'usage des niveaux dans la partie relèvent de 1c.
  • Matchs : l'adaptateur ne gère encore que la partie libre ; l'API C et engine-cli acceptent déjà les matchs.
  • Chance « très malchanceux » : aucune position de référence ne la produit.
  • Niveaux 3 et 4 plis : ils ne sont pas comparés à gnubg, car trop lents pour la suite de tests.
  • Mesure sur le téléphone : il n'y a plus de page de mesure depuis la MR !28.

Generated with Claude Code https://claude.com/claude-code

## Pourquoi L'étape 1 (`docs/etape-1.md`) a besoin de plus que le meilleur coup et la décision de videau : - l'ordinateur doit jouer à plusieurs niveaux, du débutant au plus fort (1c) ; - le conseil du maître (1d) et le bilan de fin de partie (1e) doivent dire, après chaque coup, ce qu'il valait, quel était le meilleur coup et si les dés ont aidé. Le cadrage fait de GNU Backgammon la seule autorité. Niveaux, analyses et seuils viennent donc de gnubg et sont vérifiés contre gnubg de bureau, comme le reste du moteur. ## Quoi **Ce que le moteur sait faire désormais :** - **jouer aux 9 niveaux prédéfinis de gnubg 1.08** : - débutant, amateur, intermédiaire et avancé jouent à 0 pli avec un bruit de 0,06 / 0,05 / 0,04 / 0,015. Ce bruit est déterministe : même position et même jet donnent toujours le même coup ; - les niveaux 0 à 4 plis jouent sans bruit ; - **analyser un coup joué** : son rang, son équité et celle du meilleur coup, la perte, l'appréciation ; - **analyser une décision de videau** : ne pas doubler, doubler, prendre, passer, avec la perte et l'appréciation ; - **donner la chance d'un jet** et sa catégorie, de très malchanceux à très chanceux. **Où c'est exposé :** - `engine.h` ; - `engine-cli`, avec l'option `--level` et les commandes `levels`, `analysemove`, `analysecube` et `luck` ; - l'interface `Engine` du domaine (`levelMove`, `levelCubeDecision`, `analyseMove`, `analyseCube`, `luck`), par simple ajout ; - les seuils de gnubg, en constantes : appréciation 0,03 / 0,06 / 0,12 (`arSkillLevel`) et chance 0,3 / 0,6 (`arLuckLevel`). Un test vérifie que les valeurs TypeScript sont égales à celles du moteur. **Autres changements :** - trois fiches de glossaire : Niveau de jeu, Appréciation, Chance ; - `make bench` mesure aussi « Analyse du coup joué, 2 plis ». ## Comment Le travail a été délégué à un sous-agent selon le plan `docs/plans/2026-11-03-moteur-analyse.md`, supprimé dans cette MR. L'agent a été interrompu une fois par la limite d'usage, puis a repris. **Chaque fonction reproduit le code de gnubg** qu'elle remplace, avec le fichier et la ligne d'origine cités en commentaire : - niveaux : `aecSettings` (`eval.c` l. 318-328) et le choix du coup de `ComputerTurn` (`play.c`) ; - analyse : `AnalyzeMove` (`analysis.c`) ; - chance : portage de `LuckNormal` et `LuckFirst`. `analysis.c` n'est pas dans la copie, car il dépend de toute la gestion de partie de gnubg. `engine/gnubg/` n'est pas modifié. **Les références viennent de la vraie analyse de partie de gnubg de bureau.** Pour chaque cas, `generate.py` rejoue une courte partie, puis lance « analyse game ». Pour les niveaux, la commande « play » donne le coup réellement joué. `compare.py` compare ces valeurs aux moteurs natif et WebAssembly : - 2 890 nombres comparés (32 positions, 64 niveaux, 37 analyses) ; - écart maximal de 1,64e-3, sur `move-jacoby` à 2 plis (une bascule de la recherche). Tous les autres sont sous 1e-3 ; - tous les coups choisis aux niveaux sont identiques à ceux de gnubg de bureau. **Le moteur vide désormais le cache d'évaluation de gnubg au début de chaque calcul.** C'est un changement de comportement : - **Le défaut.** Une recherche à 2 plis avec élagage laisse dans le cache des évaluations à 0 pli faites avec les réseaux d'élagage. Elles sont rangées sous la même clé que les évaluations ordinaires, car `EvalKey` (`eval.c` l. 2302) n'inclut l'élagage qu'à partir d'1 pli. - **Ses effets.** Un calcul à 0 pli qui suit reprend ces valeurs. Le défaut existe aussi dans gnubg de bureau : la chance d'un jet y varie jusqu'à 2,5e-2 selon ce qui a été analysé avant. - **Pourquoi le corriger ici.** Notre Worker garde le moteur chargé : un test échouait selon l'ordre des calculs. Un résultat ne dépend plus que de ses arguments, et un test de régression le vérifie. **Fusion avec `main`.** La session principale a fusionné `main` dans la branche. Les pages /moteur/, retirées par la MR !28, restent supprimées ; la mesure de l'analyse est passée dans `engine-wasm/benchmark.mjs`. **Seuils dans `docs/etape-1.md`.** Le document proposait 0,04 / 0,08 / 0,16. Il reprend maintenant les vrais seuils de gnubg. ## Choix et compromis - **Vider le cache coûte environ 3 ms par appel**, et le cache n'est plus réutilisé d'un appel à l'autre. Sur le PC, le meilleur coup à 2 plis passe d'une médiane de 80 à 92 ms (maximum de 321 à 447 ms), loin du seuil de 3 s. Garder le cache rendait les résultats dépendants de l'historique des calculs. - **Les identifiants de niveaux sont ceux de gnubg** (`beginner`… `4ply`). Les noms montrés au joueur relèvent du produit. - **La décision de videau aux niveaux bruités** est l'évaluation de gnubg avec bruit. Le pré-filtre « fenêtre de marché » et le double facultatif à 0 pli de l'ordinateur de gnubg sont des règles de déroulement : elles sont laissées au domaine (1c). ## Risques et déploiement - **Tolérance :** elle reste à 0,002. L'écart maximal mesuré (1,64e-3) en laisse peu si d'autres bascules apparaissent. - **Interface `Engine` :** ses méthodes existantes sont inchangées. Aucun format de données persistant n'est touché. - **Rien de visible dans l'application** tant que 1c et 1d n'utilisent pas ces fonctions. - **Retour arrière :** annuler la fusion. ## Comment vérifier ``` make lint make test # 10/10 ; « 37 analyses ... écart maximal 1.64e-03 » make reference && git diff --exit-code tests/reference build/engine-cli levels build/engine-cli --ply 2 analysemove 4HPwATDgc/ABMA cIkFAAAAAAAA 31 4HPwATDgc+IBKA # rang 2, very_bad build/engine-cli --level beginner bestmove mM/gATCw5+ABMA cAkTAAAAAAAA 64 # 24/20 24/18 build/engine-cli luck 4HPwATDgc/ABMA cIkFAAAAAAAA 66 # lucky make bench # médiane 75,7 ms, max 451 ms pour l'analyse à 2 plis sur le PC ``` ## Hors périmètre - **Déroulement de la partie :** la règle de doubler de l'ordinateur et l'usage des niveaux dans la partie relèvent de 1c. - **Matchs :** l'adaptateur ne gère encore que la partie libre ; l'API C et engine-cli acceptent déjà les matchs. - **Chance « très malchanceux » :** aucune position de référence ne la produit. - **Niveaux 3 et 4 plis :** ils ne sont pas comparés à gnubg, car trop lents pour la suite de tests. - **Mesure sur le téléphone :** il n'y a plus de page de mesure depuis la MR !28. Generated with Claude Code https://claude.com/claude-code
pierre added 8 commits 2026-11-03 14:09:34 +00:00
pierre manually merged commit 08cb89735f into main 2026-11-03 14:09:42 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: pierre/Ustagammon#32