Étape 0a.3 : moteur embarqué (cœur de gnubg), engine.h et engine-cli #7

Manually merged
pierre merged 8 commits from moteur into main 2026-11-02 18:03:48 +00:00
Owner

Pourquoi

Étape 0a.3 de la feuille de route : faire du cœur de GNU Backgammon une bibliothèque C autonome, sans GLib ni interface graphique, cachée derrière une interface propre à Ustagammon. C'est la brique que l'application embarquera, en WebAssembly ou sur Android. L'ADR 0005 a fixé la source (gnubg officiel, commit 14b67984737ded0f6de43d63dcb789f729d9b06b) et le plan d'isolement ; l'ADR 0007 impose des identifiants en anglais, d'où engine.h au lieu de moteur.h.

Quoi

  • engine/gnubg/ : copie figée de 38 fichiers de gnubg et de son COPYING. Aucun fichier n'est modifié : le sous-agent l'a vérifié avec cmp contre le clone, et la liste est dans engine/gnubg/PROVENANCE.md. Par rapport à la liste de l'ADR 0005, il a fallu ajouter drawboard.c/h (notation des coups), mtsupport.c et dice.h.
  • engine/data/ : gnubg.weights et Kazaross-XG2.xml, copiés tels quels.
  • engine/compat/ : notre couche de compatibilité.
    • un config.h écrit à la main : sans threads, C générique sur x86, chemin NEON prévu mais pas encore compilé ;
    • un shim GLib minimal, avec un petit analyseur XML compatible GMarkup pour les tables d'équité de match ;
    • des en-têtes réduits à la place de backgammon.h et consorts ;
    • les fonctions de support que gnubg attend ; le stubs.c de CBG a servi de repère, sans reprise de code.
  • engine/include/engine.h : l'interface. Fonctions engine_init, engine_shutdown, engine_decode_match, engine_legal_moves, engine_best_moves, engine_evaluate et engine_cube_decision. Positions en Position ID + Match ID, niveaux 0 à 4 (réglages prédéfinis de gnubg, sans bruit), erreurs par code de retour. Les données manquantes sont vérifiées avant d'appeler gnubg, dont l'initialisation ferait sinon un exit().
  • engine-cli/main.c : sous-commandes bestmove, moves, legal, cube et eval, options --ply et --data, sortie JSON. En cas d'erreur, il renvoie du JSON et un code de sortie non nul.
  • Build :
    • make engine compile dans Docker, avec l'image ustagammon/build-c:1 (docker/build-c/Dockerfile, Debian trixie, gcc, make, et GLib uniquement pour les outils de génération) ;
    • make build l'appelle, make setup construit l'image ;
    • code gnubg compilé avec ses options officielles, notre C en -std=c11 -Wall -Wextra -Werror ;
    • gnubg.wd et les bases de fin de partie sont générés dans build/ par les outils officiels, avec les paramètres du build officiel ;
    • une recompilation ne refait rien si rien n'a changé.
  • tests/test_engine.sh (lancé par make test) :
    • ouvertures 31, 42, 53 et 61 à 0 et 2 plis ;
    • nombre de coups légaux d'une course construite à la main ;
    • bornes et cohérence des probabilités, symétrie selon le joueur au trait ;
    • décisions de videau évidentes (pas de double au départ, videau mort en Crawford, double/passe dans une course gagnée) ;
    • 8 cas d'erreur.
  • tests/test_makefile.sh ne vide plus le vrai build/ (sinon chaque make test forçait une recompilation du moteur). tests/lint.sh n'impose pas nos règles d'espaces aux copies fidèles de gnubg.

Comment

Le travail a été délégué à un sous-agent selon un plan écrit avant la délégation, supprimé à la clôture ; son résultat est repris ici. Pour les tables d'équité, l'analyseur XML du shim permet à matchequity.c de lire Kazaross-XG2.xml comme le fait gnubg de bureau. engine_init échoue si gnubg se rabat en silence sur une autre table. Le sous-agent a contre-vérifié en liant une variante d'engine-cli à la vraie GLib : la sortie est identique au caractère près sur 6 cas (partie libre, match, Crawford, 0 et 2 plis).

