Code: Select all
/* Anybody can use this. Originally written by
Dieter Buerssner, 2004. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* We use the same here as Nalimov */
enum piece_type {nothing, pawn, knight, bishop, rook, queen, king};
enum color {white, black, empty};
#define SHORT_CASTLE_WHITE 1
#define LONG_CASTLE_WHITE 2
#define SHORT_CASTLE_BLACK 4
#define LONG_CASTLE_BLACK 8
#define TO64(r,c) (8*(r)+(c))
typedef struct {
int board[8][8];
int cboard[8][8];
int stm;
unsigned castling_state;
int eps;
} POSITION;
#if 0
#define MM_DEBUG(string) my_print(DEBUG_INFO, "%s\n", string)
#else
#define MM_DEBUG(string)
#endif
/* Nasty macro, with side effects and assuming the variable names. */
#define SET_PIECE(c, p) \
do { \
if (col > 7) { \
printf("ERROR setboard c = %c, column outside of board\n", c); \
return 1; \
} \
pos->cboard[row][col]=c;pos->board[row][col]=p;col++;} while(0)
int setboard(const char *str, char **ep, POSITION *pos)
{
int col, row, c, c2, i, j, sawwk, sawbk, board_done;
for (i=0; i<8; i++)
for (j=0; j<8; j++)
{
pos->board[i][j] = nothing;
pos->cboard[i][j] = empty;
}
pos->castling_state = 0;
pos->eps = -1;
/* trim spaces */
while (isspace(*str))
str++;
sawwk = sawbk = 0; /* didn't find kings yet */
col = 0;
row = 7;
board_done = 0;
while ((c = *str) != '\0' && !board_done)
{
MM_DEBUG(str);
switch (c)
{
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7': case '8':
col += c - '0'; break;
case '/': /* We support missing number before / */
if (--row < 0) /* Swallow additional / sometimes found at the end
of wrong FENs */
board_done = 1; /* Actually incorrect FEN */
col = 0;
break;
case 'p': SET_PIECE(black,pawn); break; /* Should check for 1st/8th row */
case 'P': SET_PIECE(white,pawn); break;
case 'n': SET_PIECE(black,knight); break;
case 'N': SET_PIECE(white,knight); break;
case 'b': SET_PIECE(black,bishop); break;
case 'B': SET_PIECE(white,bishop); break;
case 'r': SET_PIECE(black,rook); break;
case 'R': SET_PIECE(white,rook); break;
case 'q': SET_PIECE(black,queen); break;
case 'Q': SET_PIECE(white,queen); break;
case 'k': SET_PIECE(black,king); sawbk++; break;
case 'K': SET_PIECE(white,king); sawwk++; break;
case ' ': case '\t': /* We support all sorts of abbrev. */
board_done = 1;
break;
default:
{
printf("ERROR setboard c = %c, unknown piece letter\n", c);
return 1;
}
}
str++;
}
MM_DEBUG("after pos");
if (sawwk != 1 || sawbk != 1)
{
printf("ERROR setboard kings are not correct\n");
return 1;
}
while (isspace(*str)) /* Actually only spaces allowed */
str++;
MM_DEBUG("trimmed spaces");
if (*str == 'w' || *str == 'W')
{
pos->stm = 0;
str++;
}
else if (*str == 'b' || *str == 'B')
{
pos->stm = 1;
str++;
}
else
return 1; /* report stm missing */
MM_DEBUG("after color");
/* We support short FENs without castling ep etc. */
/* get castling status */
while (isspace(*str)) /* Actually no spaces allowed */
str++;
switch (*str)
{
case 'K': pos->castling_state |= SHORT_CASTLE_WHITE; str++; break;
case 'Q': pos->castling_state |= LONG_CASTLE_WHITE; str++; break;
case 'k': pos->castling_state |= SHORT_CASTLE_BLACK; str++; break;
case 'q': pos->castling_state |= LONG_CASTLE_BLACK;; str++; break;
case '-': case 'n': str++; goto no_castling;
}
MM_DEBUG("after castling 1");
while (isspace(*str)) /* Actually no spaces allowed */
str++;
switch (*str)
{
case 'K': pos->castling_state |= SHORT_CASTLE_WHITE; str++; break;
case 'Q': pos->castling_state |= LONG_CASTLE_WHITE; str++; break;
case 'k': pos->castling_state |= SHORT_CASTLE_BLACK; str++; break;
case 'q': pos->castling_state |= LONG_CASTLE_BLACK;; str++; break;
}
MM_DEBUG("after castling 2");
while (isspace(*str)) /* Actually no spaces allowed */
str++;
switch (*str)
{
case 'K': pos->castling_state |= SHORT_CASTLE_WHITE; str++; break;
case 'Q': pos->castling_state |= LONG_CASTLE_WHITE; str++; break;
case 'k': pos->castling_state |= SHORT_CASTLE_BLACK; str++; break;
case 'q': pos->castling_state |= LONG_CASTLE_BLACK;; str++; break;
}
MM_DEBUG("after castling 3");
while (isspace(*str)) /* Actually no spaces allowed */
str++;
switch (*str)
{
case 'K': pos->castling_state |= SHORT_CASTLE_WHITE; str++; break;
case 'Q': pos->castling_state |= LONG_CASTLE_WHITE; str++; break;
case 'k': pos->castling_state |= SHORT_CASTLE_BLACK; str++; break;
case 'q': pos->castling_state |= LONG_CASTLE_BLACK;; str++; break;
}
MM_DEBUG("after castling 4");
no_castling:
/* Check that castling state is in accordance with placement
of kings/rooks. */
/* Caution, Omitted here */
/* e.p. field */
while (isspace(*str))
str++;
c = *str;
MM_DEBUG("trimmed spaces for ep");
if (c == 'n' || c == '-') /* support bogos "n" in some FENs */
str++;
else
{
if (isupper(c)) /* Some compilers have broken tolower in case of
none uppercase letter */
c = tolower(c);
if (c >= 'a' && c <= 'h')
{
c2 = *++str;
str++;
if ((c2 == '3' && pos->stm == black) || (c2 == '6' && pos->stm == white))
pos->eps = (c2 - '1') * 8 + c - 'a'; /* Assumes ASCII */
}
}
MM_DEBUG("after ep");
if (isspace(*str)) /* Actually only spaces allowed */
{
while (isspace(*str)) /* Actually only spaces allowed */
str++;
/* 50 moves counter, ignore it */
while (isdigit(*str))
str++;
if (isspace(*str)) /* Actually only spaces allowed */
{
while (isspace(*str)) /* Actually only spaces allowed */
str++;
/* Move number, ignore it */
while (isdigit(*str))
str++;
}
}
if (ep) /* set end of string */
*ep = (char *)str;
MM_DEBUG("assignement ep");
/* Caution, Omitted here */
/* Check, that we have a consistent ep square. All of it omitted.
Only one check actually done, and correct FEN assumed otherwise.
Note that this check really is needed here, because the Standard
for FEN wants a set ep target, even when ep is not possible.
Nalimov code however will do strange things sometimes when ep
is set, but an epmove is not possible.
So, without changes, this code will not correct even with
correct FENs in some cases. User should not give the ep-flag,
in case ep is not possible.
*/
/* Do all sort of checks on the position, all omitted here */
/* if (verify_position(pos, &etext) == 0) ... */
return 0;
}
/* From start of tbindex.cpp. */
#define pageL 65536 /* 256 */
/* tablebase byte entry semispan length */
#define tbbe_ssL ((pageL - 4) / 2)
/* tablebase signed byte entry values */
#define bev_broken (tbbe_ssL + 1) /* illegal or busted */
#if defined (_MSC_VER)
#define TB_FASTCALL __fastcall
#else
#define TB_FASTCALL
#endif
#define T_INDEX64
#define XX 127
#define C_PIECES 3 /* Maximum # of pieces of one color OTB */
#if defined (T_INDEX64) && defined (_MSC_VER)
typedef unsigned __int64 INDEX;
#elif defined (T_INDEX64)
typedef unsigned long long INDEX;
#else
typedef unsigned long INDEX;
#endif
typedef unsigned int square;
typedef INDEX (TB_FASTCALL * PfnCalcIndex)
(square*, square*, square, int fInverse);
extern int IDescFindFromCounters (int*);
extern int FRegisteredFun (int, /*color*/int);
#define FRegistered FRegisteredFun
extern PfnCalcIndex PfnIndCalcFun (int, /*color*/int);
#define PfnIndCalc PfnIndCalcFun
extern int TB_FASTCALL L_TbtProbeTable (int, /*color*/int, INDEX);
extern int IInitializeTb(char *);
extern int FTbSetCacheSize(void *, unsigned long);
extern int cbEGTBCompBytes;
size_t egtb_cache_size = 2L*1024L*1024L;
int table_pieces=0;
static void *cache_buf = 0;
void set_egtb_cache(long siz)
{
egtb_cache_size = siz;
if(table_pieces > 0)
{
if (cache_buf)
{
free(cache_buf);
cache_buf = 0;
}
cache_buf = malloc(egtb_cache_size);
if (cache_buf && FTbSetCacheSize(cache_buf, egtb_cache_size))
printf("Using %.3f MB cache for TBs\n", egtb_cache_size/(1024. * 1024.));
else
{
table_pieces = 0;
if (cache_buf)
{
free(cache_buf);
cache_buf = 0;
}
}
}
}
/* Should use const char *, but Eugene does not ... */
void init_egtb(char *path)
{
if (cache_buf)
{
free(cache_buf);
cache_buf = 0;
}
table_pieces = IInitializeTb(path);
if(table_pieces != 0)
{
printf("%d-men endgame table bases found, %.3f MB used for decompression tables\n",
table_pieces, cbEGTBCompBytes/(1024. * 1024.));
set_egtb_cache(egtb_cache_size);
}
else
printf("No endgame tables found\n");
}
#define MYPIECE_TO_NALIMOV(p) (p) /* We use the same internal piece type */
#define ROWCOL_TO_64(r,c) ((r)*8+(c)) /* Nalimov uses a1=0, b1=1, ... h8=63 */
int probe_egtb(POSITION *pos, int *score)
{
int piece_count[10];
square white_squares[5*C_PIECES+1], black_squares[5*C_PIECES+1];
int idx_tb, side, invert, value, ep, i, j, pc, sq, nwp, nbp;
INDEX idx;
square *wp, *bp;
if (pos->castling_state)
return 0;
/* Setup arrays white_pieces and black_pieces, so that they will
work with original (=Crafty) SqFind...() macros. Set also up
piece_counts */
/* Note, that a real chess engine would probably have a piece list,
and the following code would be more efficiently done.
Also number of white and black pieces will already be known. */
for (i=0; i<10; i++)
piece_count[i] = 0;
for (i=0; i<=5*C_PIECES; i++)
white_squares[i] = black_squares[i] = 0;
nwp = nbp = 0;
for (i=0; i<8; i++)
{
for (j=0; j<8; j++)
{
if (pos->cboard[i][j] == white)
{
nwp++;
if (nwp > C_PIECES+1) /* Avoid out of bounds index */
return 0;
pc = MYPIECE_TO_NALIMOV(pos->board[i][j]);
sq = ROWCOL_TO_64(i,j);
pc = pos->board[i][j];
if (pc == king)
white_squares[5*C_PIECES] = sq;
else
white_squares[(pc-1) * C_PIECES + piece_count[pc-1]++] = sq;
}
else if (pos->cboard[i][j] == black)
{
nbp++;
if (nbp > C_PIECES+1) /* Avoid out of bounds index */
return 0;
pc = MYPIECE_TO_NALIMOV(pos->board[i][j]);
sq = ROWCOL_TO_64(i,j);
pc = pos->board[i][j];
if (pc == king)
black_squares[5*C_PIECES] = sq;
else
black_squares[(pc-1) * C_PIECES + piece_count[pc-1+5]++] = sq;
}
}
}
if (nwp + nbp > table_pieces) /* We have more pieces on the board,
than found in any TB */
return 0;
/* Everything is setup here, now call the Nalimov functions
to do the real work for the actual probing. */
idx_tb = IDescFindFromCounters(piece_count);
if (idx_tb == 0)
return 0;
else if (idx_tb > 0)
{
side = pos->stm;
invert = 0;
wp = white_squares;
bp = black_squares;
}
else
{
side = pos->stm^1;
invert = 1;
wp = black_squares;
bp = white_squares;
idx_tb = -idx_tb;
}
if(!FRegisteredFun(idx_tb, side))
return 0;
ep = (pos->eps > 0) ? pos->eps : XX; /* Careful, see comment in setboard
Note that pos uses "Nalimov squares" for
ep already */
idx = PfnIndCalcFun(idx_tb, side)(wp, bp, ep, invert);
value = L_TbtProbeTable(idx_tb, side, idx);
if(value == bev_broken)
return 0;
*score = value;
return 1;
}
/* 0 = draw; 1 = mate in one; -1 = mated; -2 = mated in 1; -3 = mated in 2 */
#define MATE_FROM_TB_SCORE(score) \
(((score) > 0) ? 32767-(score) : ((score) == 0 ? 0 : -((score)+32766)-1))
int main(void)
{
char buf[256];
POSITION pos;
int v;
init_egtb("c:/tb;c:/6men"); /* Hardcoded ... */
while(fgets(buf, sizeof buf, stdin))
{
if (setboard(buf, NULL, &pos) != 0)
break; /* for example quit ... */
if (probe_egtb(&pos, &v) == 0)
printf("pos not found in TB\n");
else
{
printf("TB returned %d which means ", v);
v = MATE_FROM_TB_SCORE(v);
if (v == 0)
printf("draw\n");
else if (v > 0)
printf("mate in %d\n", v);
else
printf("mated in %d\n", -v-1);
}
}
return 0;
}