Moteur : plateau, fin de partie, Match ID et saisie demi-coup par demi-coup

Ce dont le domaine d'une partie a besoin, sans réimplémenter de règle :
décodage d'un Position ID en plateau et position vue de l'adversaire,
résultat d'une partie finie (gammon, règle Jacoby), codage d'un Match ID,
vérification de demi-coups saisis un par un (la légalité vient de
GenerateMoves de gnubg), demi-coups de chaque coup légal, droit de doubler
et recommandations de double et de prise. Nouvelles commandes d'engine-cli
position, halfmoves et matchid, avec leurs tests natifs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Pierre MARTIN
2026-11-02 20:23:04 +01:00
co-authored by Claude Opus 5.5
parent 03b26ccba7
commit 6af82f73b0
4 changed files with 644 additions and 8 deletions
+84
View File
@@ -66,11 +66,29 @@ enum {
ENGINE_NUM_PROBABILITIES
};
/*
* Demi-coup : une dame déplacée d'une case avec un seul dé. Les cases sont
* numérotées du point de vue du joueur qui joue, comme dans la notation de
* gnubg : 1 à 24, 25 pour la barre, 0 pour la sortie.
*/
#define ENGINE_BAR 25
#define ENGINE_OFF 0
typedef struct {
int from;
int to;
} engine_half_move;
/* Un coup complet utilise au plus quatre dés (un double). */
#define ENGINE_MAX_HALF_MOVES 4
/* Un coup, avec son évaluation quand il a été classé. */
typedef struct {
char notation[32]; /* notation de gnubg, ex. « 8/5 6/5 » */
char position_id[16]; /* Position ID après le coup, plateau vu du
* joueur qui vient de jouer */
engine_half_move half_moves[ENGINE_MAX_HALF_MOVES];
int half_move_count; /* demi-coups du coup, dans un ordre jouable */
int ply; /* niveau de l'évaluation (-1 : non évalué) */
float probabilities[ENGINE_NUM_PROBABILITIES];
float equity; /* équité de classement (avec videau) */
@@ -96,8 +114,50 @@ typedef struct {
* « DOUBLE_TAKE », « DOUBLE_PASS »,
* « TOOGOOD_PASS », « NOT_AVAILABLE »… */
const char *decision_text; /* libellé anglais de gnubg, ex. « Double, take » */
int available; /* le joueur au trait a le droit de doubler */
int should_double; /* gnubg recommande de doubler : décision
* DOUBLE_TAKE, DOUBLE_PASS, DOUBLE_BEAVER,
* REDOUBLE_TAKE ou REDOUBLE_PASS (un double
* « facultatif » n'est pas recommandé) */
int should_take; /* s'il double, l'adversaire doit prendre :
* prendre lui coûte moins que passer
* (double_take <= double_pass) */
} engine_cube;
/* Un camp sur le plateau, vu de lui-même (sa case 1 est la plus proche de
* la sortie). */
typedef struct {
int points[24]; /* dames sur les cases 1 à 24 (indices 0 à 23) */
int bar; /* dames sur la barre */
int off; /* dames sorties */
int pip_count; /* compte de points */
} engine_side;
/* Plateau décodé d'un Position ID. */
typedef struct {
engine_side on_roll; /* le joueur au trait */
engine_side opponent; /* son adversaire */
char swapped_position_id[16]; /* la même position vue de l'adversaire */
} engine_board;
/* Fin de partie. */
typedef struct {
int over; /* la partie est finie */
int winner; /* 0 : le joueur au trait, 1 : son adversaire */
int kind; /* 1 : simple, 2 : gammon, 3 : backgammon */
int points; /* points gagnés : kind fois la valeur du videau,
* mais 1 fois en partie libre avec la règle
* Jacoby quand le videau n'a pas bougé */
} engine_result;
/* Où en est un coup saisi demi-coup par demi-coup. */
typedef struct {
int valid; /* les demi-coups commencent un coup légal */
int complete; /* ils forment à eux seuls un coup légal */
char position_id[16]; /* si valid : position après ces demi-coups, vue
* du joueur qui joue ; sinon chaîne vide */
} engine_half_moves_check;
/* Description lisible d'un Match ID décodé. */
typedef struct {
int match_length; /* 0 : partie libre (money game) */
@@ -125,6 +185,30 @@ const char *engine_status_message(engine_status status);
/* Décode un Match ID. */
engine_status engine_decode_match(const char *match_id, engine_match *match);
/*
* Code un Match ID (12 caractères et le zéro final), sans dés ni double en
* cours. cube_value : puissance de 2 de 1 à 4096 ; cube_owner : -1, 0 ou 1.
*/
engine_status engine_encode_match(const engine_match *match, char match_id[13]);
/* Décode un Position ID en plateau. */
engine_status engine_decode_position(const char *position_id, engine_board *board);
/* La partie est-elle finie dans cette position, et combien rapporte-t-elle ? */
engine_status engine_game_result(const char *position_id, const char *match_id,
engine_result *result);
/*
* Vérifie des demi-coups saisis un par un (count de 0 à
* ENGINE_MAX_HALF_MOVES), avec le jet die1, die2 : chacun doit être permis
* par les règles, et l'ensemble doit pouvoir se compléter en un des coups
* légaux d'engine_legal_moves. Des demi-coups refusés ne sont pas une erreur :
* check->valid vaut alors 0.
*/
engine_status engine_check_half_moves(const char *position_id, int die1, int die2,
const engine_half_move *half_moves, int count,
engine_half_moves_check *check);
/*
* Coups légaux pour une position et un jet (die1, die2 dans 1..6), sans
* évaluation (ply vaut -1). moves doit pouvoir contenir max_moves coups ;