Vérification par la session principale, sur la branche fusionnée avec main :

  • make setup && make engine : 47 s depuis zéro ; un second make engine répond « Nothing to be done » ;
  • ./build/engine-cli bestmove 4HPwATDgc/ABMA MAEAAAAAAAAA 31 → 8/5 6/5 ; à 2 plis, le 64 d'ouverture → 24/18 13/9 en 0,14 s ;
  • gnubg.wd, gnubg_os0.bd et gnubg_ts0.bd générés ici ont les mêmes SHA-256 que ceux de l'image du gnubg de référence (MR !6) ;
  • make lint et make test : 6/6.

Choix et compromis

  • Aucune modification des fichiers de gnubg : tout l'écart passe par engine/compat/. Une mise à jour de gnubg se résume alors à recopier les fichiers et à vérifier la couche de compatibilité.
  • Analyseur XML plutôt que conversion des tables en C : la table reste lue comme dans gnubg de bureau, ce qui limite les risques d'écart, au prix d'un petit analyseur à maintenir dans le shim.
  • État global de gnubg : une seule instance du moteur, pas de réinitialisation après engine_shutdown. C'est une limite assumée (ADR 0005).
  • Le pli rapporté peut être 1 dans un résultat demandé à 2 plis : c'est le comportement de gnubg quand un seul coup survit aux filtres, et c'est cohérent avec les références (champ ply de chaque coup).
  • engine-cli a une sous-commande de plus que prévu, legal, qui liste les coups sans évaluation.

Risques et déploiement

  • Nouvelle dépendance de build, soumise à la validation de Pierre (docs/organisation.md) : l'image ustagammon/build-c, qui contient gcc et make (GPLv3), plus GLib (LGPL 2.1+) uniquement pour compiler les outils officiels makeweights et makebearoff. Rien de GLib n'est embarqué dans le moteur. Ces éléments sont crédités dans CREDITS.md.
  • make setup construit une image de plus (quelques minutes la première fois).
  • Match ID : cAkAAAAAAAAA, utilisé jusqu'ici comme exemple de « partie libre, joueur 0 au trait », met en fait le joueur 1 au trait. L'équivalent avec le joueur 0 au trait est MAEAAAAAAAAA. Sur la position de départ, les deux donnent le même résultat. Les références qui utilisent cAkAAAAAAAAA restent justes, puisque générées et relues par gnubg lui-même.

Retour arrière : annuler le commit de fusion ; build/ se supprime avec make clean.

Comment vérifier

git switch main && git pull
make setup
make engine
./build/engine-cli bestmove 4HPwATDgc/ABMA MAEAAAAAAAAA 31
./build/engine-cli cube 4HPwATDgc/ABMA MAEAAAAAAAAA
make engine        # Nothing to be done
make lint && make test

Hors périmètre

  • La comparaison systématique avec les 32 positions de référence et la mesure de l'écart dû à -ffast-math : étape 0a.4, qui suit.
  • jq utilisé sur le PC par tests/test_reference.sh (ADR 0004) : corrigé à l'étape 0a.4, en passant par l'image build-c.
  • La compilation NEON (ARM) et WebAssembly : étape 0b ou 0c.
  • Les MR de ce dépôt restent affichées « open » après fusion : les hooks git du serveur Gitea ne s'exécutent pas (diagnostic à part).

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

