1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-05-02 17:49:35 +00:00

syzygy clean-up + unit test

This commit is contained in:
lucasart 2016-04-16 22:59:39 +01:00 committed by Joona Kiiski
parent ee7a68ea5f
commit 9173d29c41
4 changed files with 4181 additions and 1897 deletions

2016
src/syzygy/syzygy.epd Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,30 +5,20 @@
#ifndef TBCORE_H #ifndef TBCORE_H
#define TBCORE_H #define TBCORE_H
#include <cstdint>
#ifndef _WIN32 #ifndef _WIN32
#include <pthread.h>
#define SEP_CHAR ':' #define SEP_CHAR ':'
#define FD int #define FD int
#define FD_ERR -1 #define FD_ERR -1
#else #else
#define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#define SEP_CHAR ';' #define SEP_CHAR ';'
#define FD HANDLE #define FD HANDLE
#define FD_ERR INVALID_HANDLE_VALUE #define FD_ERR INVALID_HANDLE_VALUE
#endif #endif
#ifndef _WIN32
#define LOCK_T pthread_mutex_t
#define LOCK_INIT(x) pthread_mutex_init(&(x), NULL)
#define LOCK(x) pthread_mutex_lock(&(x))
#define UNLOCK(x) pthread_mutex_unlock(&(x))
#else
#define LOCK_T HANDLE
#define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0)
#define LOCK(x) WaitForSingleObject(x, INFINITE)
#define UNLOCK(x) ReleaseMutex(x)
#endif
#ifndef _MSC_VER #ifndef _MSC_VER
#define BSWAP32(v) __builtin_bswap32(v) #define BSWAP32(v) __builtin_bswap32(v)
#define BSWAP64(v) __builtin_bswap64(v) #define BSWAP64(v) __builtin_bswap64(v)
@ -43,27 +33,22 @@
#define DTZDIR "RTBZDIR" #define DTZDIR "RTBZDIR"
#define TBPIECES 6 #define TBPIECES 6
typedef unsigned long long uint64; const uint8_t WDL_MAGIC[4] = { 0x71, 0xe8, 0x23, 0x5d };
typedef unsigned int uint32; const uint8_t DTZ_MAGIC[4] = { 0xd7, 0x66, 0x0c, 0xa5 };
typedef unsigned char ubyte;
typedef unsigned short ushort;
const ubyte WDL_MAGIC[4] = { 0x71, 0xe8, 0x23, 0x5d };
const ubyte DTZ_MAGIC[4] = { 0xd7, 0x66, 0x0c, 0xa5 };
#define TBHASHBITS 10 #define TBHASHBITS 10
struct TBHashEntry; struct TBHashEntry;
typedef uint64 base_t; typedef uint64_t base_t;
struct PairsData { struct PairsData {
char *indextable; char *indextable;
ushort *sizetable; uint16_t *sizetable;
ubyte *data; uint8_t *data;
ushort *offset; uint16_t *offset;
ubyte *symlen; uint8_t *symlen;
ubyte *sympat; uint8_t *sympat;
int blocksize; int blocksize;
int idxbits; int idxbits;
int min_len; int min_len;
@ -72,12 +57,12 @@ struct PairsData {
struct TBEntry { struct TBEntry {
char *data; char *data;
uint64 key; uint64_t key;
uint64 mapping; uint64_t mapping;
ubyte ready; uint8_t ready;
ubyte num; uint8_t num;
ubyte symmetric; uint8_t symmetric;
ubyte has_pawns; uint8_t has_pawns;
} }
#ifndef _WIN32 #ifndef _WIN32
__attribute__((__may_alias__)) __attribute__((__may_alias__))
@ -86,82 +71,82 @@ __attribute__((__may_alias__))
struct TBEntry_piece { struct TBEntry_piece {
char *data; char *data;
uint64 key; uint64_t key;
uint64 mapping; uint64_t mapping;
ubyte ready; uint8_t ready;
ubyte num; uint8_t num;
ubyte symmetric; uint8_t symmetric;
ubyte has_pawns; uint8_t has_pawns;
ubyte enc_type; uint8_t enc_type;
struct PairsData *precomp[2]; struct PairsData *precomp[2];
int factor[2][TBPIECES]; int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES]; uint8_t pieces[2][TBPIECES];
ubyte norm[2][TBPIECES]; uint8_t norm[2][TBPIECES];
}; };
struct TBEntry_pawn { struct TBEntry_pawn {
char *data; char *data;
uint64 key; uint64_t key;
uint64 mapping; uint64_t mapping;
ubyte ready; uint8_t ready;
ubyte num; uint8_t num;
ubyte symmetric; uint8_t symmetric;
ubyte has_pawns; uint8_t has_pawns;
ubyte pawns[2]; uint8_t pawns[2];
struct { struct {
struct PairsData *precomp[2]; struct PairsData *precomp[2];
int factor[2][TBPIECES]; int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES]; uint8_t pieces[2][TBPIECES];
ubyte norm[2][TBPIECES]; uint8_t norm[2][TBPIECES];
} file[4]; } file[4];
}; };
struct DTZEntry_piece { struct DTZEntry_piece {
char *data; char *data;
uint64 key; uint64_t key;
uint64 mapping; uint64_t mapping;
ubyte ready; uint8_t ready;
ubyte num; uint8_t num;
ubyte symmetric; uint8_t symmetric;
ubyte has_pawns; uint8_t has_pawns;
ubyte enc_type; uint8_t enc_type;
struct PairsData *precomp; struct PairsData *precomp;
int factor[TBPIECES]; int factor[TBPIECES];
ubyte pieces[TBPIECES]; uint8_t pieces[TBPIECES];
ubyte norm[TBPIECES]; uint8_t norm[TBPIECES];
ubyte flags; // accurate, mapped, side uint8_t flags; // accurate, mapped, side
ushort map_idx[4]; uint16_t map_idx[4];
ubyte *map; uint8_t *map;
}; };
struct DTZEntry_pawn { struct DTZEntry_pawn {
char *data; char *data;
uint64 key; uint64_t key;
uint64 mapping; uint64_t mapping;
ubyte ready; uint8_t ready;
ubyte num; uint8_t num;
ubyte symmetric; uint8_t symmetric;
ubyte has_pawns; uint8_t has_pawns;
ubyte pawns[2]; uint8_t pawns[2];
struct { struct {
struct PairsData *precomp; struct PairsData *precomp;
int factor[TBPIECES]; int factor[TBPIECES];
ubyte pieces[TBPIECES]; uint8_t pieces[TBPIECES];
ubyte norm[TBPIECES]; uint8_t norm[TBPIECES];
} file[4]; } file[4];
ubyte flags[4]; uint8_t flags[4];
ushort map_idx[4][4]; uint16_t map_idx[4][4];
ubyte *map; uint8_t *map;
}; };
struct TBHashEntry { struct TBHashEntry {
uint64 key; uint64_t key;
struct TBEntry *ptr; struct TBEntry *ptr;
}; };
struct DTZTableEntry { struct DTZTableEntry {
uint64 key1; uint64_t key1;
uint64 key2; uint64_t key2;
struct TBEntry *entry; struct TBEntry *entry;
}; };

View file

@ -7,8 +7,6 @@
this code to other chess engines. this code to other chess engines.
*/ */
#define NOMINMAX
#include <algorithm> #include <algorithm>
#include "../position.h" #include "../position.h"
@ -29,41 +27,41 @@ int Tablebases::MaxCardinality = 0;
// Given a position with 6 or fewer pieces, produce a text string // Given a position with 6 or fewer pieces, produce a text string
// of the form KQPvKRP, where "KQP" represents the white pieces if // of the form KQPvKRP, where "KQP" represents the white pieces if
// mirror == 0 and the black pieces if mirror == 1. // mirror == false and the black pieces if mirror == true.
static void prt_str(Position& pos, char *str, int mirror) static std::string prt_str(Position& pos, bool mirror)
{ {
Color color; std::string s;
PieceType pt;
int i;
color = !mirror ? WHITE : BLACK; for (int i = 0; i <= 1; i++) {
for (pt = KING; pt >= PAWN; --pt) Color color = Color(i ^ mirror);
for (i = popcount(pos.pieces(color, pt)); i > 0; i--)
*str++ = pchr[6 - pt]; for (PieceType pt = KING; pt >= PAWN; --pt)
*str++ = 'v'; for (Bitboard b = pos.pieces(color, pt); b; b &= b - 1)
color = ~color; s += pchr[pt];
for (pt = KING; pt >= PAWN; --pt)
for (i = popcount(pos.pieces(color, pt)); i > 0; i--) if (i == 0)
*str++ = pchr[6 - pt]; s += 'v';
*str++ = 0; }
return s;
} }
// Given a position, produce a 64-bit material signature key. // Given a position, produce a 64-bit material signature key.
// If the engine supports such a key, it should equal the engine's key. // If the engine supports such a key, it should equal the engine's key.
static uint64 calc_key(Position& pos, int mirror) static uint64_t calc_key(Position& pos, bool mirror)
{ {
Color color; uint64_t key = 0;
PieceType pt;
int i;
uint64 key = 0;
color = !mirror ? WHITE : BLACK; Color color = mirror ? BLACK: WHITE;
for (pt = PAWN; pt <= KING; ++pt)
for (i = popcount(pos.pieces(color, pt)); i > 0; i--) for (PieceType pt = PAWN; pt <= KING; ++pt)
for (int i = popcount(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[WHITE][pt][i - 1]; key ^= Zobrist::psq[WHITE][pt][i - 1];
color = ~color; color = ~color;
for (pt = PAWN; pt <= KING; ++pt)
for (i = popcount(pos.pieces(color, pt)); i > 0; i--) for (PieceType pt = PAWN; pt <= KING; ++pt)
for (int i = popcount(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[BLACK][pt][i - 1]; key ^= Zobrist::psq[BLACK][pt][i - 1];
return key; return key;
@ -73,26 +71,27 @@ static uint64 calc_key(Position& pos, int mirror)
// defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white // defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white
// pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black // pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black
// pawns, ..., kings. // pawns, ..., kings.
static uint64 calc_key_from_pcs(int *pcs, int mirror) static uint64_t calc_key_from_pcs(int *pcs, bool mirror)
{ {
int color; uint64_t key = 0;
PieceType pt;
int i;
uint64 key = 0;
color = !mirror ? 0 : 8; Color color = mirror ? BLACK : WHITE;
for (pt = PAWN; pt <= KING; ++pt)
for (i = 0; i < pcs[color + pt]; i++) for (PieceType pt = PAWN; pt <= KING; ++pt)
for (int i = 0; i < pcs[8 * color + pt]; i++)
key ^= Zobrist::psq[WHITE][pt][i]; key ^= Zobrist::psq[WHITE][pt][i];
color ^= 8;
for (pt = PAWN; pt <= KING; ++pt) color = ~color;
for (i = 0; i < pcs[color + pt]; i++)
for (PieceType pt = PAWN; pt <= KING; ++pt)
for (int i = 0; i < pcs[8 * color + pt]; i++)
key ^= Zobrist::psq[BLACK][pt][i]; key ^= Zobrist::psq[BLACK][pt][i];
return key; return key;
} }
bool is_little_endian() { bool is_little_endian()
{
union { union {
int i; int i;
char c[sizeof(int)]; char c[sizeof(int)];
@ -101,7 +100,7 @@ bool is_little_endian() {
return x.c[0] == 1; return x.c[0] == 1;
} }
static ubyte decompress_pairs(struct PairsData *d, uint64 idx) static uint8_t decompress_pairs(PairsData *d, uint64_t idx)
{ {
static const bool isLittleEndian = is_little_endian(); static const bool isLittleEndian = is_little_endian();
return isLittleEndian ? decompress_pairs<true >(d, idx) return isLittleEndian ? decompress_pairs<true >(d, idx)
@ -111,12 +110,12 @@ static ubyte decompress_pairs(struct PairsData *d, uint64 idx)
// probe_wdl_table and probe_dtz_table require similar adaptations. // probe_wdl_table and probe_dtz_table require similar adaptations.
static int probe_wdl_table(Position& pos, int *success) static int probe_wdl_table(Position& pos, int *success)
{ {
struct TBEntry *ptr; TBEntry *ptr;
struct TBHashEntry *ptr2; TBHashEntry *ptr2;
uint64 idx; uint64_t idx;
uint64 key; uint64_t key;
int i; int i;
ubyte res; uint8_t res;
int p[TBPIECES]; int p[TBPIECES];
// Obtain the position's material signature key. // Obtain the position's material signature key.
@ -127,25 +126,31 @@ static int probe_wdl_table(Position& pos, int *success)
return 0; return 0;
ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++) for (i = 0; i < HSHMAX; i++)
if (ptr2[i].key == key) break; if (ptr2[i].key == key)
break;
if (i == HSHMAX) { if (i == HSHMAX) {
*success = 0; *success = 0;
return 0; return 0;
} }
ptr = ptr2[i].ptr; ptr = ptr2[i].ptr;
if (!ptr->ready) { if (!ptr->ready) {
LOCK(TB_mutex); TB_mutex.lock();
if (!ptr->ready) { if (!ptr->ready) {
char str[16]; std::string s = prt_str(pos, ptr->key != key);
prt_str(pos, str, ptr->key != key);
if (!init_table_wdl(ptr, str)) { if (!init_table_wdl(ptr, s)) {
ptr2[i].key = 0ULL; ptr2[i].key = 0ULL;
*success = 0; *success = 0;
UNLOCK(TB_mutex); TB_mutex.unlock();
return 0; return 0;
} }
// Memory barrier to ensure ptr->ready = 1 is not reordered. // Memory barrier to ensure ptr->ready = 1 is not reordered.
#ifdef _MSC_VER #ifdef _MSC_VER
_ReadWriteBarrier(); _ReadWriteBarrier();
@ -154,10 +159,12 @@ static int probe_wdl_table(Position& pos, int *success)
#endif #endif
ptr->ready = 1; ptr->ready = 1;
} }
UNLOCK(TB_mutex);
TB_mutex.unlock();
} }
int bside, mirror, cmirror; int bside, mirror, cmirror;
if (!ptr->symmetric) { if (!ptr->symmetric) {
if (key != ptr->key) { if (key != ptr->key) {
cmirror = 8; cmirror = 8;
@ -177,34 +184,42 @@ static int probe_wdl_table(Position& pos, int *success)
// pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king. // pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king.
// Pieces of the same type are guaranteed to be consecutive. // Pieces of the same type are guaranteed to be consecutive.
if (!ptr->has_pawns) { if (!ptr->has_pawns) {
struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr; TBEntry_piece *entry = (TBEntry_piece *)ptr;
ubyte *pc = entry->pieces[bside]; uint8_t *pc = entry->pieces[bside];
for (i = 0; i < entry->num;) { for (i = 0; i < entry->num;) {
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07)); (PieceType)(pc[i] & 0x07));
do { do {
p[i++] = pop_lsb(&bb); p[i++] = pop_lsb(&bb);
} while (bb); } while (bb);
} }
idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]); idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]);
res = decompress_pairs(entry->precomp[bside], idx); res = decompress_pairs(entry->precomp[bside], idx);
} else { } else {
struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr; TBEntry_pawn *entry = (TBEntry_pawn *)ptr;
int k = entry->file[0].pieces[0][0] ^ cmirror; int k = entry->file[0].pieces[0][0] ^ cmirror;
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07)); Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
i = 0; i = 0;
do { do {
p[i++] = pop_lsb(&bb) ^ mirror; p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb); } while (bb);
int f = pawn_file(entry, p); int f = pawn_file(entry, p);
ubyte *pc = entry->file[f].pieces[bside]; uint8_t *pc = entry->file[f].pieces[bside];
for (; i < entry->num;) { for (; i < entry->num;) {
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07)); (PieceType)(pc[i] & 0x07));
do { do {
p[i++] = pop_lsb(&bb) ^ mirror; p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb); } while (bb);
} }
idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]); idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]);
res = decompress_pairs(entry->file[f].precomp[bside], idx); res = decompress_pairs(entry->file[f].precomp[bside], idx);
} }
@ -214,49 +229,61 @@ static int probe_wdl_table(Position& pos, int *success)
static int probe_dtz_table(Position& pos, int wdl, int *success) static int probe_dtz_table(Position& pos, int wdl, int *success)
{ {
struct TBEntry *ptr; TBEntry *ptr;
uint64 idx; uint64_t idx;
int i, res; int i, res;
int p[TBPIECES]; int p[TBPIECES];
// Obtain the position's material signature key. // Obtain the position's material signature key.
uint64 key = pos.material_key(); uint64_t key = pos.material_key();
if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) { if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) {
for (i = 1; i < DTZ_ENTRIES; i++) for (i = 1; i < DTZ_ENTRIES; i++)
if (DTZ_table[i].key1 == key) break; if (DTZ_table[i].key1 == key)
break;
if (i < DTZ_ENTRIES) { if (i < DTZ_ENTRIES) {
struct DTZTableEntry table_entry = DTZ_table[i]; DTZTableEntry table_entry = DTZ_table[i];
for (; i > 0; i--) for (; i > 0; i--)
DTZ_table[i] = DTZ_table[i - 1]; DTZ_table[i] = DTZ_table[i - 1];
DTZ_table[0] = table_entry; DTZ_table[0] = table_entry;
} else { } else {
struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++) for (i = 0; i < HSHMAX; i++)
if (ptr2[i].key == key) break; if (ptr2[i].key == key)
break;
if (i == HSHMAX) { if (i == HSHMAX) {
*success = 0; *success = 0;
return 0; return 0;
} }
ptr = ptr2[i].ptr; ptr = ptr2[i].ptr;
char str[16]; bool mirror = (ptr->key != key);
int mirror = (ptr->key != key); std::string s = prt_str(pos, mirror);
prt_str(pos, str, mirror);
if (DTZ_table[DTZ_ENTRIES - 1].entry) if (DTZ_table[DTZ_ENTRIES - 1].entry)
free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry); free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry);
for (i = DTZ_ENTRIES - 1; i > 0; i--) for (i = DTZ_ENTRIES - 1; i > 0; i--)
DTZ_table[i] = DTZ_table[i - 1]; DTZ_table[i] = DTZ_table[i - 1];
load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror));
load_dtz_table(s, calc_key(pos, mirror), calc_key(pos, !mirror));
} }
} }
ptr = DTZ_table[0].entry; ptr = DTZ_table[0].entry;
if (!ptr) { if (!ptr) {
*success = 0; *success = 0;
return 0; return 0;
} }
int bside, mirror, cmirror; int bside, mirror, cmirror;
if (!ptr->symmetric) { if (!ptr->symmetric) {
if (key != ptr->key) { if (key != ptr->key) {
cmirror = 8; cmirror = 8;
@ -273,20 +300,25 @@ static int probe_dtz_table(Position& pos, int wdl, int *success)
} }
if (!ptr->has_pawns) { if (!ptr->has_pawns) {
struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr; DTZEntry_piece *entry = (DTZEntry_piece *)ptr;
if ((entry->flags & 1) != bside && !entry->symmetric) { if ((entry->flags & 1) != bside && !entry->symmetric) {
*success = -1; *success = -1;
return 0; return 0;
} }
ubyte *pc = entry->pieces;
uint8_t *pc = entry->pieces;
for (i = 0; i < entry->num;) { for (i = 0; i < entry->num;) {
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07)); (PieceType)(pc[i] & 0x07));
do { do {
p[i++] = pop_lsb(&bb); p[i++] = pop_lsb(&bb);
} while (bb); } while (bb);
} }
idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor);
idx = encode_piece((TBEntry_piece *)entry, entry->norm, p, entry->factor);
res = decompress_pairs(entry->precomp, idx); res = decompress_pairs(entry->precomp, idx);
if (entry->flags & 2) if (entry->flags & 2)
@ -295,27 +327,34 @@ static int probe_dtz_table(Position& pos, int wdl, int *success)
if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1)) if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1))
res *= 2; res *= 2;
} else { } else {
struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr; DTZEntry_pawn *entry = (DTZEntry_pawn *)ptr;
int k = entry->file[0].pieces[0] ^ cmirror; int k = entry->file[0].pieces[0] ^ cmirror;
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07)); Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
i = 0; i = 0;
do { do {
p[i++] = pop_lsb(&bb) ^ mirror; p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb); } while (bb);
int f = pawn_file((struct TBEntry_pawn *)entry, p);
int f = pawn_file((TBEntry_pawn *)entry, p);
if ((entry->flags[f] & 1) != bside) { if ((entry->flags[f] & 1) != bside) {
*success = -1; *success = -1;
return 0; return 0;
} }
ubyte *pc = entry->file[f].pieces;
uint8_t *pc = entry->file[f].pieces;
for (; i < entry->num;) { for (; i < entry->num;) {
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3), bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07)); (PieceType)(pc[i] & 0x07));
do { do {
p[i++] = pop_lsb(&bb) ^ mirror; p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb); } while (bb);
} }
idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor);
idx = encode_pawn((TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor);
res = decompress_pairs(entry->file[f].precomp, idx); res = decompress_pairs(entry->file[f].precomp, idx);
if (entry->flags[f] & 2) if (entry->flags[f] & 2)
@ -335,6 +374,7 @@ static ExtMove *add_underprom_caps(Position& pos, ExtMove *stack, ExtMove *end)
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) { if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) {
(*extra++).move = (Move)(move - (1 << 12)); (*extra++).move = (Move)(move - (1 << 12));
(*extra++).move = (Move)(move - (2 << 12)); (*extra++).move = (Move)(move - (2 << 12));
@ -365,24 +405,33 @@ static int probe_ab(Position& pos, int alpha, int beta, int *success)
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move capture = moves->move; Move capture = moves->move;
if (!pos.capture(capture) || type_of(capture) == ENPASSANT if (!pos.capture(capture) || type_of(capture) == ENPASSANT
|| !pos.legal(capture, ci.pinned)) || !pos.legal(capture, ci.pinned))
continue; continue;
pos.do_move(capture, st, pos.gives_check(capture, ci)); pos.do_move(capture, st, pos.gives_check(capture, ci));
v = -probe_ab(pos, -beta, -alpha, success); v = -probe_ab(pos, -beta, -alpha, success);
pos.undo_move(capture); pos.undo_move(capture);
if (*success == 0) return 0;
if (*success == 0)
return 0;
if (v > alpha) { if (v > alpha) {
if (v >= beta) { if (v >= beta) {
*success = 2; *success = 2;
return v; return v;
} }
alpha = v; alpha = v;
} }
} }
v = probe_wdl_table(pos, success); v = probe_wdl_table(pos, success);
if (*success == 0) return 0;
if (*success == 0)
return 0;
if (alpha >= v) { if (alpha >= v) {
*success = 1 + (alpha > 0); *success = 1 + (alpha > 0);
return alpha; return alpha;
@ -410,12 +459,14 @@ int Tablebases::probe_wdl(Position& pos, int *success)
// If en passant is not possible, we are done. // If en passant is not possible, we are done.
if (pos.ep_square() == SQ_NONE) if (pos.ep_square() == SQ_NONE)
return v; return v;
if (!(*success)) return 0;
if (*success == 0)
return 0;
// Now handle en passant. // Now handle en passant.
int v1 = -3; int v1 = -3;
// Generate (at least) all legal en passant captures. // Generate (at least) all legal en passant captures.
ExtMove stack[192]; ExtMove stack[MAX_MOVES];
ExtMove *moves, *end; ExtMove *moves, *end;
StateInfo st; StateInfo st;
@ -428,32 +479,45 @@ int Tablebases::probe_wdl(Position& pos, int *success)
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move capture = moves->move; Move capture = moves->move;
if (type_of(capture) != ENPASSANT if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned)) || !pos.legal(capture, ci.pinned))
continue; continue;
pos.do_move(capture, st, pos.gives_check(capture, ci)); pos.do_move(capture, st, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success); int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture); pos.undo_move(capture);
if (*success == 0) return 0;
if (*success == 0)
return 0;
if (v0 > v1) v1 = v0; if (v0 > v1) v1 = v0;
} }
if (v1 > -3) { if (v1 > -3) {
if (v1 >= v) v = v1; if (v1 >= v) v = v1;
else if (v == 0) { else if (v == 0) {
// Check whether there is at least one legal non-ep move. // Check whether there is at least one legal non-ep move.
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move capture = moves->move; Move capture = moves->move;
if (type_of(capture) == ENPASSANT) continue; if (type_of(capture) == ENPASSANT) continue;
if (pos.legal(capture, ci.pinned)) break;
if (pos.legal(capture, ci.pinned))
break;
} }
if (moves == end && !pos.checkers()) { if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end); end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) { for (; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (pos.legal(move, ci.pinned)) if (pos.legal(move, ci.pinned))
break; break;
} }
} }
// If not, then we are forced to play the losing ep capture. // If not, then we are forced to play the losing ep capture.
if (moves == end) if (moves == end)
v = v1; v = v1;
@ -469,6 +533,7 @@ static int probe_dtz_no_ep(Position& pos, int *success)
int wdl, dtz; int wdl, dtz;
wdl = probe_ab(pos, -2, 2, success); wdl = probe_ab(pos, -2, 2, success);
if (*success == 0) return 0; if (*success == 0) return 0;
if (wdl == 0) return 0; if (wdl == 0) return 0;
@ -476,7 +541,7 @@ static int probe_dtz_no_ep(Position& pos, int *success)
if (*success == 2) if (*success == 2)
return wdl == 2 ? 1 : 101; return wdl == 2 ? 1 : 101;
ExtMove stack[192]; ExtMove stack[MAX_MOVES];
ExtMove *moves, *end = NULL; ExtMove *moves, *end = NULL;
StateInfo st; StateInfo st;
CheckInfo ci(pos); CheckInfo ci(pos);
@ -491,51 +556,69 @@ static int probe_dtz_no_ep(Position& pos, int *success)
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move) if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move)
|| !pos.legal(move, ci.pinned)) || !pos.legal(move, ci.pinned))
continue; continue;
pos.do_move(move, st, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
int v = -probe_ab(pos, -2, -wdl + 1, success); int v = -probe_ab(pos, -2, -wdl + 1, success);
pos.undo_move(move); pos.undo_move(move);
if (*success == 0) return 0; if (*success == 0) return 0;
if (v == wdl) if (v == wdl)
return v == 2 ? 1 : 101; return v == 2 ? 1 : 101;
} }
} }
dtz = 1 + probe_dtz_table(pos, wdl, success); dtz = 1 + probe_dtz_table(pos, wdl, success);
if (*success >= 0) { if (*success >= 0) {
if (wdl & 1) dtz += 100; if (wdl & 1) dtz += 100;
return wdl >= 0 ? dtz : -dtz; return wdl >= 0 ? dtz : -dtz;
} }
if (wdl > 0) { if (wdl > 0) {
int best = 0xffff; int best = 0xffff;
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN
|| !pos.legal(move, ci.pinned)) || !pos.legal(move, ci.pinned))
continue; continue;
pos.do_move(move, st, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
int v = -Tablebases::probe_dtz(pos, success); int v = -Tablebases::probe_dtz(pos, success);
pos.undo_move(move); pos.undo_move(move);
if (*success == 0) return 0;
if (*success == 0)
return 0;
if (v > 0 && v + 1 < best) if (v > 0 && v + 1 < best)
best = v + 1; best = v + 1;
} }
return best; return best;
} else { } else {
int best = -1; int best = -1;
if (!pos.checkers()) if (!pos.checkers())
end = generate<NON_EVASIONS>(pos, stack); end = generate<NON_EVASIONS>(pos, stack);
else else
end = generate<EVASIONS>(pos, stack); end = generate<EVASIONS>(pos, stack);
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
int v; int v;
Move move = moves->move; Move move = moves->move;
if (!pos.legal(move, ci.pinned)) if (!pos.legal(move, ci.pinned))
continue; continue;
pos.do_move(move, st, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
if (st.rule50 == 0) { if (st.rule50 == 0) {
if (wdl == -2) v = -1; if (wdl == -2) v = -1;
else { else {
@ -545,11 +628,16 @@ static int probe_dtz_no_ep(Position& pos, int *success)
} else { } else {
v = -Tablebases::probe_dtz(pos, success) - 1; v = -Tablebases::probe_dtz(pos, success) - 1;
} }
pos.undo_move(move); pos.undo_move(move);
if (*success == 0) return 0;
if (*success == 0)
return 0;
if (v < best) if (v < best)
best = v; best = v;
} }
return best; return best;
} }
} }
@ -591,12 +679,14 @@ int Tablebases::probe_dtz(Position& pos, int *success)
if (pos.ep_square() == SQ_NONE) if (pos.ep_square() == SQ_NONE)
return v; return v;
if (*success == 0) return 0;
if (*success == 0)
return 0;
// Now handle en passant. // Now handle en passant.
int v1 = -3; int v1 = -3;
ExtMove stack[192]; ExtMove stack[MAX_MOVES];
ExtMove *moves, *end; ExtMove *moves, *end;
StateInfo st; StateInfo st;
@ -604,21 +694,29 @@ int Tablebases::probe_dtz(Position& pos, int *success)
end = generate<CAPTURES>(pos, stack); end = generate<CAPTURES>(pos, stack);
else else
end = generate<EVASIONS>(pos, stack); end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos); CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move capture = moves->move; Move capture = moves->move;
if (type_of(capture) != ENPASSANT if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned)) || !pos.legal(capture, ci.pinned))
continue; continue;
pos.do_move(capture, st, pos.gives_check(capture, ci)); pos.do_move(capture, st, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success); int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture); pos.undo_move(capture);
if (*success == 0) return 0;
if (*success == 0)
return 0;
if (v0 > v1) v1 = v0; if (v0 > v1) v1 = v0;
} }
if (v1 > -3) { if (v1 > -3) {
v1 = wdl_to_dtz[v1 + 2]; v1 = wdl_to_dtz[v1 + 2];
if (v < -100) { if (v < -100) {
if (v1 >= 0) if (v1 >= 0)
v = v1; v = v1;
@ -636,17 +734,24 @@ int Tablebases::probe_dtz(Position& pos, int *success)
} else { } else {
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (type_of(move) == ENPASSANT) continue; if (type_of(move) == ENPASSANT) continue;
if (pos.legal(move, ci.pinned)) break;
if (pos.legal(move, ci.pinned))
break;
} }
if (moves == end && !pos.checkers()) { if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end); end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) { for (; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (pos.legal(move, ci.pinned)) if (pos.legal(move, ci.pinned))
break; break;
} }
} }
if (moves == end) if (moves == end)
v = v1; v = v1;
} }
@ -661,15 +766,21 @@ static int has_repeated(StateInfo *st)
{ {
while (1) { while (1) {
int i = 4, e = std::min(st->rule50, st->pliesFromNull); int i = 4, e = std::min(st->rule50, st->pliesFromNull);
if (e < i) if (e < i)
return 0; return 0;
StateInfo *stp = st->previous->previous; StateInfo *stp = st->previous->previous;
do { do {
stp = stp->previous->previous; stp = stp->previous->previous;
if (stp->key == st->key) if (stp->key == st->key)
return 1; return 1;
i += 2; i += 2;
} while (i <= e); } while (i <= e);
st = st->previous; st = st->previous;
} }
} }
@ -693,7 +804,9 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
int success; int success;
int dtz = probe_dtz(pos, &success); int dtz = probe_dtz(pos, &success);
if (!success) return false;
if (!success)
return false;
StateInfo st; StateInfo st;
CheckInfo ci(pos); CheckInfo ci(pos);
@ -703,14 +816,18 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
Move move = rootMoves[i].pv[0]; Move move = rootMoves[i].pv[0];
pos.do_move(move, st, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
int v = 0; int v = 0;
if (pos.checkers() && dtz > 0) { if (pos.checkers() && dtz > 0) {
ExtMove s[192]; ExtMove s[MAX_MOVES];
if (generate<LEGAL>(pos, s) == s) if (generate<LEGAL>(pos, s) == s)
v = 1; v = 1;
} }
if (!v) { if (!v) {
if (st.rule50 != 0) { if (st.rule50 != 0) {
v = -Tablebases::probe_dtz(pos, &success); v = -Tablebases::probe_dtz(pos, &success);
if (v > 0) v++; if (v > 0) v++;
else if (v < 0) v--; else if (v < 0) v--;
} else { } else {
@ -718,8 +835,12 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
v = wdl_to_dtz[v + 2]; v = wdl_to_dtz[v + 2];
} }
} }
pos.undo_move(move); pos.undo_move(move);
if (!success) return false;
if (!success)
return false;
rootMoves[i].score = (Value)v; rootMoves[i].score = (Value)v;
} }
@ -730,6 +851,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
// Use 50-move counter to determine whether the root position is // Use 50-move counter to determine whether the root position is
// won, lost or drawn. // won, lost or drawn.
int wdl = 0; int wdl = 0;
if (dtz > 0) if (dtz > 0)
wdl = (dtz + cnt50 <= 100) ? 2 : 1; wdl = (dtz + cnt50 <= 100) ? 2 : 1;
else if (dtz < 0) else if (dtz < 0)
@ -737,6 +859,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
// Determine the score to report to the user. // Determine the score to report to the user.
score = wdl_to_Value[wdl + 2]; score = wdl_to_Value[wdl + 2];
// If the position is winning or losing, but too few moves left, adjust the // If the position is winning or losing, but too few moves left, adjust the
// score to show how close it is to winning or losing. // score to show how close it is to winning or losing.
// NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci(). // NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci().
@ -747,33 +870,44 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
// Now be a bit smart about filtering out moves. // Now be a bit smart about filtering out moves.
size_t j = 0; size_t j = 0;
if (dtz > 0) { // winning (or 50-move rule draw) if (dtz > 0) { // winning (or 50-move rule draw)
int best = 0xffff; int best = 0xffff;
for (size_t i = 0; i < rootMoves.size(); i++) { for (size_t i = 0; i < rootMoves.size(); i++) {
int v = rootMoves[i].score; int v = rootMoves[i].score;
if (v > 0 && v < best) if (v > 0 && v < best)
best = v; best = v;
} }
int max = best; int max = best;
// If the current phase has not seen repetitions, then try all moves // If the current phase has not seen repetitions, then try all moves
// that stay safely within the 50-move budget, if there are any. // that stay safely within the 50-move budget, if there are any.
if (!has_repeated(st.previous) && best + cnt50 <= 99) if (!has_repeated(st.previous) && best + cnt50 <= 99)
max = 99 - cnt50; max = 99 - cnt50;
for (size_t i = 0; i < rootMoves.size(); i++) { for (size_t i = 0; i < rootMoves.size(); i++) {
int v = rootMoves[i].score; int v = rootMoves[i].score;
if (v > 0 && v <= max) if (v > 0 && v <= max)
rootMoves[j++] = rootMoves[i]; rootMoves[j++] = rootMoves[i];
} }
} else if (dtz < 0) { // losing (or 50-move rule draw) } else if (dtz < 0) { // losing (or 50-move rule draw)
int best = 0; int best = 0;
for (size_t i = 0; i < rootMoves.size(); i++) { for (size_t i = 0; i < rootMoves.size(); i++) {
int v = rootMoves[i].score; int v = rootMoves[i].score;
if (v < best) if (v < best)
best = v; best = v;
} }
// Try all moves, unless we approach or have a 50-move rule draw. // Try all moves, unless we approach or have a 50-move rule draw.
if (-best * 2 + cnt50 < 100) if (-best * 2 + cnt50 < 100)
return true; return true;
for (size_t i = 0; i < rootMoves.size(); i++) { for (size_t i = 0; i < rootMoves.size(); i++) {
if (rootMoves[i].score == best) if (rootMoves[i].score == best)
rootMoves[j++] = rootMoves[i]; rootMoves[j++] = rootMoves[i];
@ -785,6 +919,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
rootMoves[j++] = rootMoves[i]; rootMoves[j++] = rootMoves[i];
} }
} }
rootMoves.resize(j, Search::RootMove(MOVE_NONE)); rootMoves.resize(j, Search::RootMove(MOVE_NONE));
return true; return true;
@ -800,7 +935,10 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves
int success; int success;
int wdl = Tablebases::probe_wdl(pos, &success); int wdl = Tablebases::probe_wdl(pos, &success);
if (!success) return false;
if (!success)
return false;
score = wdl_to_Value[wdl + 2]; score = wdl_to_Value[wdl + 2];
StateInfo st; StateInfo st;
@ -814,17 +952,23 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves
pos.do_move(move, st, pos.gives_check(move, ci)); pos.do_move(move, st, pos.gives_check(move, ci));
int v = -Tablebases::probe_wdl(pos, &success); int v = -Tablebases::probe_wdl(pos, &success);
pos.undo_move(move); pos.undo_move(move);
if (!success) return false;
if (!success)
return false;
rootMoves[i].score = (Value)v; rootMoves[i].score = (Value)v;
if (v > best) if (v > best)
best = v; best = v;
} }
size_t j = 0; size_t j = 0;
for (size_t i = 0; i < rootMoves.size(); i++) { for (size_t i = 0; i < rootMoves.size(); i++) {
if (rootMoves[i].score == best) if (rootMoves[i].score == best)
rootMoves[j++] = rootMoves[i]; rootMoves[j++] = rootMoves[i];
} }
rootMoves.resize(j, Search::RootMove(MOVE_NONE)); rootMoves.resize(j, Search::RootMove(MOVE_NONE));
return true; return true;