Files
Ustagammon/engine/include/engine.h

245 lines
10 KiB
C

/*
* engine.h -- interface C du moteur de backgammon d'Ustagammon.
*
* Copyright (C) 2026 les auteurs d'Ustagammon.
* Licence : GPLv3 ou ultérieure (voir LICENSE).
*
* Le moteur est le cœur de GNU Backgammon (engine/gnubg/), seule autorité
* pour les règles, les coups, le videau et les évaluations. Cette interface
* n'expose aucun type de gnubg.
*
* Positions : une position est décrite par deux identifiants de gnubg,
* - le Position ID (14 caractères), qui code le plateau du point de vue du
* joueur au trait ;
* - le Match ID (12 caractères), qui code le reste : longueur du match (0 en
* partie libre), score, videau et son propriétaire, joueur au trait,
* Crawford, Jacoby. Les dés éventuellement codés dans le Match ID sont
* ignorés : les dés sont passés à part.
*
* Niveaux (« ply ») : réglages prédéfinis de gnubg, sans bruit, de 0
* (évaluation statique du réseau) à 4. gnubg compte les plis à partir de 0.
*
* Équités : toujours du point de vue du joueur au trait, normalisées pour
* un videau de 1. En match, gnubg convertit les chances de gain du match en
* équité normalisée, comme il l'affiche par défaut.
*
* Probabilités, dans l'ordre de gnubg : gain, gain gammon, gain backgammon,
* perte gammon, perte backgammon (les gammons incluent les backgammons).
*
* Contraintes : une seule instance du moteur (état global de gnubg) et des
* appels sérialisés, jamais concurrents. Toutes les fonctions renvoient un
* code engine_status ; aucune ne termine le programme pour une erreur
* d'utilisation.
*/
#ifndef USTAGAMMON_ENGINE_H
#define USTAGAMMON_ENGINE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
ENGINE_OK = 0,
ENGINE_ERROR_NOT_INITIALISED, /* engine_init n'a pas réussi */
ENGINE_ERROR_DATA, /* données absentes ou invalides */
ENGINE_ERROR_POSITION_ID, /* Position ID invalide */
ENGINE_ERROR_MATCH_ID, /* Match ID invalide */
ENGINE_ERROR_DICE, /* dés hors de 1..6 */
ENGINE_ERROR_PLY, /* niveau hors de 0..ENGINE_MAX_PLY */
ENGINE_ERROR_TOO_MANY_MOVES, /* tableau de coups trop petit */
ENGINE_ERROR_INTERNAL /* échec signalé par gnubg */
} engine_status;
#define ENGINE_MAX_PLY 4
/* Nombre maximal de coups légaux pour un jet (MAX_MOVES de gnubg). */
#define ENGINE_MAX_MOVES 3060
/* Indices du tableau probabilities. */
enum {
ENGINE_WIN = 0,
ENGINE_WIN_GAMMON,
ENGINE_WIN_BACKGAMMON,
ENGINE_LOSE_GAMMON,
ENGINE_LOSE_BACKGAMMON,
ENGINE_NUM_PROBABILITIES
};
/*
* Demi-coup : un pion déplacé 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) */
} engine_move;
/* Évaluation d'une position, joueur au trait avant de lancer les dés. */
typedef struct {
float probabilities[ENGINE_NUM_PROBABILITIES];
float cubeless_equity; /* équité sans videau */
float cubeful_equity; /* équité avec videau */
} engine_evaluation;
/* Décision de videau du joueur au trait, avant de lancer les dés. */
typedef struct {
float probabilities[ENGINE_NUM_PROBABILITIES];
float no_double; /* équité si pas de double */
float double_take; /* équité si double et prend */
float double_pass; /* équité si double et passe */
float optimal; /* équité après les meilleures décisions des deux
* joueurs */
const char *decision; /* nom de la décision dans l'énumération
* cubedecision de gnubg : « NODOUBLE_TAKE »,
* « 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]; /* pions sur les cases 1 à 24 (indices 0 à 23) */
int bar; /* pions sur la barre */
int off; /* pions sortis */
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) */
int score[2];
int cube_value;
int cube_owner; /* -1 : videau au centre ; sinon 0 ou 1 */
int player_on_roll; /* 0 ou 1 */
int crawford; /* partie Crawford en cours */
int jacoby; /* règle Jacoby (partie libre) */
} engine_match;
/*
* Charge les données du moteur depuis data_dir : gnubg.wd (réseaux),
* gnubg_os0.bd et gnubg_ts0.bd (fins de partie), Kazaross-XG2.xml (table
* d'équité de match). À appeler une fois avant toute autre fonction.
*/
engine_status engine_init(const char *data_dir);
/* Libère les ressources. Le moteur ne peut pas être réinitialisé ensuite. */
void engine_shutdown(void);
/* Message d'erreur en français pour un code de retour. */
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 ;
* ENGINE_MAX_MOVES suffit toujours. *count reçoit le nombre de coups (0 si
* le joueur ne peut pas jouer).
*/
engine_status engine_legal_moves(const char *position_id, const char *match_id,
int die1, int die2,
engine_move *moves, int max_moves, int *count);
/*
* Coups légaux classés du meilleur au moins bon, au niveau ply, exactement
* comme la commande « hint » de gnubg (filtre de coups « Normal »). Les coups
* écartés par les filtres gardent l'évaluation du niveau inférieur (voir
* leur champ ply) et sont classés après ceux évalués plus profondément.
*/
engine_status engine_best_moves(const char *position_id, const char *match_id,
int die1, int die2, int ply,
engine_move *moves, int max_moves, int *count);
/* Évaluation d'une position au niveau ply. */
engine_status engine_evaluate(const char *position_id, const char *match_id,
int ply, engine_evaluation *evaluation);
/* Décision de videau au niveau ply. */
engine_status engine_cube_decision(const char *position_id, const char *match_id,
int ply, engine_cube *cube);
#ifdef __cplusplus
}
#endif
#endif /* USTAGAMMON_ENGINE_H */