Files
Ustagammon/docs/plans/2026-11-02-comparaison-references.md
T
2026-11-02 14:19:18 +01:00

6.4 KiB

Comparer le moteur embarqué aux références de gnubg de bureau

  • Date : 2026-11-02
  • Étape : 0a.4
  • Statut : terminé
  • Exécutant : sous-agent general-purpose, dans un worktree isolé

Objectif

make test vérifie automatiquement que le moteur embarqué (build/engine-cli) retrouve les résultats de gnubg de bureau sur les 32 positions de tests/reference/, aux niveaux 0 et 2 plis, avec une tolérance mesurée et justifiée.

Consignes

  1. Sortie d'engine-cli alignée sur le contrat de tests/reference/README.md : une sous-commande (par exemple engine-cli reference <position_id> <match_id> [dés], ou une option) produit, pour une position et un niveau, un objet de même structure que la partie plies["<n>"] d'un fichier de résultat (evaluation, moves ou cube). Le classement des coups suit exactement la méthode de la commande hint de gnubg (documentée dans le README du format) : nombre de coups, notation, pli réel de chaque coup. Garder engine-cli simple : réutiliser les fonctions d'engine.h, compléter engine.h si nécessaire.
  2. Comparateur : un script lisible (Python 3 ou shell avec jq) qui, pour chaque position et chaque niveau, appelle engine-cli, compare au résultat de référence et affiche un rapport clair (position, niveau, champ, valeur attendue, valeur obtenue, écart). Il tourne dans Docker (ajouter ce qu'il faut à docker/build-c/Dockerfile et changer l'étiquette de l'image dans le Makefile), jamais avec les outils du PC (ADR 0004).
  3. Critères de concordance :
    • même nombre de coups légaux ;
    • même meilleur coup, sauf si gnubg lui-même sépare les deux premiers coups de moins que la tolérance (cas d'égalité : le dire dans le rapport) ;
    • même pli réel du meilleur coup ;
    • équités et probabilités à la tolérance près, pour les coups communs ;
    • même décision de videau, et équités du videau à la tolérance près.
  4. Tolérance mesurée, pas devinée : mesurer l'écart maximal observé sur les 32 positions, l'expliquer (gnubg de bureau en SSE2 avec -ffast-math, moteur embarqué en C générique), et mesurer aussi l'écart d'un build du moteur sans -ffast-math. Fixer la tolérance avec une marge raisonnable et la justifier dans tests/reference/README.md.
  5. tests/test_engine_reference.sh, lancé par make test : lance make engine si nécessaire puis le comparateur ; échoue avec le rapport si un critère n'est pas tenu.
  6. tests/test_reference.sh n'utilise plus jq sur le PC : passer par l'image Docker de la chaîne C (ADR 0004), et mettre CREDITS.md à jour.

Contraintes :

  • respecter CLAUDE.md et les ADR 0004, 0005 et 0007 : simplicité, identifiants en anglais, commentaires en français, aucune installation hors Docker, -j2 ;
  • ne pas modifier les références (tests/reference/results/) ni les fichiers de engine/gnubg/ ; si une divergence vient d'un vrai défaut du moteur embarqué, corriger dans engine/compat/ ou engine/src, et le dire ;
  • ne rien masquer : si une position ne concorde pas, le rapport doit le montrer, avec la cause trouvée ou les hypothèses ;
  • make lint et make test doivent passer ; commits petits, en français, avec la ligne Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> ;
  • ne pas pousser ni créer de merge request.

Rapport attendu : ce qui a été fait, la tolérance retenue et sa justification (écarts mesurés avec et sans -ffast-math), les éventuelles divergences et leur cause, les commandes de vérification.

Critères de réussite

  • make test compare les 32 positions aux deux niveaux et passe.
  • La tolérance est documentée et justifiée par des mesures.
  • Une divergence volontairement introduite (par exemple une référence modifiée dans une copie) fait échouer le test avec un rapport lisible.

Résultat

Terminé le 2026-11-02, branche comparaison-travail. make lint et make test passent (7/7) ; les 32 positions concordent aux deux niveaux avec la tolérance retenue, sans aucune égalité de meilleur coup à signaler.

Fait.

  • engine-cli --ply N reference POSITION_ID MATCH_ID [DÉS] produit un niveau au format de tests/reference/ (evaluation, puis moves ou cube). Nombres écrits avec 9 chiffres significatifs (float32 exact).
  • tests/reference/compare.py (Python 3) et tests/test_engine_reference.sh (lancé par make test) : comparaison dans l'image ustagammon/build-c:2, qui reçoit Python 3 et jq. tests/test_reference.sh se relance dans cette image : plus de jq sur le PC. CREDITS.md à jour.
  • Critères, tolérance et mesures documentés dans tests/reference/README.md.

Défauts du moteur embarqué trouvés et corrigés (engine/engine.c) :

  • en match, l'équité des coups classés était convertie deux fois par mwc2eq (ScoreMove la convertit déjà) ;
  • engine_best_moves n'appliquait pas tout hint : fAnalyse était faux et RefreshMoveList absent (classement par niveau d'évaluation, puis départage des égalités).

Changements d'interface : engine_cube gagne optimal et decision_text ; decision prend le nom de l'énumération de gnubg (DOUBLE_TAKE…), comme les références (tests/test_engine.sh adapté).

Tolérance : 0,002, sur probabilités et équités. Mesures sur 2 598 nombres : écart maximal 9,5e-4 et 99 % des écarts sous 1,6e-4, que le moteur soit compilé avec ou sans -ffast-math (les deux builds diffèrent entre eux de 1,1e-6 au plus). La cause est la sigmoïde de gnubg de bureau en SSE2 avec -ffast-math, qui calcule 1/x par _mm_rcp_ps (environ 12 bits) ; preuve : le moteur compilé en SSE2 pour l'essai redonne les références au bit près. L'écart maximal vient d'un seul coup (opening-64-match, 2 plis, 8/2 6/2), où l'écart de 1e-4 fait basculer un choix discret de la recherche.

Critère de divergence vérifié : sur une copie de results/ avec une équité décalée de 0,01, un nombre de coups légaux, une décision de videau et un meilleur coup modifiés, compare.py --results <copie> échoue avec un tableau de 10 écarts lisibles.

Suites possibles : mesurer de même sur téléphone (NEON, autre approximation de la division) ; envisager un build SSE2 d'engine-cli sur PC si l'on veut une concordance au bit près (non retenu : le moteur de l'application tournera en C générique ou en NEON).