/*
Protector -- a UCI chess engine
Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)
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 <http://www.gnu.org/licenses/>.
*/
#include "tablebase.h"
#include "protector.h"
#include "io.h"
#include <assert.h>
#ifdef INCLUDE_TABLEBASE_ACCESS
#ifndef _MSC_VER
#include <pthread.h>
pthread_spinlock_t lock;
#else // _MSC_VER
bool lock = 0; // Pierre-Marie Baty -- Win32 thread locking
__declspec(dllimport) void __stdcall Sleep (unsigned long Timeout); // Pierre-Marie Baty -- for Sleep()
#endif // !_MSC_VER
volatile int tbAccessCount = 0;
#define MAX_PIECES_PER_SIDE 3
#define NO_EP 127
bool tbAvailable = FALSE;
typedef unsigned long long INDEX;
typedef unsigned int squaret;
char *cache = 0;
#define TB_FASTCALL /* __fastcall */
typedef INDEX(*PfnCalcIndex) (squaret *, squaret *, squaret, int fInverse);
extern int IInitializeTb(char *pszPath);
extern int FTbSetCacheSize(void *pv, unsigned long cbSize);
extern void VTbCloseFiles(void);
extern int IDescFindFromCounters(int *piCount);
extern int FRegisteredFun(int iTb, int side);
extern PfnCalcIndex PfnIndCalcFun(int iTb, int side);
extern int TB_FASTCALL L_TbtProbeTable(int iTb, int side, INDEX indOffset);
#define pageL 65536
#define tbbe_ssL ((pageL-4)/2)
#define bev_broken (tbbe_ssL+1) /* illegal or busted */
#define bev_mi1 tbbe_ssL /* mate in 1 move */
#define bev_mimin 1 /* mate in max moves */
#define bev_draw 0 /* draw */
#define bev_limax (-1) /* mated in max moves */
#define bev_li0 (-tbbe_ssL) /* mated in 0 moves */
#define PfnIndCalc PfnIndCalcFun
int setTablebaseCacheSize(unsigned int size)
{
unsigned long numBytes = (unsigned long) size * 1024 * 1024;
if (cache != 0)
{
}
if (cache == 0)
{
logDebug("### Could not allocate tablebase cache (%lu bytes). ###\n",
numBytes);
return -1;
}
if (FTbSetCacheSize(cache, numBytes) == 0)
{
logDebug("### Could not set tablebase cache size (%lu bytes). ###\n",
numBytes);
cache = 0;
return -2;
}
/* logDebug("table base cache size set to %lu\n", numBytes); */
return 0;
}
int initializeTablebase(const char *path)
{
int result = IInitializeTb((char *) path);
if (result > 0)
{
if (setTablebaseCacheSize(4) != 0)
{
closeTablebaseFiles();
return -1;
}
/* logDebug("Tablebases found at %s\n", path); */
}
else
{
logDebug("### Error while looking for tablebases in %s ###\n", path);
}
tbAvailable = (bool) (result > 0);
return (result > 0 ? 0 : -1);
}
void closeTablebaseFiles()
{
VTbCloseFiles();
}
static void initializePieceData(const Bitboard * pieces, int *pieceCount,
unsigned int *pieceLocation)
{
Bitboard tmp = *pieces;
Square square;
*pieceCount = 0;
ITERATE_BITBOARD(&tmp, square)
{
*(pieceLocation++) = square;
(*pieceCount)++;
}
}
static int getMateValue(int fullMoves)
{
if (fullMoves > 0)
{
return 1 - VALUE_MATED - 2 * fullMoves;
}
else
{
return VALUE_MATED - 2 * fullMoves;
}
}
int probeTablebase(const Position * position)
{
int pieceCount[10];
unsigned int whitePieces[MAX_PIECES_PER_SIDE * 5 + 1];
unsigned int blackPieces[MAX_PIECES_PER_SIDE * 5 + 1];
unsigned int *pwhite, *pblack;
int tableNr, fInvert, tableValue, fullMoves;
Color color;
INDEX index;
Square enPassantSquare = (Square) NO_EP;
initializePieceData(&position->piecesOfType[WHITE_PAWN], &pieceCount[0],
&whitePieces[0 * MAX_PIECES_PER_SIDE]);
initializePieceData(&position->piecesOfType[WHITE_KNIGHT], &pieceCount[1],
&whitePieces[1 * MAX_PIECES_PER_SIDE]);
initializePieceData(&position->piecesOfType[WHITE_BISHOP], &pieceCount[2],
&whitePieces[2 * MAX_PIECES_PER_SIDE]);
initializePieceData(&position->piecesOfType[WHITE_ROOK], &pieceCount[3],
&whitePieces[3 * MAX_PIECES_PER_SIDE]);
initializePieceData(&position->piecesOfType[WHITE_QUEEN], &pieceCount[4],
&whitePieces[4 * MAX_PIECES_PER_SIDE]);
initializePieceData(&position->piecesOfType[BLACK_PAWN], &pieceCount[5],
&blackPieces[0 * MAX_PIECES_PER_SIDE]);
initializePieceData(&position->piecesOfType[BLACK_KNIGHT], &pieceCount[6],
&blackPieces[1 * MAX_PIECES_PER_SIDE]);
initializePieceData(&position->piecesOfType[BLACK_BISHOP], &pieceCount[7],
&blackPieces[2 * MAX_PIECES_PER_SIDE]);
initializePieceData(&position->piecesOfType[BLACK_ROOK], &pieceCount[8],
&blackPieces[3 * MAX_PIECES_PER_SIDE]);
initializePieceData(&position->piecesOfType[BLACK_QUEEN], &pieceCount[9],
&blackPieces[4 * MAX_PIECES_PER_SIDE]);
tableNr = IDescFindFromCounters(pieceCount);
if (tableNr == 0)
{
return TABLEBASE_ERROR;
}
whitePieces[5 * MAX_PIECES_PER_SIDE] = position->king[WHITE];
blackPieces[5 * MAX_PIECES_PER_SIDE] = position->king[BLACK];
if (tableNr > 0)
{
color = position->activeColor;
fInvert = 0;
pwhite = whitePieces;
pblack = blackPieces;
}
else
{
color = opponent(position->activeColor);
fInvert = 1;
pwhite = blackPieces;
pblack = whitePieces;
tableNr = -tableNr;
}
#if defined __APPLE__
pthread_mutex_lock((pthread_mutex_t *) & lock);
#elif defined _MSC_VER
while (lock)
Sleep (1); // allow context switching
lock = 1; // Pierre-Marie Baty -- Win32 thread locking
#else
pthread_spin_lock(&lock);
#endif
if (FRegisteredFun(tableNr, color) == FALSE)
{
#if defined __APPLE__
pthread_mutex_unlock((pthread_mutex_t *) & lock);
#elif defined _MSC_VER
lock = 0; // Pierre-Marie Baty -- Win32 thread locking
#else
pthread_spin_unlock(&lock);
#endif
return TABLEBASE_ERROR;
}
if (position->enPassantSquare != NO_SQUARE)
{
const Bitboard attackers =
position->piecesOfType[PAWN | position->activeColor] &
generalMoves[PAWN | opponent(position->activeColor)]
[position->enPassantSquare];
if (attackers != EMPTY_BITBOARD)
{
/*
dumpSquare(position->enPassantSquare);
dumpPosition(position);
*/
enPassantSquare = position->enPassantSquare;
}
}
index = PfnIndCalcFun(tableNr, color)
(pwhite, pblack, enPassantSquare, fInvert);
tableValue = L_TbtProbeTable(tableNr, color, index);
#if defined __APPLE__
pthread_mutex_unlock((pthread_mutex_t *) & lock);
#elif defined _MSC_VER
lock = 0; // Pierre-Marie Baty -- Win32 thread locking
#else
pthread_spin_unlock(&lock);
#endif
if (tableValue == bev_broken)
{
return TABLEBASE_ERROR;
}
if (tableValue == 0)
{
return 0;
}
fullMoves = (tableValue > 0 ?
1 + tbbe_ssL - tableValue : bev_li0 - tableValue);
return getMateValue(fullMoves);
}
int initializeModuleTablebase()
{
#if defined __APPLE__
pthread_mutex_init((pthread_mutex_t *) & lock, tbAccessCount);
#elif defined _MSC_VER
lock = 0; // Pierre-Marie Baty -- Win32 thread locking
#else
pthread_spin_init(&lock, tbAccessCount);
#endif
if (commandlineOptions.tablebasePath != 0)
{
return initializeTablebase(commandlineOptions.tablebasePath);
}
else
{
return 0;
}
}
static int testTableFinder()
{
#ifndef NDEBUG
Variation variation;
int tableValue;
initializeVariation(&variation, "B6k/8/8/8/8/8/8/K6N w - - 0 1");
tableValue = probeTablebase(&variation.singlePosition);
initializeVariation(&variation, "B6k/8/8/8/8/8/8/K6N b - - 0 1");
tableValue = probeTablebase(&variation.singlePosition);
#endif
return 0;
}
static int testMateValues()
{
#ifndef NDEBUG
Variation variation;
int tableValue;
initializeVariation(&variation, "R6k/8/6K1/8/8/8/8/8 b - - 0 1");
tableValue = probeTablebase(&variation.singlePosition);
assert(tableValue
== VALUE_MATED
);
initializeVariation(&variation, "7k/8/6K1/8/8/8/8/R7 w - - 0 1");
tableValue = probeTablebase(&variation.singlePosition);
assert(tableValue
== -VALUE_MATED
- 1);
#endif
return 0;
}
int testModuleTablebase()
{
int result;
if (tbAvailable == FALSE)
{
return 0;
}
if (commandlineOptions.tablebasePath == 0)
{
return 0;
}
if ((result = testTableFinder()) != 0)
{
return result;
}
if ((result = testMateValues()) != 0)
{
return result;
}
return 0;
}
#endif