Positions de référence évaluées par gnubg (0 et 2 plis)
32 positions (ouvertures, milieu, prime, blitz, course, sortie, videau, match, Crawford, Jacoby), générées par make reference ; format documenté dans tests/reference/README.md, vérifié par tests/test_reference.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8677193bee
commit
b199698b11
@@ -0,0 +1,229 @@
|
||||
# Positions de référence
|
||||
|
||||
Ce dossier contient des positions de backgammon évaluées par le GNU Backgammon
|
||||
de bureau, compilé dans Docker au commit figé par l'ADR 0005. Le moteur
|
||||
embarqué d'Ustagammon (et son `engine-cli`) doit retrouver ces résultats, à
|
||||
une tolérance près. **Ce document est le contrat du format** : `engine-cli`
|
||||
produit pour une position un objet de même structure que la partie `plies`
|
||||
d'un fichier de résultat.
|
||||
|
||||
| Fichier | Rôle |
|
||||
| --- | --- |
|
||||
| `positions.json` | Positions d'entrée, versionnées, écrites à la main |
|
||||
| `results/<id>.json` | Résultats de gnubg, un fichier par position, générés |
|
||||
| `generate.py` | Script exécuté dans gnubg qui produit `results/` |
|
||||
|
||||
Commandes :
|
||||
|
||||
```
|
||||
make reference-image # construit l'image Docker du gnubg de référence
|
||||
make reference # régénère results/ (une vingtaine de secondes)
|
||||
make test # vérifie la cohérence (tests/test_reference.sh)
|
||||
```
|
||||
|
||||
`make reference` est déterministe : deux exécutions donnent des fichiers
|
||||
identiques (`git diff --exit-code tests/reference`).
|
||||
|
||||
## Référence utilisée
|
||||
|
||||
- **gnubg** : GNU Backgammon 1.08.003, commit
|
||||
14b67984737ded0f6de43d63dcb789f729d9b06b, compilé par
|
||||
`docker/gnubg-reference/Dockerfile` (Debian trixie, sans interface
|
||||
graphique, SIMD SSE2 forcé, drapeaux par défaut de gnubg `-O3 -ffast-math`).
|
||||
- **Données** : `gnubg.weights` du dépôt, et `gnubg.wd`, `gnubg_os0.bd`,
|
||||
`gnubg_ts0.bd` produits dans l'image par les outils officiels (`makeweights`,
|
||||
`makebearoff -o 6 -s 7999999`, `makebearoff -t 6x6`). Leurs sommes SHA-256
|
||||
sont dans l'image, `/usr/local/share/gnubg/donnees.sha256` ; le moteur
|
||||
embarqué doit utiliser exactement ces fichiers.
|
||||
- **Table d'équité de match** : Kazaross-XG2 (`met/Kazaross-XG2.xml`), la
|
||||
table par défaut de gnubg.
|
||||
- **Autres réglages** : ceux par défaut de gnubg. En particulier les castors
|
||||
(beavers) sont autorisés en partie libre (ils ne figurent pas dans le Match
|
||||
ID) et ignorés en match, et le filtre de coups est le filtre « Normal ».
|
||||
|
||||
## Positions d'entrée : `positions.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"positions": [
|
||||
{
|
||||
"id": "opening-31",
|
||||
"type": "move",
|
||||
"categories": ["opening"],
|
||||
"description": "Position de départ, premier coup 3-1, partie libre.",
|
||||
"position_id": "4HPwATDgc/ABMA",
|
||||
"match_id": "cIkFAAAAAAAA",
|
||||
"dice": [3, 1]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `id` : identifiant unique, sert de nom de fichier dans `results/`.
|
||||
- `type` : `"move"` (dés jetés : quel coup jouer) ou `"cube"` (dés pas encore
|
||||
jetés : faut-il doubler).
|
||||
- `categories` : étiquettes de couverture. Valeurs utilisées : `opening`,
|
||||
`middlegame`, `prime`, `blitz`, `race`, `bearoff`, `edge_case`,
|
||||
`cube_no_double`, `cube_double_take`, `cube_double_pass`, `cube_redouble`,
|
||||
`cube_too_good`, `match`, `crawford`, `post_crawford`, `jacoby`.
|
||||
- `position_id`, `match_id` : la position au format GNU Backgammon. Le Match ID
|
||||
porte tout le contexte : dés, joueur au trait, videau et propriétaire,
|
||||
longueur du match, score, Crawford, règle Jacoby.
|
||||
- `dice` : les dés, redondants avec le Match ID (le générateur vérifie qu'ils
|
||||
concordent) ; `null` pour une décision de videau.
|
||||
|
||||
Pour ajouter une position : obtenir ses identifiants dans gnubg (ils
|
||||
s'affichent sous le plateau, ou `show gnubgid`), l'ajouter à
|
||||
`positions.json`, puis `make reference`.
|
||||
|
||||
## Résultats : `results/<id>.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "opening-31",
|
||||
"type": "move",
|
||||
"categories": ["opening"],
|
||||
"description": "…",
|
||||
"position_id": "4HPwATDgc/ABMA",
|
||||
"match_id": "cIkFAAAAAAAA",
|
||||
"gnubg_commit": "14b67984737ded0f6de43d63dcb789f729d9b06b",
|
||||
"met": "Kazaross-XG2",
|
||||
"context": {
|
||||
"dice": [3, 1],
|
||||
"match_length": 0,
|
||||
"score_player": 0,
|
||||
"score_opponent": 0,
|
||||
"crawford": false,
|
||||
"jacoby": true,
|
||||
"beavers": true,
|
||||
"cube_value": 1,
|
||||
"cube_owner": "centered"
|
||||
},
|
||||
"plies": {
|
||||
"0": { "evaluation": { … }, "moves": { … } },
|
||||
"2": { "evaluation": { … }, "moves": { … } }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`context` est le contexte tel que gnubg l'a relu dans le Match ID :
|
||||
`match_length` vaut 0 en partie libre ; `score_player` est le score du joueur
|
||||
au trait ; `cube_owner` vaut `centered`, `player` (le joueur au trait) ou
|
||||
`opponent` ; `beavers` dit si les castors comptent (partie libre seulement).
|
||||
Voir `results/opening-31.json` pour un fichier complet.
|
||||
|
||||
### Niveaux : `plies`
|
||||
|
||||
Les clés `"0"` et `"2"` sont des niveaux comptés en **plis à la manière de
|
||||
gnubg** : 0 pli = évaluation statique du réseau de neurones, 2 plis = deux
|
||||
demi-coups de recherche. Ce sont les réglages prédéfinis « 0ply » et « 2ply »
|
||||
de gnubg (`aecSettings` dans `eval.c`), sans bruit : cubeful, déterministe,
|
||||
élagage (`prune`) désactivé à 0 pli et activé à 2 plis.
|
||||
|
||||
### Conventions communes
|
||||
|
||||
- **Point de vue** : tout est donné du point de vue du **joueur au trait**
|
||||
(celui qui a les dés ou qui doit décider de doubler). Une équité positive
|
||||
lui est favorable.
|
||||
- **Probabilités** (`probabilities`), entre 0 et 1, issues des sorties du
|
||||
réseau de gnubg :
|
||||
- `win` : probabilité de gagner la partie ;
|
||||
- `win_gammon` : de gagner au moins un gammon (backgammons compris) ;
|
||||
- `win_backgammon` : de gagner un backgammon ;
|
||||
- `lose_gammon` : de perdre au moins un gammon (backgammons compris) ;
|
||||
- `lose_backgammon` : de perdre un backgammon.
|
||||
La probabilité de perdre est `1 - win`.
|
||||
- **Équités** : en points pour un videau à 1. En match, gnubg convertit sa
|
||||
probabilité de gagner le match en « équité monétaire normalisée »
|
||||
(`mwc2eq`) ; c'est cette valeur qui est donnée.
|
||||
- **Nombres** : gnubg calcule en float32 ; chaque nombre est écrit sous la
|
||||
forme décimale la plus courte qui redonne exactement ce float32.
|
||||
|
||||
### `evaluation` : la position avant le jet des dés
|
||||
|
||||
```json
|
||||
"evaluation": {
|
||||
"probabilities": { "win": 0.52483594, "win_gammon": 0.14950693,
|
||||
"win_backgammon": 0.006851064, "lose_gammon": 0.124798514,
|
||||
"lose_backgammon": 0.0052878163 },
|
||||
"equity": 0.07594353
|
||||
}
|
||||
```
|
||||
|
||||
Sortie de `gnubg.evaluate` : les cinq probabilités et l'équité **sans videau**
|
||||
(cubeless) correspondante, au score du match, pour la position *avant* que le
|
||||
joueur au trait jette les dés (même pour une position de type `move`).
|
||||
|
||||
### `moves` : classement des coups (type `move`)
|
||||
|
||||
```json
|
||||
"moves": {
|
||||
"legal_move_count": 16,
|
||||
"ranking": [
|
||||
{ "rank": 1, "move": "8/5 6/5", "ply": 1, "equity": 0.20373425,
|
||||
"equity_diff": 0.0, "probabilities": { … } },
|
||||
…
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `legal_move_count` : nombre de coups légaux au sens de gnubg, c'est-à-dire de
|
||||
**positions d'arrivée distinctes** (deux suites de déplacements qui mènent à
|
||||
la même position comptent une fois). 0 si aucun coup n'est possible.
|
||||
- `ranking` : les 10 meilleurs coups (tous s'il y en a moins), dans l'ordre de
|
||||
la commande `hint` de gnubg :
|
||||
- `move` : notation de gnubg (`FormatMove`), points comptés du point de vue
|
||||
du joueur au trait, `bar` et `off`, `*` pour une frappe, `(2)` pour un
|
||||
déplacement répété ;
|
||||
- `ply` : niveau auquel ce coup a réellement été évalué. À 2 plis, gnubg
|
||||
évalue d'abord tous les coups à 0 pli puis ne garde pour les plis
|
||||
suivants que les meilleurs (filtre « Normal » : à 0 pli les 12 meilleurs
|
||||
au plus dans un écart de 0,24 ; à 1 pli les 6 meilleurs au plus dans un
|
||||
écart de 0,12). Les coups éliminés gardent une évaluation à 0 ou 1 pli.
|
||||
Si un seul coup survit à un filtre, gnubg s'arrête là : c'est le cas de
|
||||
l'exemple ci-dessus (3-1 d'ouverture à 2 plis), où `8/5 6/5` reste seul
|
||||
après le filtre de 1 pli et garde donc `"ply": 1`, comme dans l'affichage
|
||||
« Cubeful 1-ply » de gnubg. Les coups forcés restent à 0 pli ;
|
||||
- `equity` : équité **avec videau** (cubeful) après ce coup ;
|
||||
- `equity_diff` : `equity` moins celle du premier coup (0 ou négatif) ;
|
||||
- `probabilities` : les cinq probabilités après ce coup, toujours du point de
|
||||
vue du joueur qui vient de jouer.
|
||||
|
||||
Le classement reproduit exactement `hint` : `FindnSaveBestMoves` avec
|
||||
`fAnalyse` vrai, puis `RefreshMoveList` qui départage les équités égales.
|
||||
|
||||
### `cube` : décision de videau (type `cube`)
|
||||
|
||||
```json
|
||||
"cube": {
|
||||
"no_double": 0.83056116,
|
||||
"double_take": 0.9218164,
|
||||
"double_pass": 1.0,
|
||||
"optimal": 0.9218164,
|
||||
"decision": "DOUBLE_TAKE",
|
||||
"decision_text": "Double, take"
|
||||
}
|
||||
```
|
||||
|
||||
Sortie de `gnubg.cfevaluate` (`GeneralCubeDecisionE` puis
|
||||
`FindCubeDecision`), en équités avec videau ramenées à un videau de valeur 1
|
||||
(même quand le videau est déjà à 2, voir `cube-redouble`) ; exemple tiré de `cube-double-take` :
|
||||
|
||||
- `no_double` : si le joueur au trait ne double pas ;
|
||||
- `double_take` : s'il double et que l'adversaire prend ;
|
||||
- `double_pass` : s'il double et que l'adversaire passe ; vaut 1 par
|
||||
construction (en match, la probabilité de gagner le match après le passe,
|
||||
tirée de la table d'équité, est normalisée à 1 par `mwc2eq`) ;
|
||||
- `optimal` : l'équité retenue après les meilleures décisions des deux côtés ;
|
||||
- `decision` : nom de la décision dans l'énumération `cubedecision` de
|
||||
`eval.h` de gnubg (`NODOUBLE_TAKE`, `DOUBLE_TAKE`, `DOUBLE_PASS`,
|
||||
`TOOGOOD_PASS`, `REDOUBLE_TAKE`, `NODOUBLE_DEADCUBE`…) ;
|
||||
- `decision_text` : le libellé anglais correspondant de gnubg.
|
||||
|
||||
## Comparaison avec le moteur embarqué
|
||||
|
||||
Une concordance au bit près n'est pas attendue (ADR 0005) : `-ffast-math` et
|
||||
le jeu d'instructions (SSE2 ici, NEON sur téléphone, C générique en
|
||||
WebAssembly) changent les derniers chiffres. Les tests de concordance devront
|
||||
donc comparer les nombres avec une tolérance, et accepter qu'entre deux coups
|
||||
d'équités presque égales l'ordre s'inverse.
|
||||
Reference in New Issue
Block a user