Files
Ustagammon/engine/gnubg/drawboard.c
T
Pierre MARTINandClaude Opus 5.5 792f6395bf Copie figée du cœur de GNU Backgammon (commit 14b6798)
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>
2026-11-02 13:56:23 +01:00

1141 lines
30 KiB
C

/*
* Copyright (C) 1999-2002 Gary Wong <gtw@gnu.org>
* Copyright (C) 2000-2020 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* $Id: drawboard.c,v 1.82 2020/02/23 20:20:37 plm Exp $
*/
#include "config.h"
#include <glib.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "backgammon.h"
#include "drawboard.h"
#include "positionid.h"
#include "matchequity.h"
int fClockwise = FALSE; /* Player 1 moves clockwise */
/*
* GNU Backgammon Position ID: 0123456789ABCD
* Match ID : 0123456789ABCD
* +13-14-15-16-17-18------19-20-21-22-23-24-+ O: gnubg (Cube: 2)
* | | | O O O O O | OO 0 points
* | | | O O | OO Cube offered at 2
* | | | O | O
* | | | | O
* | | | | O
* v| |BAR| | Cube: 1 (7 point match)
* | | | | X
* | | | | X
* | | | | X
* | | | X X X X | X Rolled 11
* | | | X X X X X | XX 0 points
* +12-11-10--9--8--7-------6--5--4--3--2--1-+ X: Gary (Cube: 2)
*
*/
static char *
DrawBoardStd(char *sz, const TanBoard anBoard, int fRoll, char *asz[], char *szMatchID, int nChequers)
{
char *pch = sz, *pchIn;
unsigned int x, y;
unsigned int cOffO = nChequers, cOffX = nChequers;
static char achX[17] = " X6789ABCDEF", achO[17] = " O6789ABCDEF";
for (x = 0; x < 25; x++) {
cOffO -= anBoard[0][x];
cOffX -= anBoard[1][x];
}
pch += sprintf(pch, " %-15s %s: ", _("GNU Backgammon"), _("Position ID"));
if (fRoll)
strcpy(pch, PositionID((ConstTanBoard) anBoard));
else {
TanBoard an;
for (x = 0; x < 25; x++) {
an[0][x] = anBoard[1][x];
an[1][x] = anBoard[0][x];
}
strcpy(pch, PositionID((ConstTanBoard) an));
}
pch += L_POSITIONID;
*pch++ = '\n';
/* match id */
if (szMatchID && *szMatchID) {
pch += sprintf(pch, " %s : %s\n", _("Match ID"), szMatchID);
}
strcpy(pch, fRoll ? " +13-14-15-16-17-18------19-20-21-22-23-24-+ " :
" +12-11-10--9--8--7-------6--5--4--3--2--1-+ ");
pch += 49;
if (asz[0])
for (pchIn = asz[0]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
for (y = 0; y < 4; y++) {
*pch++ = ' ';
*pch++ = '|';
for (x = 12; x < 18; x++) {
*pch++ = ' ';
*pch++ = anBoard[1][x] > y ? 'X' : anBoard[0][23 - x] > y ? 'O' : ' ';
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = anBoard[0][24] > y ? 'O' : ' ';
*pch++ = ' ';
*pch++ = '|';
for (; x < 24; x++) {
*pch++ = ' ';
*pch++ = anBoard[1][x] > y ? 'X' : anBoard[0][23 - x] > y ? 'O' : ' ';
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
for (x = 0; x < 3; x++)
*pch++ = (cOffO > 5 * x + y) ? 'O' : ' ';
*pch++ = ' ';
if (y < 2 && asz[y + 1])
for (pchIn = asz[y + 1]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
}
*pch++ = ' ';
*pch++ = '|';
for (x = 12; x < 18; x++) {
*pch++ = ' ';
*pch++ = anBoard[1][x] ? achX[anBoard[1][x]] : achO[anBoard[0][23 - x]];
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = achO[anBoard[0][24]];
*pch++ = ' ';
*pch++ = '|';
for (; x < 24; x++) {
*pch++ = ' ';
*pch++ = anBoard[1][x] ? achX[anBoard[1][x]] : achO[anBoard[0][23 - x]];
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
for (x = 0; x < 3; x++)
*pch++ = (cOffO > 5 * x + 4) ? 'O' : ' ';
*pch++ = '\n';
*pch++ = fRoll ? 'v' : '^';
strcpy(pch, "| |BAR| | ");
pch = strchr(pch, 0);
if (asz[3])
for (pchIn = asz[3]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
*pch++ = ' ';
*pch++ = '|';
for (x = 11; x > 5; x--) {
*pch++ = ' ';
*pch++ = anBoard[1][x] ? achX[anBoard[1][x]] : achO[anBoard[0][23 - x]];
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = achX[anBoard[1][24]];
*pch++ = ' ';
*pch++ = '|';
for (; x < UINT_MAX; x--) {
*pch++ = ' ';
*pch++ = anBoard[1][x] ? achX[anBoard[1][x]] : achO[anBoard[0][23 - x]];
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
for (x = 0; x < 3; x++)
*pch++ = (cOffX > 5 * x + 4) ? 'X' : ' ';
*pch++ = '\n';
for (y = 3; y < UINT_MAX; y--) {
*pch++ = ' ';
*pch++ = '|';
for (x = 11; x > 5; x--) {
*pch++ = ' ';
*pch++ = anBoard[1][x] > y ? 'X' : anBoard[0][23 - x] > y ? 'O' : ' ';
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = anBoard[1][24] > y ? 'X' : ' ';
*pch++ = ' ';
*pch++ = '|';
for (; x < UINT_MAX; x--) {
*pch++ = ' ';
*pch++ = anBoard[1][x] > y ? 'X' : anBoard[0][23 - x] > y ? 'O' : ' ';
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
for (x = 0; x < 3; x++)
*pch++ = (cOffX > 5 * x + y) ? 'X' : ' ';
*pch++ = ' ';
if (y < 2 && asz[5 - y])
for (pchIn = asz[5 - y]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
}
strcpy(pch, fRoll ? " +12-11-10--9--8--7-------6--5--4--3--2--1-+ " :
" +13-14-15-16-17-18------19-20-21-22-23-24-+ ");
pch += 49;
if (asz[6])
for (pchIn = asz[6]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
*pch = 0;
return sz;
}
/*
* GNU Backgammon Position ID: 0123456789ABCD
* Match ID : 0123456789ABCD
* +24-23-22-21-20-19------18-17-16-15-14-13-+ O: gnubg (Cube: 2)
* OO | O O O O O | | | 0 points
* OO | O O | | | Cube offered at 2
* O | O | | |
* O | | | |
* O | | | |
* | |BAR| |v Cube: 1 (7 point match)
* X | | | |
* X | | | |
* X | | | |
* X | X X X X | | | Rolled 11
* XX | X X X X X | | | 0 points
* +-1--2--3--4--5--6-------7--8--9-10-11-12-+ X: Gary (Cube: 2)
*
*/
static char *
DrawBoardCls(char *sz, const TanBoard anBoard, int fRoll, char *asz[], char *szMatchID, int nChequers)
{
char *pch = sz, *pchIn;
unsigned int x, y, cOffO = nChequers, cOffX = nChequers;
static char achX[17] = " X6789ABCDEF", achO[17] = " O6789ABCDEF";
for (x = 0; x < 25; x++) {
cOffO -= anBoard[0][x];
cOffX -= anBoard[1][x];
}
pch += sprintf(pch, "%18s %s: ", _("GNU Backgammon"), _("Position ID"));
if (fRoll)
strcpy(pch, PositionID((ConstTanBoard) anBoard));
else {
TanBoard an;
for (x = 0; x < 25; x++) {
an[0][x] = anBoard[1][x];
an[1][x] = anBoard[0][x];
}
strcpy(pch, PositionID((ConstTanBoard) an));
}
pch += L_POSITIONID;
*pch++ = '\n';
/* match id */
if (szMatchID && *szMatchID) {
pch += sprintf(pch, " %s : %s\n", _("Match ID"), szMatchID);
}
strcpy(pch, fRoll ? " +24-23-22-21-20-19------18-17-16-15-14-13-+ " :
" +-1--2--3--4--5--6-------7--8--9-10-11-12-+ ");
pch += 49;
if (asz[0])
for (pchIn = asz[0]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
for (y = 0; y < 4; y++) {
for (x = 2; x < UINT_MAX; x--)
*pch++ = (cOffO > 5 * x + y) ? 'O' : ' ';
*pch++ = ' ';
*pch++ = '|';
for (x = 23; x > 17; x--) {
*pch++ = ' ';
*pch++ = anBoard[1][x] > y ? 'X' : anBoard[0][23 - x] > y ? 'O' : ' ';
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = anBoard[0][24] > y ? 'O' : ' ';
*pch++ = ' ';
*pch++ = '|';
for (; x > 11; x--) {
*pch++ = ' ';
*pch++ = anBoard[1][x] > y ? 'X' : anBoard[0][23 - x] > y ? 'O' : ' ';
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = ' ';
if (y < 2 && asz[y + 1])
for (pchIn = asz[y + 1]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
}
for (x = 2; x < UINT_MAX; x--)
*pch++ = (cOffO > 5 * x + 4) ? 'O' : ' ';
*pch++ = ' ';
*pch++ = '|';
for (x = 23; x > 17; x--) {
*pch++ = ' ';
*pch++ = anBoard[1][x] ? achX[anBoard[1][x]] : achO[anBoard[0][23 - x]];
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = achO[anBoard[0][24]];
*pch++ = ' ';
*pch++ = '|';
for (; x > 11; x--) {
*pch++ = ' ';
*pch++ = anBoard[1][x] ? achX[anBoard[1][x]] : achO[anBoard[0][23 - x]];
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = ' ';
*pch++ = '\n';
strcpy(pch, " | |BAR| |");
pch = strchr(pch, 0);
*pch++ = fRoll ? 'v' : '^';
*pch++ = ' ';
if (asz[3])
for (pchIn = asz[3]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
for (x = 2; x < UINT_MAX; x--)
*pch++ = (cOffX > 5 * x + 4) ? 'X' : ' ';
*pch++ = ' ';
*pch++ = '|';
for (x = 0; x < 6; x++) {
*pch++ = ' ';
*pch++ = anBoard[1][x] ? achX[anBoard[1][x]] : achO[anBoard[0][23 - x]];
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = achX[anBoard[1][24]];
*pch++ = ' ';
*pch++ = '|';
for (; x < 12; x++) {
*pch++ = ' ';
*pch++ = anBoard[1][x] ? achX[anBoard[1][x]] : achO[anBoard[0][23 - x]];
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = ' ';
*pch++ = '\n';
for (y = 3; y < UINT_MAX; y--) {
for (x = 2; x < UINT_MAX; x--)
*pch++ = (cOffX > 5 * x + y) ? 'X' : ' ';
*pch++ = ' ';
*pch++ = '|';
for (x = 0; x < 6; x++) {
*pch++ = ' ';
*pch++ = anBoard[1][x] > y ? 'X' : anBoard[0][23 - x] > y ? 'O' : ' ';
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = anBoard[1][24] > y ? 'X' : ' ';
*pch++ = ' ';
*pch++ = '|';
for (; x < 12; x++) {
*pch++ = ' ';
*pch++ = anBoard[1][x] > y ? 'X' : anBoard[0][23 - x] > y ? 'O' : ' ';
*pch++ = ' ';
}
*pch++ = '|';
*pch++ = ' ';
*pch++ = ' ';
if (y < 2 && asz[5 - y])
for (pchIn = asz[5 - y]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
}
strcpy(pch, fRoll ? " +-1--2--3--4--5--6-------7--8--9-10-11-12-+ " :
" +24-23-22-21-20-19------18-17-16-15-14-13-+ ");
pch += 49;
if (asz[6])
for (pchIn = asz[6]; *pchIn; pchIn++)
*pch++ = *pchIn;
*pch++ = '\n';
*pch = 0;
return sz;
}
extern char *
DrawBoard(char *sz, const TanBoard anBoard, int fRoll, char *asz[], char *szMatchID, int nChequers)
{
if (fClockwise == FALSE)
return (DrawBoardStd(sz, anBoard, fRoll, asz, szMatchID, nChequers));
return (DrawBoardCls(sz, anBoard, fRoll, asz, szMatchID, nChequers));
}
static char *
FormatPoint(char *pch, int n)
{
g_assert(n >= 0);
/*don't translate 'off' and 'bar' as these may be used in UserCommand at a later
* point */
if (!n) {
strcpy(pch, ("off"));
return pch + strlen(("off"));
} else if (n == 25) {
strcpy(pch, ("bar"));
return pch + strlen(("bar"));
} else if (n > 9)
*pch++ = (char) ((n / 10) + '0');
*pch++ = (char) ((n % 10) + '0');
return pch;
}
static char *
FormatPointPlain(char *pch, int n)
{
g_assert(n >= 0);
if (n > 9)
*pch++ = (char) ((n / 10) + '0');
*pch++ = (char) ((n % 10) + '0');
return pch;
}
extern char *
FormatMovePlain(char *sz, const TanBoard anBoard, const int anMove[8])
{
char *pch = sz;
int i, j;
for (i = 0; i < 8 && anMove[i] >= 0; i += 2) {
pch = FormatPointPlain(pch, anMove[i] + 1);
*pch++ = '/';
pch = FormatPointPlain(pch, anMove[i + 1] + 1);
if (anBoard && anMove[i + 1] >= 0 && anBoard[0][23 - anMove[i + 1]]) {
for (j = 1;; j += 2)
if (j > i) {
*pch++ = '*';
break;
} else if (anMove[i + 1] == anMove[j])
break;
}
if (i < 6)
*pch++ = ' ';
}
*pch = 0;
return sz;
}
static int
CompareMovesSimple(const void *p0, const void *p1)
{
int n0 = *((const int *) p0), n1 = *((const int *) p1);
if (n0 != n1)
return n1 - n0;
else
return *((const int *) p1 + 1) - *((const int *) p0 + 1);
}
extern void
CanonicalMoveOrder(int an[])
{
int i;
for (i = 0; i < 4 && an[2 * i] > -1; i++);
qsort(an, i, sizeof(int) << 1, CompareMovesSimple);
}
extern char *
FormatMove(char *sz, const TanBoard anBoard, const int anMove[8])
{
char *pch = sz;
int aanMove[4][4], *pnSource[4], *pnDest[4], i, j;
int fl = 0;
int anCount[4], nMoves, nDuplicate, k;
/* Re-order moves into 2-dimensional array. */
for (i = 0; i < 4 && anMove[i << 1] >= 0; i++) {
aanMove[i][0] = anMove[i << 1] + 1;
aanMove[i][1] = anMove[(i << 1) | 1] + 1;
pnSource[i] = aanMove[i];
pnDest[i] = aanMove[i] + 1;
}
while (i < 4) {
aanMove[i][0] = aanMove[i][1] = -1;
pnSource[i++] = NULL;
}
/* Order the moves in decreasing order of source point. */
qsort(aanMove, 4, 4 * sizeof(int), CompareMovesSimple);
/* Combine moves of a single chequer. */
for (i = 0; i < 4; i++)
for (j = i; j < 4; j++)
if (pnSource[i] && pnSource[j] && *pnDest[i] == *pnSource[j]) {
if (anBoard[0][24 - *pnDest[i]])
/* Hitting blot; record intermediate point. */
*++pnDest[i] = *pnDest[j];
else
/* Non-hit; elide intermediate point. */
*pnDest[i] = *pnDest[j];
pnSource[j] = NULL;
}
/* Compact array. */
i = 0;
for (j = 0; j < 4; j++)
if (pnSource[j]) {
if (j > i) {
pnSource[i] = pnSource[j];
pnDest[i] = pnDest[j];
}
i++;
}
while (i < 4)
pnSource[i++] = NULL;
for (i = 0; i < 4; i++)
anCount[i] = pnSource[i] ? 1 : 0;
for (i = 0; i < 3; i++) {
if (pnSource[i]) {
nMoves = (int) (pnDest[i] - pnSource[i]);
for (j = i + 1; j < 4; j++) {
if (pnSource[j]) {
nDuplicate = 1;
if (pnDest[j] - pnSource[j] != nMoves)
nDuplicate = 0;
else
for (k = 0; k <= nMoves && nDuplicate; k++) {
if (pnSource[i][k] != pnSource[j][k])
nDuplicate = 0;
}
if (nDuplicate) {
anCount[i]++;
pnSource[j] = NULL;
}
}
}
}
}
/* Compact array. */
i = 0;
for (j = 0; j < 4; j++)
if (pnSource[j]) {
if (j > i) {
pnSource[i] = pnSource[j];
pnDest[i] = pnDest[j];
anCount[i] = anCount[j];
}
i++;
}
if (i < 4)
pnSource[i] = NULL;
for (i = 0; i < 4 && pnSource[i]; i++) {
if (i)
*pch++ = ' ';
pch = FormatPoint(pch, *pnSource[i]);
for (j = 1; pnSource[i] + j < pnDest[i]; j++) {
*pch = '/';
pch = FormatPoint(pch + 1, pnSource[i][j]);
*pch++ = '*';
fl |= 1 << pnSource[i][j];
}
*pch = '/';
pch = FormatPoint(pch + 1, *pnDest[i]);
if (*pnDest[i] && anBoard[0][24 - *pnDest[i]] && !(fl & (1 << *pnDest[i]))) {
*pch++ = '*';
fl |= 1 << *pnDest[i];
}
if (anCount[i] > 1) {
*pch++ = '(';
*pch++ = (char) ('0' + anCount[i]);
*pch++ = ')';
}
}
*pch = 0;
return sz;
}
extern int
ParseMove(char *pch, int an[8])
{
/*
* anUser[] size had been 8 for years without apparent issues, even
* when instrumented by clang's address sanitizer. Gcc 5 complains
* ("array subscript is above array bounds") until it is raised to 12
* then gcc 6 does the same until the size is 17.
*/
int i, j, iBegin, iEnd, n, c = 0, anUser[17];
unsigned fl = 0;
while (*pch) {
if (isspace(*pch)) {
pch++;
continue;
} else if (isdigit(*pch)) {
if (c == 8) {
/* Too many points. */
errno = EINVAL;
return -1;
}
if ((anUser[c] = (int) strtol(pch, &pch, 10)) < 0 || anUser[c] > 25) {
/* Invalid point number. */
errno = EINVAL;
return -1;
}
c++;
continue;
} else
switch (*pch) {
case 'o':
case 'O':
case '-':
if (c == 8) {
/* Too many points. */
errno = EINVAL;
return -1;
}
anUser[c++] = 0;
if (*pch != '-' && (pch[1] == 'f' || pch[1] == 'F')) {
pch++;
if (pch[1] == 'f' || pch[1] == 'F')
pch++;
}
break;
case 'b':
case 'B':
if (c == 8) {
/* Too many points. */
errno = EINVAL;
return -1;
}
anUser[c++] = 25;
if (pch[1] == 'a' || pch[1] == 'A') {
pch++;
if (pch[1] == 'r' || pch[1] == 'R')
pch++;
}
break;
case '/':
if (!c || fl & (1 << c)) {
/* Leading '/', or duplicate '/'s. */
errno = EINVAL;
return -1;
}
fl |= 1 << c;
break;
case '*':
case ',':
case ')':
/* Currently ignored. */
break;
case '(':
if ((n = (int) strtol(pch + 1, &pch, 10) - 1) < 1) {
/* invalid count */
errno = EINVAL;
return -1;
}
if (c < 2) {
/* incomplete move before ( -- syntax error */
errno = EINVAL;
return -1;
}
if (fl & (1 << c)) {
/* / immediately before ( -- syntax error */
errno = EINVAL;
return -1;
}
for (iBegin = c - 1; iBegin >= 0; iBegin--)
if (!(fl & (1 << iBegin)))
break;
if (iBegin < 0) {
/* no / anywhere before ( -- syntax error */
errno = EINVAL;
return -1;
}
iEnd = c;
if (c + (iEnd - iBegin) * n > 8) {
/* Too many moves. */
errno = EINVAL;
return -1;
}
for (i = 0; i < n; i++)
for (j = iBegin; j < iEnd; j++) {
if (fl & (1 << j))
fl |= 1 << c;
anUser[c++] = anUser[j];
}
break;
default:
errno = EINVAL;
return -1;
}
pch++;
}
if (fl & (1 << c)) {
/* Trailing '/'. */
errno = EINVAL;
return -1;
}
for (i = 0, j = 0; j < c; j++) {
if (i == 8) {
/* Too many moves. */
errno = EINVAL;
return -1;
}
if (((i & 1) && anUser[j] == 25) || (!(i & 1) && !anUser[j])) {
/* Trying to move from off the board, or to the bar. */
errno = EINVAL;
return -1;
}
an[i] = anUser[j];
if ((i & 1) && (fl & (1 << (j + 1)))) {
/* Combined move; this destination is also the next source. */
if (i == 7) {
/* Too many moves. */
errno = EINVAL;
return -1;
}
if (!an[i] || an[i] == 25) {
errno = EINVAL;
return -1;
}
an[++i] = anUser[j];
}
i++;
}
if (i & 1) {
/* Incomplete last move. */
errno = EINVAL;
return -1;
}
if (i < 8)
an[i] = -1;
CanonicalMoveOrder(an);
return i >> 1;
}
/*
* Generate FIBS board
*
* (see <URL:http://www.fibs.com/fibs_interface.html#board_state>)
*
* GNUbg extends the meaning of the "doubled" field, so the sign of
* the field indicates who doubled. This is necessary to correctly
* detects beavers and raccoons.
*/
extern char *
FIBSBoard(char *pch, TanBoard anBoard, int fRoll,
const char *szPlayer, const char *szOpp, int nMatchTo,
int nScore, int nOpponent, int nDice0, int nDice1,
int nCube, int fCubeOwner, int fDoubled, int fTurn, int fCrawford, int nChequers, int fPostCrawford)
{
char *sz = pch;
int i, anOff[2];
/* Names and match length/score */
strcpy(sz, "board:");
for (sz += 6; *szPlayer; szPlayer++)
*sz++ = (*szPlayer != ':' ? *szPlayer : '_');
for (*sz++ = ':'; *szOpp; szOpp++)
*sz++ = (*szOpp != ':' ? *szOpp : '_');
sprintf(sz, ":%d:%d:%d:", nMatchTo, nScore, nOpponent);
/* Opponent on bar */
sprintf(strchr(sz, 0), "%d:", -(int) anBoard[0][24]);
/* Board */
for (i = 0; i < 24; i++) {
int point = (int) anBoard[0][23 - i];
sprintf(strchr(sz, 0), "%d:", (point > 0) ? -point : (int) anBoard[1][i]);
}
/* Player on bar */
sprintf(strchr(sz, 0), "%u:", anBoard[1][24]);
/* Whose turn */
strcat(sz, fRoll ? "1:" : "-1:");
anOff[0] = anOff[1] = nChequers ? nChequers : 15;
for (i = 0; i < 25; i++) {
anOff[0] -= anBoard[0][i];
anOff[1] -= anBoard[1][i];
}
sprintf(strchr(sz, 0), "%d:%d:%d:%d:%d:%d:%d:%d:1:-1:0:25:%d:%d:0:0:0:"
"0:%d:%d", nDice0, nDice1, nDice0, nDice1, fTurn < 0 ? 1 : nCube,
fTurn < 0 || fCubeOwner != 0, fTurn < 0 || fCubeOwner != 1,
fDoubled ? (fTurn ? -1 : 1) : 0, anOff[1], anOff[0], fPostCrawford, !fCrawford);
return pch;
}
extern int
ProcessFIBSBoardInfo(FIBSBoardInfo * brdInfo, ProcessedFIBSBoard * procBrd)
{
int i, n, fCanDouble, fOppCanDouble, anOppDice[2];
int fNonCrawford, fPostCrawford;
int nTurn, nColor, nDirection;
int anFIBSBoard[26];
int fMustSwap = 0;
GString *tmpName;
procBrd->nMatchTo = brdInfo->nMatchTo;
procBrd->nScore = brdInfo->nScore;
procBrd->nScoreOpp = brdInfo->nScoreOpp;
nTurn = brdInfo->nTurn;
nColor = brdInfo->nColor;
nDirection = brdInfo->nDirection;
procBrd->fDoubled = brdInfo->fDoubled;
procBrd->nCube = brdInfo->nCube;
fCanDouble = brdInfo->fCanDouble;
fOppCanDouble = brdInfo->fOppCanDouble;
memcpy(procBrd->anDice, brdInfo->anDice, sizeof(brdInfo->anDice));
memcpy(anOppDice, brdInfo->anOppDice, sizeof(brdInfo->anOppDice));
memcpy(anFIBSBoard, brdInfo->anFIBSBoard, sizeof(brdInfo->anFIBSBoard));
fNonCrawford = brdInfo->fNonCrawford;
fPostCrawford = brdInfo->fPostCrawford;
/* Not yet supported set to zero */
procBrd->nResignation = 0;
for (i = 0; i < 26; ++i)
anFIBSBoard[i] = -anFIBSBoard[i];
/* FIBS has a maximum match length of 99. Unlimited matches are
* encoded with a match length of 9999.
*/
if (procBrd->nMatchTo == 9999)
procBrd->nMatchTo = 0;
if (procBrd->nMatchTo && (procBrd->nMatchTo <= procBrd->nScore || procBrd->nMatchTo <= procBrd->nScoreOpp))
return -1;
/* If the match length exceeds MAXSCORE we correct the match length
* to MAXSCORE and the scores to the closest equivalents.
*/
if (procBrd->nMatchTo > MAXSCORE) {
if (procBrd->nMatchTo - procBrd->nScore > MAXSCORE)
procBrd->nScore = 0;
else
procBrd->nScore -= procBrd->nMatchTo - MAXSCORE;
if (procBrd->nMatchTo - procBrd->nScoreOpp > MAXSCORE)
procBrd->nScoreOpp = 0;
else
procBrd->nScoreOpp -= procBrd->nMatchTo - MAXSCORE;
procBrd->nMatchTo = MAXSCORE;
}
/* Consistency check: 0 is a valid value for nColor but signifies
* end of game which is invalid for our purposes here.
*/
if (!nTurn || !nColor || !nDirection)
return -1;
/* Check whether the cube was turned. That is indicated by the
* pfDoubed flag for the "player" and by setting the may double
* flag to zero for both players for the opponent. Actually
* we could completely ignore the pfDoubled flag but it could
* still help, when processing data from other sources than
* fibs.com.
*/
if (!procBrd->fDoubled && !fCanDouble && !fOppCanDouble)
procBrd->fDoubled = 1;
if (procBrd->fDoubled)
fMustSwap = !fMustSwap;
if (nTurn * nColor < 0)
fMustSwap = !fMustSwap;
/* Opponent's turn? */
if (fMustSwap) {
int nTmp;
nTmp = procBrd->nScore;
procBrd->nScore = procBrd->nScoreOpp;
procBrd->nScoreOpp = nTmp;
nTmp = fCanDouble;
fCanDouble = fOppCanDouble;
fOppCanDouble = nTmp;
tmpName = brdInfo->gsName;
brdInfo->gsName = brdInfo->gsOpp;
brdInfo->gsOpp = tmpName;
}
if (nTurn * nColor < 0)
nDirection = -nDirection;
nColor = nTurn > 0 ? 1 : -1;
for (i = 0; i < 24; ++i) {
n = nDirection < 0 ? anFIBSBoard[i + 1] : anFIBSBoard[25 - i - 1];
if (nColor * n < 0) {
procBrd->anBoard[1][i] = n < 0 ? -n : n;
procBrd->anBoard[0][23 - i] = 0;
} else if (nColor * n > 0) {
procBrd->anBoard[1][i] = 0;
procBrd->anBoard[0][23 - i] = n < 0 ? -n : n;
} else {
procBrd->anBoard[1][i] = procBrd->anBoard[0][23 - i] = 0;
}
}
if (nDirection < 0) {
n = anFIBSBoard[25];
procBrd->anBoard[1][24] = n < 0 ? -n : n;
n = anFIBSBoard[0];
procBrd->anBoard[0][24] = n < 0 ? -n : n;
} else {
n = anFIBSBoard[0];
procBrd->anBoard[1][24] = n < 0 ? -n : n;
n = anFIBSBoard[25];
procBrd->anBoard[0][24] = n < 0 ? -n : n;
}
/* See https://savannah.gnu.org/bugs/?36485 for this. */
if (procBrd->fDoubled)
SwapSides(procBrd->anBoard);
if (!procBrd->anDice[0] && anOppDice[0]) {
procBrd->anDice[0] = anOppDice[0];
procBrd->anDice[1] = anOppDice[1];
}
/*
* Crawford detection. This is rather tricky with FIBS board states
* because FIBS sets both may-double flags to 1 in the Crawford game.
*
* We have to inspect the last and second last field for that.
* The last field is the post-Crawford flag, the second last
* field is the non-Crawford flag.
*
* Until at least one of the players is 1-away, you cannot deduce whether
* the Crawford rule is in use or not. Once one of the players
* is 1-away, the non-Crawford flag is set to 3 if the Crawford rule
* is not in use; otherwise everything is still 0.
*
* Once the Crawford game is finished, the post-Crawford flag is set
* to 1.
*
* Since we cannot find out whether the Crawford rule is in use or not
* in the pre-Crawford games, the cubeful evaluation can be slightly
* biased. But since the vast majority of matches on FIBS is played
* with the Crawford rule and we assume usage of the Crawford rule
* as a default, the bias is negligible.
*/
if (!procBrd->nMatchTo) {
procBrd->fCrawford = 0;
} else {
if (((procBrd->nMatchTo - procBrd->nScore) == 1) || ((procBrd->nMatchTo - procBrd->nScoreOpp) == 1)) {
if (fNonCrawford || fPostCrawford) {
procBrd->fCrawford = 0;
} else {
procBrd->fCrawford = 1;
}
} else {
procBrd->fCrawford = 0;
}
}
procBrd->fCubeOwner = fCanDouble != fOppCanDouble ? fCanDouble : -1;
g_strlcpy(procBrd->szPlayer, brdInfo->gsName->str, MAX_NAME_LEN);
g_strlcpy(procBrd->szOpp, brdInfo->gsOpp->str, MAX_NAME_LEN);
return 0;
}