## Pourquoi Étape 0a.3 de la feuille de route : faire du cœur de GNU Backgammon une bibliothèque C autonome, sans GLib ni interface graphique, cachée derrière une interface propre à Ustagammon. C'est la brique que l'application embarquera, en WebAssembly ou sur Android. L'ADR 0005 a fixé la source (gnubg officiel, commit 14b67984737ded0f6de43d63dcb789f729d9b06b) et le plan d'isolement ; l'ADR 0007 impose des identifiants en anglais, d'où `engine.h` au lieu de `moteur.h`. ## Quoi - **`engine/gnubg/`** : copie figée de 38 fichiers de gnubg et de son `COPYING`. **Aucun fichier n'est modifié** : le sous-agent l'a vérifié avec `cmp` contre le clone, et la liste est dans `engine/gnubg/PROVENANCE.md`. Par rapport à la liste de l'ADR 0005, il a fallu ajouter `drawboard.c/h` (notation des coups), `mtsupport.c` et `dice.h`. - **`engine/data/`** : `gnubg.weights` et `Kazaross-XG2.xml`, copiés tels quels. - **`engine/compat/`** : notre couche de compatibilité. - un `config.h` écrit à la main : sans threads, C générique sur x86, chemin NEON prévu mais pas encore compilé ; - un shim GLib minimal, avec un petit analyseur XML compatible GMarkup pour les tables d'équité de match ; - des en-têtes réduits à la place de `backgammon.h` et consorts ; - les fonctions de support que gnubg attend ; le `stubs.c` de CBG a servi de repère, sans reprise de code. - **`engine/include/engine.h`** : l'interface. Fonctions `engine_init`, `engine_shutdown`, `engine_decode_match`, `engine_legal_moves`, `engine_best_moves`, `engine_evaluate` et `engine_cube_decision`. Positions en Position ID + Match ID, niveaux 0 à 4 (réglages prédéfinis de gnubg, sans bruit), erreurs par code de retour. Les données manquantes sont vérifiées avant d'appeler gnubg, dont l'initialisation ferait sinon un `exit()`. - **`engine-cli/main.c`** : sous-commandes `bestmove`, `moves`, `legal`, `cube` et `eval`, options `--ply` et `--data`, sortie JSON. En cas d'erreur, il renvoie du JSON et un code de sortie non nul. - **Build** : - `make engine` compile dans Docker, avec l'image `ustagammon/build-c:1` (`docker/build-c/Dockerfile`, Debian trixie, gcc, make, et GLib uniquement pour les outils de génération) ; - `make build` l'appelle, `make setup` construit l'image ; - code gnubg compilé avec ses options officielles, notre C en `-std=c11 -Wall -Wextra -Werror` ; - `gnubg.wd` et les bases de fin de partie sont générés dans `build/` par les outils officiels, avec les paramètres du build officiel ; - une recompilation ne refait rien si rien n'a changé. - **`tests/test_engine.sh`** (lancé par `make test`) : - ouvertures 31, 42, 53 et 61 à 0 et 2 plis ; - nombre de coups légaux d'une course construite à la main ; - bornes et cohérence des probabilités, symétrie selon le joueur au trait ; - décisions de videau évidentes (pas de double au départ, videau mort en Crawford, double/passe dans une course gagnée) ; - 8 cas d'erreur. - `tests/test_makefile.sh` ne vide plus le vrai `build/` (sinon chaque `make test` forçait une recompilation du moteur). `tests/lint.sh` n'impose pas nos règles d'espaces aux copies fidèles de gnubg. ## Comment Le travail a été délégué à un sous-agent selon un plan écrit avant la délégation, supprimé à la clôture ; son résultat est repris ici. Pour les tables d'équité, l'analyseur XML du shim permet à `matchequity.c` de lire `Kazaross-XG2.xml` comme le fait gnubg de bureau. `engine_init` échoue si gnubg se rabat en silence sur une autre table. Le sous-agent a contre-vérifié en liant une variante d'`engine-cli` à la vraie GLib : la sortie est identique au caractère près sur 6 cas (partie libre, match, Crawford, 0 et 2 plis). Vérification par la session principale, sur la branche fusionnée avec `main` : - `make setup && make engine` : 47 s depuis zéro ; un second `make engine` répond « Nothing to be done » ; - `./build/engine-cli bestmove 4HPwATDgc/ABMA MAEAAAAAAAAA 31` → `8/5 6/5` ; à 2 plis, le 64 d'ouverture → `24/18 13/9` en 0,14 s ; - `gnubg.wd`, `gnubg_os0.bd` et `gnubg_ts0.bd` générés ici ont **les mêmes SHA-256** que ceux de l'image du gnubg de référence (MR !6) ; - `make lint` et `make test` : 6/6. ## Choix et compromis - **Aucune modification des fichiers de gnubg** : tout l'écart passe par `engine/compat/`. Une mise à jour de gnubg se résume alors à recopier les fichiers et à vérifier la couche de compatibilité. - **Analyseur XML plutôt que conversion des tables en C** : la table reste lue comme dans gnubg de bureau, ce qui limite les risques d'écart, au prix d'un petit analyseur à maintenir dans le shim. - **État global de gnubg** : une seule instance du moteur, pas de réinitialisation après `engine_shutdown`. C'est une limite assumée (ADR 0005). - **Le pli rapporté peut être 1 dans un résultat demandé à 2 plis** : c'est le comportement de gnubg quand un seul coup survit aux filtres, et c'est cohérent avec les références (champ `ply` de chaque coup). - **`engine-cli` a une sous-commande de plus que prévu**, `legal`, qui liste les coups sans évaluation. ## Risques et déploiement - **Nouvelle dépendance de build**, soumise à la validation de Pierre (`docs/organisation.md`) : l'image `ustagammon/build-c`, qui contient gcc et make (GPLv3), plus GLib (LGPL 2.1+) uniquement pour compiler les outils officiels `makeweights` et `makebearoff`. Rien de GLib n'est embarqué dans le moteur. Ces éléments sont crédités dans `CREDITS.md`. - `make setup` construit une image de plus (quelques minutes la première fois). - **Match ID** : `cAkAAAAAAAAA`, utilisé jusqu'ici comme exemple de « partie libre, joueur 0 au trait », met en fait le joueur 1 au trait. L'équivalent avec le joueur 0 au trait est `MAEAAAAAAAAA`. Sur la position de départ, les deux donnent le même résultat. Les références qui utilisent `cAkAAAAAAAAA` restent justes, puisque générées et relues par gnubg lui-même. Retour arrière : annuler le commit de fusion ; `build/` se supprime avec `make clean`. ## Comment vérifier ``` git switch main && git pull make setup make engine ./build/engine-cli bestmove 4HPwATDgc/ABMA MAEAAAAAAAAA 31 ./build/engine-cli cube 4HPwATDgc/ABMA MAEAAAAAAAAA make engine # Nothing to be done make lint && make test ``` ## Hors périmètre - La comparaison systématique avec les 32 positions de référence et la mesure de l'écart dû à `-ffast-math` : étape 0a.4, qui suit. - `jq` utilisé sur le PC par `tests/test_reference.sh` (ADR 0004) : corrigé à l'étape 0a.4, en passant par l'image `build-c`. - La compilation NEON (ARM) et WebAssembly : étape 0b ou 0c. - Les MR de ce dépôt restent affichées « open » après fusion : les hooks git du serveur Gitea ne s'exécutent pas (diagnostic à part). Generated with Claude Code https://claude.com/claude-code
pierre added 8 commits 2026-11-02 13:03:28 +00:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fichiers repris sans modification dans engine/gnubg/, poids et table
d'équité Kazaross-XG2 dans engine/data/, provenance détaillée dans
engine/gnubg/PROVENANCE.md. Le lint ne vérifie plus les espaces de ces
copies, pour qu'elles restent identiques à l'original.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
engine/compat/ remplace ce que le cœur de gnubg attend du reste de gnubg
et de GLib : config.h écrit à la main (sans threads, C générique, NEON
prévu), shim GLib minimal (dont un analyseur XML pour les tables d'équité
et mmap pour les bases de fin de partie), en-têtes réduits et fonctions de
support.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
engine.h expose initialisation, coups légaux, classement des coups,
évaluation et décision de videau, avec Position ID et Match ID, sans type
gnubg ; erreurs par code de retour. engine-cli l'interroge et répond en
JSON (bestmove, moves, legal, cube, eval ; option --ply).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Image ustagammon/build-c (Debian trixie, gcc, GLib pour les seuls outils
de génération), construite par make setup. engine/engine.mk compile le
moteur et engine-cli, et génère gnubg.wd et les bases .bd avec makeweights
et makebearoff, comme le Makefile.am de gnubg. Dépendances suivies : un
second make engine ne recompile rien. make build appelle make engine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/test_engine.sh : coups d'ouverture connus à 0 et 2 plis, nombre de
coups légaux, bornes et symétrie des probabilités, décisions de videau
évidentes, erreurs. test_makefile.sh essaie make clean sur un dossier
jetable, pour ne plus forcer la recompilation du moteur à chaque test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fusion du travail du sous-agent (branche moteur-embarque-travail) ; plan
supprimé à la clôture, son résultat est repris dans la merge request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pierre manually merged commit 51801e8491 into main 2026-11-02 18:03:48 +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#7