Moteur : videau de l'ordinateur comme ComputerTurn, match après une partie, Match ID d'un match

Fenêtre de marché et double facultatif à 0 pli (play.c), réponse au
double, règle de Crawford automatique ; commandes double, take et
aftergame d'engine-cli, matchid étendu aux matchs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Pierre MARTIN
2026-11-09 18:42:35 +01:00
co-authored by Claude Opus 5.5
parent 71bff4def1
commit c0f827c5ba
4 changed files with 393 additions and 19 deletions
+154
View File
@@ -156,6 +156,8 @@ engine_status_message(engine_status status)
return "coup joué absent des coups légaux";
case ENGINE_ERROR_CUBE_ACTION:
return "décision de videau inconnue";
case ENGINE_ERROR_RESULT:
return "résultat de partie invalide";
}
return "erreur inconnue";
}
@@ -667,6 +669,158 @@ engine_level_cube_decision(const char *position_id, const char *match_id, int le
return cube_decision(&pos, &context, cube);
}
/* -------------------------------------------- Videau de l'ordinateur */
/* Valeur maximale du videau (MAX_CUBE dans backgammon.h de gnubg, non
* repris ici). */
#define MAX_CUBE_VALUE (1 << 12)
/* La décision de videau de gnubg pour une position, avec ces réglages. */
static engine_status
find_cube_decision(position *pos, const evalcontext *context, cubedecision *decision)
{
float output[2][NUM_ROLLOUT_OUTPUTS];
float equities[NUM_CUBEFUL_OUTPUTS];
if (GeneralCubeDecisionE(output, (ConstTanBoard) pos->board, &pos->cube, context, NULL) < 0)
return ENGINE_ERROR_INTERNAL;
*decision = FindCubeDecision(equities, output, &pos->cube);
if ((size_t) *decision >= sizeof cube_decision_names / sizeof cube_decision_names[0])
return ENGINE_ERROR_INTERNAL;
return ENGINE_OK;
}
/* Voir engine.h : ComputerTurn, play.c l. 1312-1398. */
engine_status
engine_level_double_decision(const char *position_id, const char *match_id, int level,
engine_level_double *result)
{
position pos;
evalcontext context, market_context;
float output[NUM_ROLLOUT_OUTPUTS];
cubedecision decision;
engine_status status;
int available = 0;
memset(result, 0, sizeof *result);
status = decode_position(position_id, match_id, &pos);
if (status == ENGINE_OK)
status = eval_context_for_level(level, &context);
if (status != ENGINE_OK)
return status;
/* « if (ms.fCubeUse && !ms.anDice[0] && ms.nCube < MAX_CUBE && GetDPEq(...)) » */
GetDPEq(&available, NULL, &pos.cube);
if (!available || pos.cube.nCube >= MAX_CUBE_VALUE)
return ENGINE_OK;
result->available = 1;
/* Fenêtre de marché : évaluation sans videau, un pli de moins. */
market_context = context;
market_context.fCubeful = FALSE;
if (market_context.nPlies)
market_context.nPlies--;
if (EvaluatePosition(NULL, (ConstTanBoard) pos.board, output, &pos.cube, &market_context))
return ENGINE_ERROR_INTERNAL;
if (output[0] < GetDoublePointDeadCube(output, &pos.cube))
return ENGINE_OK;
result->market_window = 1;
status = find_cube_decision(&pos, &context, &decision);
if (status != ENGINE_OK)
return status;
result->decision = cube_decision_names[decision];
switch (decision) {
case DOUBLE_TAKE:
case REDOUBLE_TAKE:
case DOUBLE_PASS:
case REDOUBLE_PASS:
case DOUBLE_BEAVER:
result->doubles = 1;
break;
case OPTIONAL_DOUBLE_BEAVER:
case OPTIONAL_DOUBLE_TAKE:
case OPTIONAL_REDOUBLE_TAKE:
case OPTIONAL_DOUBLE_PASS:
case OPTIONAL_REDOUBLE_PASS:
/* « double if 0-ply except when about to lose game » */
result->doubles = context.nPlies == 0 && output[0] > 0.001f;
break;
default:
/* « better leave cube where it is » */
break;
}
return ENGINE_OK;
}
/* Voir engine.h : ComputerTurn, play.c l. 1139-1235. */
engine_status
engine_level_take_decision(const char *position_id, const char *match_id, int level,
int *takes, const char **decision_name)
{
position pos;
evalcontext context;
cubedecision decision;
engine_status status;
status = decode_position(position_id, match_id, &pos);
if (status == ENGINE_OK)
status = eval_context_for_level(level, &context);
if (status == ENGINE_OK)
status = find_cube_decision(&pos, &context, &decision);
if (status != ENGINE_OK)
return status;
switch (decision) {
case DOUBLE_PASS:
case TOOGOOD_PASS:
case REDOUBLE_PASS:
case TOOGOODRE_PASS:
case OPTIONAL_DOUBLE_PASS:
case OPTIONAL_REDOUBLE_PASS:
*takes = 0;
break;
default:
*takes = 1;
break;
}
*decision_name = cube_decision_names[decision];
return ENGINE_OK;
}
/* ------------------------------------------------------------------ Match */
/* Voir engine.h : play.c l. 303, 1817-1820 et 4277. */
engine_status
engine_match_after_game(const char *match_id, int winner, int points,
engine_match_after *after)
{
engine_match m;
engine_status status = engine_decode_match(match_id, &m);
int length, post_crawford;
if (status != ENGINE_OK)
return status;
if (winner < 0 || winner > 1 || points < 1)
return ENGINE_ERROR_RESULT;
memset(after, 0, sizeof *after);
length = m.match_length;
after->score[0] = m.score[0];
after->score[1] = m.score[1];
after->score[winner] += points;
if (length == 0)
return ENGINE_OK;
after->match_over = after->score[winner] >= length;
if (after->match_over)
return ENGINE_OK;
/* Partie Crawford déjà jouée, lue comme gnubg (play.c l. 303). */
post_crawford = !m.crawford && (m.score[0] == length - 1 || m.score[1] == length - 1);
/* play.c l. 1817-1820. */
post_crawford |= m.crawford && after->score[winner] < length;
after->crawford = !post_crawford && !m.crawford && after->score[winner] == length - 1
&& after->score[!winner] != length - 1;
return ENGINE_OK;
}
/* -------------------------------------------- Analyse d'une décision de videau */
/* Le plus petit de deux nombres. */