Files
Ustagammon/docs/plans/2026-11-02-moteur-embarque.md
T
2026-11-02 13:58:01 +01:00

7.4 KiB

Moteur embarqué : extraction du cœur de gnubg, moteur.h et engine-cli

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

Renommage (conventions de code décidées par Pierre pendant la tâche : identifiants en anglais) : moteur.h devient engine/include/engine.h, l'API C est préfixée engine_ et les clés JSON sont en anglais. Le texte ci-dessous garde la rédaction d'origine.

Objectif

Le cœur de GNU Backgammon (commit 14b67984737ded0f6de43d63dcb789f729d9b06b, ADR 0005) compile comme bibliothèque C autonome, sans GLib ni interface graphique, derrière l'interface engine/include/moteur.h, et un programme engine-cli l'interroge en ligne de commande avec une sortie JSON.

Consignes

  1. Sources : copie figée des seuls fichiers nécessaires de gnubg dans engine/gnubg/ (liste de départ et plan d'isolement : ADR 0005), avec un engine/gnubg/PROVENANCE.md (commit exact, liste des fichiers, toute modification apportée à un fichier d'origine, signalée aussi en commentaire dans le fichier). Garder les en-têtes de copyright. Modifier les fichiers d'origine le moins possible : préférer un config.h, un shim glib.h et un en-tête réduit propres à Ustagammon dans engine/compat/.
  2. Données : gnubg.weights et les tables d'équité de match nécessaires (Kazaross-XG2 par défaut) copiées dans engine/data/. Les fichiers générés (gnubg.wd, bases gnubg_os0.bd, gnubg_ts0.bd) sont produits par le build avec les outils officiels (makeweights, makebearoff) compilés pour l'hôte, dans build/, jamais commités. Paramètres de génération identiques à ceux du build officiel de gnubg (Makefile.am).
  3. Interface engine/include/moteur.h, en C, documentée en français, positions au format Position ID + Match ID, sans exposer de type gnubg :
    • initialisation (chemin des données) et libération ;
    • coups légaux pour une position et des dés ;
    • classement des coups avec équités et probabilités, pour un niveau donné (au minimum 0-ply et 2-ply, réglages prédéfinis de gnubg, sans bruit) ;
    • décision de videau (équités sans double, double/prend, double/passe, et décision), en match et en partie libre (Crawford, Jacoby) ;
    • évaluation d'une position (5 probabilités et équité) ;
    • erreurs signalées par code de retour, jamais par exit.
  4. engine-cli/ : engine-cli bestmove <position_id> <match_id> <dés> (ex. 31), engine-cli moves …, engine-cli cube <position_id> <match_id>, engine-cli eval <position_id> <match_id>, option de niveau (--ply 0|2), sortie JSON sur la sortie standard, erreurs en JSON avec un code de sortie non nul.
  5. Build : compilation dans Docker (image docker/build-c/Dockerfile sur debian:trixie, déjà présente), jamais avec les outils de l'hôte. make engine produit build/engine-cli et les données ; make build l'appelle. Dépendances make correctes : rien n'est recompilé si rien n'a changé (le vérifier : un second make engine ne recompile rien). -j2 maximum. make clean supprime build/. Les nouvelles cibles apparaissent dans make help.
  6. Tests (tests/test_engine.sh, lancé par make test, qui suppose make engine déjà fait ou le lance) : position de départ (Position ID 4HPwATDgc/ABMA) avec plusieurs jets d'ouverture dont le meilleur coup est connu sans ambiguïté (ex. 31 : 8/5 6/5), nombre de coups légaux d'une position connue, symétrie et bornes des probabilités, gestion d'un identifiant invalide, décision de videau d'une position manifestement « pas de double ». La comparaison systématique au gnubg de bureau est l'étape suivante (0a.4), pas celle-ci.
  7. Mesure : temps de bestmove à 0-ply et 2-ply sur la position de départ, rapporté (pas de seuil à tenir sur le PC).

Contraintes :

  • respecter CLAUDE.md (français, -j2, aucune installation hors Docker) ;
  • pas de threads (USE_MULTITHREAD désactivé) ; C générique sur x86 pour commencer (pas de SSE/AVX obligatoire), en gardant le chemin NEON possible ;
  • ne pas copier de code de CBG ; s'en inspirer est permis (clone en lecture seule indiqué dans la consigne de l'agent), en le citant ;
  • make lint et make test doivent passer ; commits petits, en français ;
  • ne pas pousser ni créer de merge request ;
  • ne rien simuler : un échec est rapporté avec sa cause.

Rapport attendu : ce qui a été fait, fichiers repris et modifiés, commandes de vérification, temps de compilation, tailles des données générées, temps de calcul mesurés, difficultés, écarts au plan.

Critères de réussite

  • make engine && ./build/engine-cli bestmove 4HPwATDgc/ABMA cAkAAAAAAAAA 31 affiche le meilleur coup en JSON (8/5 6/5).
  • Un second make engine ne recompile rien.
  • make lint et make test passent.

Résultat

Terminé (2026-11-02), branche moteur-embarque-travail. Les trois critères sont remplis : make engine && ./build/engine-cli bestmove 4HPwATDgc/ABMA cAkAAAAAAAAA 31 répond 8/5 6/5 en JSON ; un second make engine affiche « Nothing to be done » ; make lint et make test passent.

  • Sources : 38 fichiers de gnubg (plus COPYING) copiés sans aucune modification (engine/gnubg/PROVENANCE.md) ; en plus de la liste de l'ADR 0005 : drawboard.c/h (notation des coups, FormatMove), mtsupport.c (données du fil unique), dice.h. Tout le reste est remplacé dans engine/compat/ (config.h, shim GLib, en-têtes réduits, fonctions de support).
  • Tables d'équité : pas de conversion en tableau C ni de repli sur Zadeh ; le shim fournit un petit analyseur XML compatible GMarkup, et matchequity.c lit Kazaross-XG2.xml à l'exécution, comme gnubg de bureau. engine_init échoue si la table chargée n'est pas celle-ci. Contre-vérification : une variante d'engine-cli liée à la vraie GLib donne une sortie identique au caractère près (partie libre, match en 5 points, Crawford, 0 et 2 plis).
  • Données : générées au build par les outils officiels compilés avec la vraie GLib, mêmes paramètres que Makefile.am : gnubg.wd 408 016 octets, gnubg_os0.bd 1 465 788 octets, gnubg_ts0.bd 6 830 248 octets.
  • Build : image ustagammon/build-c:1 (docker/build-c/Dockerfile), engine/engine.mk lancé par make engine avec -j2 ; build complet depuis zéro : 43 s, génération des données comprise. Options de gnubg (-O3 -ffast-math -DG_DISABLE_ASSERT) ; notre C en -std=c11 -Wall -Wextra -Werror.
  • Temps de bestmove (position de départ, processus complet, données en cache disque) : 0-ply 0,02 à 0,03 s ; 2-ply 0,03 s pour 31, 0,12 à 0,20 s pour 53, 64, 21 ; environ 35 Mo de mémoire. Premier lancement à froid : 0,8 s.
  • Match ID : cAkAAAAAAAAA met le joueur 1 au trait (bit fMove à 1) ; le Match ID équivalent avec le joueur 0 au trait est MAEAAAAAAAAA. Les deux donnent le même résultat sur la position de départ (testé).
  • Écarts au plan : identifiants en anglais (voir l'encadré) ; sous-commande legal ajoutée (coups légaux sans évaluation) ; make test ne vide plus build/ (test_makefile.sh essaie make clean sur un dossier jetable), sinon chaque test recompilait le moteur ; mesure de l'écart avec et sans -ffast-math laissée à l'étape 0a.4.