From 00f0441841821f8b189c39518b07c61eaef846be Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Wed, 13 Jul 2016 17:00:30 -0700 Subject: [PATCH 01/75] Always use TB_CUSTOM_POP_COUNT if defined. If not defined and if also TB_NO_HW_POP_COUNT is not defined, use a x86 hardware instruction, but only if on a recognized compiler/architecture that has one. Fixes issue #9 (ARM compatibility). --- src/tbconfig.h | 8 ++++++++ src/tbprobe.c | 27 ++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/tbconfig.h b/src/tbconfig.h index c0abbaf..38b5c0e 100644 --- a/src/tbconfig.h +++ b/src/tbconfig.h @@ -80,6 +80,14 @@ /* * Define TB_NO_HW_POP_COUNT if there is no hardware popcount instruction. + * + * Note: if defined, TB_CUSTOM_POP_COUNT is always used in preference + * to any built-in popcount functions. + * + * If no custom popcount function is defined, and if the following + * define is not set, the code will attempt to use an available hardware + * popcnt (currently supported on x86_64 architecture only) and otherwise + * will fall back to a software implementation. */ /* #define TB_NO_HW_POP_COUNT */ diff --git a/src/tbprobe.c b/src/tbprobe.c index 5470cff..769467d 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -29,10 +29,6 @@ #include "tbprobe.h" -#ifdef __GNUC__ -#include -#endif - #define WHITE_KING (TB_WPAWN + 5) #define WHITE_QUEEN (TB_WPAWN + 4) #define WHITE_ROOK (TB_WPAWN + 3) @@ -68,21 +64,34 @@ #define BEST_NONE 0xFFFF #define SCORE_ILLEGAL 0x7FFF -#ifndef TB_NO_HW_POP_COUNT -#ifdef TB_CUSTOM_POP_COUNT +#undef TB_SOFTWARE_POP_COUNT + +#if defined(TB_CUSTOM_POP_COUNT) #define popcount(x) TB_CUSTOM_POP_COUNT(x) -#else +#elif defined(TB_NO_HW_POP_COUNT) +#define TB_SOFTWARE_POP_COUNT +#elif defined (__GNUC__) && defined(__x86_64__) && defined(__SSE4_2__) #include #define popcount(x) _mm_popcnt_u64((x)) -#endif +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) && defined(_M_AMD64) +#include +#define popcount(x) _mm_popcnt_u64((x)) #else -static inline unsigned popcount(uint64_t x) +#define TB_SOFTWARE_POP_COUNT +#endif + +#ifdef TB_SOFTWARE_POP_COUNT +// Not a recognized compiler/architecture that has popcount: +// fall back to a software popcount. This one is still reasonably +// fast (faster than GCC's builtin). +static inline unsigned tb_software_popcount(uint64_t x) { x = x - ((x >> 1) & 0x5555555555555555ull); x = (x & 0x3333333333333333ull) + ((x >> 2) & 0x3333333333333333ull); x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0full; return (x * 0x0101010101010101ull) >> 56; } +#define popcount(x) tb_software_popcount(x) #endif #define poplsb(x) ((x) & ((x) - 1)) From 358dbc1b34f394c4ebc2ff5317c4e7f8b1d7e73d Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Mon, 16 Jan 2017 14:07:35 -0800 Subject: [PATCH 02/75] Fix issues found by Coverity Scan: 1. Check return value from fstat. 2. Use strncpy not strcpy in constructing file path. --- src/tbcore.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/tbcore.c b/src/tbcore.c index 2b1bf7c..3261d41 100644 --- a/src/tbcore.c +++ b/src/tbcore.c @@ -86,20 +86,36 @@ static FD open_tb(const char *str, const char *suffix) { int i; FD fd; - char file[256]; + int remain; +#ifdef _WIN32 + const int MAX_LEN = MAX_PATH; +#else + // assume 256 + const int MAX_LEN = 256; +#endif + remain = MAX_LEN-1; // allow room for null + char file[MAX_LEN]; for (i = 0; i < num_paths; i++) { - strcpy(file, paths[i]); - strcat(file, "/"); - strcat(file, str); - strcat(file, suffix); + strncpy(file, paths[i], remain); + remain -= strlen(paths[i]); + if (remain <=0) break; + strncat(file, "/", remain); + remain--; + if (remain <=0) break; + strncat(file, str, remain); + remain -= strlen(str); + if (remain <=0) break; + strncat(file, suffix, remain); #ifndef _WIN32 fd = open(file, O_RDONLY); #else fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #endif - if (fd != FD_ERR) return fd; + if (fd != FD_ERR) { + return fd; + } } return FD_ERR; } @@ -120,7 +136,10 @@ static char *map_file(const char *name, const char *suffix, uint64 *mapping) return NULL; #ifndef _WIN32 struct stat statbuf; - fstat(fd, &statbuf); + if (fstat(fd, &statbuf)) { + close_tb(fd); + return NULL; + } *mapping = statbuf.st_size; char *data = (char *)mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); From 9d26101efc362e060f2d48edb2ba220aeabc520b Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Fri, 20 Jan 2017 16:11:17 -0800 Subject: [PATCH 03/75] Set precomp, data, and mapping fields to NULL after free. Write error messages to stderr, not stdout. Add error checking for unmap function. --- src/tbcore.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/tbcore.c b/src/tbcore.c index 3261d41..1b3c694 100644 --- a/src/tbcore.c +++ b/src/tbcore.c @@ -137,6 +137,7 @@ static char *map_file(const char *name, const char *suffix, uint64 *mapping) #ifndef _WIN32 struct stat statbuf; if (fstat(fd, &statbuf)) { + perror("fstat"); close_tb(fd); return NULL; } @@ -144,7 +145,7 @@ static char *map_file(const char *name, const char *suffix, uint64 *mapping) char *data = (char *)mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); if (data == (char *)(-1)) { - printf("Could not mmap() %s.\n", name); + fprintf(stderr,"Could not mmap() %s.\n", name); exit(1); } #else @@ -154,13 +155,13 @@ static char *map_file(const char *name, const char *suffix, uint64 *mapping) HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, NULL); if (map == NULL) { - printf("CreateFileMapping() failed.\n"); + fprintf(stderr,"CreateFileMapping() failed.\n"); exit(1); } *mapping = (uint64)map; char *data = (char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); if (data == NULL) { - printf("MapViewOfFile() failed, name = %s%s, error = %lu.\n", name, suffix, GetLastError()); + fprintf(stderr,"MapViewOfFile() failed, name = %s%s, error = %lu.\n", name, suffix, GetLastError()); exit(1); } #endif @@ -172,14 +173,20 @@ static char *map_file(const char *name, const char *suffix, uint64 *mapping) static void unmap_file(char *data, uint64 size) { if (!data) return; - munmap(data, size); + if (!munmap(data, size)) { + perror("munmap"); + } } #else static void unmap_file(char *data, uint64 mapping) { if (!data) return; - UnmapViewOfFile(data); - CloseHandle((HANDLE)mapping); + if (!UnmapViewOfFile(data)) { + fprintf(stderr, "unmap failed, error code %d", GetLastError()); + } + if (!CloseHandle((HANDLE)mapping)) { + fprintf(stderr, "CloseHandle failed, error code %d", GetLastError()); + } } #endif @@ -191,7 +198,7 @@ static void add_to_hash(struct TBEntry *ptr, uint64 key) while (i < HSHMAX && TB_hash[hshidx][i].ptr) i++; if (i == HSHMAX) { - printf("HSHMAX too low!\n"); + fprintf(stderr,"HSHMAX too low!\n"); exit(1); } else { TB_hash[hshidx][i].key = key; @@ -245,13 +252,13 @@ static void init_tb(char *str) key2 = calc_key_from_pcs(pcs, 1); if (pcs[TB_WPAWN] + pcs[TB_BPAWN] == 0) { if (TBnum_piece == TBMAX_PIECE) { - printf("TBMAX_PIECE limit too low!\n"); + fprintf(stderr,"TBMAX_PIECE limit too low!\n"); exit(1); } entry = (struct TBEntry *)&TB_piece[TBnum_piece++]; } else { if (TBnum_pawn == TBMAX_PAWN) { - printf("TBMAX_PAWN limit too low!\n"); + fprintf(stderr,"TBMAX_PAWN limit too low!\n"); exit(1); } entry = (struct TBEntry *)&TB_pawn[TBnum_pawn++]; @@ -1307,13 +1314,13 @@ static int init_table_wdl(struct TBEntry *entry, char *str) // first mmap the table into memory entry->data = map_file(str, WDLSUFFIX, &entry->mapping); if (!entry->data) { - printf("Could not find %s" WDLSUFFIX "\n", str); + fprintf(stderr,"Could not find %s" WDLSUFFIX "\n", str); return 0; } ubyte *data = (ubyte *)entry->data; if (((uint32 *)data)[0] != WDL_MAGIC) { - printf("Corrupted table.\n"); + fprintf(stderr,"Corrupted table.\n"); unmap_file(entry->data, entry->mapping); entry->data = 0; return 0; @@ -1423,7 +1430,7 @@ static int init_table_dtz(struct TBEntry *entry) return 0; if (((uint32 *)data)[0] != DTZ_MAGIC) { - printf("Corrupted table.\n"); + fprintf(stderr,"Corrupted table.\n"); return 0; } @@ -1634,11 +1641,13 @@ void load_dtz_table(char *str, uint64 key1, uint64 key2) static void free_wdl_entry(struct TBEntry *entry) { unmap_file(entry->data, entry->mapping); + entry->data = NULL; entry->mapping = 0; if (!entry->has_pawns) { struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; free(ptr->precomp[0]); if (ptr->precomp[1]) free(ptr->precomp[1]); + ptr->precomp[0] = ptr->precomp[1] = NULL; } else { struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; int f; @@ -1646,6 +1655,7 @@ static void free_wdl_entry(struct TBEntry *entry) free(ptr->file[f].precomp[0]); if (ptr->file[f].precomp[1]) free(ptr->file[f].precomp[1]); + ptr->file[f].precomp[0] = ptr->file[f].precomp[1] = NULL; } } } @@ -1653,14 +1663,18 @@ static void free_wdl_entry(struct TBEntry *entry) static void free_dtz_entry(struct TBEntry *entry) { unmap_file(entry->data, entry->mapping); + entry->data = NULL; entry->mapping = 0; if (!entry->has_pawns) { struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; free(ptr->precomp); + ptr->precomp = NULL; } else { struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; int f; - for (f = 0; f < 4; f++) + for (f = 0; f < 4; f++) { free(ptr->file[f].precomp); + ptr->file[f].precomp = NULL; + } } free(entry); } From 237e1ab274a98cdf9b621829b93500428e55d7aa Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 21 Jan 2017 08:58:08 -0800 Subject: [PATCH 04/75] Fix compiler warnings. Fix logic in sanity check in tb_init_impl. --- src/tbcore.c | 32 ++++++++++++++++---------------- src/tbprobe.c | 8 ++++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/tbcore.c b/src/tbcore.c index 1b3c694..5668687 100644 --- a/src/tbcore.c +++ b/src/tbcore.c @@ -86,7 +86,7 @@ static FD open_tb(const char *str, const char *suffix) { int i; FD fd; - int remain; + size_t remain; #ifdef _WIN32 const int MAX_LEN = MAX_PATH; #else @@ -1070,7 +1070,7 @@ static uint64 calc_factors_piece(int *factor, int num, int order, ubyte *norm, u f = 1; for (i = norm[0], k = 0; i < num || k == order; k++) { if (k == order) { - factor[0] = f; + factor[0] = (int)f; #ifndef CONNECTED_KINGS f *= pivfac[enc_type]; #else @@ -1080,7 +1080,7 @@ static uint64 calc_factors_piece(int *factor, int num, int order, ubyte *norm, u f *= mfactor[enc_type - 2]; #endif } else { - factor[i] = f; + factor[i] = (int)f; f *= subfactor(norm[i], n); n -= norm[i]; i += norm[i]; @@ -1102,13 +1102,13 @@ static uint64 calc_factors_pawn(int *factor, int num, int order, int order2, uby f = 1; for (k = 0; i < num || k == order || k == order2; k++) { if (k == order) { - factor[0] = f; + factor[0] = (int)f; f *= pfactor[norm[0] - 1][file]; } else if (k == order2) { - factor[norm[0]] = f; + factor[norm[0]] = (int)f; f *= subfactor(norm[norm[0]], 48 - norm[0]); } else { - factor[i] = f; + factor[i] = (int)f; f *= subfactor(norm[i], n); n -= norm[i]; i += norm[i]; @@ -1274,7 +1274,7 @@ static struct PairsData *setup_pairs(unsigned char *data, uint64 tb_size, uint64 d->min_len = min_len; *next = &data[12 + 2 * h + 3 * num_syms + (num_syms & 1)]; - int num_indices = (tb_size + (1ULL << idxbits) - 1) >> idxbits; + int num_indices = (int)((tb_size + (1ULL << idxbits) - 1) >> idxbits); size[0] = 6ULL * num_indices; size[1] = 2ULL * num_blocks; size[2] = (1ULL << blocksize) * real_num_blocks; @@ -1451,8 +1451,8 @@ static int init_table_dtz(struct TBEntry *entry) if (ptr->flags & 2) { int i; for (i = 0; i < 4; i++) { - ptr->map_idx[i] = (data + 1 - ptr->map); - data += 1 + data[0]; + ptr->map_idx[i] = (ushort)(data + 1 - ptr->map); + data += 1 + data[0]; } data += ((uintptr_t)data) & 0x01; } @@ -1483,11 +1483,11 @@ static int init_table_dtz(struct TBEntry *entry) ptr->map = data; for (f = 0; f < files; f++) { if (ptr->flags[f] & 2) { - int i; - for (i = 0; i < 4; i++) { - ptr->map_idx[f][i] = (data + 1 - ptr->map); - data += 1 + data[0]; - } + int i; + for (i = 0; i < 4; i++) { + ptr->map_idx[f][i] = (ushort)(data + 1 - ptr->map); + data += 1 + data[0]; + } } } data += ((uintptr_t)data) & 0x01; @@ -1517,8 +1517,8 @@ static ubyte decompress_pairs(struct PairsData *d, uint64 idx) if (!d->idxbits) return d->min_len; - uint32 mainidx = idx >> d->idxbits; - int litidx = (idx & ((1 << d->idxbits) - 1)) - (1 << (d->idxbits - 1)); + uint32 mainidx = (uint32)(idx >> d->idxbits); + int litidx = (int)(idx & (((uint64)1 << d->idxbits) - 1)) - ((uint64)1 << (d->idxbits - 1)); uint32 block = *(uint32 *)(d->indextable + 6 * mainidx); litidx += *(ushort *)(d->indextable + 6 * mainidx + 4); if (litidx < 0) { diff --git a/src/tbprobe.c b/src/tbprobe.c index 769467d..ce66691 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -822,7 +822,7 @@ static int probe_dtz_table(const struct pos *pos, int wdl, int *success) } ptr = ptr2[i].ptr; char str[16]; - int mirror = (ptr->key != key); + const bool mirror = (ptr->key != key); prt_str(pos, str, mirror); if (DTZ_table[DTZ_ENTRIES - 1].entry) free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry); @@ -1806,9 +1806,9 @@ static uint16_t probe_root(const struct pos *pos, int *score, bool tb_init_impl(const char *path) { - if (sizeof(uint64_t) != 8 && // Paranoid check - sizeof(uint32_t) != 4 && - sizeof(uint16_t) != 2 && + if (sizeof(uint64_t) != 8 || // Paranoid check + sizeof(uint32_t) != 4 || + sizeof(uint16_t) != 2 || sizeof(uint8_t) != 1) return false; king_attacks_init(); From 085ff599f77aaef28b9a4d8187b71cef90b07f56 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Mon, 17 Apr 2017 11:27:46 -0700 Subject: [PATCH 05/75] Make oppdiag array explicitly signed (fixes compile issue with clang for ARM target). Update copyright and license info. --- LICENSE | 1 + src/apps/Makefile | 2 +- src/tbconfig.h | 1 + src/tbcore.c | 27 +++++++++++++++++++++++---- src/tbprobe.c | 5 ++--- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/LICENSE b/LICENSE index f5d0f72..8eb910f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2015 basil00 +Modifications Copyright (c) 2016-2017 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/apps/Makefile b/src/apps/Makefile index f07abd2..0c4030a 100644 --- a/src/apps/Makefile +++ b/src/apps/Makefile @@ -7,7 +7,7 @@ ifeq ($(UNAME),Darwin) endif CC=clang STRIP=strip -CFLAGS=-std=gnu99 -O2 -Wall -D TB_NO_THREADS -D TB_NO_HW_POP_COUNT -I.. +CFLAGS=-std=c99 -O2 -Wall -D TB_NO_THREADS -I.. main: $(TARGET) diff --git a/src/tbconfig.h b/src/tbconfig.h index 38b5c0e..e8bd0fc 100644 --- a/src/tbconfig.h +++ b/src/tbconfig.h @@ -1,6 +1,7 @@ /* * tbconfig.h * (C) 2015 basil, all rights reserved, + * Modifications Copyright 2016 Jon Dart * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), diff --git a/src/tbcore.c b/src/tbcore.c index 5668687..c5dba40 100644 --- a/src/tbcore.c +++ b/src/tbcore.c @@ -1,7 +1,26 @@ /* - Copyright (c) 2011-2015 Ronald de Man - This file may be redistributed and/or modified without restrictions. - + * Copyright (c) 2011-2015 Ronald de Man + * Copyright (c) 2016-2017 Jon Dart + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* tbcore.c contains engine-independent routines of the tablebase probing code. This file should not need to much adaptation to add tablebase probing to a particular engine, provided the engine is written in C or C++. @@ -417,7 +436,7 @@ void init_tablebases(const char *path) // printf("Found %d tablebases.\n", TBnum_piece + TBnum_pawn); } -static const char offdiag[] = { +static const signed char offdiag[] = { 0,-1,-1,-1,-1,-1,-1,-1, 1, 0,-1,-1,-1,-1,-1,-1, 1, 1, 0,-1,-1,-1,-1,-1, diff --git a/src/tbprobe.c b/src/tbprobe.c index ce66691..0bf7212 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1,9 +1,8 @@ /* * tbprobe.c - * Copyright (c) 2013-2015 Ronald de Man - * This file may be redistributed and/or modified without restrictions. + * Copyright (c) 2013-2016 Ronald de Man + * Copyright (c) 2016-2017 Jon Dart * - * (C) 2015 basil, all rights reserved, * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation From 6998b4bb987376f55737ea4c6618bd075225a6d5 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Mon, 15 May 2017 09:25:59 -0700 Subject: [PATCH 06/75] Use atomic variable instead of explicit memory barrier if compiling under C++ 11. Add some asserts. --- src/tbcore.c | 5 +++++ src/tbcore.h | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/tbcore.c b/src/tbcore.c index c5dba40..9a3d2f3 100644 --- a/src/tbcore.c +++ b/src/tbcore.c @@ -26,6 +26,7 @@ a particular engine, provided the engine is written in C or C++. */ +#include #include #ifndef TB_NO_STDINT #include @@ -799,6 +800,7 @@ static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int uint64 idx; int i, j, k, m, l, p; int n = ptr->num; + assert(n>=0 && n<6); if (pos[0] & 0x04) { for (i = 0; i < n; i++) @@ -863,6 +865,8 @@ static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int p = pos[m]; for (l = 0, j = 0; l < i; l++) j += (p > pos[l]); + assert(m-i >= 0 && m-i < 5); + assert(p-j >= 0 && p-j < 64); s += binomial[m - i][p - j]; } idx += ((uint64)s) * ((uint64)factor[i]); @@ -877,6 +881,7 @@ static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int uint64 idx; int i, j, k, m, l, p; int n = ptr->num; + assert(n>=0 && n<6); if (ptr->enc_type < 3) { if (pos[0] & 0x04) { diff --git a/src/tbcore.h b/src/tbcore.h index 9b0b679..898bcd0 100644 --- a/src/tbcore.h +++ b/src/tbcore.h @@ -5,6 +5,10 @@ #ifndef TBCORE_H #define TBCORE_H +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#include +#endif + #ifndef _WIN32 #include #define SEP_CHAR ':' @@ -92,7 +96,11 @@ struct TBEntry_piece { char *data; uint64 key; uint64 mapping; +#if defined(__cplusplus) && (__cplusplus >= 201103L) + atomic ready; +#else ubyte ready; +#endif ubyte num; ubyte symmetric; ubyte has_pawns; @@ -107,7 +115,11 @@ struct TBEntry_pawn { char *data; uint64 key; uint64 mapping; +#if defined(__cplusplus) && (__cplusplus >= 201103L) + atomic ready; +#else ubyte ready; +#endif ubyte num; ubyte symmetric; ubyte has_pawns; From 64685b54da02f36676e4d6a4a503b95b42fc711c Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Mon, 15 May 2017 15:05:39 -0700 Subject: [PATCH 07/75] Use a define to select use of atomic variables. Don't rely on __cplusplus value to select becauase that is unreliable for MSVC. --- src/tbconfig.h | 8 +++++++- src/tbcore.h | 4 ++-- src/tbprobe.c | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tbconfig.h b/src/tbconfig.h index e8bd0fc..abc1561 100644 --- a/src/tbconfig.h +++ b/src/tbconfig.h @@ -1,7 +1,7 @@ /* * tbconfig.h * (C) 2015 basil, all rights reserved, - * Modifications Copyright 2016 Jon Dart + * Modifications Copyright 2016-2017 Jon Dart * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -92,6 +92,12 @@ */ /* #define TB_NO_HW_POP_COUNT */ +/** + * Define TB_USE_ATOMIC to use C++ 11 (or higher) feature + * (recommended if using C++ and compiler supports it). + */ +/* #define TB_USE_ATOMIC */ + /***************************************************************************/ /* ENGINE INTEGRATION CONFIG */ /***************************************************************************/ diff --git a/src/tbcore.h b/src/tbcore.h index 898bcd0..bb4c3da 100644 --- a/src/tbcore.h +++ b/src/tbcore.h @@ -5,7 +5,7 @@ #ifndef TBCORE_H #define TBCORE_H -#if defined(__cplusplus) && (__cplusplus >= 201103L) +#if defined(__cplusplus) && defined(TB_USE_ATOMIC) #include #endif @@ -96,7 +96,7 @@ struct TBEntry_piece { char *data; uint64 key; uint64 mapping; -#if defined(__cplusplus) && (__cplusplus >= 201103L) +#if defined(__cplusplus) && defined(TB_USE_ATOMIC) atomic ready; #else ubyte ready; diff --git a/src/tbprobe.c b/src/tbprobe.c index 0bf7212..285d1e8 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -702,10 +702,12 @@ static int probe_wdl_table(const struct pos *pos, int *success) return 0; } // Memory barrier to ensure ptr->ready = 1 is not reordered. +#if !defined(__cplusplus) || !defined(TB_USE_ATOMIC) #ifdef __GNUC__ __asm__ __volatile__ ("" ::: "memory"); #elif defined(_MSC_VER) MemoryBarrier(); +#endif #endif ptr->ready = 1; } From 980a993ff63ba7745caca2b9da58c04f7bc6ff86 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 28 May 2017 20:09:51 -0700 Subject: [PATCH 08/75] Add check in is_legal for king bitmap empty. --- src/tbprobe.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tbprobe.c b/src/tbprobe.c index 285d1e8..ccb4b0d 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1197,6 +1197,8 @@ static bool is_legal(const struct pos *pos) uint64_t us = (pos->turn? pos->black: pos->white), them = (pos->turn? pos->white: pos->black); uint64_t king = pos->kings & us; + if (!king) + return false; unsigned sq = lsb(king); if (king_attacks(sq) & (pos->kings & them)) return false; From 7f51a4b12671950383ce42392294892db0c35d41 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 20 Aug 2017 13:43:01 -0700 Subject: [PATCH 09/75] Use angle-bracket include for tbconfig.h. This facilitates overridding the config file by including it from another directory. --- src/tbprobe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tbprobe.h b/src/tbprobe.h index c36a5d9..96dcb85 100644 --- a/src/tbprobe.h +++ b/src/tbprobe.h @@ -24,7 +24,7 @@ #ifndef TBPROBE_H #define TBPROBE_H -#include "tbconfig.h" +#include #ifdef __cplusplus extern "C" From 0563ea22600d8ad2a5ec9bddf948508d36ee48f8 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 24 Mar 2018 10:10:28 -0700 Subject: [PATCH 10/75] Don't make locking code depend on preprocessor variable TB_HAVE_THREADS: it was not being defined. Use std::mutex if C++11 compile. Update copyright. --- src/tbcore.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/tbcore.h b/src/tbcore.h index bb4c3da..54e6c65 100644 --- a/src/tbcore.h +++ b/src/tbcore.h @@ -1,5 +1,6 @@ /* Copyright (c) 2011-2015 Ronald de Man + Copyright 2017-2018 Jon Dart */ #ifndef TBCORE_H @@ -21,7 +22,15 @@ #define FD_ERR INVALID_HANDLE_VALUE #endif -#ifdef TB_HAVE_THREADS +#if defined(__cplusplus) && (__cplusplus >= 201103L) + +#include +#define LOCK_T std::mutex +#define LOCK_INIT(x) +#define LOCK(x) x.lock() +#define UNLOCK(x) x.unlock() + +#else #ifndef _WIN32 #define LOCK_T pthread_mutex_t #define LOCK_INIT(x) pthread_mutex_init(&(x), NULL) @@ -33,11 +42,7 @@ #define LOCK(x) WaitForSingleObject(x, INFINITE) #define UNLOCK(x) ReleaseMutex(x) #endif -#else /* !TB_HAVE_THREADS */ -#define LOCK_T int -#define LOCK_INIT(x) /* NOP */ -#define LOCK(x) /* NOP */ -#define UNLOCK(x) /* NOP */ + #endif #define WDLSUFFIX ".rtbw" From be1800541d054dfa57571cc8174be1a195c6ed36 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 24 Mar 2018 18:27:46 -0700 Subject: [PATCH 11/75] Don't include lock code if TB_NO_THREADS is defined. --- src/tbcore.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tbcore.h b/src/tbcore.h index 54e6c65..8cef6ee 100644 --- a/src/tbcore.h +++ b/src/tbcore.h @@ -22,6 +22,7 @@ #define FD_ERR INVALID_HANDLE_VALUE #endif +#ifndef TB_NO_THREADS #if defined(__cplusplus) && (__cplusplus >= 201103L) #include @@ -43,6 +44,12 @@ #define UNLOCK(x) ReleaseMutex(x) #endif +#endif +#else /* TB_NO_THREADS */ +#define LOCK_T int +#define LOCK_INIT(x) /* NOP */ +#define LOCK(x) /* NOP */ +#define UNLOCK(x) /* NOP */ #endif #define WDLSUFFIX ".rtbw" From 46d711b83740f993a714d2c23932010be25093c6 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Thu, 19 Jul 2018 14:56:15 -0700 Subject: [PATCH 12/75] Fix asserts in encode_piece (assert was tripping on 6-man tbs: reported by Andrew Grant). --- src/tbcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tbcore.c b/src/tbcore.c index 9a3d2f3..d93f99e 100644 --- a/src/tbcore.c +++ b/src/tbcore.c @@ -800,7 +800,7 @@ static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int uint64 idx; int i, j, k, m, l, p; int n = ptr->num; - assert(n>=0 && n<6); + assert(n>=0 && n<7); if (pos[0] & 0x04) { for (i = 0; i < n; i++) @@ -881,7 +881,7 @@ static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int uint64 idx; int i, j, k, m, l, p; int n = ptr->num; - assert(n>=0 && n<6); + assert(n>=0 && n<7); if (ptr->enc_type < 3) { if (pos[0] & 0x04) { From 7fc1cde00e7982d29ecd01e26f07cbb54a7ab494 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 25 Nov 2018 11:01:58 -0800 Subject: [PATCH 13/75] add std namespace to atomic declarations --- src/tbcore.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tbcore.h b/src/tbcore.h index 8cef6ee..1c563b5 100644 --- a/src/tbcore.h +++ b/src/tbcore.h @@ -109,7 +109,7 @@ struct TBEntry_piece { uint64 key; uint64 mapping; #if defined(__cplusplus) && defined(TB_USE_ATOMIC) - atomic ready; + std::atomic ready; #else ubyte ready; #endif @@ -128,7 +128,7 @@ struct TBEntry_pawn { uint64 key; uint64 mapping; #if defined(__cplusplus) && (__cplusplus >= 201103L) - atomic ready; + std::atomic ready; #else ubyte ready; #endif From f7bbe69edfdea87cc8e9e1d5d9f3eed259b3bfa0 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 25 Nov 2018 16:41:46 -0800 Subject: [PATCH 14/75] Remove size limitation on file paths. --- src/tbcore.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/tbcore.c b/src/tbcore.c index d93f99e..8ee23aa 100644 --- a/src/tbcore.c +++ b/src/tbcore.c @@ -8,10 +8,10 @@ * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -106,33 +106,26 @@ static FD open_tb(const char *str, const char *suffix) { int i; FD fd; - size_t remain; + char *file; + + for (i = 0; i < num_paths; i++) { + file = (char*)malloc(strlen(paths[i]) + strlen(str) + + strlen(suffix) + 2); + strcpy(file, paths[i]); #ifdef _WIN32 - const int MAX_LEN = MAX_PATH; + strcat(file,"\\"); #else - // assume 256 - const int MAX_LEN = 256; + strcat(file,"/"); #endif - remain = MAX_LEN-1; // allow room for null - char file[MAX_LEN]; - - for (i = 0; i < num_paths; i++) { - strncpy(file, paths[i], remain); - remain -= strlen(paths[i]); - if (remain <=0) break; - strncat(file, "/", remain); - remain--; - if (remain <=0) break; - strncat(file, str, remain); - remain -= strlen(str); - if (remain <=0) break; - strncat(file, suffix, remain); + strcat(file, str); + strcat(file, suffix); #ifndef _WIN32 fd = open(file, O_RDONLY); #else fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #endif + free(file); if (fd != FD_ERR) { return fd; } From f3333649e023d2454fd390e786291ceb6d922997 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 25 Nov 2018 16:43:48 -0800 Subject: [PATCH 15/75] update copyrights --- LICENSE | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 8eb910f..9a37ffa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ The MIT License (MIT) Copyright (c) 2015 basil00 -Modifications Copyright (c) 2016-2017 by Jon Dart +Modifications Copyright (c) 2016-2018 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5b20015..2c2fcbd 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ License (C) 2013-2015 Ronald de Man (original code) (C) 2015 basil (new modifications) +(C) 2016-2018 Jon Dart (additional modifications) Ronald de Man's original code can be "redistributed and/or modified without restrictions". From d9a12a9c2e1cd2ab1d50c11858312711e03da13c Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Tue, 27 Nov 2018 17:27:17 -0800 Subject: [PATCH 16/75] Consistently use TB_USE_ATOMIC define to select usage of atomic variables. --- src/tbcore.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tbcore.h b/src/tbcore.h index 1c563b5..9b0b33e 100644 --- a/src/tbcore.h +++ b/src/tbcore.h @@ -127,7 +127,7 @@ struct TBEntry_pawn { char *data; uint64 key; uint64 mapping; -#if defined(__cplusplus) && (__cplusplus >= 201103L) +#if defined(__cplusplus) && defined(TB_USE_ATOMIC) std::atomic ready; #else ubyte ready; From 59f96c00e9762672eb36006b6aeb9b9338864ba3 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 9 Dec 2018 14:27:34 -0800 Subject: [PATCH 17/75] Fix issue 22 in basil00/Fathom/: properly disambiguate moves in move_to_str. Note: this still does not output complete SAN, because no indication of check or mate. --- src/apps/fathom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apps/fathom.c b/src/apps/fathom.c index e58406b..d5e5ed5 100644 --- a/src/apps/fathom.c +++ b/src/apps/fathom.c @@ -326,9 +326,9 @@ static void move_to_str(const struct pos *pos, unsigned move, char *str) *str++ = 'a' + f; else if (tb_pop_count(att) > 1) { - if (tb_pop_count(att & (BOARD_FILE_A >> f)) <= 1) + if (tb_pop_count(att & (BOARD_FILE_A >> f)) == 1) *str++ = 'a' + f; - else if (tb_pop_count(att & (BOARD_RANK_1 >> r)) <= 1) + else if (tb_pop_count(att & (BOARD_RANK_1 << (8*r))) == 1) *str++ = '1' + r; else { From 4a4b403640d3aa22c805430f2f9192cd80456cda Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 20 Jan 2019 16:13:49 -0800 Subject: [PATCH 18/75] Fix compilation when TB_CUSTOM_LSB is not defined. The previous code assumed gcc on x86_64. Now we use builtins if possible and otherwise a De Bruijn multiplication implementation. --- README.md | 2 +- src/tbprobe.c | 48 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2c2fcbd..46fd73d 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ License (C) 2013-2015 Ronald de Man (original code) (C) 2015 basil (new modifications) -(C) 2016-2018 Jon Dart (additional modifications) +(C) 2016-2019 Jon Dart (additional modifications) Ronald de Man's original code can be "redistributed and/or modified without restrictions". diff --git a/src/tbprobe.c b/src/tbprobe.c index ccb4b0d..6e50224 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1,7 +1,7 @@ /* * tbprobe.c * Copyright (c) 2013-2016 Ronald de Man - * Copyright (c) 2016-2017 Jon Dart + * Copyright (c) 2016-2017, 2019 Jon Dart * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,10 @@ #include #include +#if defined(_MSC_VER) +#include +#endif + #include "tbprobe.h" #define WHITE_KING (TB_WPAWN + 5) @@ -135,13 +139,45 @@ unsigned TB_LARGEST = 0; #ifdef TB_CUSTOM_LSB #define lsb(b) TB_CUSTOM_LSB(b) #else -static inline unsigned lsb(uint64_t b) -{ - size_t idx; - __asm__("bsfq %1, %0": "=r"(idx): "rm"(b)); - return idx; +#if defined(__GNUC__) +static inline unsigned lsb(uint64_t b) { + assert(b != 0); + return __builtin_ffsll(b)-1; +} +#elif defined(_MSC_VER) +static inline unsigned lsb(uint64_t b) { + assert(b != 0); + DWORD index; +#ifdef _WIN64 + _BitScanForward64(&index,b); + return (unsigned)index; +#else + if (b & 0xffffffffULL) { + _BitScanForward(&index,(unsigned long)(b & 0xffffffffULL)); + return (unsigned)index; + } + else { + _BitScanForward(&index,(unsigned long)(b >> 32)); + return 32 + (unsigned)index; + } +#endif +} +#else +/* not a compiler/architecture with recognized builtins */ +static uint32_t get_bit32(uint64_t x) { + return (uint32_t)(((int32_t)(x))&-((int32_t)(x))); } +static const unsigned MAGIC32 = 0xe89b2be; +static const uint32_t MagicTable32[32] = {31,0,9,1,10,20,13,2,7,11,21,23,17,14,3,25,30,8,19,12,6,22,16,24,29,18,5,15,28,4,27,26}; +static unsigned lsb(uint64_t b) { + if (b & 0xffffffffULL) + return MagicTable32[(get_bit32(b & 0xffffffffULL)*MAGIC32)>>27]; + else + return MagicTable32[(get_bit32(b >> 32)*MAGIC32)>>27]+32; +} +#endif #endif + #define square(r, f) (8 * (r) + (f)) #ifdef TB_KING_ATTACKS From 0439ca1b01163ef842ebb1ad635549bc2f930ae8 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 20 Jan 2019 16:20:10 -0800 Subject: [PATCH 19/75] Remove use of strdup (not defined in C99). Update copyright. --- src/apps/fathom.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/apps/fathom.c b/src/apps/fathom.c index d5e5ed5..8ddb433 100644 --- a/src/apps/fathom.c +++ b/src/apps/fathom.c @@ -1,6 +1,7 @@ /* * fathom.c - * (C) 2015 basil, all rights reserved, + * (C) 2015 basil, all rights reserved. + * (C) 2018-2019 Jon Dart, All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -567,7 +568,7 @@ int main(int argc, char **argv) {"test", 0, 0, OPTION_TEST}, {NULL, 0, 0, 0} }; - const char *path = NULL; + char *path = NULL; bool test = false; while (true) { @@ -578,8 +579,9 @@ int main(int argc, char **argv) switch (opt) { case OPTION_PATH: - path = strdup(optarg); + path = (char*)malloc(sizeof(char)*(strlen(optarg)+1)); assert(path != NULL); + strcpy(path,optarg); break; case OPTION_TEST: test = true; From 9f93dfbe9ccbbc833c878f21d501f26ead92a77f Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 26 Jan 2019 11:26:02 -0800 Subject: [PATCH 20/75] remove reference to binaries --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 46fd73d..52fd21b 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,6 @@ For more information, run the following command: fathom --help -Pre-compiled versions of `fathom` (for all platforms) are available from here: - -* https://github.com/basil00/Fathom/releases - Programming API --------------- From c6ff18937cef65c0c00660575265aa2792f2f6ab Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 30 Mar 2019 11:07:13 -0700 Subject: [PATCH 21/75] first cut at 7man support (from Cfish) --- src/apps/Makefile | 7 +- src/tbchess.c | 1060 +++++++++++++ src/tbcore.c | 1701 --------------------- src/tbcore.h | 197 --- src/tbprobe.c | 3680 ++++++++++++++++++++++++--------------------- src/tbprobe.h | 12 +- 6 files changed, 3000 insertions(+), 3657 deletions(-) create mode 100644 src/tbchess.c delete mode 100644 src/tbcore.c delete mode 100644 src/tbcore.h diff --git a/src/apps/Makefile b/src/apps/Makefile index 0c4030a..2e60c35 100644 --- a/src/apps/Makefile +++ b/src/apps/Makefile @@ -5,15 +5,16 @@ endif ifeq ($(UNAME),Darwin) TARGET=fathom.macosx endif -CC=clang +CC?=gcc STRIP=strip -CFLAGS=-std=c99 -O2 -Wall -D TB_NO_THREADS -I.. +#CFLAGS=-std=c99 -O2 -Wall -D TB_NO_THREADS -I.. +CFLAGS=-std=c99 -g -Wall -DDEBUG -DTB_NO_THREADS -I.. main: $(TARGET) fathom.linux: $(CC) $(CFLAGS) fathom.c ../tbprobe.c -o fathom.linux - $(STRIP) fathom.linux +# $(STRIP) fathom.linux fathom.macosx: $(CC) $(CFLAGS) fathom.c ../tbprobe.c -o fathom.macosx diff --git a/src/tbchess.c b/src/tbchess.c new file mode 100644 index 0000000..119ba0c --- /dev/null +++ b/src/tbchess.c @@ -0,0 +1,1060 @@ +#define TB_PAWN 1 +#define TB_KNIGHT 2 +#define TB_BISHOP 3 +#define TB_ROOK 4 +#define TB_QUEEN 5 +#define TB_KING 6 + +#define TB_WPAWN TB_PAWN +#define TB_BPAWN (TB_PAWN | 8) + +#define WHITE_KING (TB_WPAWN + 5) +#define WHITE_QUEEN (TB_WPAWN + 4) +#define WHITE_ROOK (TB_WPAWN + 3) +#define WHITE_BISHOP (TB_WPAWN + 2) +#define WHITE_KNIGHT (TB_WPAWN + 1) +#define WHITE_PAWN TB_WPAWN +#define BLACK_KING (TB_BPAWN + 5) +#define BLACK_QUEEN (TB_BPAWN + 4) +#define BLACK_ROOK (TB_BPAWN + 3) +#define BLACK_BISHOP (TB_BPAWN + 2) +#define BLACK_KNIGHT (TB_BPAWN + 1) +#define BLACK_PAWN TB_BPAWN + +#define PRIME_WHITE_QUEEN 11811845319353239651ull +#define PRIME_WHITE_ROOK 10979190538029446137ull +#define PRIME_WHITE_BISHOP 12311744257139811149ull +#define PRIME_WHITE_KNIGHT 15202887380319082783ull +#define PRIME_WHITE_PAWN 17008651141875982339ull +#define PRIME_BLACK_QUEEN 15484752644942473553ull +#define PRIME_BLACK_ROOK 18264461213049635989ull +#define PRIME_BLACK_BISHOP 15394650811035483107ull +#define PRIME_BLACK_KNIGHT 13469005675588064321ull +#define PRIME_BLACK_PAWN 11695583624105689831ull + +#define BOARD_RANK_EDGE 0x8181818181818181ull +#define BOARD_FILE_EDGE 0xFF000000000000FFull +#define BOARD_EDGE (BOARD_RANK_EDGE | BOARD_FILE_EDGE) +#define BOARD_RANK_1 0x00000000000000FFull +#define BOARD_FILE_A 0x8080808080808080ull + +#define KEY_KvK 0 + +#define BEST_NONE 0xFFFF +#define SCORE_ILLEGAL 0x7FFF + +#define rank(s) ((s) >> 3) +#define file(s) ((s) & 0x07) +#define board(s) ((uint64_t)1 << (s)) +#ifdef TB_CUSTOM_LSB +#define lsb(b) TB_CUSTOM_LSB(b) +#else +#if defined(__GNUC__) +static inline unsigned lsb(uint64_t b) { + assert(b != 0); + return __builtin_ffsll(b)-1; +} +#elif defined(_MSC_VER) +static inline unsigned lsb(uint64_t b) { + assert(b != 0); + DWORD index; +#ifdef _WIN64 + _BitScanForward64(&index,b); + return (unsigned)index; +#else + if (b & 0xffffffffULL) { + _BitScanForward(&index,(unsigned long)(b & 0xffffffffULL)); + return (unsigned)index; + } + else { + _BitScanForward(&index,(unsigned long)(b >> 32)); + return 32 + (unsigned)index; + } +#endif +} +#else +/* not a compiler/architecture with recognized builtins */ +static uint32_t get_bit32(uint64_t x) { + return (uint32_t)(((int32_t)(x))&-((int32_t)(x))); +} +static const unsigned MAGIC32 = 0xe89b2be; +static const uint32_t MagicTable32[32] = {31,0,9,1,10,20,13,2,7,11,21,23,17,14,3,25,30,8,19,12,6,22,16,24,29,18,5,15,28,4,27,26}; +static unsigned lsb(uint64_t b) { + if (b & 0xffffffffULL) + return MagicTable32[(get_bit32(b & 0xffffffffULL)*MAGIC32)>>27]; + else + return MagicTable32[(get_bit32(b >> 32)*MAGIC32)>>27]+32; +} +#endif +#endif + +#define square(r, f) (8 * (r) + (f)) + +#ifdef TB_KING_ATTACKS +#define king_attacks(s) TB_KING_ATTACKS(s) +#define king_attacks_init() /* NOP */ +#else /* TB_KING_ATTACKS */ + +static uint64_t king_attacks_table[64]; + +#define king_attacks(s) king_attacks_table[(s)] + +static void king_attacks_init(void) +{ + for (unsigned s = 0; s < 64; s++) + { + unsigned r = rank(s); + unsigned f = file(s); + uint64_t b = 0; + if (r != 0 && f != 0) + b |= board(square(r-1, f-1)); + if (r != 0) + b |= board(square(r-1, f)); + if (r != 0 && f != 7) + b |= board(square(r-1, f+1)); + if (f != 7) + b |= board(square(r, f+1)); + if (r != 7 && f != 7) + b |= board(square(r+1, f+1)); + if (r != 7) + b |= board(square(r+1, f)); + if (r != 7 && f != 0) + b |= board(square(r+1, f-1)); + if (f != 0) + b |= board(square(r, f-1)); + king_attacks_table[s] = b; + } +} + +#endif /* TB_KING_ATTACKS */ + +#ifdef TB_KNIGHT_ATTACKS +#define knight_attacks(s) TB_KNIGHT_ATTACKS(s) +#define knight_attacks_init() /* NOP */ +#else /* TB_KNIGHT_ATTACKS */ + +static uint64_t knight_attacks_table[64]; + +#define knight_attacks(s) knight_attacks_table[(s)] + +static void knight_attacks_init(void) +{ + for (unsigned s = 0; s < 64; s++) + { + int r1, r = rank(s); + int f1, f = file(s); + uint64_t b = 0; + r1 = r-1; f1 = f-2; + if (r1 >= 0 && f1 >= 0) + b |= board(square(r1, f1)); + r1 = r-1; f1 = f+2; + if (r1 >= 0 && f1 <= 7) + b |= board(square(r1, f1)); + r1 = r-2; f1 = f-1; + if (r1 >= 0 && f1 >= 0) + b |= board(square(r1, f1)); + r1 = r-2; f1 = f+1; + if (r1 >= 0 && f1 <= 7) + b |= board(square(r1, f1)); + r1 = r+1; f1 = f-2; + if (r1 <= 7 && f1 >= 0) + b |= board(square(r1, f1)); + r1 = r+1; f1 = f+2; + if (r1 <= 7 && f1 <= 7) + b |= board(square(r1, f1)); + r1 = r+2; f1 = f-1; + if (r1 <= 7 && f1 >= 0) + b |= board(square(r1, f1)); + r1 = r+2; f1 = f+1; + if (r1 <= 7 && f1 <= 7) + b |= board(square(r1, f1)); + knight_attacks_table[s] = b; + } +} + +#endif /* TB_KNIGHT_ATTACKS */ + +#ifdef TB_BISHOP_ATTACKS +#define bishop_attacks(s, occ) TB_BISHOP_ATTACKS(s, occ) +#define bishop_attacks_init() /* NOP */ +#else /* TB_BISHOP_ATTACKS */ + +static uint64_t diag_attacks_table[64][64]; +static uint64_t anti_attacks_table[64][64]; + +static const unsigned square2diag_table[64] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, + 14, 0, 1, 2, 3, 4, 5, 6, + 13, 14, 0, 1, 2, 3, 4, 5, + 12, 13, 14, 0, 1, 2, 3, 4, + 11, 12, 13, 14, 0, 1, 2, 3, + 10, 11, 12, 13, 14, 0, 1, 2, + 9, 10, 11, 12, 13, 14, 0, 1, + 8, 9, 10, 11, 12, 13, 14, 0 +}; + +static const unsigned square2anti_table[64] = +{ + 8, 9, 10, 11, 12, 13, 14, 0, + 9, 10, 11, 12, 13, 14, 0, 1, + 10, 11, 12, 13, 14, 0, 1, 2, + 11, 12, 13, 14, 0, 1, 2, 3, + 12, 13, 14, 0, 1, 2, 3, 4, + 13, 14, 0, 1, 2, 3, 4, 5, + 14, 0, 1, 2, 3, 4, 5, 6, + 0, 1, 2, 3, 4, 5, 6, 7 +}; + +static const uint64_t diag2board_table[15] = +{ + 0x8040201008040201ull, + 0x0080402010080402ull, + 0x0000804020100804ull, + 0x0000008040201008ull, + 0x0000000080402010ull, + 0x0000000000804020ull, + 0x0000000000008040ull, + 0x0000000000000080ull, + 0x0100000000000000ull, + 0x0201000000000000ull, + 0x0402010000000000ull, + 0x0804020100000000ull, + 0x1008040201000000ull, + 0x2010080402010000ull, + 0x4020100804020100ull, +}; + +static const uint64_t anti2board_table[15] = +{ + 0x0102040810204080ull, + 0x0204081020408000ull, + 0x0408102040800000ull, + 0x0810204080000000ull, + 0x1020408000000000ull, + 0x2040800000000000ull, + 0x4080000000000000ull, + 0x8000000000000000ull, + 0x0000000000000001ull, + 0x0000000000000102ull, + 0x0000000000010204ull, + 0x0000000001020408ull, + 0x0000000102040810ull, + 0x0000010204081020ull, + 0x0001020408102040ull, +}; + +static inline size_t diag2index(uint64_t b, unsigned d) +{ + b *= 0x0101010101010101ull; + b >>= 56; + b >>= 1; + return (size_t)b; +} + +static inline size_t anti2index(uint64_t b, unsigned a) +{ + return diag2index(b, a); +} + +#define diag(s) square2diag_table[(s)] +#define anti(s) square2anti_table[(s)] +#define diag2board(d) diag2board_table[(d)] +#define anti2board(a) anti2board_table[(a)] + +static uint64_t bishop_attacks(unsigned sq, uint64_t occ) +{ + occ &= ~board(sq); + unsigned d = diag(sq), a = anti(sq); + uint64_t d_occ = occ & (diag2board(d) & ~BOARD_EDGE); + uint64_t a_occ = occ & (anti2board(a) & ~BOARD_EDGE); + size_t d_idx = diag2index(d_occ, d); + size_t a_idx = anti2index(a_occ, a); + uint64_t d_attacks = diag_attacks_table[sq][d_idx]; + uint64_t a_attacks = anti_attacks_table[sq][a_idx]; + return d_attacks | a_attacks; +} + +static void bishop_attacks_init(void) +{ + for (unsigned idx = 0; idx < 64; idx++) + { + unsigned idx1 = idx << 1; + for (unsigned s = 0; s < 64; s++) + { + int r = rank(s); + int f = file(s); + uint64_t b = 0; + for (int i = -1; f + i >= 0 && r + i >= 0; i--) + { + unsigned occ = (1 << (f + i)); + b |= board(square(r + i, f + i)); + if (idx1 & occ) + break; + } + for (int i = 1; f + i <= 7 && r + i <= 7; i++) + { + unsigned occ = (1 << (f + i)); + b |= board(square(r + i, f + i)); + if (idx1 & occ) + break; + } + diag_attacks_table[s][idx] = b; + } + } + + for (unsigned idx = 0; idx < 64; idx++) + { + unsigned idx1 = idx << 1; + for (unsigned s = 0; s < 64; s++) + { + int r = rank(s); + int f = file(s); + uint64_t b = 0; + for (int i = -1; f + i >= 0 && r - i <= 7; i--) + { + unsigned occ = (1 << (f + i)); + b |= board(square(r - i, f + i)); + if (idx1 & occ) + break; + } + for (int i = 1; f + i <= 7 && r - i >= 0; i++) + { + unsigned occ = (1 << (f + i)); + b |= board(square(r - i, f + i)); + if (idx1 & occ) + break; + } + anti_attacks_table[s][idx] = b; + } + } +} + +#endif /* TB_BISHOP_ATTACKS */ + +#ifdef TB_ROOK_ATTACKS +#define rook_attacks(s, occ) TB_ROOK_ATTACKS(s, occ) +#define rook_attacks_init() /* NOP */ +#else /* TB_ROOK_ATTACKS */ + +static uint64_t rank_attacks_table[64][64]; +static uint64_t file_attacks_table[64][64]; + +static inline size_t rank2index(uint64_t b, unsigned r) +{ + b >>= (8 * r); + b >>= 1; + return (size_t)b; +} + +static inline size_t file2index(uint64_t b, unsigned f) +{ + b >>= f; + b *= 0x0102040810204080ull; + b >>= 56; + b >>= 1; + return (size_t)b; +} + +#define rank2board(r) (0xFFull << (8 * (r))) +#define file2board(f) (0x0101010101010101ull << (f)) + +static uint64_t rook_attacks(unsigned sq, uint64_t occ) +{ + occ &= ~board(sq); + unsigned r = rank(sq), f = file(sq); + uint64_t r_occ = occ & (rank2board(r) & ~BOARD_RANK_EDGE); + uint64_t f_occ = occ & (file2board(f) & ~BOARD_FILE_EDGE); + size_t r_idx = rank2index(r_occ, r); + size_t f_idx = file2index(f_occ, f); + uint64_t r_attacks = rank_attacks_table[sq][r_idx]; + uint64_t f_attacks = file_attacks_table[sq][f_idx]; + return r_attacks | f_attacks; +} + +static void rook_attacks_init(void) +{ + for (unsigned idx = 0; idx < 64; idx++) + { + unsigned idx1 = idx << 1, occ; + for (int f = 0; f <= 7; f++) + { + uint64_t b = 0; + if (f > 0) + { + int i = f-1; + do + { + occ = (1 << i); + b |= board(square(0, i)); + i--; + } + while (!(idx1 & occ) && i >= 0); + } + if (f < 7) + { + int i = f+1; + do + { + occ = (1 << i); + b |= board(square(0, i)); + i++; + } + while (!(idx1 & occ) && i <= 7); + } + for (int r = 0; r <= 7; r++) + { + rank_attacks_table[square(r, f)][idx] = b; + b <<= 8; + } + } + } + for (unsigned idx = 0; idx < 64; idx++) + { + unsigned idx1 = idx << 1, occ; + for (int r = 0; r <= 7; r++) + { + uint64_t b = 0; + if (r > 0) + { + int i = r-1; + do + { + occ = (1 << i); + b |= board(square(i, 0)); + i--; + } + while (!(idx1 & occ) && i >= 0); + } + if (r < 7) + { + int i = r+1; + do + { + occ = (1 << i); + b |= board(square(i, 0)); + i++; + } + while (!(idx1 & occ) && i <= 7); + } + for (int f = 0; f <= 7; f++) + { + file_attacks_table[square(r, f)][idx] = b; + b <<= 1; + } + } + } +} + +#endif /* TB_ROOK_ATTACKS */ + +#ifdef TB_QUEEN_ATTACKS +#define queen_attacks(s, occ) TB_QUEEN_ATTACKS(s, occ) +#else /* TB_QUEEN_ATTACKS */ +#define queen_attacks(s, occ) \ + (rook_attacks((s), (occ)) | bishop_attacks((s), (occ))) +#endif /* TB_QUEEN_ATTACKS */ + +#ifdef TB_PAWN_ATTACKS +#define pawn_attacks(s, c) TB_PAWN_ATTACKS(s, c) +#define pawn_attacks_init() /* NOP */ +#else /* TB_PAWN_ATTACKS */ + +static uint64_t pawn_attacks_table[2][64]; + +#define pawn_attacks(s, c) pawn_attacks_table[(c)][(s)] + +static void pawn_attacks_init(void) +{ + for (unsigned s = 0; s < 64; s++) + { + int r = rank(s); + int f = file(s); + + uint64_t b = 0; + if (r != 7) + { + if (f != 0) + b |= board(square(r+1, f-1)); + if (f != 7) + b |= board(square(r+1, f+1)); + } + pawn_attacks_table[1][s] = b; + + b = 0; + if (r != 0) + { + if (f != 0) + b |= board(square(r-1, f-1)); + if (f != 7) + b |= board(square(r-1, f+1)); + } + pawn_attacks_table[0][s] = b; + } +} + +#endif /* TB_PAWN_ATTACKS */ + +static void prt_str(const struct Pos *pos, char *str, bool mirror) +{ + uint64_t white = pos->white, black = pos->black; + int i; + if (mirror) + { + uint64_t tmp = white; + white = black; + black = tmp; + } + *str++ = 'K'; + for (i = popcount(white & pos->queens); i > 0; i--) + *str++ = 'Q'; + for (i = popcount(white & pos->rooks); i > 0; i--) + *str++ = 'R'; + for (i = popcount(white & pos->bishops); i > 0; i--) + *str++ = 'B'; + for (i = popcount(white & pos->knights); i > 0; i--) + *str++ = 'N'; + for (i = popcount(white & pos->pawns); i > 0; i--) + *str++ = 'P'; + *str++ = 'v'; + *str++ = 'K'; + for (i = popcount(black & pos->queens); i > 0; i--) + *str++ = 'Q'; + for (i = popcount(black & pos->rooks); i > 0; i--) + *str++ = 'R'; + for (i = popcount(black & pos->bishops); i > 0; i--) + *str++ = 'B'; + for (i = popcount(black & pos->knights); i > 0; i--) + *str++ = 'N'; + for (i = popcount(black & pos->pawns); i > 0; i--) + *str++ = 'P'; + *str++ = '\0'; +} + +/* + * Given a position, produce a 64-bit material signature key. + */ +static uint64_t calc_key(const struct Pos *pos, bool mirror) +{ + uint64_t white = pos->white, black = pos->black; + if (mirror) + { + uint64_t tmp = white; + white = black; + black = tmp; + } + return popcount(white & pos->queens) * PRIME_WHITE_QUEEN + + popcount(white & pos->rooks) * PRIME_WHITE_ROOK + + popcount(white & pos->bishops) * PRIME_WHITE_BISHOP + + popcount(white & pos->knights) * PRIME_WHITE_KNIGHT + + popcount(white & pos->pawns) * PRIME_WHITE_PAWN + + popcount(black & pos->queens) * PRIME_BLACK_QUEEN + + popcount(black & pos->rooks) * PRIME_BLACK_ROOK + + popcount(black & pos->bishops) * PRIME_BLACK_BISHOP + + popcount(black & pos->knights) * PRIME_BLACK_KNIGHT + + popcount(black & pos->pawns) * PRIME_BLACK_PAWN; +} + +static uint64_t calc_key_from_pcs(int *pcs, int mirror) +{ + mirror = (mirror? 8: 0); + return pcs[WHITE_QUEEN ^ mirror] * PRIME_WHITE_QUEEN + + pcs[WHITE_ROOK ^ mirror] * PRIME_WHITE_ROOK + + pcs[WHITE_BISHOP ^ mirror] * PRIME_WHITE_BISHOP + + pcs[WHITE_KNIGHT ^ mirror] * PRIME_WHITE_KNIGHT + + pcs[WHITE_PAWN ^ mirror] * PRIME_WHITE_PAWN + + pcs[BLACK_QUEEN ^ mirror] * PRIME_BLACK_QUEEN + + pcs[BLACK_ROOK ^ mirror] * PRIME_BLACK_ROOK + + pcs[BLACK_BISHOP ^ mirror] * PRIME_BLACK_BISHOP + + pcs[BLACK_KNIGHT ^ mirror] * PRIME_BLACK_KNIGHT + + pcs[BLACK_PAWN ^ mirror] * PRIME_BLACK_PAWN; +} + +static uint64_t get_pieces(const struct Pos *pos, uint8_t code) +{ + switch (code) + { + case WHITE_KING: + return pos->kings & pos->white; + case WHITE_QUEEN: + return pos->queens & pos->white; + case WHITE_ROOK: + return pos->rooks & pos->white; + case WHITE_BISHOP: + return pos->bishops & pos->white; + case WHITE_KNIGHT: + return pos->knights & pos->white; + case WHITE_PAWN: + return pos->pawns & pos->white; + case BLACK_KING: + return pos->kings & pos->black; + case BLACK_QUEEN: + return pos->queens & pos->black; + case BLACK_ROOK: + return pos->rooks & pos->black; + case BLACK_BISHOP: + return pos->bishops & pos->black; + case BLACK_KNIGHT: + return pos->knights & pos->black; + case BLACK_PAWN: + return pos->pawns & pos->black; + default: + return 0; // Dummy. + } +} + +#define make_move(promote, from, to) \ + ((((promote) & 0x7) << 12) | (((from) & 0x3F) << 6) | ((to) & 0x3F)) +#define move_from(move) \ + (((move) >> 6) & 0x3F) +#define move_to(move) \ + ((move) & 0x3F) +#define move_promotes(move) \ + (((move) >> 12) & 0x7) + +#define MAX_MOVES TB_MAX_MOVES +#define MOVE_STALEMATE 0xFFFF +#define MOVE_CHECKMATE 0xFFFE + +static uint16_t *add_move(uint16_t *moves, bool promotes, unsigned from, + unsigned to) +{ + if (!promotes) + *moves++ = make_move(TB_PROMOTES_NONE, from, to); + else + { + *moves++ = make_move(TB_PROMOTES_QUEEN, from, to); + *moves++ = make_move(TB_PROMOTES_KNIGHT, from, to); + *moves++ = make_move(TB_PROMOTES_ROOK, from, to); + *moves++ = make_move(TB_PROMOTES_BISHOP, from, to); + } + return moves; +} + +/* + * Generate all captures or promotions. + */ +static uint16_t *gen_captures_or_promotions(const struct Pos *pos, + uint16_t *moves) +{ + uint64_t occ = pos->white | pos->black; + uint64_t us = (pos->turn? pos->white: pos->black), + them = (pos->turn? pos->black: pos->white); + uint64_t b, att; + { + unsigned from = lsb(pos->kings & us); + for (att = king_attacks(from) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->queens; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = queen_attacks(from, occ) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->rooks; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = rook_attacks(from, occ) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->bishops; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = bishop_attacks(from, occ) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->knights; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = knight_attacks(from) & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->pawns; b; b = poplsb(b)) + { + unsigned from = lsb(b); + att = pawn_attacks(from, pos->turn); + if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) + { + unsigned to = pos->ep; + moves = add_move(moves, false, from, to); + } + for (att = att & them; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, + to); + } + if (pos->turn && rank(from) == 6) + { + unsigned to = from + 8; + if ((board(to) & occ) == 0) + moves = add_move(moves, true, from, to); + } + else if (!pos->turn && rank(from) == 1) + { + unsigned to = from - 8; + if ((board(to) & occ) == 0) + moves = add_move(moves, true, from, to); + } + } + return moves; +} + +/* + * Generate all non-capture pawn moves and promotions. + */ +static uint16_t *gen_pawn_quiets_or_promotions(const struct Pos *pos, + uint16_t *moves) +{ + uint64_t occ = pos->white | pos->black; + uint64_t us = (pos->turn? pos->white: pos->black); + uint64_t b, att; + + for (b = us & pos->pawns; b; b = poplsb(b)) + { + unsigned from = lsb(b); + unsigned next = from + (pos->turn? 8: -8); + att = 0; + if ((board(next) & occ) == 0) + { + att |= board(next); + unsigned next2 = from + (pos->turn? 16: -16); + if ((pos->turn? rank(from) == 1: rank(from) == 6) && + ((board(next2) & occ) == 0)) + att |= board(next2); + } + for (; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, + to); + } + } + return moves; +} + +/* + * Generate all en passant captures. + */ +static uint16_t *gen_pawn_ep_captures(const struct Pos *pos, uint16_t *moves) +{ + if (pos->ep == 0) + return moves; + uint64_t ep = board(pos->ep); + unsigned to = pos->ep; + uint64_t us = (pos->turn? pos->white: pos->black); + uint64_t b; + for (b = us & pos->pawns; b; b = poplsb(b)) + { + unsigned from = lsb(b); + if ((pawn_attacks(from, pos->turn) & ep) != 0) + moves = add_move(moves, false, from, to); + } + return moves; +} + +/* + * Generate all moves. + */ +static uint16_t *gen_moves(const struct Pos *pos, uint16_t *moves) +{ + uint64_t occ = pos->white | pos->black; + uint64_t us = (pos->turn? pos->white: pos->black), + them = (pos->turn? pos->black: pos->white); + uint64_t b, att; + + { + unsigned from = lsb(pos->kings & us); + for (att = king_attacks(from) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->queens; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = queen_attacks(from, occ) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->rooks; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = rook_attacks(from, occ) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->bishops; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = bishop_attacks(from, occ) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->knights; b; b = poplsb(b)) + { + unsigned from = lsb(b); + for (att = knight_attacks(from) & ~us; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, false, from, to); + } + } + for (b = us & pos->pawns; b; b = poplsb(b)) + { + unsigned from = lsb(b); + unsigned next = from + (pos->turn? 8: -8); + att = pawn_attacks(from, pos->turn); + if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) + { + unsigned to = pos->ep; + moves = add_move(moves, false, from, to); + } + att &= them; + if ((board(next) & occ) == 0) + { + att |= board(next); + unsigned next2 = from + (pos->turn? 16: -16); + if ((pos->turn? rank(from) == 1: rank(from) == 6) && + ((board(next2) & occ) == 0)) + att |= board(next2); + } + for (; att; att = poplsb(att)) + { + unsigned to = lsb(att); + moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, + to); + } + } + return moves; +} + +/* + * Test if the given move is an en passant capture. + */ +static bool is_en_passant(const struct Pos *pos, uint16_t move) +{ + uint16_t from = move_from(move); + uint16_t to = move_to(move); + uint64_t us = (pos->turn? pos->white: pos->black); + if (pos->ep == 0) + return false; + if (to != pos->ep) + return false; + if ((board(from) & us & pos->pawns) == 0) + return false; + return true; +} + +/* + * Test if the given position is legal. + * (Pawns on backrank? Can the king be captured?) + */ +static bool is_legal(const struct Pos *pos) +{ + uint64_t occ = pos->white | pos->black; + uint64_t us = (pos->turn? pos->black: pos->white), + them = (pos->turn? pos->white: pos->black); + uint64_t king = pos->kings & us; + if (!king) + return false; + unsigned sq = lsb(king); + if (king_attacks(sq) & (pos->kings & them)) + return false; + uint64_t ratt = rook_attacks(sq, occ); + uint64_t batt = bishop_attacks(sq, occ); + if (ratt & (pos->rooks & them)) + return false; + if (batt & (pos->bishops & them)) + return false; + if ((ratt | batt) & (pos->queens & them)) + return false; + if (knight_attacks(sq) & (pos->knights & them)) + return false; + if (pawn_attacks(sq, !pos->turn) & (pos->pawns & them)) + return false; + return true; +} + +/* + * Test if the king is in check. + */ +static bool is_check(const struct Pos *pos) +{ + uint64_t occ = pos->white | pos->black; + uint64_t us = (pos->turn? pos->white: pos->black), + them = (pos->turn? pos->black: pos->white); + uint64_t king = pos->kings & us; + unsigned sq = lsb(king); + uint64_t ratt = rook_attacks(sq, occ); + uint64_t batt = bishop_attacks(sq, occ); + if (ratt & (pos->rooks & them)) + return true; + if (batt & (pos->bishops & them)) + return true; + if ((ratt | batt) & (pos->queens & them)) + return true; + if (knight_attacks(sq) & (pos->knights & them)) + return true; + if (pawn_attacks(sq, pos->turn) & (pos->pawns & them)) + return true; + return false; +} + +/* + * Test if the position is valid. + */ +static bool is_valid(const struct Pos *pos) +{ + if (popcount(pos->kings) != 2) + return false; + if (popcount(pos->kings & pos->white) != 1) + return false; + if (popcount(pos->kings & pos->black) != 1) + return false; + if ((pos->white & pos->black) != 0) + return false; + if ((pos->kings & pos->queens) != 0) + return false; + if ((pos->kings & pos->rooks) != 0) + return false; + if ((pos->kings & pos->bishops) != 0) + return false; + if ((pos->kings & pos->knights) != 0) + return false; + if ((pos->kings & pos->pawns) != 0) + return false; + if ((pos->queens & pos->rooks) != 0) + return false; + if ((pos->queens & pos->bishops) != 0) + return false; + if ((pos->queens & pos->knights) != 0) + return false; + if ((pos->queens & pos->pawns) != 0) + return false; + if ((pos->rooks & pos->bishops) != 0) + return false; + if ((pos->rooks & pos->knights) != 0) + return false; + if ((pos->rooks & pos->pawns) != 0) + return false; + if ((pos->bishops & pos->knights) != 0) + return false; + if ((pos->bishops & pos->pawns) != 0) + return false; + if ((pos->knights & pos->pawns) != 0) + return false; + if (pos->pawns & BOARD_FILE_EDGE) + return false; + if ((pos->white | pos->black) != + (pos->kings | pos->queens | pos->rooks | pos->bishops | pos->knights | + pos->pawns)) + return false; + return is_legal(pos); +} + +#define do_bb_move(b, from, to) \ + (((b) & (~board(to)) & (~board(from))) | \ + ((((b) >> (from)) & 0x1) << (to))) + +static bool do_move(struct Pos *pos, const struct Pos *pos0, uint16_t move) +{ + unsigned from = move_from(move); + unsigned to = move_to(move); + unsigned promotes = move_promotes(move); + pos->turn = !pos0->turn; + pos->white = do_bb_move(pos0->white, from, to); + pos->black = do_bb_move(pos0->black, from, to); + pos->kings = do_bb_move(pos0->kings, from, to); + pos->queens = do_bb_move(pos0->queens, from, to); + pos->rooks = do_bb_move(pos0->rooks, from, to); + pos->bishops = do_bb_move(pos0->bishops, from, to); + pos->knights = do_bb_move(pos0->knights, from, to); + pos->pawns = do_bb_move(pos0->pawns, from, to); + pos->ep = 0; + if (promotes != TB_PROMOTES_NONE) + { + pos->pawns &= ~board(to); // Promotion + switch (promotes) + { + case TB_PROMOTES_QUEEN: + pos->queens |= board(to); break; + case TB_PROMOTES_ROOK: + pos->rooks |= board(to); break; + case TB_PROMOTES_BISHOP: + pos->bishops |= board(to); break; + case TB_PROMOTES_KNIGHT: + pos->knights |= board(to); break; + } + pos->rule50 = 0; + } + else if ((board(from) & pos0->pawns) != 0) + { + pos->rule50 = 0; // Pawn move + if (rank(from) == 1 && rank(to) == 3 && + (pawn_attacks(from+8, true) & pos0->pawns & pos0->black) != 0) + pos->ep = from+8; + else if (rank(from) == 6 && rank(to) == 4 && + (pawn_attacks(from-8, false) & pos0->pawns & pos0->white) != 0) + pos->ep = from-8; + else if (to == pos0->ep) + { + unsigned ep_to = (pos0->turn? to-8: to+8); + uint64_t ep_mask = ~board(ep_to); + pos->white &= ep_mask; + pos->black &= ep_mask; + pos->pawns &= ep_mask; + } + } + else if ((board(to) & (pos0->white | pos0->black)) != 0) + pos->rule50 = 0; // Capture + else + pos->rule50 = pos0->rule50 + 1; // Normal move + if (!is_legal(pos)) + return false; + return true; +} + +/* + * Test if the king is in checkmate. + */ +static bool is_mate(const struct Pos *pos) +{ + if (!is_check(pos)) + return false; + uint16_t moves0[MAX_MOVES]; + uint16_t *moves = moves0; + uint16_t *end = gen_moves(pos, moves); + for (; moves < end; moves++) + { + struct Pos pos1; + if (do_move(&pos1, pos, *moves)) + return false; + } + return true; +} + + diff --git a/src/tbcore.c b/src/tbcore.c deleted file mode 100644 index 8ee23aa..0000000 --- a/src/tbcore.c +++ /dev/null @@ -1,1701 +0,0 @@ -/* - * Copyright (c) 2011-2015 Ronald de Man - * Copyright (c) 2016-2017 Jon Dart - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -/* - tbcore.c contains engine-independent routines of the tablebase probing code. - This file should not need to much adaptation to add tablebase probing to - a particular engine, provided the engine is written in C or C++. -*/ - -#include -#include -#ifndef TB_NO_STDINT -#include -#endif -#include -#include -#include -#include -#ifndef _WIN32 -#include -#include -#endif -#include "tbcore.h" - -#if !defined(DECOMP64) && defined(_LP64) -// use 64-bit decompression if OS is 64-bit -// (appears not to work so commented out for now) -//#define DECOMP64 -#endif - -#define TBMAX_PIECE 254 -#define TBMAX_PAWN 256 -#define HSHMAX 5 - -// for variants where kings can connect and/or captured -// #define CONNECTED_KINGS - -#define Swap(a,b) {int tmp=a;a=b;b=tmp;} - -#define TB_PAWN 1 -#define TB_KNIGHT 2 -#define TB_BISHOP 3 -#define TB_ROOK 4 -#define TB_QUEEN 5 -#define TB_KING 6 - -#define TB_WPAWN TB_PAWN -#define TB_BPAWN (TB_PAWN | 8) - -#ifndef TB_NO_THREADS -static LOCK_T TB_MUTEX; -#endif - -#ifdef TB_CUSTOM_BSWAP32 -#define internal_bswap32(x) TB_CUSTOM_BSWAP32(x) -#else -#define internal_bswap32(x) __builtin_bswap32(x) -#endif - -#ifdef TB_CUSTOM_BSWAP64 -#define internal_bswap64(x) TB_CUSTOM_BSWAP64(x) -#else -#define internal_bswap64(x) __builtin_bswap64(x) -#endif - -static int initialized = 0; -static int num_paths = 0; -static char *path_string = NULL; -static char **paths = NULL; - -static int TBnum_piece, TBnum_pawn; -static struct TBEntry_piece TB_piece[TBMAX_PIECE]; -static struct TBEntry_pawn TB_pawn[TBMAX_PAWN]; - -static struct TBHashEntry TB_hash[1 << TBHASHBITS][HSHMAX]; - -#define DTZ_ENTRIES 64 - -static struct DTZTableEntry DTZ_table[DTZ_ENTRIES]; - -static void init_indices(void); -static uint64_t calc_key_from_pcs(int *pcs, int mirror); -static void free_wdl_entry(struct TBEntry *entry); -static void free_dtz_entry(struct TBEntry *entry); - -static FD open_tb(const char *str, const char *suffix) -{ - int i; - FD fd; - char *file; - - for (i = 0; i < num_paths; i++) { - file = (char*)malloc(strlen(paths[i]) + strlen(str) + - strlen(suffix) + 2); - strcpy(file, paths[i]); -#ifdef _WIN32 - strcat(file,"\\"); -#else - strcat(file,"/"); -#endif - strcat(file, str); - strcat(file, suffix); -#ifndef _WIN32 - fd = open(file, O_RDONLY); -#else - fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); -#endif - free(file); - if (fd != FD_ERR) { - return fd; - } - } - return FD_ERR; -} - -static void close_tb(FD fd) -{ -#ifndef _WIN32 - close(fd); -#else - CloseHandle(fd); -#endif -} - -static char *map_file(const char *name, const char *suffix, uint64 *mapping) -{ - FD fd = open_tb(name, suffix); - if (fd == FD_ERR) - return NULL; -#ifndef _WIN32 - struct stat statbuf; - if (fstat(fd, &statbuf)) { - perror("fstat"); - close_tb(fd); - return NULL; - } - *mapping = statbuf.st_size; - char *data = (char *)mmap(NULL, statbuf.st_size, PROT_READ, - MAP_SHARED, fd, 0); - if (data == (char *)(-1)) { - fprintf(stderr,"Could not mmap() %s.\n", name); - exit(1); - } -#else - DWORD size_low, size_high; - size_low = GetFileSize(fd, &size_high); -// *size = ((uint64)size_high) << 32 | ((uint64)size_low); - HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, - NULL); - if (map == NULL) { - fprintf(stderr,"CreateFileMapping() failed.\n"); - exit(1); - } - *mapping = (uint64)map; - char *data = (char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); - if (data == NULL) { - fprintf(stderr,"MapViewOfFile() failed, name = %s%s, error = %lu.\n", name, suffix, GetLastError()); - exit(1); - } -#endif - close_tb(fd); - return data; -} - -#ifndef _WIN32 -static void unmap_file(char *data, uint64 size) -{ - if (!data) return; - if (!munmap(data, size)) { - perror("munmap"); - } -} -#else -static void unmap_file(char *data, uint64 mapping) -{ - if (!data) return; - if (!UnmapViewOfFile(data)) { - fprintf(stderr, "unmap failed, error code %d", GetLastError()); - } - if (!CloseHandle((HANDLE)mapping)) { - fprintf(stderr, "CloseHandle failed, error code %d", GetLastError()); - } -} -#endif - -static void add_to_hash(struct TBEntry *ptr, uint64 key) -{ - int i, hshidx; - hshidx = key >> (64 - TBHASHBITS); - i = 0; - while (i < HSHMAX && TB_hash[hshidx][i].ptr) - i++; - if (i == HSHMAX) { - fprintf(stderr,"HSHMAX too low!\n"); - exit(1); - } else { - TB_hash[hshidx][i].key = key; - TB_hash[hshidx][i].ptr = ptr; - } -} - -static char pchr[] = {'K', 'Q', 'R', 'B', 'N', 'P'}; - -static void init_tb(char *str) -{ - FD fd; - struct TBEntry *entry; - int i, j, pcs[16]; - uint64 key, key2; - int color; - char *s; - - fd = open_tb(str, WDLSUFFIX); - if (fd == FD_ERR) return; - close_tb(fd); - - for (i = 0; i < 16; i++) - pcs[i] = 0; - color = 0; - for (s = str; *s; s++) - switch (*s) { - case 'P': - pcs[TB_PAWN | color]++; - break; - case 'N': - pcs[TB_KNIGHT | color]++; - break; - case 'B': - pcs[TB_BISHOP | color]++; - break; - case 'R': - pcs[TB_ROOK | color]++; - break; - case 'Q': - pcs[TB_QUEEN | color]++; - break; - case 'K': - pcs[TB_KING | color]++; - break; - case 'v': - color = 0x08; - break; - } - key = calc_key_from_pcs(pcs, 0); - key2 = calc_key_from_pcs(pcs, 1); - if (pcs[TB_WPAWN] + pcs[TB_BPAWN] == 0) { - if (TBnum_piece == TBMAX_PIECE) { - fprintf(stderr,"TBMAX_PIECE limit too low!\n"); - exit(1); - } - entry = (struct TBEntry *)&TB_piece[TBnum_piece++]; - } else { - if (TBnum_pawn == TBMAX_PAWN) { - fprintf(stderr,"TBMAX_PAWN limit too low!\n"); - exit(1); - } - entry = (struct TBEntry *)&TB_pawn[TBnum_pawn++]; - } - entry->key = key; - entry->ready = 0; - entry->num = 0; - for (i = 0; i < 16; i++) - entry->num += pcs[i]; - entry->symmetric = (key == key2); - entry->has_pawns = (pcs[TB_WPAWN] + pcs[TB_BPAWN] > 0); - if (entry->num > TB_LARGEST) - TB_LARGEST = entry->num; - - if (entry->has_pawns) { - struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; - ptr->pawns[0] = pcs[TB_WPAWN]; - ptr->pawns[1] = pcs[TB_BPAWN]; - if (pcs[TB_BPAWN] > 0 - && (pcs[TB_WPAWN] == 0 || pcs[TB_BPAWN] < pcs[TB_WPAWN])) { - ptr->pawns[0] = pcs[TB_BPAWN]; - ptr->pawns[1] = pcs[TB_WPAWN]; - } - } else { - struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; - for (i = 0, j = 0; i < 16; i++) - if (pcs[i] == 1) j++; - if (j >= 3) ptr->enc_type = 0; - else if (j == 2) ptr->enc_type = 2; - else { /* only for suicide */ - j = 16; - for (i = 0; i < 16; i++) { - if (pcs[i] < j && pcs[i] > 1) j = pcs[i]; - ptr->enc_type = 1 + j; - } - } - } - add_to_hash(entry, key); - if (key2 != key) add_to_hash(entry, key2); -} - -void init_tablebases(const char *path) -{ - char str[16]; - int i, j, k, l; - - if (initialized) { - free(path_string); - free(paths); - struct TBEntry *entry; - for (i = 0; i < TBnum_piece; i++) { - entry = (struct TBEntry *)&TB_piece[i]; - free_wdl_entry(entry); - } - for (i = 0; i < TBnum_pawn; i++) { - entry = (struct TBEntry *)&TB_pawn[i]; - free_wdl_entry(entry); - } - for (i = 0; i < DTZ_ENTRIES; i++) - if (DTZ_table[i].entry) - free_dtz_entry(DTZ_table[i].entry); - } else { - init_indices(); - initialized = 1; - } - - const char *p = path; - if (strlen(p) == 0 || !strcmp(p, "")) return; - path_string = (char *)malloc(strlen(p) + 1); - strcpy(path_string, p); - num_paths = 0; - for (i = 0;; i++) { - if (path_string[i] != SEP_CHAR) - num_paths++; - while (path_string[i] && path_string[i] != SEP_CHAR) - i++; - if (!path_string[i]) break; - path_string[i] = 0; - } - paths = (char **)malloc(num_paths * sizeof(char *)); - for (i = j = 0; i < num_paths; i++) { - while (!path_string[j]) j++; - paths[i] = &path_string[j]; - while (path_string[j]) j++; - } - - LOCK_INIT(TB_MUTEX); - - TBnum_piece = TBnum_pawn = 0; - TB_LARGEST = 0; - - for (i = 0; i < (1 << TBHASHBITS); i++) - for (j = 0; j < HSHMAX; j++) { - TB_hash[i][j].key = 0ULL; - TB_hash[i][j].ptr = NULL; - } - - for (i = 0; i < DTZ_ENTRIES; i++) - DTZ_table[i].entry = NULL; - - for (i = 1; i < 6; i++) { - sprintf(str, "K%cvK", pchr[i]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) { - sprintf(str, "K%cvK%c", pchr[i], pchr[j]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) { - sprintf(str, "K%c%cvK", pchr[i], pchr[j]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = 1; k < 6; k++) { - sprintf(str, "K%c%cvK%c", pchr[i], pchr[j], pchr[k]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = j; k < 6; k++) { - sprintf(str, "K%c%c%cvK", pchr[i], pchr[j], pchr[k]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = i; k < 6; k++) - for (l = (i == k) ? j : k; l < 6; l++) { - sprintf(str, "K%c%cvK%c%c", pchr[i], pchr[j], pchr[k], pchr[l]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = j; k < 6; k++) - for (l = 1; l < 6; l++) { - sprintf(str, "K%c%c%cvK%c", pchr[i], pchr[j], pchr[k], pchr[l]); - init_tb(str); - } - - for (i = 1; i < 6; i++) - for (j = i; j < 6; j++) - for (k = j; k < 6; k++) - for (l = k; l < 6; l++) { - sprintf(str, "K%c%c%c%cvK", pchr[i], pchr[j], pchr[k], pchr[l]); - init_tb(str); - } - -// printf("Found %d tablebases.\n", TBnum_piece + TBnum_pawn); -} - -static const signed char offdiag[] = { - 0,-1,-1,-1,-1,-1,-1,-1, - 1, 0,-1,-1,-1,-1,-1,-1, - 1, 1, 0,-1,-1,-1,-1,-1, - 1, 1, 1, 0,-1,-1,-1,-1, - 1, 1, 1, 1, 0,-1,-1,-1, - 1, 1, 1, 1, 1, 0,-1,-1, - 1, 1, 1, 1, 1, 1, 0,-1, - 1, 1, 1, 1, 1, 1, 1, 0 -}; - -static const ubyte triangle[] = { - 6, 0, 1, 2, 2, 1, 0, 6, - 0, 7, 3, 4, 4, 3, 7, 0, - 1, 3, 8, 5, 5, 8, 3, 1, - 2, 4, 5, 9, 9, 5, 4, 2, - 2, 4, 5, 9, 9, 5, 4, 2, - 1, 3, 8, 5, 5, 8, 3, 1, - 0, 7, 3, 4, 4, 3, 7, 0, - 6, 0, 1, 2, 2, 1, 0, 6 -}; - -static const ubyte invtriangle[] = { - 1, 2, 3, 10, 11, 19, 0, 9, 18, 27 -}; - -static const ubyte invdiag[] = { - 0, 9, 18, 27, 36, 45, 54, 63, - 7, 14, 21, 28, 35, 42, 49, 56 -}; - -static const ubyte flipdiag[] = { - 0, 8, 16, 24, 32, 40, 48, 56, - 1, 9, 17, 25, 33, 41, 49, 57, - 2, 10, 18, 26, 34, 42, 50, 58, - 3, 11, 19, 27, 35, 43, 51, 59, - 4, 12, 20, 28, 36, 44, 52, 60, - 5, 13, 21, 29, 37, 45, 53, 61, - 6, 14, 22, 30, 38, 46, 54, 62, - 7, 15, 23, 31, 39, 47, 55, 63 -}; - -static const ubyte lower[] = { - 28, 0, 1, 2, 3, 4, 5, 6, - 0, 29, 7, 8, 9, 10, 11, 12, - 1, 7, 30, 13, 14, 15, 16, 17, - 2, 8, 13, 31, 18, 19, 20, 21, - 3, 9, 14, 18, 32, 22, 23, 24, - 4, 10, 15, 19, 22, 33, 25, 26, - 5, 11, 16, 20, 23, 25, 34, 27, - 6, 12, 17, 21, 24, 26, 27, 35 -}; - -static const ubyte diag[] = { - 0, 0, 0, 0, 0, 0, 0, 8, - 0, 1, 0, 0, 0, 0, 9, 0, - 0, 0, 2, 0, 0, 10, 0, 0, - 0, 0, 0, 3, 11, 0, 0, 0, - 0, 0, 0, 12, 4, 0, 0, 0, - 0, 0, 13, 0, 0, 5, 0, 0, - 0, 14, 0, 0, 0, 0, 6, 0, - 15, 0, 0, 0, 0, 0, 0, 7 -}; - -static const ubyte flap[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 6, 12, 18, 18, 12, 6, 0, - 1, 7, 13, 19, 19, 13, 7, 1, - 2, 8, 14, 20, 20, 14, 8, 2, - 3, 9, 15, 21, 21, 15, 9, 3, - 4, 10, 16, 22, 22, 16, 10, 4, - 5, 11, 17, 23, 23, 17, 11, 5, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static const ubyte ptwist[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 47, 35, 23, 11, 10, 22, 34, 46, - 45, 33, 21, 9, 8, 20, 32, 44, - 43, 31, 19, 7, 6, 18, 30, 42, - 41, 29, 17, 5, 4, 16, 28, 40, - 39, 27, 15, 3, 2, 14, 26, 38, - 37, 25, 13, 1, 0, 12, 24, 36, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static const ubyte invflap[] = { - 8, 16, 24, 32, 40, 48, - 9, 17, 25, 33, 41, 49, - 10, 18, 26, 34, 42, 50, - 11, 19, 27, 35, 43, 51 -}; - -static const ubyte invptwist[] = { - 52, 51, 44, 43, 36, 35, 28, 27, 20, 19, 12, 11, - 53, 50, 45, 42, 37, 34, 29, 26, 21, 18, 13, 10, - 54, 49, 46, 41, 38, 33, 30, 25, 22, 17, 14, 9, - 55, 48, 47, 40, 39, 32, 31, 24, 23, 16, 15, 8 -}; - -static const ubyte file_to_file[] = { - 0, 1, 2, 3, 3, 2, 1, 0 -}; - -#ifndef CONNECTED_KINGS -static const short KK_idx[10][64] = { - { -1, -1, -1, 0, 1, 2, 3, 4, - -1, -1, -1, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, - 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57 }, - { 58, -1, -1, -1, 59, 60, 61, 62, - 63, -1, -1, -1, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, - 76, 77, 78, 79, 80, 81, 82, 83, - 84, 85, 86, 87, 88, 89, 90, 91, - 92, 93, 94, 95, 96, 97, 98, 99, - 100,101,102,103,104,105,106,107, - 108,109,110,111,112,113,114,115}, - {116,117, -1, -1, -1,118,119,120, - 121,122, -1, -1, -1,123,124,125, - 126,127,128,129,130,131,132,133, - 134,135,136,137,138,139,140,141, - 142,143,144,145,146,147,148,149, - 150,151,152,153,154,155,156,157, - 158,159,160,161,162,163,164,165, - 166,167,168,169,170,171,172,173 }, - {174, -1, -1, -1,175,176,177,178, - 179, -1, -1, -1,180,181,182,183, - 184, -1, -1, -1,185,186,187,188, - 189,190,191,192,193,194,195,196, - 197,198,199,200,201,202,203,204, - 205,206,207,208,209,210,211,212, - 213,214,215,216,217,218,219,220, - 221,222,223,224,225,226,227,228 }, - {229,230, -1, -1, -1,231,232,233, - 234,235, -1, -1, -1,236,237,238, - 239,240, -1, -1, -1,241,242,243, - 244,245,246,247,248,249,250,251, - 252,253,254,255,256,257,258,259, - 260,261,262,263,264,265,266,267, - 268,269,270,271,272,273,274,275, - 276,277,278,279,280,281,282,283 }, - {284,285,286,287,288,289,290,291, - 292,293, -1, -1, -1,294,295,296, - 297,298, -1, -1, -1,299,300,301, - 302,303, -1, -1, -1,304,305,306, - 307,308,309,310,311,312,313,314, - 315,316,317,318,319,320,321,322, - 323,324,325,326,327,328,329,330, - 331,332,333,334,335,336,337,338 }, - { -1, -1,339,340,341,342,343,344, - -1, -1,345,346,347,348,349,350, - -1, -1,441,351,352,353,354,355, - -1, -1, -1,442,356,357,358,359, - -1, -1, -1, -1,443,360,361,362, - -1, -1, -1, -1, -1,444,363,364, - -1, -1, -1, -1, -1, -1,445,365, - -1, -1, -1, -1, -1, -1, -1,446 }, - { -1, -1, -1,366,367,368,369,370, - -1, -1, -1,371,372,373,374,375, - -1, -1, -1,376,377,378,379,380, - -1, -1, -1,447,381,382,383,384, - -1, -1, -1, -1,448,385,386,387, - -1, -1, -1, -1, -1,449,388,389, - -1, -1, -1, -1, -1, -1,450,390, - -1, -1, -1, -1, -1, -1, -1,451 }, - {452,391,392,393,394,395,396,397, - -1, -1, -1, -1,398,399,400,401, - -1, -1, -1, -1,402,403,404,405, - -1, -1, -1, -1,406,407,408,409, - -1, -1, -1, -1,453,410,411,412, - -1, -1, -1, -1, -1,454,413,414, - -1, -1, -1, -1, -1, -1,455,415, - -1, -1, -1, -1, -1, -1, -1,456 }, - {457,416,417,418,419,420,421,422, - -1,458,423,424,425,426,427,428, - -1, -1, -1, -1, -1,429,430,431, - -1, -1, -1, -1, -1,432,433,434, - -1, -1, -1, -1, -1,435,436,437, - -1, -1, -1, -1, -1,459,438,439, - -1, -1, -1, -1, -1, -1,460,440, - -1, -1, -1, -1, -1, -1, -1,461 } -}; -#else -static const short PP_idx[10][64] = { - { 0, -1, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, - -1, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 61 }, - { 62, -1, -1, 63, 64, 65, -1, 66, - -1, 67, 68, 69, 70, 71, 72, -1, - 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, 95, 96, - -1, 97, 98, 99,100,101,102,103, - -1,104,105,106,107,108,109, -1, - 110, -1,111,112,113,114, -1,115 }, - {116, -1, -1, -1,117, -1, -1,118, - -1,119,120,121,122,123,124, -1, - -1,125,126,127,128,129,130, -1, - 131,132,133,134,135,136,137,138, - -1,139,140,141,142,143,144,145, - -1,146,147,148,149,150,151, -1, - -1,152,153,154,155,156,157, -1, - 158, -1, -1,159,160, -1, -1,161 }, - {162, -1, -1, -1, -1, -1, -1,163, - -1,164, -1,165,166,167,168, -1, - -1,169,170,171,172,173,174, -1, - -1,175,176,177,178,179,180, -1, - -1,181,182,183,184,185,186, -1, - -1, -1,187,188,189,190,191, -1, - -1,192,193,194,195,196,197, -1, - 198, -1, -1, -1, -1, -1, -1,199 }, - {200, -1, -1, -1, -1, -1, -1,201, - -1,202, -1, -1,203, -1,204, -1, - -1, -1,205,206,207,208, -1, -1, - -1,209,210,211,212,213,214, -1, - -1, -1,215,216,217,218,219, -1, - -1, -1,220,221,222,223, -1, -1, - -1,224, -1,225,226, -1,227, -1, - 228, -1, -1, -1, -1, -1, -1,229 }, - {230, -1, -1, -1, -1, -1, -1,231, - -1,232, -1, -1, -1, -1,233, -1, - -1, -1,234, -1,235,236, -1, -1, - -1, -1,237,238,239,240, -1, -1, - -1, -1, -1,241,242,243, -1, -1, - -1, -1,244,245,246,247, -1, -1, - -1,248, -1, -1, -1, -1,249, -1, - 250, -1, -1, -1, -1, -1, -1,251 }, - { -1, -1, -1, -1, -1, -1, -1,259, - -1,252, -1, -1, -1, -1,260, -1, - -1, -1,253, -1, -1,261, -1, -1, - -1, -1, -1,254,262, -1, -1, -1, - -1, -1, -1, -1,255, -1, -1, -1, - -1, -1, -1, -1, -1,256, -1, -1, - -1, -1, -1, -1, -1, -1,257, -1, - -1, -1, -1, -1, -1, -1, -1,258 }, - { -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1,268, -1, - -1, -1,263, -1, -1,269, -1, -1, - -1, -1, -1,264,270, -1, -1, -1, - -1, -1, -1, -1,265, -1, -1, -1, - -1, -1, -1, -1, -1,266, -1, -1, - -1, -1, -1, -1, -1, -1,267, -1, - -1, -1, -1, -1, -1, -1, -1, -1 }, - { -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1,274, -1, -1, - -1, -1, -1,271,275, -1, -1, -1, - -1, -1, -1, -1,272, -1, -1, -1, - -1, -1, -1, -1, -1,273, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 }, - { -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1,277, -1, -1, -1, - -1, -1, -1, -1,276, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 } -}; - -static const ubyte test45[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 0, 0, 0, 0, 0, - 1, 1, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static const ubyte mtwist[] = { - 15, 63, 55, 47, 40, 48, 56, 12, - 62, 11, 39, 31, 24, 32, 8, 57, - 54, 38, 7, 23, 16, 4, 33, 49, - 46, 30, 22, 3, 0, 17, 25, 41, - 45, 29, 21, 2, 1, 18, 26, 42, - 53, 37, 6, 20, 19, 5, 34, 50, - 61, 10, 36, 28, 27, 35, 9, 58, - 14, 60, 52, 44, 43, 51, 59, 13 -}; -#endif - -static int binomial[5][64]; -static int pawnidx[5][24]; -static int pfactor[5][4]; -#ifdef CONNECTED_KINGS -static int multidx[5][10]; -static int mfactor[5]; -#endif - -static void init_indices(void) -{ - int i, j, k; - -// binomial[k-1][n] = Bin(n, k) - for (i = 0; i < 5; i++) - for (j = 0; j < 64; j++) { - int f = j; - int l = 1; - for (k = 1; k <= i; k++) { - f *= (j - k); - l *= (k + 1); - } - binomial[i][j] = f / l; - } - - for (i = 0; i < 5; i++) { - int s = 0; - for (j = 0; j < 6; j++) { - pawnidx[i][j] = s; - s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; - } - pfactor[i][0] = s; - s = 0; - for (; j < 12; j++) { - pawnidx[i][j] = s; - s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; - } - pfactor[i][1] = s; - s = 0; - for (; j < 18; j++) { - pawnidx[i][j] = s; - s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; - } - pfactor[i][2] = s; - s = 0; - for (; j < 24; j++) { - pawnidx[i][j] = s; - s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; - } - pfactor[i][3] = s; - } - -#ifdef CONNECTED_KINGS - for (i = 0; i < 5; i++) { - int s = 0; - for (j = 0; j < 10; j++) { - multidx[i][j] = s; - s += (i == 0) ? 1 : binomial[i - 1][mtwist[invtriangle[j]]]; - } - mfactor[i] = s; - } -#endif -} - -#ifndef CONNECTED_KINGS -static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor) -{ - uint64 idx; - int i, j, k, m, l, p; - int n = ptr->num; - assert(n>=0 && n<7); - - if (pos[0] & 0x04) { - for (i = 0; i < n; i++) - pos[i] ^= 0x07; - } - if (pos[0] & 0x20) { - for (i = 0; i < n; i++) - pos[i] ^= 0x38; - } - - for (i = 0; i < n; i++) - if (offdiag[pos[i]]) break; - if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0) - for (i = 0; i < n; i++) - pos[i] = flipdiag[pos[i]]; - - switch (ptr->enc_type) { - - case 0: /* 111 */ - i = (pos[1] > pos[0]); - j = (pos[2] > pos[0]) + (pos[2] > pos[1]); - - if (offdiag[pos[0]]) - idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j); - else if (offdiag[pos[1]]) - idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j; - else if (offdiag[pos[2]]) - idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]]; - else - idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j); - i = 3; - break; - - case 1: /* K3 */ - j = (pos[2] > pos[0]) + (pos[2] > pos[1]); - - idx = KK_idx[triangle[pos[0]]][pos[1]]; - if (idx < 441) - idx = idx + 441 * (pos[2] - j); - else { - idx = 441*62 + (idx - 441) + 21 * lower[pos[2]]; - if (!offdiag[pos[2]]) - idx -= j * 21; - } - i = 3; - break; - - default: /* K2 */ - idx = KK_idx[triangle[pos[0]]][pos[1]]; - i = 2; - break; - } - idx *= factor[0]; - - for (; i < n;) { - int t = norm[i]; - for (j = i; j < i + t; j++) - for (k = j + 1; k < i + t; k++) - if (pos[j] > pos[k]) Swap(pos[j], pos[k]); - int s = 0; - for (m = i; m < i + t; m++) { - p = pos[m]; - for (l = 0, j = 0; l < i; l++) - j += (p > pos[l]); - assert(m-i >= 0 && m-i < 5); - assert(p-j >= 0 && p-j < 64); - s += binomial[m - i][p - j]; - } - idx += ((uint64)s) * ((uint64)factor[i]); - i += t; - } - - return idx; -} -#else -static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor) -{ - uint64 idx; - int i, j, k, m, l, p; - int n = ptr->num; - assert(n>=0 && n<7); - - if (ptr->enc_type < 3) { - if (pos[0] & 0x04) { - for (i = 0; i < n; i++) - pos[i] ^= 0x07; - } - if (pos[0] & 0x20) { - for (i = 0; i < n; i++) - pos[i] ^= 0x38; - } - - for (i = 0; i < n; i++) - if (offdiag[pos[i]]) break; - if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0) - for (i = 0; i < n; i++) - pos[i] = flipdiag[pos[i]]; - - switch (ptr->enc_type) { - - case 0: /* 111 */ - i = (pos[1] > pos[0]); - j = (pos[2] > pos[0]) + (pos[2] > pos[1]); - - if (offdiag[pos[0]]) - idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j); - else if (offdiag[pos[1]]) - idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j; - else if (offdiag[pos[2]]) - idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]]; - else - idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j); - i = 3; - break; - - case 2: /* 11 */ - i = (pos[1] > pos[0]); - - if (offdiag[pos[0]]) - idx = triangle[pos[0]] * 63 + (pos[1] - i); - else if (offdiag[pos[1]]) - idx = 6*63 + diag[pos[0]] * 28 + lower[pos[1]]; - else - idx = 6*63 + 4*28 + (diag[pos[0]]) * 7 + (diag[pos[1]] - i); - i = 2; - break; - - } - } else if (ptr->enc_type == 3) { /* 2, e.g. KKvK */ - if (triangle[pos[0]] > triangle[pos[1]]) - Swap(pos[0], pos[1]); - if (pos[0] & 0x04) - for (i = 0; i < n; i++) - pos[i] ^= 0x07; - if (pos[0] & 0x20) - for (i = 0; i < n; i++) - pos[i] ^= 0x38; - if (offdiag[pos[0]] > 0 || (offdiag[pos[0]] == 0 && offdiag[pos[1]] > 0)) - for (i = 0; i < n; i++) - pos[i] = flipdiag[pos[i]]; - if (test45[pos[1]] && triangle[pos[0]] == triangle[pos[1]]) { - Swap(pos[0], pos[1]); - for (i = 0; i < n; i++) - pos[i] = flipdiag[pos[i] ^ 0x38]; - } - idx = PP_idx[triangle[pos[0]]][pos[1]]; - i = 2; - } else { /* 3 and higher, e.g. KKKvK and KKKKvK */ - for (i = 1; i < norm[0]; i++) - if (triangle[pos[0]] > triangle[pos[i]]) - Swap(pos[0], pos[i]); - if (pos[0] & 0x04) - for (i = 0; i < n; i++) - pos[i] ^= 0x07; - if (pos[0] & 0x20) - for (i = 0; i < n; i++) - pos[i] ^= 0x38; - if (offdiag[pos[0]] > 0) - for (i = 0; i < n; i++) - pos[i] = flipdiag[pos[i]]; - for (i = 1; i < norm[0]; i++) - for (j = i + 1; j < norm[0]; j++) - if (mtwist[pos[i]] > mtwist[pos[j]]) - Swap(pos[i], pos[j]); - - idx = multidx[norm[0] - 1][triangle[pos[0]]]; - for (i = 1; i < norm[0]; i++) - idx += binomial[i - 1][mtwist[pos[i]]]; - } - idx *= factor[0]; - - for (; i < n;) { - int t = norm[i]; - for (j = i; j < i + t; j++) - for (k = j + 1; k < i + t; k++) - if (pos[j] > pos[k]) Swap(pos[j], pos[k]); - int s = 0; - for (m = i; m < i + t; m++) { - p = pos[m]; - for (l = 0, j = 0; l < i; l++) - j += (p > pos[l]); - s += binomial[m - i][p - j]; - } - idx += ((uint64)s) * ((uint64)factor[i]); - i += t; - } - - return idx; -} -#endif - -// determine file of leftmost pawn and sort pawns -static int pawn_file(struct TBEntry_pawn *ptr, int *pos) -{ - int i; - - for (i = 1; i < ptr->pawns[0]; i++) - if (flap[pos[0]] > flap[pos[i]]) - Swap(pos[0], pos[i]); - - return file_to_file[pos[0] & 0x07]; -} - -static uint64 encode_pawn(struct TBEntry_pawn *ptr, ubyte *norm, int *pos, int *factor) -{ - uint64 idx; - int i, j, k, m, s, t; - int n = ptr->num; - - if (pos[0] & 0x04) - for (i = 0; i < n; i++) - pos[i] ^= 0x07; - - for (i = 1; i < ptr->pawns[0]; i++) - for (j = i + 1; j < ptr->pawns[0]; j++) - if (ptwist[pos[i]] < ptwist[pos[j]]) - Swap(pos[i], pos[j]); - - t = ptr->pawns[0] - 1; - idx = pawnidx[t][flap[pos[0]]]; - for (i = t; i > 0; i--) - idx += binomial[t - i][ptwist[pos[i]]]; - idx *= factor[0]; - -// remaining pawns - i = ptr->pawns[0]; - t = i + ptr->pawns[1]; - if (t > i) { - for (j = i; j < t; j++) - for (k = j + 1; k < t; k++) - if (pos[j] > pos[k]) Swap(pos[j], pos[k]); - s = 0; - for (m = i; m < t; m++) { - int p = pos[m]; - for (k = 0, j = 0; k < i; k++) - j += (p > pos[k]); - s += binomial[m - i][p - j - 8]; - } - idx += ((uint64)s) * ((uint64)factor[i]); - i = t; - } - - for (; i < n;) { - t = norm[i]; - for (j = i; j < i + t; j++) - for (k = j + 1; k < i + t; k++) - if (pos[j] > pos[k]) Swap(pos[j], pos[k]); - s = 0; - for (m = i; m < i + t; m++) { - int p = pos[m]; - for (k = 0, j = 0; k < i; k++) - j += (p > pos[k]); - s += binomial[m - i][p - j]; - } - idx += ((uint64)s) * ((uint64)factor[i]); - i += t; - } - - return idx; -} - -static ubyte decompress_pairs(struct PairsData *d, uint64 index); - -// place k like pieces on n squares -static int subfactor(int k, int n) -{ - int i, f, l; - - f = n; - l = 1; - for (i = 1; i < k; i++) { - f *= n - i; - l *= i + 1; - } - - return f / l; -} - -static uint64 calc_factors_piece(int *factor, int num, int order, ubyte *norm, ubyte enc_type) -{ - int i, k, n; - uint64 f; -#ifndef CONNECTED_KINGS - static int pivfac[] = { 31332, 28056, 462 }; -#else - static int pivfac[] = { 31332, 0, 518, 278 }; -#endif - - n = 64 - norm[0]; - - f = 1; - for (i = norm[0], k = 0; i < num || k == order; k++) { - if (k == order) { - factor[0] = (int)f; -#ifndef CONNECTED_KINGS - f *= pivfac[enc_type]; -#else - if (enc_type < 4) - f *= pivfac[enc_type]; - else - f *= mfactor[enc_type - 2]; -#endif - } else { - factor[i] = (int)f; - f *= subfactor(norm[i], n); - n -= norm[i]; - i += norm[i]; - } - } - - return f; -} - -static uint64 calc_factors_pawn(int *factor, int num, int order, int order2, ubyte *norm, int file) -{ - int i, k, n; - uint64 f; - - i = norm[0]; - if (order2 < 0x0f) i += norm[i]; - n = 64 - i; - - f = 1; - for (k = 0; i < num || k == order || k == order2; k++) { - if (k == order) { - factor[0] = (int)f; - f *= pfactor[norm[0] - 1][file]; - } else if (k == order2) { - factor[norm[0]] = (int)f; - f *= subfactor(norm[norm[0]], 48 - norm[0]); - } else { - factor[i] = (int)f; - f *= subfactor(norm[i], n); - n -= norm[i]; - i += norm[i]; - } - } - - return f; -} - -static void set_norm_piece(struct TBEntry_piece *ptr, ubyte *norm, ubyte *pieces) -{ - int i, j; - - for (i = 0; i < ptr->num; i++) - norm[i] = 0; - - switch (ptr->enc_type) { - case 0: - norm[0] = 3; - break; - case 2: - norm[0] = 2; - break; - default: - norm[0] = ptr->enc_type - 1; - break; - } - - for (i = norm[0]; i < ptr->num; i += norm[i]) - for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) - norm[i]++; -} - -static void set_norm_pawn(struct TBEntry_pawn *ptr, ubyte *norm, ubyte *pieces) -{ - int i, j; - - for (i = 0; i < ptr->num; i++) - norm[i] = 0; - - norm[0] = ptr->pawns[0]; - if (ptr->pawns[1]) norm[ptr->pawns[0]] = ptr->pawns[1]; - - for (i = ptr->pawns[0] + ptr->pawns[1]; i < ptr->num; i += norm[i]) - for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) - norm[i]++; -} - -static void setup_pieces_piece(struct TBEntry_piece *ptr, unsigned char *data, uint64 *tb_size) -{ - int i; - int order; - - for (i = 0; i < ptr->num; i++) - ptr->pieces[0][i] = data[i + 1] & 0x0f; - order = data[0] & 0x0f; - set_norm_piece(ptr, ptr->norm[0], ptr->pieces[0]); - tb_size[0] = calc_factors_piece(ptr->factor[0], ptr->num, order, ptr->norm[0], ptr->enc_type); - - for (i = 0; i < ptr->num; i++) - ptr->pieces[1][i] = data[i + 1] >> 4; - order = data[0] >> 4; - set_norm_piece(ptr, ptr->norm[1], ptr->pieces[1]); - tb_size[1] = calc_factors_piece(ptr->factor[1], ptr->num, order, ptr->norm[1], ptr->enc_type); -} - -static void setup_pieces_piece_dtz(struct DTZEntry_piece *ptr, unsigned char *data, uint64 *tb_size) -{ - int i; - int order; - - for (i = 0; i < ptr->num; i++) - ptr->pieces[i] = data[i + 1] & 0x0f; - order = data[0] & 0x0f; - set_norm_piece((struct TBEntry_piece *)ptr, ptr->norm, ptr->pieces); - tb_size[0] = calc_factors_piece(ptr->factor, ptr->num, order, ptr->norm, ptr->enc_type); -} - -static void setup_pieces_pawn(struct TBEntry_pawn *ptr, unsigned char *data, uint64 *tb_size, int f) -{ - int i, j; - int order, order2; - - j = 1 + (ptr->pawns[1] > 0); - order = data[0] & 0x0f; - order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; - for (i = 0; i < ptr->num; i++) - ptr->file[f].pieces[0][i] = data[i + j] & 0x0f; - set_norm_pawn(ptr, ptr->file[f].norm[0], ptr->file[f].pieces[0]); - tb_size[0] = calc_factors_pawn(ptr->file[f].factor[0], ptr->num, order, order2, ptr->file[f].norm[0], f); - - order = data[0] >> 4; - order2 = ptr->pawns[1] ? (data[1] >> 4) : 0x0f; - for (i = 0; i < ptr->num; i++) - ptr->file[f].pieces[1][i] = data[i + j] >> 4; - set_norm_pawn(ptr, ptr->file[f].norm[1], ptr->file[f].pieces[1]); - tb_size[1] = calc_factors_pawn(ptr->file[f].factor[1], ptr->num, order, order2, ptr->file[f].norm[1], f); -} - -static void setup_pieces_pawn_dtz(struct DTZEntry_pawn *ptr, unsigned char *data, uint64 *tb_size, int f) -{ - int i, j; - int order, order2; - - j = 1 + (ptr->pawns[1] > 0); - order = data[0] & 0x0f; - order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; - for (i = 0; i < ptr->num; i++) - ptr->file[f].pieces[i] = data[i + j] & 0x0f; - set_norm_pawn((struct TBEntry_pawn *)ptr, ptr->file[f].norm, ptr->file[f].pieces); - tb_size[0] = calc_factors_pawn(ptr->file[f].factor, ptr->num, order, order2, ptr->file[f].norm, f); -} - -static void calc_symlen(struct PairsData *d, int s, char *tmp) -{ - int s1, s2; - - int w = *(int *)(d->sympat + 3 * s); - s2 = (w >> 12) & 0x0fff; - if (s2 == 0x0fff) - d->symlen[s] = 0; - else { - s1 = w & 0x0fff; - if (!tmp[s1]) calc_symlen(d, s1, tmp); - if (!tmp[s2]) calc_symlen(d, s2, tmp); - d->symlen[s] = d->symlen[s1] + d->symlen[s2] + 1; - } - tmp[s] = 1; -} - -static struct PairsData *setup_pairs(unsigned char *data, uint64 tb_size, uint64 *size, unsigned char **next, ubyte *flags, int wdl) -{ - struct PairsData *d; - int i; - - *flags = data[0]; - if (data[0] & 0x80) { - d = (struct PairsData *)malloc(sizeof(struct PairsData)); - d->idxbits = 0; - if (wdl) - d->min_len = data[1]; - else - d->min_len = 0; - *next = data + 2; - size[0] = size[1] = size[2] = 0; - return d; - } - - int blocksize = data[1]; - int idxbits = data[2]; - int real_num_blocks = *(uint32 *)(&data[4]); - int num_blocks = real_num_blocks + *(ubyte *)(&data[3]); - int max_len = data[8]; - int min_len = data[9]; - int h = max_len - min_len + 1; - int num_syms = *(ushort *)(&data[10 + 2 * h]); - d = (struct PairsData *)malloc(sizeof(struct PairsData) + (h - 1) * sizeof(base_t) + num_syms); - d->blocksize = blocksize; - d->idxbits = idxbits; - d->offset = (ushort *)(&data[10]); - d->symlen = ((ubyte *)d) + sizeof(struct PairsData) + (h - 1) * sizeof(base_t); - d->sympat = &data[12 + 2 * h]; - d->min_len = min_len; - *next = &data[12 + 2 * h + 3 * num_syms + (num_syms & 1)]; - - int num_indices = (int)((tb_size + (1ULL << idxbits) - 1) >> idxbits); - size[0] = 6ULL * num_indices; - size[1] = 2ULL * num_blocks; - size[2] = (1ULL << blocksize) * real_num_blocks; - - // char tmp[num_syms]; - char tmp[4096]; - for (i = 0; i < num_syms; i++) - tmp[i] = 0; - for (i = 0; i < num_syms; i++) - if (!tmp[i]) - calc_symlen(d, i, tmp); - - d->base[h - 1] = 0; - for (i = h - 2; i >= 0; i--) - d->base[i] = (d->base[i + 1] + d->offset[i] - d->offset[i + 1]) / 2; -#ifdef DECOMP64 - for (i = 0; i < h; i++) - d->base[i] <<= 64 - (min_len + i); -#else - for (i = 0; i < h; i++) - d->base[i] <<= 32 - (min_len + i); -#endif - - d->offset -= d->min_len; - - return d; -} - -static int init_table_wdl(struct TBEntry *entry, char *str) -{ - ubyte *next; - int f, s; - uint64 tb_size[8]; - uint64 size[8 * 3]; - ubyte flags; - - // first mmap the table into memory - entry->data = map_file(str, WDLSUFFIX, &entry->mapping); - if (!entry->data) { - fprintf(stderr,"Could not find %s" WDLSUFFIX "\n", str); - return 0; - } - - ubyte *data = (ubyte *)entry->data; - if (((uint32 *)data)[0] != WDL_MAGIC) { - fprintf(stderr,"Corrupted table.\n"); - unmap_file(entry->data, entry->mapping); - entry->data = 0; - return 0; - } - - int split = data[4] & 0x01; - int files = data[4] & 0x02 ? 4 : 1; - - data += 5; - - if (!entry->has_pawns) { - struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; - setup_pieces_piece(ptr, data, &tb_size[0]); - data += ptr->num + 1; - data += ((uintptr_t)data) & 0x01; - - ptr->precomp[0] = setup_pairs(data, tb_size[0], &size[0], &next, &flags, 1); - data = next; - if (split) { - ptr->precomp[1] = setup_pairs(data, tb_size[1], &size[3], &next, &flags, 1); - data = next; - } else - ptr->precomp[1] = NULL; - - ptr->precomp[0]->indextable = (char *)data; - data += size[0]; - if (split) { - ptr->precomp[1]->indextable = (char *)data; - data += size[3]; - } - - ptr->precomp[0]->sizetable = (ushort *)data; - data += size[1]; - if (split) { - ptr->precomp[1]->sizetable = (ushort *)data; - data += size[4]; - } - - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->precomp[0]->data = data; - data += size[2]; - if (split) { - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->precomp[1]->data = data; - } - } else { - struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; - s = 1 + (ptr->pawns[1] > 0); - for (f = 0; f < 4; f++) { - setup_pieces_pawn((struct TBEntry_pawn *)ptr, data, &tb_size[2 * f], f); - data += ptr->num + s; - } - data += ((uintptr_t)data) & 0x01; - - for (f = 0; f < files; f++) { - ptr->file[f].precomp[0] = setup_pairs(data, tb_size[2 * f], &size[6 * f], &next, &flags, 1); - data = next; - if (split) { - ptr->file[f].precomp[1] = setup_pairs(data, tb_size[2 * f + 1], &size[6 * f + 3], &next, &flags, 1); - data = next; - } else - ptr->file[f].precomp[1] = NULL; - } - - for (f = 0; f < files; f++) { - ptr->file[f].precomp[0]->indextable = (char *)data; - data += size[6 * f]; - if (split) { - ptr->file[f].precomp[1]->indextable = (char *)data; - data += size[6 * f + 3]; - } - } - - for (f = 0; f < files; f++) { - ptr->file[f].precomp[0]->sizetable = (ushort *)data; - data += size[6 * f + 1]; - if (split) { - ptr->file[f].precomp[1]->sizetable = (ushort *)data; - data += size[6 * f + 4]; - } - } - - for (f = 0; f < files; f++) { - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->file[f].precomp[0]->data = data; - data += size[6 * f + 2]; - if (split) { - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->file[f].precomp[1]->data = data; - data += size[6 * f + 5]; - } - } - } - - return 1; -} - -static int init_table_dtz(struct TBEntry *entry) -{ - ubyte *data = (ubyte *)entry->data; - ubyte *next; - int f, s; - uint64 tb_size[4]; - uint64 size[4 * 3]; - - if (!data) - return 0; - - if (((uint32 *)data)[0] != DTZ_MAGIC) { - fprintf(stderr,"Corrupted table.\n"); - return 0; - } - - int files = data[4] & 0x02 ? 4 : 1; - - data += 5; - - if (!entry->has_pawns) { - struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; - setup_pieces_piece_dtz(ptr, data, &tb_size[0]); - data += ptr->num + 1; - data += ((uintptr_t)data) & 0x01; - - ptr->precomp = setup_pairs(data, tb_size[0], &size[0], &next, &(ptr->flags), 0); - data = next; - - ptr->map = data; - if (ptr->flags & 2) { - int i; - for (i = 0; i < 4; i++) { - ptr->map_idx[i] = (ushort)(data + 1 - ptr->map); - data += 1 + data[0]; - } - data += ((uintptr_t)data) & 0x01; - } - - ptr->precomp->indextable = (char *)data; - data += size[0]; - - ptr->precomp->sizetable = (ushort *)data; - data += size[1]; - - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->precomp->data = data; - data += size[2]; - } else { - struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; - s = 1 + (ptr->pawns[1] > 0); - for (f = 0; f < 4; f++) { - setup_pieces_pawn_dtz(ptr, data, &tb_size[f], f); - data += ptr->num + s; - } - data += ((uintptr_t)data) & 0x01; - - for (f = 0; f < files; f++) { - ptr->file[f].precomp = setup_pairs(data, tb_size[f], &size[3 * f], &next, &(ptr->flags[f]), 0); - data = next; - } - - ptr->map = data; - for (f = 0; f < files; f++) { - if (ptr->flags[f] & 2) { - int i; - for (i = 0; i < 4; i++) { - ptr->map_idx[f][i] = (ushort)(data + 1 - ptr->map); - data += 1 + data[0]; - } - } - } - data += ((uintptr_t)data) & 0x01; - - for (f = 0; f < files; f++) { - ptr->file[f].precomp->indextable = (char *)data; - data += size[3 * f]; - } - - for (f = 0; f < files; f++) { - ptr->file[f].precomp->sizetable = (ushort *)data; - data += size[3 * f + 1]; - } - - for (f = 0; f < files; f++) { - data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); - ptr->file[f].precomp->data = data; - data += size[3 * f + 2]; - } - } - - return 1; -} - -static ubyte decompress_pairs(struct PairsData *d, uint64 idx) -{ - if (!d->idxbits) - return d->min_len; - - uint32 mainidx = (uint32)(idx >> d->idxbits); - int litidx = (int)(idx & (((uint64)1 << d->idxbits) - 1)) - ((uint64)1 << (d->idxbits - 1)); - uint32 block = *(uint32 *)(d->indextable + 6 * mainidx); - litidx += *(ushort *)(d->indextable + 6 * mainidx + 4); - if (litidx < 0) { - do { - litidx += d->sizetable[--block] + 1; - } while (litidx < 0); - } else { - while (litidx > d->sizetable[block]) - litidx -= d->sizetable[block++] + 1; - } - - uint32 *ptr = (uint32 *)(d->data + (block << d->blocksize)); - - int m = d->min_len; - ushort *offset = d->offset; - base_t *base = d->base - m; - ubyte *symlen = d->symlen; - int sym, bitcnt; - -#ifdef DECOMP64 - uint64 code = internal_bswap64(*((uint64 *)ptr)); - ptr += 2; - bitcnt = 0; // number of "empty bits" in code - for (;;) { - int l = m; - while (code < base[l]) l++; - sym = offset[l] + ((code - base[l]) >> (64 - l)); - if (litidx < (int)symlen[sym] + 1) break; - litidx -= (int)symlen[sym] + 1; - code <<= l; - bitcnt += l; - if (bitcnt >= 32) { - bitcnt -= 32; - uint32 data = *ptr++; - code |= ((uint64)(internal_bswap32(data))) << bitcnt; - } - } -#else - uint32 next = 0; - uint32 data = *ptr++; - uint32 code = internal_bswap32(data); - bitcnt = 0; // number of bits in next - for (;;) { - int l = m; - while (code < base[l]) l++; - sym = offset[l] + ((code - base[l]) >> (32 - l)); - if (litidx < (int)symlen[sym] + 1) break; - litidx -= (int)symlen[sym] + 1; - code <<= l; - if (bitcnt < l) { - if (bitcnt) { - code |= (next >> (32 - l)); - l -= bitcnt; - } - data = *ptr++; - next = internal_bswap32(data); - bitcnt = 32; - } - code |= (next >> (32 - l)); - next <<= l; - bitcnt -= l; - } -#endif - - ubyte *sympat = d->sympat; - while (symlen[sym] != 0) { - int w = *(int *)(sympat + 3 * sym); - int s1 = w & 0x0fff; - if (litidx < (int)symlen[s1] + 1) - sym = s1; - else { - litidx -= (int)symlen[s1] + 1; - sym = (w >> 12) & 0x0fff; - } - } - - return *(sympat + 3 * sym); -} - -void load_dtz_table(char *str, uint64 key1, uint64 key2) -{ - int i; - struct TBEntry *ptr, *ptr3; - struct TBHashEntry *ptr2; - - DTZ_table[0].key1 = key1; - DTZ_table[0].key2 = key2; - DTZ_table[0].entry = NULL; - - // find corresponding WDL entry - ptr2 = TB_hash[key1 >> (64 - TBHASHBITS)]; - for (i = 0; i < HSHMAX; i++) - if (ptr2[i].key == key1) break; - if (i == HSHMAX) return; - ptr = ptr2[i].ptr; - - ptr3 = (struct TBEntry *)malloc(ptr->has_pawns - ? sizeof(struct DTZEntry_pawn) - : sizeof(struct DTZEntry_piece)); - - ptr3->data = map_file(str, DTZSUFFIX, &ptr3->mapping); - ptr3->key = ptr->key; - ptr3->num = ptr->num; - ptr3->symmetric = ptr->symmetric; - ptr3->has_pawns = ptr->has_pawns; - if (ptr3->has_pawns) { - struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr3; - entry->pawns[0] = ((struct TBEntry_pawn *)ptr)->pawns[0]; - entry->pawns[1] = ((struct TBEntry_pawn *)ptr)->pawns[1]; - } else { - struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr3; - entry->enc_type = ((struct TBEntry_piece *)ptr)->enc_type; - } - if (!init_table_dtz(ptr3)) - free(ptr3); - else - DTZ_table[0].entry = ptr3; -} - -static void free_wdl_entry(struct TBEntry *entry) -{ - unmap_file(entry->data, entry->mapping); - entry->data = NULL; entry->mapping = 0; - if (!entry->has_pawns) { - struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; - free(ptr->precomp[0]); - if (ptr->precomp[1]) - free(ptr->precomp[1]); - ptr->precomp[0] = ptr->precomp[1] = NULL; - } else { - struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; - int f; - for (f = 0; f < 4; f++) { - free(ptr->file[f].precomp[0]); - if (ptr->file[f].precomp[1]) - free(ptr->file[f].precomp[1]); - ptr->file[f].precomp[0] = ptr->file[f].precomp[1] = NULL; - } - } -} - -static void free_dtz_entry(struct TBEntry *entry) -{ - unmap_file(entry->data, entry->mapping); - entry->data = NULL; entry->mapping = 0; - if (!entry->has_pawns) { - struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; - free(ptr->precomp); - ptr->precomp = NULL; - } else { - struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; - int f; - for (f = 0; f < 4; f++) { - free(ptr->file[f].precomp); - ptr->file[f].precomp = NULL; - } - } - free(entry); -} - -static int wdl_to_map[5] = { 1, 3, 0, 2, 0 }; -static ubyte pa_flags[5] = { 8, 0, 0, 0, 4 }; - diff --git a/src/tbcore.h b/src/tbcore.h deleted file mode 100644 index 9b0b33e..0000000 --- a/src/tbcore.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - Copyright (c) 2011-2015 Ronald de Man - Copyright 2017-2018 Jon Dart -*/ - -#ifndef TBCORE_H -#define TBCORE_H - -#if defined(__cplusplus) && defined(TB_USE_ATOMIC) -#include -#endif - -#ifndef _WIN32 -#include -#define SEP_CHAR ':' -#define FD int -#define FD_ERR -1 -#else -#include -#define SEP_CHAR ';' -#define FD HANDLE -#define FD_ERR INVALID_HANDLE_VALUE -#endif - -#ifndef TB_NO_THREADS -#if defined(__cplusplus) && (__cplusplus >= 201103L) - -#include -#define LOCK_T std::mutex -#define LOCK_INIT(x) -#define LOCK(x) x.lock() -#define UNLOCK(x) x.unlock() - -#else -#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 - -#endif -#else /* TB_NO_THREADS */ -#define LOCK_T int -#define LOCK_INIT(x) /* NOP */ -#define LOCK(x) /* NOP */ -#define UNLOCK(x) /* NOP */ -#endif - -#define WDLSUFFIX ".rtbw" -#define DTZSUFFIX ".rtbz" -#define WDLDIR "RTBWDIR" -#define DTZDIR "RTBZDIR" -#define TBPIECES 6 - -#define WDL_MAGIC 0x5d23e871 -#define DTZ_MAGIC 0xa50c66d7 - -#define TBHASHBITS 10 - -typedef unsigned long long uint64; -typedef unsigned int uint32; -typedef unsigned char ubyte; -typedef unsigned short ushort; - -struct TBHashEntry; - -#ifdef DECOMP64 -typedef uint64 base_t; -#else -typedef uint32 base_t; -#endif - -struct PairsData { - char *indextable; - ushort *sizetable; - ubyte *data; - ushort *offset; - ubyte *symlen; - ubyte *sympat; - int blocksize; - int idxbits; - int min_len; - base_t base[1]; // C++ complains about base[]... -}; - -struct TBEntry { - char *data; - uint64 key; - uint64 mapping; - ubyte ready; - ubyte num; - ubyte symmetric; - ubyte has_pawns; -} -#ifdef __GNUC__ -__attribute__((__may_alias__)); -#else -; -#endif - -struct TBEntry_piece { - char *data; - uint64 key; - uint64 mapping; -#if defined(__cplusplus) && defined(TB_USE_ATOMIC) - std::atomic ready; -#else - ubyte ready; -#endif - ubyte num; - ubyte symmetric; - ubyte has_pawns; - ubyte enc_type; - struct PairsData *precomp[2]; - int factor[2][TBPIECES]; - ubyte pieces[2][TBPIECES]; - ubyte norm[2][TBPIECES]; -}; - -struct TBEntry_pawn { - char *data; - uint64 key; - uint64 mapping; -#if defined(__cplusplus) && defined(TB_USE_ATOMIC) - std::atomic ready; -#else - ubyte ready; -#endif - ubyte num; - ubyte symmetric; - ubyte has_pawns; - ubyte pawns[2]; - struct { - struct PairsData *precomp[2]; - int factor[2][TBPIECES]; - ubyte pieces[2][TBPIECES]; - ubyte norm[2][TBPIECES]; - } file[4]; -}; - -struct DTZEntry_piece { - char *data; - uint64 key; - uint64 mapping; - ubyte ready; - ubyte num; - ubyte symmetric; - ubyte has_pawns; - ubyte enc_type; - struct PairsData *precomp; - int factor[TBPIECES]; - ubyte pieces[TBPIECES]; - ubyte norm[TBPIECES]; - ubyte flags; // accurate, mapped, side - ushort map_idx[4]; - ubyte *map; -}; - -struct DTZEntry_pawn { - char *data; - uint64 key; - uint64 mapping; - ubyte ready; - ubyte num; - ubyte symmetric; - ubyte has_pawns; - ubyte pawns[2]; - struct { - struct PairsData *precomp; - int factor[TBPIECES]; - ubyte pieces[TBPIECES]; - ubyte norm[TBPIECES]; - } file[4]; - ubyte flags[4]; - ushort map_idx[4][4]; - ubyte *map; -}; - -struct TBHashEntry { - uint64 key; - struct TBEntry *ptr; -}; - -struct DTZTableEntry { - uint64 key1; - uint64 key2; - struct TBEntry *entry; -}; - -#endif - diff --git a/src/tbprobe.c b/src/tbprobe.c index 6e50224..fba24e1 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1,72 +1,73 @@ /* - * tbprobe.c - * Copyright (c) 2013-2016 Ronald de Man - * Copyright (c) 2016-2017, 2019 Jon Dart - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ + Copyright (c) 2013-2018 Ronald de Man + Copyright (c) 2019 Jon Dart + This file may be redistributed and/or modified without restrictions. +*/ #include +#include +#include #include #include +#include +#ifdef TB_NO_STDBOOL +#typedef uint8 bool +#else +#include +#endif +#include "tbprobe.h" -#if defined(_MSC_VER) -#include +#define TB_PIECES 7 +#define TB_HASHBITS (TB_PIECES < 7 ? 11 : 12) +#define TB_MAX_PIECE (TB_PIECES < 7 ? 254 : 650) +#define TB_MAX_PAWN (TB_PIECES < 7 ? 256 : 861) + +#ifndef _WIN32 +#include +#define SEP_CHAR ':' +#define FD int +#define FD_ERR -1 +typedef size_t map_t; +#else +#include +#define SEP_CHAR ';' +#define FD HANDLE +#define FD_ERR INVALID_HANDLE_VALUE +typedef HANDLE map_t; #endif -#include "tbprobe.h" +// Threading support +#ifndef TB_NO_THREADS +#if defined(__cplusplus) && (__cplusplus >= 201103L) + +#include +#define LOCK_T std::mutex +#define LOCK_INIT(x) +#define LOCK(x) x.lock() +#define UNLOCK(x) x.unlock() + +#else +#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 -#define WHITE_KING (TB_WPAWN + 5) -#define WHITE_QUEEN (TB_WPAWN + 4) -#define WHITE_ROOK (TB_WPAWN + 3) -#define WHITE_BISHOP (TB_WPAWN + 2) -#define WHITE_KNIGHT (TB_WPAWN + 1) -#define WHITE_PAWN TB_WPAWN -#define BLACK_KING (TB_BPAWN + 5) -#define BLACK_QUEEN (TB_BPAWN + 4) -#define BLACK_ROOK (TB_BPAWN + 3) -#define BLACK_BISHOP (TB_BPAWN + 2) -#define BLACK_KNIGHT (TB_BPAWN + 1) -#define BLACK_PAWN TB_BPAWN - -#define PRIME_WHITE_QUEEN 11811845319353239651ull -#define PRIME_WHITE_ROOK 10979190538029446137ull -#define PRIME_WHITE_BISHOP 12311744257139811149ull -#define PRIME_WHITE_KNIGHT 15202887380319082783ull -#define PRIME_WHITE_PAWN 17008651141875982339ull -#define PRIME_BLACK_QUEEN 15484752644942473553ull -#define PRIME_BLACK_ROOK 18264461213049635989ull -#define PRIME_BLACK_BISHOP 15394650811035483107ull -#define PRIME_BLACK_KNIGHT 13469005675588064321ull -#define PRIME_BLACK_PAWN 11695583624105689831ull - -#define BOARD_RANK_EDGE 0x8181818181818181ull -#define BOARD_FILE_EDGE 0xFF000000000000FFull -#define BOARD_EDGE (BOARD_RANK_EDGE | BOARD_FILE_EDGE) -#define BOARD_RANK_1 0x00000000000000FFull -#define BOARD_FILE_A 0x8080808080808080ull - -#define KEY_KvK 0 - -#define BEST_NONE 0xFFFF -#define SCORE_ILLEGAL 0x7FFF +#endif +#else /* TB_NO_THREADS */ +#define LOCK_T int +#define LOCK_INIT(x) /* NOP */ +#define LOCK(x) /* NOP */ +#define UNLOCK(x) /* NOP */ +#endif +// population count implementation #undef TB_SOFTWARE_POP_COUNT #if defined(TB_CUSTOM_POP_COUNT) @@ -86,7 +87,7 @@ #ifdef TB_SOFTWARE_POP_COUNT // Not a recognized compiler/architecture that has popcount: // fall back to a software popcount. This one is still reasonably -// fast (faster than GCC's builtin). +// fast. static inline unsigned tb_software_popcount(uint64_t x) { x = x - ((x >> 1) & 0x5555555555555555ull); @@ -99,20 +100,30 @@ static inline unsigned tb_software_popcount(uint64_t x) #define poplsb(x) ((x) & ((x) - 1)) -#define make_move(promote, from, to) \ - ((((promote) & 0x7) << 12) | (((from) & 0x3F) << 6) | ((to) & 0x3F)) -#define move_from(move) \ - (((move) >> 6) & 0x3F) -#define move_to(move) \ - ((move) & 0x3F) -#define move_promotes(move) \ - (((move) >> 12) & 0x7) +int TB_MaxCardinality = 0, TB_MaxCardinalityDTM = 0; +extern int TB_CardinalityDTM; + +static const char *tbSuffix[] = { ".rtbw", ".rtbm", ".rtbz" }; +static uint32_t tbMagic[] = { 0x5d23e871, 0x88ac504b, 0xa50c66d7 }; + +enum { WDL, DTM, DTZ }; +enum { PIECE_ENC, FILE_ENC, RANK_ENC }; + +typedef int32_t Value; + +struct RootMove { + uint16_t move; + uint16_t pv[TB_MAX_PLY]; + unsigned pvSize; + Value tbScore; +}; -#define MAX_MOVES TB_MAX_MOVES -#define MOVE_STALEMATE 0xFFFF -#define MOVE_CHECKMATE 0xFFFE +struct RootMoves { + unsigned size; + struct RootMove moves[TB_MAX_MOVES]; +}; -struct pos +struct Pos { uint64_t white; uint64_t black; @@ -127,1867 +138,2034 @@ struct pos bool turn; }; -static bool do_move(struct pos *pos, const struct pos *pos0, uint16_t move); -static int probe_dtz(const struct pos *pos, int *success); - -unsigned TB_LARGEST = 0; -#include "tbcore.c" +// Attack and move generation code +#include "tbchess.c" + +struct PairsData { + uint8_t *indexTable; + uint16_t *sizeTable; + uint8_t *data; + uint16_t *offset; + uint8_t *symLen; + uint8_t *symPat; + uint8_t blockSize; + uint8_t idxBits; + uint8_t minLen; + uint8_t constValue[2]; + uint64_t base[]; // must be base[1] in C++ +}; -#define rank(s) ((s) >> 3) -#define file(s) ((s) & 0x07) -#define board(s) ((uint64_t)1 << (s)) -#ifdef TB_CUSTOM_LSB -#define lsb(b) TB_CUSTOM_LSB(b) -#else -#if defined(__GNUC__) -static inline unsigned lsb(uint64_t b) { - assert(b != 0); - return __builtin_ffsll(b)-1; -} -#elif defined(_MSC_VER) -static inline unsigned lsb(uint64_t b) { - assert(b != 0); - DWORD index; -#ifdef _WIN64 - _BitScanForward64(&index,b); - return (unsigned)index; -#else - if (b & 0xffffffffULL) { - _BitScanForward(&index,(unsigned long)(b & 0xffffffffULL)); - return (unsigned)index; - } - else { - _BitScanForward(&index,(unsigned long)(b >> 32)); - return 32 + (unsigned)index; - } -#endif -} -#else -/* not a compiler/architecture with recognized builtins */ -static uint32_t get_bit32(uint64_t x) { - return (uint32_t)(((int32_t)(x))&-((int32_t)(x))); -} -static const unsigned MAGIC32 = 0xe89b2be; -static const uint32_t MagicTable32[32] = {31,0,9,1,10,20,13,2,7,11,21,23,17,14,3,25,30,8,19,12,6,22,16,24,29,18,5,15,28,4,27,26}; -static unsigned lsb(uint64_t b) { - if (b & 0xffffffffULL) - return MagicTable32[(get_bit32(b & 0xffffffffULL)*MAGIC32)>>27]; - else - return MagicTable32[(get_bit32(b >> 32)*MAGIC32)>>27]+32; -} -#endif -#endif +struct EncInfo { + struct PairsData *precomp; + size_t factor[TB_PIECES]; + uint8_t pieces[TB_PIECES]; + uint8_t norm[TB_PIECES]; +}; -#define square(r, f) (8 * (r) + (f)) +struct BaseEntry { + uint64_t key; + uint8_t *data[3]; + map_t mapping[3]; + atomic_bool ready[3]; + uint8_t num; + bool symmetric, hasPawns, hasDtm, hasDtz; + union { + bool kk_enc; + uint8_t pawns[2]; + }; + bool dtmLossOnly; +}; -#ifdef TB_KING_ATTACKS -#define king_attacks(s) TB_KING_ATTACKS(s) -#define king_attacks_init() /* NOP */ -#else /* TB_KING_ATTACKS */ +struct PieceEntry { + struct BaseEntry be; + struct EncInfo ei[5]; // 2 + 2 + 1 + uint16_t *dtmMap; + uint16_t dtmMapIdx[2][2]; + void *dtzMap; + uint16_t dtzMapIdx[4]; + uint8_t dtzFlags; +}; -static uint64_t king_attacks_table[64]; +struct PawnEntry { + struct BaseEntry be; + struct EncInfo ei[24]; // 4 * 2 + 6 * 2 + 4 + uint16_t *dtmMap; + uint16_t dtmMapIdx[6][2][2]; + void *dtzMap; + uint16_t dtzMapIdx[4][4]; + uint8_t dtzFlags[4]; + bool dtmSwitched; +}; -#define king_attacks(s) king_attacks_table[(s)] +struct TbHashEntry { + uint64_t key; + struct BaseEntry *ptr; +}; -static void king_attacks_init(void) -{ - for (unsigned s = 0; s < 64; s++) - { - unsigned r = rank(s); - unsigned f = file(s); - uint64_t b = 0; - if (r != 0 && f != 0) - b |= board(square(r-1, f-1)); - if (r != 0) - b |= board(square(r-1, f)); - if (r != 0 && f != 7) - b |= board(square(r-1, f+1)); - if (f != 7) - b |= board(square(r, f+1)); - if (r != 7 && f != 7) - b |= board(square(r+1, f+1)); - if (r != 7) - b |= board(square(r+1, f)); - if (r != 7 && f != 0) - b |= board(square(r+1, f-1)); - if (f != 0) - b |= board(square(r, f-1)); - king_attacks_table[s] = b; - } -} +static LOCK_T tbMutex; +static int initialized = 0; +static int numPaths = 0; +static char *pathString = NULL; +static char **paths = NULL; -#endif /* TB_KING_ATTACKS */ +static int tbNumPiece, tbNumPawn; +static int numWdl, numDtm, numDtz; -#ifdef TB_KNIGHT_ATTACKS -#define knight_attacks(s) TB_KNIGHT_ATTACKS(s) -#define knight_attacks_init() /* NOP */ -#else /* TB_KNIGHT_ATTACKS */ +static struct PieceEntry *pieceEntry; +static struct PawnEntry *pawnEntry; +static struct TbHashEntry tbHash[1 << TB_HASHBITS]; -static uint64_t knight_attacks_table[64]; +static void init_indices(void); -#define knight_attacks(s) knight_attacks_table[(s)] +// Forward declarations. These functions without the tb_ +// prefix take a pos structure as input. +static int probe_wdl(struct Pos *pos, int *success); +static int probe_dtz(struct Pos *pos, int *success); +static Value probe_dtm(struct Pos *pos, int wdl, int *success); +static int root_probe_wdl(struct Pos *pos, struct RootMoves *rm); +static int root_probe_dtz(struct Pos *pos, struct RootMoves *rm); +static int root_probe_dtm(struct Pos *pos, struct RootMoves *rm); +static void expand_mate(struct Pos *pos, struct RootMove *move); -static void knight_attacks_init(void) +unsigned tb_probe_wdl_impl( + uint64_t white, + uint64_t black, + uint64_t kings, + uint64_t queens, + uint64_t rooks, + uint64_t bishops, + uint64_t knights, + uint64_t pawns, + unsigned ep, + bool turn) { - for (unsigned s = 0; s < 64; s++) + struct Pos pos = { - int r1, r = rank(s); - int f1, f = file(s); - uint64_t b = 0; - r1 = r-1; f1 = f-2; - if (r1 >= 0 && f1 >= 0) - b |= board(square(r1, f1)); - r1 = r-1; f1 = f+2; - if (r1 >= 0 && f1 <= 7) - b |= board(square(r1, f1)); - r1 = r-2; f1 = f-1; - if (r1 >= 0 && f1 >= 0) - b |= board(square(r1, f1)); - r1 = r-2; f1 = f+1; - if (r1 >= 0 && f1 <= 7) - b |= board(square(r1, f1)); - r1 = r+1; f1 = f-2; - if (r1 <= 7 && f1 >= 0) - b |= board(square(r1, f1)); - r1 = r+1; f1 = f+2; - if (r1 <= 7 && f1 <= 7) - b |= board(square(r1, f1)); - r1 = r+2; f1 = f-1; - if (r1 <= 7 && f1 >= 0) - b |= board(square(r1, f1)); - r1 = r+2; f1 = f+1; - if (r1 <= 7 && f1 <= 7) - b |= board(square(r1, f1)); - knight_attacks_table[s] = b; - } + white, + black, + kings, + queens, + rooks, + bishops, + knights, + pawns, + 0, + (uint8_t)ep, + turn + }; + int success; + int v = probe_wdl(&pos, &success); + if (success == 0) + return TB_RESULT_FAILED; + return (unsigned)(v + 2); } -#endif /* TB_KNIGHT_ATTACKS */ - -#ifdef TB_BISHOP_ATTACKS -#define bishop_attacks(s, occ) TB_BISHOP_ATTACKS(s, occ) -#define bishop_attacks_init() /* NOP */ -#else /* TB_BISHOP_ATTACKS */ +unsigned tb_probe_root_impl( + uint64_t white, + uint64_t black, + uint64_t kings, + uint64_t queens, + uint64_t rooks, + uint64_t bishops, + uint64_t knights, + uint64_t pawns, + unsigned rule50, + unsigned ep, + bool turn, + unsigned *results) +{ + struct Pos pos = + { + white, + black, + kings, + queens, + rooks, + bishops, + knights, + pawns, + (uint8_t)rule50, + (uint8_t)ep, + turn + }; + int dtz; + if (!is_valid(&pos)) + return TB_RESULT_FAILED; + uint16_t move = probe_root(&pos, &dtz, results); + if (move == 0) + return TB_RESULT_FAILED; + if (move == MOVE_CHECKMATE) + return TB_RESULT_CHECKMATE; + if (move == MOVE_STALEMATE) + return TB_RESULT_STALEMATE; + unsigned res = 0; + res = TB_SET_WDL(res, dtz_to_wdl(rule50, dtz)); + res = TB_SET_DTZ(res, (dtz < 0? -dtz: dtz)); + res = TB_SET_FROM(res, move_from(move)); + res = TB_SET_TO(res, move_to(move)); + res = TB_SET_PROMOTES(res, move_promotes(move)); + res = TB_SET_EP(res, is_en_passant(&pos, move)); + return res; +} -static uint64_t diag_attacks_table[64][64]; -static uint64_t anti_attacks_table[64][64]; -static const unsigned square2diag_table[64] = +// Given a position, produce a text string of the form KQPvKRP, where +// "KQP" represents the white pieces if flip == false and the black pieces +// if flip == true. +static void prt_str(Pos *pos, char *str, bool flip) { - 0, 1, 2, 3, 4, 5, 6, 7, - 14, 0, 1, 2, 3, 4, 5, 6, - 13, 14, 0, 1, 2, 3, 4, 5, - 12, 13, 14, 0, 1, 2, 3, 4, - 11, 12, 13, 14, 0, 1, 2, 3, - 10, 11, 12, 13, 14, 0, 1, 2, - 9, 10, 11, 12, 13, 14, 0, 1, - 8, 9, 10, 11, 12, 13, 14, 0 -}; + int color = !flip ? WHITE : BLACK; + + for (int pt = KING; pt >= PAWN; pt--) + for (int i = popcount(pieces_cp(color, pt)); i > 0; i--) + *str++ = PieceToChar[pt]; + *str++ = 'v'; + color ^= 1; + for (int pt = KING; pt >= PAWN; pt--) + for (int i = popcount(pieces_cp(color, pt)); i > 0; i--) + *str++ = PieceToChar[pt]; + *str++ = 0; +} -static const unsigned square2anti_table[64] = +// Produce a 64-bit material key corresponding to the material combination +// defined by pcs[16], where pcs[1], ..., pcs[6] are the number of white +// pawns, ..., kings and pcs[9], ..., pcs[14] are the number of black +// pawns, ..., kings. +static uint64_t calc_key_from_pcs(int *pcs, bool flip) { - 8, 9, 10, 11, 12, 13, 14, 0, - 9, 10, 11, 12, 13, 14, 0, 1, - 10, 11, 12, 13, 14, 0, 1, 2, - 11, 12, 13, 14, 0, 1, 2, 3, - 12, 13, 14, 0, 1, 2, 3, 4, - 13, 14, 0, 1, 2, 3, 4, 5, - 14, 0, 1, 2, 3, 4, 5, 6, - 0, 1, 2, 3, 4, 5, 6, 7 -}; + uint64_t key = 0; -static const uint64_t diag2board_table[15] = -{ - 0x8040201008040201ull, - 0x0080402010080402ull, - 0x0000804020100804ull, - 0x0000008040201008ull, - 0x0000000080402010ull, - 0x0000000000804020ull, - 0x0000000000008040ull, - 0x0000000000000080ull, - 0x0100000000000000ull, - 0x0201000000000000ull, - 0x0402010000000000ull, - 0x0804020100000000ull, - 0x1008040201000000ull, - 0x2010080402010000ull, - 0x4020100804020100ull, -}; + int color = !flip ? 0 : 8; + for (int i = W_PAWN; i <= B_KING; i++) + key += matKey[i] * pcs[i ^ color]; -static const uint64_t anti2board_table[15] = -{ - 0x0102040810204080ull, - 0x0204081020408000ull, - 0x0408102040800000ull, - 0x0810204080000000ull, - 0x1020408000000000ull, - 0x2040800000000000ull, - 0x4080000000000000ull, - 0x8000000000000000ull, - 0x0000000000000001ull, - 0x0000000000000102ull, - 0x0000000000010204ull, - 0x0000000001020408ull, - 0x0000000102040810ull, - 0x0000010204081020ull, - 0x0001020408102040ull, -}; + return key; +} -static inline size_t diag2index(uint64_t b, unsigned d) +// Produce a 64-bit material key corresponding to the material combination +// piece[0], ..., piece[num - 1], where each value corresponds to a piece +// (1-6 for white pawn-king, 9-14 for black pawn-king). +static uint64_t calc_key_from_pieces(uint8_t *piece, int num) { - b *= 0x0101010101010101ull; - b >>= 56; - b >>= 1; - return (size_t)b; + uint64_t key = 0; + + for (int i = 0; i < num; i++) + if (piece[i]) + key += matKey[piece[i]]; + + return key; } -static inline size_t anti2index(uint64_t b, unsigned a) +static FD open_tb(const char *str, const char *suffix) { - return diag2index(b, a); + int i; + FD fd; + char *file; + + for (i = 0; i < num_paths; i++) { + file = (char*)malloc(strlen(paths[i]) + strlen(str) + + strlen(suffix) + 2); + strcpy(file, paths[i]); +#ifdef _WIN32 + strcat(file,"\\"); +#else + strcat(file,"/"); +#endif + strcat(file, str); + strcat(file, suffix); +#ifndef _WIN32 + fd = open(file, O_RDONLY); +#else + fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif + free(file); + if (fd != FD_ERR) { + return fd; + } + } + return FD_ERR; } -#define diag(s) square2diag_table[(s)] -#define anti(s) square2anti_table[(s)] -#define diag2board(d) diag2board_table[(d)] -#define anti2board(a) anti2board_table[(a)] +static bool test_tb(const char *str, const char *suffix) +{ + FD fd = open_tb(str, suffix); + if (fd != FD_ERR) { + size_t size = file_size(fd); + close_file(fd); + if ((size & 63) != 16) { + fprintf(stderr, "Incomplete tablebase file %s.%s\n", str, suffix); + printf("info string Incomplete tablebase file %s.%s\n", str, suffix); + fd = FD_ERR; + } + } + return fd != FD_ERR; +} -static uint64_t bishop_attacks(unsigned sq, uint64_t occ) +static void *map_tb(const char *name, const char *suffix, map_t *mapping) { - occ &= ~board(sq); - unsigned d = diag(sq), a = anti(sq); - uint64_t d_occ = occ & (diag2board(d) & ~BOARD_EDGE); - uint64_t a_occ = occ & (anti2board(a) & ~BOARD_EDGE); - size_t d_idx = diag2index(d_occ, d); - size_t a_idx = anti2index(a_occ, a); - uint64_t d_attacks = diag_attacks_table[sq][d_idx]; - uint64_t a_attacks = anti_attacks_table[sq][a_idx]; - return d_attacks | a_attacks; + FD fd = open_tb(name, suffix); + if (fd == FD_ERR) + return NULL; + + void *data = map_file(fd, mapping); + if (data == NULL) { + fprintf(stderr, "Could not map %s%s into memory.\n", name, suffix); + exit(EXIT_FAILURE); + } + + close_file(fd); + + return data; } -static void bishop_attacks_init(void) +static void add_to_hash(void *ptr, uint64_t key) { - for (unsigned idx = 0; idx < 64; idx++) - { - unsigned idx1 = idx << 1; - for (unsigned s = 0; s < 64; s++) - { - int r = rank(s); - int f = file(s); - uint64_t b = 0; - for (int i = -1; f + i >= 0 && r + i >= 0; i--) - { - unsigned occ = (1 << (f + i)); - b |= board(square(r + i, f + i)); - if (idx1 & occ) - break; - } - for (int i = 1; f + i <= 7 && r + i <= 7; i++) - { - unsigned occ = (1 << (f + i)); - b |= board(square(r + i, f + i)); - if (idx1 & occ) - break; - } - diag_attacks_table[s][idx] = b; - } - } + int idx; - for (unsigned idx = 0; idx < 64; idx++) - { - unsigned idx1 = idx << 1; - for (unsigned s = 0; s < 64; s++) - { - int r = rank(s); - int f = file(s); - uint64_t b = 0; - for (int i = -1; f + i >= 0 && r - i <= 7; i--) - { - unsigned occ = (1 << (f + i)); - b |= board(square(r - i, f + i)); - if (idx1 & occ) - break; - } - for (int i = 1; f + i <= 7 && r - i >= 0; i++) - { - unsigned occ = (1 << (f + i)); - b |= board(square(r - i, f + i)); - if (idx1 & occ) - break; - } - anti_attacks_table[s][idx] = b; - } - } + idx = key >> (64 - TB_HASHBITS); + while (tbHash[idx].ptr) + idx = (idx + 1) & ((1 << TB_HASHBITS) - 1); + + tbHash[idx].key = key; + tbHash[idx].ptr = ptr; } -#endif /* TB_BISHOP_ATTACKS */ +#define pchr(i) PieceToChar[QUEEN - (i)] +#define Swap(a,b) {int tmp=a;a=b;b=tmp;} + +static void init_tb(char *str) +{ + if (!test_tb(str, tbSuffix[WDL])) + return; + + int pcs[16]; + for (int i = 0; i < 16; i++) + pcs[i] = 0; + int color = 0; + for (char *s = str; *s; s++) + if (*s == 'v') + color = 8; + else + for (int i = PAWN; i <= KING; i++) + if (*s == PieceToChar[i]) { + pcs[i | color]++; + break; + } -#ifdef TB_ROOK_ATTACKS -#define rook_attacks(s, occ) TB_ROOK_ATTACKS(s, occ) -#define rook_attacks_init() /* NOP */ -#else /* TB_ROOK_ATTACKS */ + uint64_t key = calc_key_from_pcs(pcs, false); + uint64_t key2 = calc_key_from_pcs(pcs, true); + + bool hasPawns = pcs[W_PAWN] || pcs[B_PAWN]; + + struct BaseEntry *be = hasPawns ? &pawnEntry[tbNumPawn++].be + : &pieceEntry[tbNumPiece++].be; + be->hasPawns = hasPawns; + be->key = key; + be->symmetric = key == key2; + be->num = 0; + for (int i = 0; i < 16; i++) + be->num += pcs[i]; + + numWdl++; + numDtm += be->hasDtm = test_tb(str, tbSuffix[DTM]); + numDtz += be->hasDtz = test_tb(str, tbSuffix[DTZ]); + + TB_MaxCardinality = max(TB_MaxCardinality, be->num); + if (be->hasDtm) + TB_MaxCardinalityDTM = max(TB_MaxCardinalityDTM, be->num); + + for (int type = 0; type < 3; type++) + atomic_init(&be->ready[type], false); + + if (!be->hasPawns) { + int j = 0; + for (int i = 0; i < 16; i++) + if (pcs[i] == 1) j++; + be->kk_enc = j == 2; + } else { + be->pawns[0] = pcs[W_PAWN]; + be->pawns[1] = pcs[B_PAWN]; + if (pcs[B_PAWN] && (!pcs[W_PAWN] || pcs[W_PAWN] > pcs[B_PAWN])) + Swap(be->pawns[0], be->pawns[1]); + } + + add_to_hash(be, key); + if (key != key2) + add_to_hash(be, key2); +} -static uint64_t rank_attacks_table[64][64]; -static uint64_t file_attacks_table[64][64]; +#define PIECE(x) ((struct PieceEntry *)(x)) +#define PAWN(x) ((struct PawnEntry *)(x)) -static inline size_t rank2index(uint64_t b, unsigned r) +INLINE int num_tables(struct BaseEntry *be, const int type) { - b >>= (8 * r); - b >>= 1; - return (size_t)b; + return be->hasPawns ? type == DTM ? 6 : 4 : 1; } -static inline size_t file2index(uint64_t b, unsigned f) +INLINE struct EncInfo *first_ei(struct BaseEntry *be, const int type) { - b >>= f; - b *= 0x0102040810204080ull; - b >>= 56; - b >>= 1; - return (size_t)b; + return be->hasPawns + ? &PAWN(be)->ei[type == WDL ? 0 : type == DTM ? 8 : 20] + : &PIECE(be)->ei[type == WDL ? 0 : type == DTM ? 2 : 4]; } -#define rank2board(r) (0xFFull << (8 * (r))) -#define file2board(f) (0x0101010101010101ull << (f)) +static void free_tb_entry(struct BaseEntry *be) +{ + for (int type = 0; type < 3; type++) { + if (atomic_load_explicit(&be->ready[type], memory_order_relaxed)) { + unmap_file(be->data[type], be->mapping[type]); + int num = num_tables(be, type); + struct EncInfo *ei = first_ei(be, type); + for (int t = 0; t < num; t++) { + free(ei[t].precomp); + if (type != DTZ) + free(ei[num + t].precomp); + } + atomic_store_explicit(&be->ready[type], false, memory_order_relaxed); + } + } +} -static uint64_t rook_attacks(unsigned sq, uint64_t occ) +void tb_free(void) { - occ &= ~board(sq); - unsigned r = rank(sq), f = file(sq); - uint64_t r_occ = occ & (rank2board(r) & ~BOARD_RANK_EDGE); - uint64_t f_occ = occ & (file2board(f) & ~BOARD_FILE_EDGE); - size_t r_idx = rank2index(r_occ, r); - size_t f_idx = file2index(f_occ, f); - uint64_t r_attacks = rank_attacks_table[sq][r_idx]; - uint64_t f_attacks = file_attacks_table[sq][f_idx]; - return r_attacks | f_attacks; + tb_init(""); + free(pieceEntry); + free(pawnEntry); } -static void rook_attacks_init(void) +void tb_init(char *path) { - for (unsigned idx = 0; idx < 64; idx++) - { - unsigned idx1 = idx << 1, occ; - for (int f = 0; f <= 7; f++) - { - uint64_t b = 0; - if (f > 0) - { - int i = f-1; - do - { - occ = (1 << i); - b |= board(square(0, i)); - i--; - } - while (!(idx1 & occ) && i >= 0); - } - if (f < 7) - { - int i = f+1; - do - { - occ = (1 << i); - b |= board(square(0, i)); - i++; - } - while (!(idx1 & occ) && i <= 7); - } - for (int r = 0; r <= 7; r++) - { - rank_attacks_table[square(r, f)][idx] = b; - b <<= 8; - } - } + if (!initialized) { + init_indices(); + initialized = 1; + king_attacks_init(); + knight_attacks_init(); + bishop_attacks_init(); + rook_attacks_init(); + pawn_attacks_init(); + if (path == NULL) + path = ""; + init_tablebases(path); + } + + // if pathString is set, we need to clean up first. + if (pathString) { + free(pathString); + free(paths); + + for (int i = 0; i < tbNumPiece; i++) + free_tb_entry((struct BaseEntry *)&pieceEntry[i]); + for (int i = 0; i < tbNumPawn; i++) + free_tb_entry((struct BaseEntry *)&pawnEntry[i]); + + LOCK_DESTROY(tbMutex); + + pathString = NULL; + numWdl = numDtm = numDtz = 0; + } + + // if path is an empty string or equals "", we are done. + const char *p = path; + if (strlen(p) == 0 || !strcmp(p, "")) return; + + pathString = malloc(strlen(p) + 1); + strcpy(pathString, p); + numPaths = 0; + for (int i = 0;; i++) { + if (pathString[i] != SEP_CHAR) + numPaths++; + while (pathString[i] && pathString[i] != SEP_CHAR) + i++; + if (!pathString[i]) break; + pathString[i] = 0; + } + paths = malloc(numPaths * sizeof(*paths)); + for (int i = 0, j = 0; i < numPaths; i++) { + while (!pathString[j]) j++; + paths[i] = &pathString[j]; + while (pathString[j]) j++; + } + + LOCK_INIT(tbMutex); + + tbNumPiece = tbNumPawn = 0; + TB_MaxCardinality = TB_MaxCardinalityDTM = 0; + + if (!pieceEntry) { + pieceEntry = malloc(TB_MAX_PIECE * sizeof(*pieceEntry)); + pawnEntry = malloc(TB_MAX_PAWN * sizeof(*pawnEntry)); + if (!pieceEntry || !pawnEntry) { + fprintf(stderr, "Out of memory.\n"); + exit(EXIT_FAILURE); } - for (unsigned idx = 0; idx < 64; idx++) - { - unsigned idx1 = idx << 1, occ; - for (int r = 0; r <= 7; r++) - { - uint64_t b = 0; - if (r > 0) - { - int i = r-1; - do - { - occ = (1 << i); - b |= board(square(i, 0)); - i--; - } - while (!(idx1 & occ) && i >= 0); - } - if (r < 7) - { - int i = r+1; - do - { - occ = (1 << i); - b |= board(square(i, 0)); - i++; - } - while (!(idx1 & occ) && i <= 7); - } - for (int f = 0; f <= 7; f++) - { - file_attacks_table[square(r, f)][idx] = b; - b <<= 1; - } - } + } + + for (int i = 0; i < (1 << TB_HASHBITS); i++) + tbHash[i] = (struct TbHashEntry){ 0 }; + + char str[16]; + int i, j, k, l, m; + + for (i = 0; i < 5; i++) { + sprintf(str, "K%cvK", pchr(i)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) { + sprintf(str, "K%cvK%c", pchr(i), pchr(j)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) { + sprintf(str, "K%c%cvK", pchr(i), pchr(j)); + init_tb(str); } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = 0; k < 5; k++) { + sprintf(str, "K%c%cvK%c", pchr(i), pchr(j), pchr(k)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) { + sprintf(str, "K%c%c%cvK", pchr(i), pchr(j), pchr(k)); + init_tb(str); + } + + // 6- and 7-piece TBs make sense only with a 64-bit address space + if (sizeof(size_t) < 8 || TB_PIECES < 6) + goto finished; + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = i; k < 5; k++) + for (l = (i == k) ? j : k; l < 5; l++) { + sprintf(str, "K%c%cvK%c%c", pchr(i), pchr(j), pchr(k), pchr(l)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = 0; l < 5; l++) { + sprintf(str, "K%c%c%cvK%c", pchr(i), pchr(j), pchr(k), pchr(l)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = k; l < 5; l++) { + sprintf(str, "K%c%c%c%cvK", pchr(i), pchr(j), pchr(k), pchr(l)); + init_tb(str); + } + + if (TB_PIECES < 7) + goto finished; + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = k; l < 5; l++) + for (m = l; m < 5; m++) { + sprintf(str, "K%c%c%c%c%cvK", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = k; l < 5; l++) + for (m = 0; m < 5; m++) { + sprintf(str, "K%c%c%c%cvK%c", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); + init_tb(str); + } + + for (i = 0; i < 5; i++) + for (j = i; j < 5; j++) + for (k = j; k < 5; k++) + for (l = 0; l < 5; l++) + for (m = l; m < 5; m++) { + sprintf(str, "K%c%c%cvK%c%c", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); + init_tb(str); + } + +finished: + printf("info string Found %d WDL, %d DTM and %d DTZ tablebase files.\n", + numWdl, numDtm, numDtz); + fflush(stdout); } -#endif /* TB_ROOK_ATTACKS */ +static const int8_t OffDiag[] = { + 0,-1,-1,-1,-1,-1,-1,-1, + 1, 0,-1,-1,-1,-1,-1,-1, + 1, 1, 0,-1,-1,-1,-1,-1, + 1, 1, 1, 0,-1,-1,-1,-1, + 1, 1, 1, 1, 0,-1,-1,-1, + 1, 1, 1, 1, 1, 0,-1,-1, + 1, 1, 1, 1, 1, 1, 0,-1, + 1, 1, 1, 1, 1, 1, 1, 0 +}; + +static const uint8_t Triangle[] = { + 6, 0, 1, 2, 2, 1, 0, 6, + 0, 7, 3, 4, 4, 3, 7, 0, + 1, 3, 8, 5, 5, 8, 3, 1, + 2, 4, 5, 9, 9, 5, 4, 2, + 2, 4, 5, 9, 9, 5, 4, 2, + 1, 3, 8, 5, 5, 8, 3, 1, + 0, 7, 3, 4, 4, 3, 7, 0, + 6, 0, 1, 2, 2, 1, 0, 6 +}; + +static const uint8_t FlipDiag[] = { + 0, 8, 16, 24, 32, 40, 48, 56, + 1, 9, 17, 25, 33, 41, 49, 57, + 2, 10, 18, 26, 34, 42, 50, 58, + 3, 11, 19, 27, 35, 43, 51, 59, + 4, 12, 20, 28, 36, 44, 52, 60, + 5, 13, 21, 29, 37, 45, 53, 61, + 6, 14, 22, 30, 38, 46, 54, 62, + 7, 15, 23, 31, 39, 47, 55, 63 +}; + +static const uint8_t Lower[] = { + 28, 0, 1, 2, 3, 4, 5, 6, + 0, 29, 7, 8, 9, 10, 11, 12, + 1, 7, 30, 13, 14, 15, 16, 17, + 2, 8, 13, 31, 18, 19, 20, 21, + 3, 9, 14, 18, 32, 22, 23, 24, + 4, 10, 15, 19, 22, 33, 25, 26, + 5, 11, 16, 20, 23, 25, 34, 27, + 6, 12, 17, 21, 24, 26, 27, 35 +}; + +static const uint8_t Diag[] = { + 0, 0, 0, 0, 0, 0, 0, 8, + 0, 1, 0, 0, 0, 0, 9, 0, + 0, 0, 2, 0, 0, 10, 0, 0, + 0, 0, 0, 3, 11, 0, 0, 0, + 0, 0, 0, 12, 4, 0, 0, 0, + 0, 0, 13, 0, 0, 5, 0, 0, + 0, 14, 0, 0, 0, 0, 6, 0, + 15, 0, 0, 0, 0, 0, 0, 7 +}; + +static const uint8_t Flap[2][64] = { + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 12, 18, 18, 12, 6, 0, + 1, 7, 13, 19, 19, 13, 7, 1, + 2, 8, 14, 20, 20, 14, 8, 2, + 3, 9, 15, 21, 21, 15, 9, 3, + 4, 10, 16, 22, 22, 16, 10, 4, + 5, 11, 17, 23, 23, 17, 11, 5, + 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 3, 2, 1, 0, + 4, 5, 6, 7, 7, 6, 5, 4, + 8, 9, 10, 11, 11, 10, 9, 8, + 12, 13, 14, 15, 15, 14, 13, 12, + 16, 17, 18, 19, 19, 18, 17, 16, + 20, 21, 22, 23, 23, 22, 21, 20, + 0, 0, 0, 0, 0, 0, 0, 0 } +}; -#ifdef TB_QUEEN_ATTACKS -#define queen_attacks(s, occ) TB_QUEEN_ATTACKS(s, occ) -#else /* TB_QUEEN_ATTACKS */ -#define queen_attacks(s, occ) \ - (rook_attacks((s), (occ)) | bishop_attacks((s), (occ))) -#endif /* TB_QUEEN_ATTACKS */ +static const uint8_t PawnTwist[2][64] = { + { 0, 0, 0, 0, 0, 0, 0, 0, + 47, 35, 23, 11, 10, 22, 34, 46, + 45, 33, 21, 9, 8, 20, 32, 44, + 43, 31, 19, 7, 6, 18, 30, 42, + 41, 29, 17, 5, 4, 16, 28, 40, + 39, 27, 15, 3, 2, 14, 26, 38, + 37, 25, 13, 1, 0, 12, 24, 36, + 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, + 47, 45, 43, 41, 40, 42, 44, 46, + 39, 37, 35, 33, 32, 34, 36, 38, + 31, 29, 27, 25, 24, 26, 28, 30, + 23, 21, 19, 17, 16, 18, 20, 22, + 15, 13, 11, 9, 8, 10, 12, 14, + 7, 5, 3, 1, 0, 2, 4, 6, + 0, 0, 0, 0, 0, 0, 0, 0 } +}; -#ifdef TB_PAWN_ATTACKS -#define pawn_attacks(s, c) TB_PAWN_ATTACKS(s, c) -#define pawn_attacks_init() /* NOP */ -#else /* TB_PAWN_ATTACKS */ +static const int16_t KKIdx[10][64] = { + { -1, -1, -1, 0, 1, 2, 3, 4, + -1, -1, -1, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57 }, + { 58, -1, -1, -1, 59, 60, 61, 62, + 63, -1, -1, -1, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, + 100,101,102,103,104,105,106,107, + 108,109,110,111,112,113,114,115}, + {116,117, -1, -1, -1,118,119,120, + 121,122, -1, -1, -1,123,124,125, + 126,127,128,129,130,131,132,133, + 134,135,136,137,138,139,140,141, + 142,143,144,145,146,147,148,149, + 150,151,152,153,154,155,156,157, + 158,159,160,161,162,163,164,165, + 166,167,168,169,170,171,172,173 }, + {174, -1, -1, -1,175,176,177,178, + 179, -1, -1, -1,180,181,182,183, + 184, -1, -1, -1,185,186,187,188, + 189,190,191,192,193,194,195,196, + 197,198,199,200,201,202,203,204, + 205,206,207,208,209,210,211,212, + 213,214,215,216,217,218,219,220, + 221,222,223,224,225,226,227,228 }, + {229,230, -1, -1, -1,231,232,233, + 234,235, -1, -1, -1,236,237,238, + 239,240, -1, -1, -1,241,242,243, + 244,245,246,247,248,249,250,251, + 252,253,254,255,256,257,258,259, + 260,261,262,263,264,265,266,267, + 268,269,270,271,272,273,274,275, + 276,277,278,279,280,281,282,283 }, + {284,285,286,287,288,289,290,291, + 292,293, -1, -1, -1,294,295,296, + 297,298, -1, -1, -1,299,300,301, + 302,303, -1, -1, -1,304,305,306, + 307,308,309,310,311,312,313,314, + 315,316,317,318,319,320,321,322, + 323,324,325,326,327,328,329,330, + 331,332,333,334,335,336,337,338 }, + { -1, -1,339,340,341,342,343,344, + -1, -1,345,346,347,348,349,350, + -1, -1,441,351,352,353,354,355, + -1, -1, -1,442,356,357,358,359, + -1, -1, -1, -1,443,360,361,362, + -1, -1, -1, -1, -1,444,363,364, + -1, -1, -1, -1, -1, -1,445,365, + -1, -1, -1, -1, -1, -1, -1,446 }, + { -1, -1, -1,366,367,368,369,370, + -1, -1, -1,371,372,373,374,375, + -1, -1, -1,376,377,378,379,380, + -1, -1, -1,447,381,382,383,384, + -1, -1, -1, -1,448,385,386,387, + -1, -1, -1, -1, -1,449,388,389, + -1, -1, -1, -1, -1, -1,450,390, + -1, -1, -1, -1, -1, -1, -1,451 }, + {452,391,392,393,394,395,396,397, + -1, -1, -1, -1,398,399,400,401, + -1, -1, -1, -1,402,403,404,405, + -1, -1, -1, -1,406,407,408,409, + -1, -1, -1, -1,453,410,411,412, + -1, -1, -1, -1, -1,454,413,414, + -1, -1, -1, -1, -1, -1,455,415, + -1, -1, -1, -1, -1, -1, -1,456 }, + {457,416,417,418,419,420,421,422, + -1,458,423,424,425,426,427,428, + -1, -1, -1, -1, -1,429,430,431, + -1, -1, -1, -1, -1,432,433,434, + -1, -1, -1, -1, -1,435,436,437, + -1, -1, -1, -1, -1,459,438,439, + -1, -1, -1, -1, -1, -1,460,440, + -1, -1, -1, -1, -1, -1, -1,461 } +}; -static uint64_t pawn_attacks_table[2][64]; +static const uint8_t FileToFile[] = { 0, 1, 2, 3, 3, 2, 1, 0 }; +static const int WdlToMap[5] = { 1, 3, 0, 2, 0 }; +static const uint8_t PAFlags[5] = { 8, 0, 0, 0, 4 }; -#define pawn_attacks(s, c) pawn_attacks_table[(c)][(s)] +static size_t Binomial[7][64]; +static size_t PawnIdx[2][6][24]; +static size_t PawnFactorFile[6][4]; +static size_t PawnFactorRank[6][6]; -static void pawn_attacks_init(void) +static void init_indices(void) { - for (unsigned s = 0; s < 64; s++) - { - int r = rank(s); - int f = file(s); - - uint64_t b = 0; - if (r != 7) - { - if (f != 0) - b |= board(square(r+1, f-1)); - if (f != 7) - b |= board(square(r+1, f+1)); - } - pawn_attacks_table[1][s] = b; - - b = 0; - if (r != 0) - { - if (f != 0) - b |= board(square(r-1, f-1)); - if (f != 7) - b |= board(square(r-1, f+1)); - } - pawn_attacks_table[0][s] = b; + int i, j, k; + + // Binomial[k][n] = Bin(n, k) + for (i = 0; i < 7; i++) + for (j = 0; j < 64; j++) { + size_t f = 1; + size_t l = 1; + for (k = 0; k < i; k++) { + f *= (j - k); + l *= (k + 1); + } + Binomial[i][j] = f / l; } + + for (i = 0; i < 6; i++) { + size_t s = 0; + for (j = 0; j < 24; j++) { + PawnIdx[0][i][j] = s; + s += Binomial[i][PawnTwist[0][(1 + (j % 6)) * 8 + (j / 6)]]; + if ((j + 1) % 6 == 0) { + PawnFactorFile[i][j / 6] = s; + s = 0; + } + } + } + + for (i = 0; i < 6; i++) { + size_t s = 0; + for (j = 0; j < 24; j++) { + PawnIdx[1][i][j] = s; + s += Binomial[i][PawnTwist[1][(1 + (j / 4)) * 8 + (j % 4)]]; + if ((j + 1) % 4 == 0) { + PawnFactorRank[i][j / 4] = s; + s = 0; + } + } + } } -#endif /* TB_PAWN_ATTACKS */ +INLINE int leading_pawn(int *p, struct BaseEntry *be, const int enc) +{ + for (int i = 1; i < be->pawns[0]; i++) + if (Flap[enc-1][p[0]] > Flap[enc-1][p[i]]) + Swap(p[0], p[i]); + + return enc == FILE_ENC ? FileToFile[p[0] & 7] : (p[0] - 8) >> 3; +} -static void prt_str(const struct pos *pos, char *str, bool mirror) +INLINE size_t encode(int *p, struct EncInfo *ei, struct BaseEntry *be, + const int enc) { - uint64_t white = pos->white, black = pos->black; - int i; - if (mirror) - { - uint64_t tmp = white; - white = black; - black = tmp; + int n = be->num; + size_t idx; + int k; + + if (p[0] & 0x04) + for (int i = 0; i < n; i++) + p[i] ^= 0x07; + + if (enc == PIECE_ENC) { + if (p[0] & 0x20) + for (int i = 0; i < n; i++) + p[i] ^= 0x38; + + for (int i = 0; i < n; i++) + if (OffDiag[p[i]]) { + if (OffDiag[p[i]] > 0 && i < (be->kk_enc ? 2 : 3)) + for (int j = 0; j < n; j++) + p[j] = FlipDiag[p[j]]; + break; + } + + if (be->kk_enc) { + idx = KKIdx[Triangle[p[0]]][p[1]]; + k = 2; + } else { + int s1 = (p[1] > p[0]); + int s2 = (p[2] > p[0]) + (p[2] > p[1]); + + if (OffDiag[p[0]]) + idx = Triangle[p[0]] * 63*62 + (p[1] - s1) * 62 + (p[2] - s2); + else if (OffDiag[p[1]]) + idx = 6*63*62 + Diag[p[0]] * 28*62 + Lower[p[1]] * 62 + p[2] - s2; + else if (OffDiag[p[2]]) + idx = 6*63*62 + 4*28*62 + Diag[p[0]] * 7*28 + (Diag[p[1]] - s1) * 28 + Lower[p[2]]; + else + idx = 6*63*62 + 4*28*62 + 4*7*28 + Diag[p[0]] * 7*6 + (Diag[p[1]] - s1) * 6 + (Diag[p[2]] - s2); + k = 3; + } + idx *= ei->factor[0]; + } else { + for (int i = 1; i < be->pawns[0]; i++) + for (int j = i + 1; j < be->pawns[0]; j++) + if (PawnTwist[enc-1][p[i]] < PawnTwist[enc-1][p[j]]) + Swap(p[i], p[j]); + + k = be->pawns[0]; + idx = PawnIdx[enc-1][k-1][Flap[enc-1][p[0]]]; + for (int i = 1; i < k; i++) + idx += Binomial[k-i][PawnTwist[enc-1][p[i]]]; + idx *= ei->factor[0]; + + // Pawns of other color + if (be->pawns[1]) { + int t = k + be->pawns[1]; + for (int i = k; i < t; i++) + for (int j = i + 1; j < t; j++) + if (p[i] > p[j]) Swap(p[i], p[j]); + size_t s = 0; + for (int i = k; i < t; i++) { + int sq = p[i]; + int skips = 0; + for (int j = 0; j < k; j++) + skips += (sq > p[j]); + s += Binomial[i - k + 1][sq - skips - 8]; + } + idx += s * ei->factor[k]; + k = t; } - *str++ = 'K'; - for (i = popcount(white & pos->queens); i > 0; i--) - *str++ = 'Q'; - for (i = popcount(white & pos->rooks); i > 0; i--) - *str++ = 'R'; - for (i = popcount(white & pos->bishops); i > 0; i--) - *str++ = 'B'; - for (i = popcount(white & pos->knights); i > 0; i--) - *str++ = 'N'; - for (i = popcount(white & pos->pawns); i > 0; i--) - *str++ = 'P'; - *str++ = 'v'; - *str++ = 'K'; - for (i = popcount(black & pos->queens); i > 0; i--) - *str++ = 'Q'; - for (i = popcount(black & pos->rooks); i > 0; i--) - *str++ = 'R'; - for (i = popcount(black & pos->bishops); i > 0; i--) - *str++ = 'B'; - for (i = popcount(black & pos->knights); i > 0; i--) - *str++ = 'N'; - for (i = popcount(black & pos->pawns); i > 0; i--) - *str++ = 'P'; - *str++ = '\0'; + } + + for (; k < n;) { + int t = k + ei->norm[k]; + for (int i = k; i < t; i++) + for (int j = i + 1; j < t; j++) + if (p[i] > p[j]) Swap(p[i], p[j]); + size_t s = 0; + for (int i = k; i < t; i++) { + int sq = p[i]; + int skips = 0; + for (int j = 0; j < k; j++) + skips += (sq > p[j]); + s += Binomial[i - k + 1][sq - skips]; + } + idx += s * ei->factor[k]; + k = t; + } + + return idx; } -/* - * Given a position, produce a 64-bit material signature key. - */ -static uint64_t calc_key(const struct pos *pos, bool mirror) +static size_t encode_piece(int *p, struct EncInfo *ei, struct BaseEntry *be) { - uint64_t white = pos->white, black = pos->black; - if (mirror) - { - uint64_t tmp = white; - white = black; - black = tmp; - } - return popcount(white & pos->queens) * PRIME_WHITE_QUEEN + - popcount(white & pos->rooks) * PRIME_WHITE_ROOK + - popcount(white & pos->bishops) * PRIME_WHITE_BISHOP + - popcount(white & pos->knights) * PRIME_WHITE_KNIGHT + - popcount(white & pos->pawns) * PRIME_WHITE_PAWN + - popcount(black & pos->queens) * PRIME_BLACK_QUEEN + - popcount(black & pos->rooks) * PRIME_BLACK_ROOK + - popcount(black & pos->bishops) * PRIME_BLACK_BISHOP + - popcount(black & pos->knights) * PRIME_BLACK_KNIGHT + - popcount(black & pos->pawns) * PRIME_BLACK_PAWN; + return encode(p, ei, be, PIECE_ENC); } -static uint64_t calc_key_from_pcs(int *pcs, int mirror) +static size_t encode_pawn_f(int *p, struct EncInfo *ei, struct BaseEntry *be) { - mirror = (mirror? 8: 0); - return pcs[WHITE_QUEEN ^ mirror] * PRIME_WHITE_QUEEN + - pcs[WHITE_ROOK ^ mirror] * PRIME_WHITE_ROOK + - pcs[WHITE_BISHOP ^ mirror] * PRIME_WHITE_BISHOP + - pcs[WHITE_KNIGHT ^ mirror] * PRIME_WHITE_KNIGHT + - pcs[WHITE_PAWN ^ mirror] * PRIME_WHITE_PAWN + - pcs[BLACK_QUEEN ^ mirror] * PRIME_BLACK_QUEEN + - pcs[BLACK_ROOK ^ mirror] * PRIME_BLACK_ROOK + - pcs[BLACK_BISHOP ^ mirror] * PRIME_BLACK_BISHOP + - pcs[BLACK_KNIGHT ^ mirror] * PRIME_BLACK_KNIGHT + - pcs[BLACK_PAWN ^ mirror] * PRIME_BLACK_PAWN; + return encode(p, ei, be, FILE_ENC); } -static uint64_t get_pieces(const struct pos *pos, uint8_t code) +static size_t encode_pawn_r(int *p, struct EncInfo *ei, struct BaseEntry *be) { - switch (code) - { - case WHITE_KING: - return pos->kings & pos->white; - case WHITE_QUEEN: - return pos->queens & pos->white; - case WHITE_ROOK: - return pos->rooks & pos->white; - case WHITE_BISHOP: - return pos->bishops & pos->white; - case WHITE_KNIGHT: - return pos->knights & pos->white; - case WHITE_PAWN: - return pos->pawns & pos->white; - case BLACK_KING: - return pos->kings & pos->black; - case BLACK_QUEEN: - return pos->queens & pos->black; - case BLACK_ROOK: - return pos->rooks & pos->black; - case BLACK_BISHOP: - return pos->bishops & pos->black; - case BLACK_KNIGHT: - return pos->knights & pos->black; - case BLACK_PAWN: - return pos->pawns & pos->black; - default: - return 0; // Dummy. - } + return encode(p, ei, be, RANK_ENC); } -static int probe_wdl_table(const struct pos *pos, int *success) +// Count number of placements of k like pieces on n squares +static size_t subfactor(size_t k, size_t n) { - struct TBEntry *ptr; - struct TBHashEntry *ptr2; - uint64_t idx; - uint64_t key; - int i; - uint8_t res; - int p[TBPIECES]; - - // Obtain the position's material signature key. - key = calc_key(pos, false); - - // Test for KvK. - if (key == KEY_KvK) - return 0; + size_t f = n; + size_t l = 1; + for (size_t i = 1; i < k; i++) { + f *= n - i; + l *= i + 1; + } + + return f / l; +} - ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; - for (i = 0; i < HSHMAX; i++) - { - if (ptr2[i].key == key) - break; - } - if (i == HSHMAX) - { - *success = 0; - return 0; +static size_t init_enc_info(struct EncInfo *ei, struct BaseEntry *be, + uint8_t *tb, int shift, int t, const int enc) +{ + bool morePawns = enc != PIECE_ENC && be->pawns[1] > 0; + + for (int i = 0; i < be->num; i++) { + ei->pieces[i] = (tb[i + 1 + morePawns] >> shift) & 0x0f; + ei->norm[i] = 0; + } + + int order = (tb[0] >> shift) & 0x0f; + int order2 = morePawns ? (tb[1] >> shift) & 0x0f : 0x0f; + + int k = ei->norm[0] = enc != PIECE_ENC ? be->pawns[0] + : be->kk_enc ? 2 : 3; + + if (morePawns) { + ei->norm[k] = be->pawns[1]; + k += ei->norm[k]; + } + + for (int i = k; i < be->num; i += ei->norm[i]) + for (int j = i; j < be->num && ei->pieces[j] == ei->pieces[i]; j++) + ei->norm[i]++; + + int n = 64 - k; + size_t f = 1; + + for (int i = 0; k < be->num || i == order || i == order2; i++) { + if (i == order) { + ei->factor[0] = f; + f *= enc == FILE_ENC ? PawnFactorFile[ei->norm[0] - 1][t] + : enc == RANK_ENC ? PawnFactorRank[ei->norm[0] - 1][t] + : be->kk_enc ? 462 : 31332; + } else if (i == order2) { + ei->factor[ei->norm[0]] = f; + f *= subfactor(ei->norm[ei->norm[0]], 48 - ei->norm[0]); + } else { + ei->factor[k] = f; + f *= subfactor(ei->norm[k], n); + n -= ei->norm[k]; + k += ei->norm[k]; } + } - ptr = ptr2[i].ptr; - if (!ptr->ready) - { - LOCK(TB_MUTEX); - if (!ptr->ready) - { - char str[16]; - prt_str(pos, str, ptr->key != key); - if (!init_table_wdl(ptr, str)) - { - ptr2[i].key = 0ULL; - *success = 0; - UNLOCK(TB_MUTEX); - return 0; - } - // Memory barrier to ensure ptr->ready = 1 is not reordered. -#if !defined(__cplusplus) || !defined(TB_USE_ATOMIC) -#ifdef __GNUC__ - __asm__ __volatile__ ("" ::: "memory"); -#elif defined(_MSC_VER) - MemoryBarrier(); -#endif -#endif - ptr->ready = 1; - } - UNLOCK(TB_MUTEX); - } + return f; +} - int bside, mirror, cmirror; - if (!ptr->symmetric) - { - if (key != ptr->key) - { - cmirror = 8; - mirror = 0x38; - bside = pos->turn; +static void calc_symLen(struct PairsData *d, uint32_t s, char *tmp) +{ + uint8_t *w = d->symPat + 3 * s; + uint32_t s2 = (w[2] << 4) | (w[1] >> 4); + if (s2 == 0x0fff) + d->symLen[s] = 0; + else { + uint32_t s1 = ((w[1] & 0xf) << 8) | w[0]; + if (!tmp[s1]) calc_symLen(d, s1, tmp); + if (!tmp[s2]) calc_symLen(d, s2, tmp); + d->symLen[s] = d->symLen[s1] + d->symLen[s2] + 1; + } + tmp[s] = 1; +} + +static struct PairsData *setup_pairs(uint8_t **ptr, size_t tb_size, + size_t *size, uint8_t *flags, int type) +{ + struct PairsData *d; + uint8_t *data = *ptr; + + *flags = data[0]; + if (data[0] & 0x80) { + d = malloc(sizeof(*d)); + d->idxBits = 0; + d->constValue[0] = type == WDL ? data[1] : 0; + d->constValue[1] = 0; + *ptr = data + 2; + size[0] = size[1] = size[2] = 0; + return d; + } + + uint8_t blockSize = data[1]; + uint8_t idxBits = data[2]; + uint32_t realNumBlocks = read_le_u32(&data[4]); + uint32_t numBlocks = realNumBlocks + data[3]; + int maxLen = data[8]; + int minLen = data[9]; + int h = maxLen - minLen + 1; + uint32_t numSyms = read_le_u16(&data[10 + 2 * h]); + d = malloc(sizeof(*d) + h * sizeof(uint64_t) + numSyms); + d->blockSize = blockSize; + d->idxBits = idxBits; + d->offset = (uint16_t *)&data[10]; + d->symLen = (uint8_t *)d + sizeof(*d) + h * sizeof(uint64_t); + d->symPat = &data[12 + 2 * h]; + d->minLen = minLen; + *ptr = &data[12 + 2 * h + 3 * numSyms + (numSyms & 1)]; + + size_t num_indices = (tb_size + (1ULL << idxBits) - 1) >> idxBits; + size[0] = 6ULL * num_indices; + size[1] = 2ULL * numBlocks; + size[2] = (size_t)realNumBlocks << blockSize; + + char tmp[numSyms]; + memset(tmp, 0, numSyms); + for (uint32_t s = 0; s < numSyms; s++) + if (!tmp[s]) + calc_symLen(d, s, tmp); + + d->base[h - 1] = 0; + for (int i = h - 2; i >= 0; i--) + d->base[i] = (d->base[i + 1] + read_le_u16((uint8_t *)(d->offset + i)) - read_le_u16((uint8_t *)(d->offset + i + 1))) / 2; + for (int i = 0; i < h; i++) + d->base[i] <<= 64 - (minLen + i); + + d->offset -= d->minLen; + + return d; +} + +static bool init_table(struct BaseEntry *be, const char *str, int type) +{ + uint8_t *data = map_tb(str, tbSuffix[type], &be->mapping[type]); + if (!data) return false; + + if (read_le_u32(data) != tbMagic[type]) { + fprintf(stderr, "Corrupted table.\n"); + unmap_file(data, be->mapping[type]); + return false; + } + + be->data[type] = data; + + bool split = type != DTZ && (data[4] & 0x01); + if (type == DTM) + be->dtmLossOnly = data[4] & 0x04; + + data += 5; + + size_t tb_size[6][2]; + int num = num_tables(be, type); + struct EncInfo *ei = first_ei(be, type); + int enc = !be->hasPawns ? PIECE_ENC : type != DTM ? FILE_ENC : RANK_ENC; + + for (int t = 0; t < num; t++) { + tb_size[t][0] = init_enc_info(&ei[t], be, data, 0, t, enc); + if (split) + tb_size[t][1] = init_enc_info(&ei[num + t], be, data, 4, t, enc); + data += be->num + 1 + (be->hasPawns && be->pawns[1]); + } + data += (uintptr_t)data & 1; + + size_t size[6][2][3]; + for (int t = 0; t < num; t++) { + uint8_t flags; + ei[t].precomp = setup_pairs(&data, tb_size[t][0], size[t][0], &flags, type); + if (type == DTZ) { + if (!be->hasPawns) + PIECE(be)->dtzFlags = flags; + else + PAWN(be)->dtzFlags[t] = flags; + } + if (split) + ei[num + t].precomp = setup_pairs(&data, tb_size[t][1], size[t][1], &flags, type); + else if (type != DTZ) + ei[num + t].precomp = NULL; + } + + if (type == DTM && !be->dtmLossOnly) { + uint16_t *map = (uint16_t *)data; + *(be->hasPawns ? &PAWN(be)->dtmMap : &PIECE(be)->dtmMap) = map; + uint16_t (*mapIdx)[2][2] = be->hasPawns ? &PAWN(be)->dtmMapIdx[0] + : &PIECE(be)->dtmMapIdx; + for (int t = 0; t < num; t++) { + for (int i = 0; i < 2; i++) { + mapIdx[t][0][i] = (uint16_t *)data + 1 - map; + data += 2 + 2 * read_le_u16(data); + } + if (split) { + for (int i = 0; i < 2; i++) { + mapIdx[t][1][i] = (uint16_t *)data + 1 - map; + data += 2 + 2 * read_le_u16(data); } - else - { - cmirror = mirror = 0; - bside = !pos->turn; + } + } + } + + if (type == DTZ) { + void *map = data; + *(be->hasPawns ? &PAWN(be)->dtzMap : &PIECE(be)->dtzMap) = map; + uint16_t (*mapIdx)[4] = be->hasPawns ? &PAWN(be)->dtzMapIdx[0] + : &PIECE(be)->dtzMapIdx; + uint8_t *flags = be->hasPawns ? &PAWN(be)->dtzFlags[0] + : &PIECE(be)->dtzFlags; + for (int t = 0; t < num; t++) { + if (flags[t] & 2) { + if (!(flags[t] & 16)) { + for (int i = 0; i < 4; i++) { + mapIdx[t][i] = data + 1 - (uint8_t *)map; + data += 1 + data[0]; + } + } else { + data += (uintptr_t)data & 0x01; + for (int i = 0; i < 4; i++) { + mapIdx[t][i] = (uint16_t *)data + 1 - (uint16_t *)map; + data += 2 + 2 * read_le_u16(data); + } } + } } - else - { - cmirror = (pos->turn? 0: 8); - mirror = (pos->turn? 0: 0x38); - bside = 0; + data += (uintptr_t)data & 0x01; + } + + for (int t = 0; t < num; t++) { + ei[t].precomp->indexTable = data; + data += size[t][0][0]; + if (split) { + ei[num + t].precomp->indexTable = data; + data += size[t][1][0]; } - - // p[i] is to contain the square 0-63 (A1-H8) for a piece of type - // pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king. - // Pieces of the same type are guaranteed to be consecutive. - if (!ptr->has_pawns) - { - struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr; - uint8_t *pc = entry->pieces[bside]; - for (i = 0; i < entry->num; ) - { - uint64_t bb = get_pieces(pos, pc[i] ^ cmirror); - do - { - p[i++] = lsb(bb); - bb = poplsb(bb); - } while (bb); - } - idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]); - res = decompress_pairs(entry->precomp[bside], idx); + } + + for (int t = 0; t < num; t++) { + ei[t].precomp->sizeTable = (uint16_t *)data; + data += size[t][0][1]; + if (split) { + ei[num + t].precomp->sizeTable = (uint16_t *)data; + data += size[t][1][1]; } - else - { - struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr; - int k = entry->file[0].pieces[0][0] ^ cmirror; - uint64_t bb = get_pieces(pos, k); - i = 0; - do { - p[i++] = lsb(bb) ^ mirror; - bb = poplsb(bb); - } while (bb); - int f = pawn_file(entry, p); - uint8_t *pc = entry->file[f].pieces[bside]; - for (; i < entry->num; ) - { - bb = get_pieces(pos, pc[i] ^ cmirror); - do - { - p[i++] = lsb(bb) ^ mirror; - bb = poplsb(bb); - } while (bb); - } - idx = encode_pawn(entry, entry->file[f].norm[bside], p, - entry->file[f].factor[bside]); - res = decompress_pairs(entry->file[f].precomp[bside], idx); + } + + for (int t = 0; t < num; t++) { + data = (uint8_t *)(((uintptr_t)data + 0x3f) & ~0x3f); + ei[t].precomp->data = data; + data += size[t][0][2]; + if (split) { + data = (uint8_t *)(((uintptr_t)data + 0x3f) & ~0x3f); + ei[num + t].precomp->data = data; + data += size[t][1][2]; } + } + + if (type == DTM && be->hasPawns) + PAWN(be)->dtmSwitched = + calc_key_from_pieces(ei[0].pieces, be->num) != be->key; - return ((int)res) - 2; + return true; } -static int probe_dtz_table(const struct pos *pos, int wdl, int *success) +static uint8_t *decompress_pairs(struct PairsData *d, size_t idx) { - struct TBEntry *ptr; - uint64_t idx; - int i, res; - int p[TBPIECES]; + if (!d->idxBits) + return d->constValue; - // Obtain the position's material signature key. - uint64_t key = calc_key(pos, false); + uint32_t mainIdx = idx >> d->idxBits; + int litIdx = (idx & (((size_t)1 << d->idxBits) - 1)) - ((size_t)1 << (d->idxBits - 1)); + uint32_t block; + memcpy(&block, d->indexTable + 6 * mainIdx, sizeof(block)); + block = from_le_u32(block); - if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) - { - for (i = 1; i < DTZ_ENTRIES; i++) - { - if (DTZ_table[i].key1 == key) - break; - } - if (i < DTZ_ENTRIES) - { - struct DTZTableEntry table_entry = DTZ_table[i]; - for (; i > 0; i--) - DTZ_table[i] = DTZ_table[i - 1]; - DTZ_table[0] = table_entry; - } - else - { - struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; - for (i = 0; i < HSHMAX; i++) - { - if (ptr2[i].key == key) - break; - } - if (i == HSHMAX) - { - *success = 0; - return 0; - } - ptr = ptr2[i].ptr; - char str[16]; - const bool mirror = (ptr->key != key); - prt_str(pos, str, mirror); - if (DTZ_table[DTZ_ENTRIES - 1].entry) - free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry); - for (i = DTZ_ENTRIES - 1; i > 0; i--) - DTZ_table[i] = DTZ_table[i - 1]; - uint64_t key1 = calc_key(pos, mirror); - uint64_t key2 = calc_key(pos, !mirror); - load_dtz_table(str, key1, key2); - } - } + uint16_t idxOffset = *(uint16_t *)(d->indexTable + 6 * mainIdx + 4); + litIdx += from_le_u16(idxOffset); - ptr = DTZ_table[0].entry; - if (!ptr) - { - *success = 0; - return 0; + if (litIdx < 0) + while (litIdx < 0) + litIdx += d->sizeTable[--block] + 1; + else + while (litIdx > d->sizeTable[block]) + litIdx -= d->sizeTable[block++] + 1; + + uint32_t *ptr = (uint32_t *)(d->data + ((size_t)block << d->blockSize)); + + int m = d->minLen; + uint16_t *offset = d->offset; + uint64_t *base = d->base - m; + uint8_t *symLen = d->symLen; + uint32_t sym, bitCnt; + + uint64_t code = from_be_u64(*(uint64_t *)ptr); + + ptr += 2; + bitCnt = 0; // number of "empty bits" in code + for (;;) { + int l = m; + while (code < base[l]) l++; + sym = from_le_u16(offset[l]); + sym += (code - base[l]) >> (64 - l); + if (litIdx < (int)symLen[sym] + 1) break; + litIdx -= (int)symLen[sym] + 1; + code <<= l; + bitCnt += l; + if (bitCnt >= 32) { + bitCnt -= 32; + uint32_t tmp = from_be_u32(*ptr++); + code |= (uint64_t)tmp << bitCnt; } - - int bside, mirror, cmirror; - if (!ptr->symmetric) - { - if (key != ptr->key) - { - cmirror = 8; - mirror = 0x38; - bside = pos->turn; - } - else - { - cmirror = mirror = 0; - bside = !pos->turn; - } - } - else - { - cmirror = (pos->turn? 0: 8); - mirror = (pos->turn? 0: 0x38); - bside = 0; - } - - if (!ptr->has_pawns) - { - struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr; - if ((entry->flags & 1) != bside && !entry->symmetric) - { - *success = -1; - return 0; - } - uint8_t *pc = entry->pieces; - for (i = 0; i < entry->num;) - { - uint64_t bb = get_pieces(pos, pc[i] ^ cmirror); - do - { - p[i++] = lsb(bb); - bb = poplsb(bb); - } - while (bb); - } - idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, - entry->factor); - res = decompress_pairs(entry->precomp, idx); - - if (entry->flags & 2) - res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res]; - if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1)) - res *= 2; - } - else - { - struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr; - int k = entry->file[0].pieces[0] ^ cmirror; - uint64_t bb = get_pieces(pos, k); - i = 0; - do - { - p[i++] = lsb(bb) ^ mirror; - bb = poplsb(bb); - } - while (bb); - int f = pawn_file((struct TBEntry_pawn *)entry, p); - if ((entry->flags[f] & 1) != bside) - { - *success = -1; - return 0; - } - uint8_t *pc = entry->file[f].pieces; - for (; i < entry->num;) - { - bb = get_pieces(pos, pc[i] ^ cmirror); - do - { - p[i++] = lsb(bb) ^ mirror; - bb = poplsb(bb); - } - while (bb); - } - idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, - p, entry->file[f].factor); - res = decompress_pairs(entry->file[f].precomp, idx); - - if (entry->flags[f] & 2) - res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res]; - if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1)) - res *= 2; + } + + uint8_t *symPat = d->symPat; + while (symLen[sym] != 0) { + uint8_t *w = symPat + (3 * sym); + int s1 = ((w[1] & 0xf) << 8) | w[0]; + if (litIdx < (int)symLen[s1] + 1) + sym = s1; + else { + litIdx -= (int)symLen[s1] + 1; + sym = (w[2] << 4) | (w[1] >> 4); } + } - return res; + return &symPat[3 * sym]; } -static uint16_t *add_move(uint16_t *moves, bool promotes, unsigned from, - unsigned to) +// p[i] is to contain the square 0-63 (A1-H8) for a piece of type +// pc[i] ^ flip, where 1 = white pawn, ..., 14 = black king and pc ^ flip +// flips between white and black if flip == true. +// Pieces of the same type are guaranteed to be consecutive. +INLINE int fill_squares(Pos *pos, uint8_t *pc, bool flip, int mirror, int *p, + int i) { - if (!promotes) - *moves++ = make_move(TB_PROMOTES_NONE, from, to); - else - { - *moves++ = make_move(TB_PROMOTES_QUEEN, from, to); - *moves++ = make_move(TB_PROMOTES_KNIGHT, from, to); - *moves++ = make_move(TB_PROMOTES_ROOK, from, to); - *moves++ = make_move(TB_PROMOTES_BISHOP, from, to); - } - return moves; + Bitboard bb = pieces_cp((pc[i] >> 3) ^ flip, pc[i] & 7); + do { + p[i++] = pop_lsb(&bb) ^ mirror; + } while (bb); + return i; } -/* - * Generate all captures or promotions. - */ -static uint16_t *gen_captures_or_promotions(const struct pos *pos, - uint16_t *moves) +INLINE int probe_table(Pos *pos, int s, int *success, const int type) { - uint64_t occ = pos->white | pos->black; - uint64_t us = (pos->turn? pos->white: pos->black), - them = (pos->turn? pos->black: pos->white); - uint64_t b, att; - { - unsigned from = lsb(pos->kings & us); - for (att = king_attacks(from) & them; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } - } - for (b = us & pos->queens; b; b = poplsb(b)) - { - unsigned from = lsb(b); - for (att = queen_attacks(from, occ) & them; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } + // Obtain the position's material-signature key + uint64_t key = pos_material_key(); + + // Test for KvK + if (type == WDL && key == 2ULL) + return 0; + + int hashIdx = key >> (64 - TB_HASHBITS); + while (tbHash[hashIdx].key && tbHash[hashIdx].key != key) + hashIdx = (hashIdx + 1) & ((1 << TB_HASHBITS) - 1); + if (!tbHash[hashIdx].ptr) { + *success = 0; + return 0; + } + + struct BaseEntry *be = tbHash[hashIdx].ptr; + if ((type == DTM && !be->hasDtm) || (type == DTZ && !be->hasDtz)) { + *success = 0; + return 0; + } + + // Use double-checked locking to reduce locking overhead + if (!atomic_load_explicit(&be->ready[type], memory_order_acquire)) { + LOCK(tbMutex); + if (!atomic_load_explicit(&be->ready[type], memory_order_relaxed)) { + char str[16]; + prt_str(pos, str, be->key != key); + if (!init_table(be, str, type)) { + tbHash[hashIdx].ptr = NULL; // mark as deleted + *success = 0; + UNLOCK(tbMutex); + return 0; + } + atomic_store_explicit(&be->ready[type], true, memory_order_release); } - for (b = us & pos->rooks; b; b = poplsb(b)) - { - unsigned from = lsb(b); - for (att = rook_attacks(from, occ) & them; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } + UNLOCK(tbMutex); + } + + bool bside, flip; + if (!be->symmetric) { + flip = key != be->key; + bside = (pos_stm() == WHITE) == flip; + if (type == DTM && be->hasPawns && PAWN(be)->dtmSwitched) { + flip = !flip; + bside = !bside; } - for (b = us & pos->bishops; b; b = poplsb(b)) - { - unsigned from = lsb(b); - for (att = bishop_attacks(from, occ) & them; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } + } else { + flip = pos_stm() != WHITE; + bside = false; + } + + struct EncInfo *ei = first_ei(be, type); + int p[TB_PIECES]; + size_t idx; + int t = 0; + uint8_t flags; + + if (!be->hasPawns) { + if (type == DTZ) { + flags = PIECE(be)->dtzFlags; + if ((flags & 1) != bside && !be->symmetric) { + *success = -1; + return 0; + } } - for (b = us & pos->knights; b; b = poplsb(b)) - { - unsigned from = lsb(b); - for (att = knight_attacks(from) & them; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } + ei = type != DTZ ? &ei[bside] : ei; + for (int i = 0; i < be->num;) + i = fill_squares(pos, ei->pieces, flip, 0, p, i); + idx = encode_piece(p, ei, be); + } else { + int i = fill_squares(pos, ei->pieces, flip, flip ? 0x38 : 0, p, 0); + t = leading_pawn(p, be, type != DTM ? FILE_ENC : RANK_ENC); + if (type == DTZ) { + flags = PAWN(be)->dtzFlags[t]; + if ((flags & 1) != bside && !be->symmetric) { + *success = -1; + return 0; + } } - for (b = us & pos->pawns; b; b = poplsb(b)) - { - unsigned from = lsb(b); - att = pawn_attacks(from, pos->turn); - if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) - { - unsigned to = pos->ep; - moves = add_move(moves, false, from, to); - } - for (att = att & them; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, - to); - } - if (pos->turn && rank(from) == 6) - { - unsigned to = from + 8; - if ((board(to) & occ) == 0) - moves = add_move(moves, true, from, to); - } - else if (!pos->turn && rank(from) == 1) - { - unsigned to = from - 8; - if ((board(to) & occ) == 0) - moves = add_move(moves, true, from, to); - } + ei = type == WDL ? &ei[t + 4 * bside] + : type == DTM ? &ei[t + 6 * bside] : &ei[t]; + while (i < be->num) + i = fill_squares(pos, ei->pieces, flip, flip ? 0x38 : 0, p, i); + idx = type != DTM ? encode_pawn_f(p, ei, be) : encode_pawn_r(p, ei, be); + } + + uint8_t *w = decompress_pairs(ei->precomp, idx); + + if (type == WDL) + return (int)w[0] - 2; + + int v = w[0] + ((w[1] & 0x0f) << 8); + + if (type == DTM) { + if (!be->dtmLossOnly) + v = from_le_u16(be->hasPawns + ? PAWN(be)->dtmMap[PAWN(be)->dtmMapIdx[t][bside][s] + v] + : PIECE(be)->dtmMap[PIECE(be)->dtmMapIdx[bside][s] + v]); + } else { + if (flags & 2) { + int m = WdlToMap[s + 2]; + if (!(flags & 16)) + v = be->hasPawns + ? ((uint8_t *)PAWN(be)->dtzMap)[PAWN(be)->dtzMapIdx[t][m] + v] + : ((uint8_t *)PIECE(be)->dtzMap)[PIECE(be)->dtzMapIdx[m] + v]; + else + v = from_le_u16(be->hasPawns + ? ((uint16_t *)PAWN(be)->dtzMap)[PAWN(be)->dtzMapIdx[t][m] + v] + : ((uint16_t *)PIECE(be)->dtzMap)[PIECE(be)->dtzMapIdx[m] + v]); } - return moves; -} - -/* - * Generate all non-capture pawn moves and promotions. - */ -static uint16_t *gen_pawn_quiets_or_promotions(const struct pos *pos, - uint16_t *moves) -{ - uint64_t occ = pos->white | pos->black; - uint64_t us = (pos->turn? pos->white: pos->black); - uint64_t b, att; + if (!(flags & PAFlags[s + 2]) || (s & 1)) + v *= 2; + } - for (b = us & pos->pawns; b; b = poplsb(b)) - { - unsigned from = lsb(b); - unsigned next = from + (pos->turn? 8: -8); - att = 0; - if ((board(next) & occ) == 0) - { - att |= board(next); - unsigned next2 = from + (pos->turn? 16: -16); - if ((pos->turn? rank(from) == 1: rank(from) == 6) && - ((board(next2) & occ) == 0)) - att |= board(next2); - } - for (; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, - to); - } - } - return moves; + return v; } -/* - * Generate all en passant captures. - */ -static uint16_t *gen_pawn_ep_captures(const struct pos *pos, uint16_t *moves) +static int probe_wdl_table(struct Pos *pos, int *success) { - if (pos->ep == 0) - return moves; - uint64_t ep = board(pos->ep); - unsigned to = pos->ep; - uint64_t us = (pos->turn? pos->white: pos->black); - uint64_t b; - for (b = us & pos->pawns; b; b = poplsb(b)) - { - unsigned from = lsb(b); - if ((pawn_attacks(from, pos->turn) & ep) != 0) - moves = add_move(moves, false, from, to); - } - return moves; + return probe_table(pos, 0, success, WDL); } -/* - * Generate all moves. - */ -static uint16_t *gen_moves(const struct pos *pos, uint16_t *moves) +static int probe_dtm_table(struct Pos *pos, int won, int *success) { - uint64_t occ = pos->white | pos->black; - uint64_t us = (pos->turn? pos->white: pos->black), - them = (pos->turn? pos->black: pos->white); - uint64_t b, att; - - { - unsigned from = lsb(pos->kings & us); - for (att = king_attacks(from) & ~us; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } - } - for (b = us & pos->queens; b; b = poplsb(b)) - { - unsigned from = lsb(b); - for (att = queen_attacks(from, occ) & ~us; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } - } - for (b = us & pos->rooks; b; b = poplsb(b)) - { - unsigned from = lsb(b); - for (att = rook_attacks(from, occ) & ~us; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } - } - for (b = us & pos->bishops; b; b = poplsb(b)) - { - unsigned from = lsb(b); - for (att = bishop_attacks(from, occ) & ~us; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } - } - for (b = us & pos->knights; b; b = poplsb(b)) - { - unsigned from = lsb(b); - for (att = knight_attacks(from) & ~us; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, false, from, to); - } - } - for (b = us & pos->pawns; b; b = poplsb(b)) - { - unsigned from = lsb(b); - unsigned next = from + (pos->turn? 8: -8); - att = pawn_attacks(from, pos->turn); - if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) - { - unsigned to = pos->ep; - moves = add_move(moves, false, from, to); - } - att &= them; - if ((board(next) & occ) == 0) - { - att |= board(next); - unsigned next2 = from + (pos->turn? 16: -16); - if ((pos->turn? rank(from) == 1: rank(from) == 6) && - ((board(next2) & occ) == 0)) - att |= board(next2); - } - for (; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, - to); - } - } - return moves; + return probe_table(pos, won, success, DTM); } -/* - * Test if the given move is an en passant capture. - */ -static bool is_en_passant(const struct pos *pos, uint16_t move) +static int probe_dtz_table(struct Pos *pos, int wdl, int *success) { - uint16_t from = move_from(move); - uint16_t to = move_to(move); - uint64_t us = (pos->turn? pos->white: pos->black); - if (pos->ep == 0) - return false; - if (to != pos->ep) - return false; - if ((board(from) & us & pos->pawns) == 0) - return false; - return true; + return probe_table(pos, wdl, success, DTZ); } -/* - * Test if the given position is legal. - * (Pawns on backrank? Can the king be captured?) - */ -static bool is_legal(const struct pos *pos) +// Add underpromotion captures to list of captures. +static ExtMove *add_underprom_caps(struct Pos *pos, ExtMove *m, ExtMove *end) { - uint64_t occ = pos->white | pos->black; - uint64_t us = (pos->turn? pos->black: pos->white), - them = (pos->turn? pos->white: pos->black); - uint64_t king = pos->kings & us; - if (!king) - return false; - unsigned sq = lsb(king); - if (king_attacks(sq) & (pos->kings & them)) - return false; - uint64_t ratt = rook_attacks(sq, occ); - uint64_t batt = bishop_attacks(sq, occ); - if (ratt & (pos->rooks & them)) - return false; - if (batt & (pos->bishops & them)) - return false; - if ((ratt | batt) & (pos->queens & them)) - return false; - if (knight_attacks(sq) & (pos->knights & them)) - return false; - if (pawn_attacks(sq, !pos->turn) & (pos->pawns & them)) - return false; - return true; -} + ExtMove *extra = end; + + for (; m < end; m++) { + Move move = m->move; + if (type_of_m(move) == PROMOTION && piece_on(to_sq(move))) { + (*extra++).move = (Move)(move - (1 << 12)); + (*extra++).move = (Move)(move - (2 << 12)); + (*extra++).move = (Move)(move - (3 << 12)); + } + } -/* - * Test if the king is in check. - */ -static bool is_check(const struct pos *pos) -{ - uint64_t occ = pos->white | pos->black; - uint64_t us = (pos->turn? pos->white: pos->black), - them = (pos->turn? pos->black: pos->white); - uint64_t king = pos->kings & us; - unsigned sq = lsb(king); - uint64_t ratt = rook_attacks(sq, occ); - uint64_t batt = bishop_attacks(sq, occ); - if (ratt & (pos->rooks & them)) - return true; - if (batt & (pos->bishops & them)) - return true; - if ((ratt | batt) & (pos->queens & them)) - return true; - if (knight_attacks(sq) & (pos->knights & them)) - return true; - if (pawn_attacks(sq, pos->turn) & (pos->pawns & them)) - return true; - return false; + return extra; } -/* - * Test if the king is in checkmate. - */ -static bool is_mate(const struct pos *pos) +// probe_ab() is not called for positions with en passant captures. +static int probe_ab(Pos *pos, int alpha, int beta, int *success) { - if (!is_check(pos)) - return false; - uint16_t moves0[MAX_MOVES]; - uint16_t *moves = moves0; - uint16_t *end = gen_moves(pos, moves); - for (; moves < end; moves++) - { - struct pos pos1; - if (do_move(&pos1, pos, *moves)) - return false; + assert(ep_square() == 0); + + // Generate (at least) all legal captures including (under)promotions. + // It is OK to generate more, as long as they are filtered out below. + ExtMove *m = (pos->st-1)->endMoves; + ExtMove *end = !pos_checkers() + ? add_underprom_caps(pos, m, generate_captures(pos, m)) + : generate_evasions(pos, m); + pos->st->endMoves = end; + + for (; m < end; m++) { + Move move = m->move; + if (!is_capture(pos, move) || !is_legal(pos, move)) + continue; + do_move(pos, move, gives_check(pos, pos->st, move)); + int v = -probe_ab(pos, -beta, -alpha, success); + undo_move(pos, move); + if (*success == 0) return 0; + if (v > alpha) { + if (v >= beta) + return v; + alpha = v; } - return true; -} - -/* - * Test if the position is valid. - */ -static bool is_valid(const struct pos *pos) -{ - if (popcount(pos->kings) != 2) - return false; - if (popcount(pos->kings & pos->white) != 1) - return false; - if (popcount(pos->kings & pos->black) != 1) - return false; - if ((pos->white & pos->black) != 0) - return false; - if ((pos->kings & pos->queens) != 0) - return false; - if ((pos->kings & pos->rooks) != 0) - return false; - if ((pos->kings & pos->bishops) != 0) - return false; - if ((pos->kings & pos->knights) != 0) - return false; - if ((pos->kings & pos->pawns) != 0) - return false; - if ((pos->queens & pos->rooks) != 0) - return false; - if ((pos->queens & pos->bishops) != 0) - return false; - if ((pos->queens & pos->knights) != 0) - return false; - if ((pos->queens & pos->pawns) != 0) - return false; - if ((pos->rooks & pos->bishops) != 0) - return false; - if ((pos->rooks & pos->knights) != 0) - return false; - if ((pos->rooks & pos->pawns) != 0) - return false; - if ((pos->bishops & pos->knights) != 0) - return false; - if ((pos->bishops & pos->pawns) != 0) - return false; - if ((pos->knights & pos->pawns) != 0) - return false; - if (pos->pawns & BOARD_FILE_EDGE) - return false; - if ((pos->white | pos->black) != - (pos->kings | pos->queens | pos->rooks | pos->bishops | pos->knights | - pos->pawns)) - return false; - return is_legal(pos); -} + } -#define do_bb_move(b, from, to) \ - (((b) & (~board(to)) & (~board(from))) | \ - ((((b) >> (from)) & 0x1) << (to))) + int v = probe_wdl_table(pos, success); -static bool do_move(struct pos *pos, const struct pos *pos0, uint16_t move) -{ - unsigned from = move_from(move); - unsigned to = move_to(move); - unsigned promotes = move_promotes(move); - pos->turn = !pos0->turn; - pos->white = do_bb_move(pos0->white, from, to); - pos->black = do_bb_move(pos0->black, from, to); - pos->kings = do_bb_move(pos0->kings, from, to); - pos->queens = do_bb_move(pos0->queens, from, to); - pos->rooks = do_bb_move(pos0->rooks, from, to); - pos->bishops = do_bb_move(pos0->bishops, from, to); - pos->knights = do_bb_move(pos0->knights, from, to); - pos->pawns = do_bb_move(pos0->pawns, from, to); - pos->ep = 0; - if (promotes != TB_PROMOTES_NONE) - { - pos->pawns &= ~board(to); // Promotion - switch (promotes) - { - case TB_PROMOTES_QUEEN: - pos->queens |= board(to); break; - case TB_PROMOTES_ROOK: - pos->rooks |= board(to); break; - case TB_PROMOTES_BISHOP: - pos->bishops |= board(to); break; - case TB_PROMOTES_KNIGHT: - pos->knights |= board(to); break; - } - pos->rule50 = 0; - } - else if ((board(from) & pos0->pawns) != 0) - { - pos->rule50 = 0; // Pawn move - if (rank(from) == 1 && rank(to) == 3 && - (pawn_attacks(from+8, true) & pos0->pawns & pos0->black) != 0) - pos->ep = from+8; - else if (rank(from) == 6 && rank(to) == 4 && - (pawn_attacks(from-8, false) & pos0->pawns & pos0->white) != 0) - pos->ep = from-8; - else if (to == pos0->ep) - { - unsigned ep_to = (pos0->turn? to-8: to+8); - uint64_t ep_mask = ~board(ep_to); - pos->white &= ep_mask; - pos->black &= ep_mask; - pos->pawns &= ep_mask; - } - } - else if ((board(to) & (pos0->white | pos0->black)) != 0) - pos->rule50 = 0; // Capture - else - pos->rule50 = pos0->rule50 + 1; // Normal move - if (!is_legal(pos)) - return false; - return true; + return alpha >= v ? alpha : v; } -static int probe_ab(const struct pos *pos, int alpha, int beta, int *success) +// Probe the WDL table for a particular position. +// +// If *success != 0, the probe was successful. +// +// If *success == 2, the position has a winning capture, or the position +// is a cursed win and has a cursed winning capture, or the position +// has an ep capture as only best move. +// This is used in probe_dtz(). +// +// The return value is from the point of view of the side to move: +// -2 : loss +// -1 : loss, but draw under 50-move rule +// 0 : draw +// 1 : win, but draw under 50-move rule +// 2 : win +int probe_wdl(struct Pos *pos, int *success) { - int v; - uint16_t moves0[64]; - uint16_t *moves = moves0; - uint16_t *end = gen_captures_or_promotions(pos, moves); - for (; moves < end; moves++) - { - if (is_en_passant(pos, *moves)) - continue; - struct pos pos1; - if (!do_move(&pos1, pos, *moves)) - continue; - v = -probe_ab(&pos1, -beta, -alpha, success); - if (*success == 0) - return 0; - if (v > alpha) - { - if (v >= beta) - { - *success = 2; - return v; - } - alpha = v; - } - } - - v = probe_wdl_table(pos, success); - if (*success == 0) - return 0; - if (alpha >= v) - { - *success = 1 + (alpha > 0); - return alpha; + *success = 1; + + // Generate (at least) all legal captures including (under)promotions. + ExtMove *m = (pos->st-1)->endMoves; + ExtMove *end = !pos_checkers() + ? add_underprom_caps(pos, m, generate_captures(pos, m)) + : generate_evasions(pos, m); + pos->st->endMoves = end; + + int bestCap = -3, bestEp = -3; + + // We do capture resolution, letting bestCap keep track of the best + // capture without ep rights and letting bestEp keep track of still + // better ep captures if they exist. + + for (; m < end; m++) { + Move move = m->move; + if (!is_capture(pos, move) || !is_legal(pos, move)) + continue; + do_move(pos, move, gives_check(pos, pos->st, move)); + int v = -probe_ab(pos, -2, -bestCap, success); + undo_move(pos, move); + if (*success == 0) return 0; + if (v > bestCap) { + if (v == 2) { + *success = 2; + return 2; + } + if (type_of_m(move) != ENPASSANT) + bestCap = v; + else if (v > bestEp) + bestEp = v; } - else - { - *success = 1; - return v; - } -} + } -static int probe_wdl(const struct pos *pos, int *success) -{ - *success = 1; - int v = probe_ab(pos, -2, 2, success); - if (*success == 0) - return 0; + int v = probe_wdl_table(pos, success); + if (*success == 0) return 0; - // If en passant is not possible, we are done. - if (pos->ep == 0) - return v; + // Now max(v, bestCap) is the WDL value of the position without ep rights. + // If the position without ep rights is not stalemate or no ep captures + // exist, then the value of the position is max(v, bestCap, bestEp). + // If the position without ep rights is stalemate and bestEp > -3, + // then the value of the position is bestEp (and we will have v == 0). - // Now handle en passant. - int v1 = -3; - uint16_t moves0[2]; // Max=2 possible en-passant captures. - uint16_t *moves = moves0; - uint16_t *end = gen_pawn_ep_captures(pos, moves); - for (; moves < end; moves++) - { - struct pos pos1; - if (!do_move(&pos1, pos, *moves)) - continue; - int v0 = -probe_ab(&pos1, -2, 2, success); - if (*success == 0) - return 0; - if (v0 > v1) - v1 = v0; + if (bestEp > bestCap) { + if (bestEp > v) { // ep capture (possibly cursed losing) is best. + *success = 2; + return bestEp; } - if (v1 > -3) - { - if (v1 >= v) - v = v1; - else if (v == 0) - { - // Check whether there is at least one legal non-ep move. - uint16_t moves0[MAX_MOVES]; - uint16_t *moves = moves0; - uint16_t *end = gen_moves(pos, moves); - bool found = false; - for (; moves < end; moves++) - { - if (is_en_passant(pos, *moves)) - continue; - struct pos pos1; - if (do_move(&pos1, pos, *moves)) - { - found = true; - break; - } - } - if (!found) - v = v1; // Forced to play the losing ep capture. - } + bestCap = bestEp; + } + + // Now max(v, bestCap) is the WDL value of the position unless + // the position without ep rights is stalemate and bestEp > -3. + + if (bestCap >= v) { + // No need to test for the stalemate case here: either there are + // non-ep captures, or bestCap == bestEp >= v anyway. + *success = 1 + (bestCap > 0); + return bestCap; + } + + // Now handle the stalemate case. + if (bestEp > -3 && v == 0) { + // Check for stalemate in the position with ep captures. + for (m = (pos->st-1)->endMoves; m < end; m++) { + Move move = m->move; + if (type_of_m(move) == ENPASSANT) continue; + if (is_legal(pos, move)) break; + } + if (m == end && !pos_checkers()) { + end = generate_quiets(pos, end); + for (; m < end; m++) { + Move move = m->move; + if (is_legal(pos, move)) + break; + } + } + if (m == end) { // Stalemate detected. + *success = 2; + return bestEp; } + } - return v; + // Stalemate / en passant not an issue, so v is the correct value. + + return v; } -static int probe_dtz_no_ep(const struct pos *pos, int *success) +#if 0 +// This will not be called for positions with en passant captures +static Value probe_dtm_dc(Pos *pos, int won, int *success) { - int wdl, dtz; - wdl = probe_ab(pos, -2, 2, success); - if (wdl == 0) - return 0; - if (*success == 2) - return wdl == 2 ? 1 : 101; + assert(ep_square() == 0); + + Value v, bestCap = -VALUE_INFINITE; + + ExtMove *end, *m = (pos->st-1)->endMoves; + + // Generate at least all legal captures including (under)promotions + if (!pos_checkers()) { + end = generate_captures(pos, m); + end = add_underprom_caps(pos, m, end); + } else + end = generate_evasions(pos, m); + pos->st->endMoves = end; + + for (; m < end; m++) { + Move move = m->move; + if (!is_capture(pos, move) || !is_legal(pos, move)) + continue; + do_move(pos, move, gives_check(pos, pos->st, move)); + if (!won) + v = -probe_dtm_dc(pos, 1, success) + 1; + else if (probe_ab(pos, -1, 0, success) < 0 && *success) + v = -probe_dtm_dc(pos, 0, success) - 1; + else + v = -VALUE_INFINITE; + undo_move(pos, move); + bestCap = max(bestCap, v); + if (*success == 0) return 0; + } - uint16_t moves0[MAX_MOVES]; - uint16_t *moves = moves0, *end = NULL; - - if (wdl > 0) - { - // Generate at least all legal non-capturing pawn moves - // including non-capturing promotions. - end = gen_pawn_quiets_or_promotions(pos, moves); - for (; moves < end; moves++) - { - struct pos pos1; - if (!do_move(&pos1, pos, *moves)) - continue; - int v = (pos1.ep == 0? - -probe_ab(&pos1, -2, -wdl + 1, success): - -probe_wdl(&pos1, success)); - if (*success == 0) - return 0; - if (v == wdl) - return (v == 2? 1: 101); - } - } + int dtm = probe_dtm_table(pos, won, success); + v = won ? VALUE_MATE - 2 * dtm + 1 : -VALUE_MATE + 2 * dtm; - dtz = 1 + probe_dtz_table(pos, wdl, success); - if (*success >= 0) - { - if (wdl & 1) - dtz += 100; - return (wdl >= 0? dtz: -dtz); - } - - if (wdl > 0) - { - int best = BEST_NONE; - moves = moves0; - end = gen_moves(pos, moves); - for (; moves < end; moves++) - { - struct pos pos1; - if (!do_move(&pos1, pos, *moves)) - continue; - if (pos1.rule50 == 0) - continue; - int v = -probe_dtz(&pos1, success); - if (*success == 0) - return 0; - if (v > 0 && v + 1 < best) - best = v + 1; - } - assert(best != BEST_NONE); - return best; - } - else - { - int best = -1; - end = gen_moves(pos, moves); - for (; moves < end; moves++) - { - int v; - struct pos pos1; - if (!do_move(&pos1, pos, *moves)) - continue; - if (pos1.rule50 == 0) - { - if (wdl == -2) - v = -1; - else - { - v = probe_ab(&pos1, 1, 2, success); - v = (v == 2) ? 0 : -101; - } - } - else - v = -probe_dtz(&pos1, success) - 1; - if (*success == 0) - return 0; - if (v < best) - best = v; - } - return best; - } + return max(bestCap, v); } +#endif -static const int wdl_to_dtz[] = -{ - -1, -101, 0, 101, 1 -}; +static Value probe_dtm_win(Pos *pos, int *success); -/* - * Probe the DTZ table for a particular position. - * If *success != 0, the probe was successful. - * The return value is from the point of view of the side to move: - * n < -100 : loss, but draw under 50-move rule - * -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0) - * 0 : draw - * 1 < n <= 100 : win in n ply (assuming 50-move counter == 0) - * 100 < n : win, but draw under 50-move rule - * - * The return value n can be off by 1: a return value -n can mean a loss - * in n+1 ply and a return value +n can mean a win in n+1 ply. This - * cannot happen for tables with positions exactly on the "edge" of - * the 50-move rule. - * - * This implies that if dtz > 0 is returned, the position is certainly - * a win if dtz + 50-move-counter <= 99. Care must be taken that the engine - * picks moves that preserve dtz + 50-move-counter <= 99. - * - * If n = 100 immediately after a capture or pawn move, then the position - * is also certainly a win, and during the whole phase until the next - * capture or pawn move, the inequality to be preserved is - * dtz + 50-movecounter <= 100. - * - * In short, if a move is available resulting in dtz + 50-move-counter <= 99, - * then do not accept moves leading to dtz + 50-move-counter == 100. - */ -static int probe_dtz(const struct pos *pos, int *success) +// Probe a position known to lose by probing the DTM table and looking +// at captures. +static Value probe_dtm_loss(Pos *pos, int *success) { - *success = 1; - int v = probe_dtz_no_ep(pos, success); + Value v, best = -VALUE_INFINITE, numEp = 0; + + // Generate at least all legal captures including (under)promotions + ExtMove *end, *m = (pos->st-1)->endMoves; + end = pos_checkers() ? generate_evasions(pos, m) + : add_underprom_caps(pos, m, generate_captures(pos, m)); + pos->st->endMoves = end; + + for (; m < end; m++) { + Move move = m->move; + if (!is_capture(pos, move) || !is_legal(pos, move)) + continue; + if (type_of_m(move) == ENPASSANT) + numEp++; + do_move(pos, move, gives_check(pos, pos->st, move)); + v = -probe_dtm_win(pos, success) + 1; + undo_move(pos, move); + best = max(best, v); if (*success == 0) - return 0; + return 0; + } - if (pos->ep == 0) - return v; + // If there are en passant captures, the position without ep rights + // may be a stalemate. If it is, we must avoid probing the DTM table. + if (numEp != 0 && generate_legal(pos, m) == m + numEp) + return best; - int v1 = -3; - uint16_t moves0[2]; // Max=2 possible en-passant captures. - uint16_t *moves = moves0; - uint16_t *end = gen_pawn_ep_captures(pos, moves); - for (; moves < end; moves++) - { - struct pos pos1; - if (!do_move(&pos1, pos, *moves)) - continue; - int v0 = -probe_ab(&pos1, -2, 2, success); - if (*success == 0) - return 0; - if (v0 > v1) - v1 = v0; - } - - if (v1 > -3) - { - v1 = wdl_to_dtz[v1 + 2]; - if (v < -100) - { - if (v1 >= 0) - v = v1; - } - else if (v < 0) - { - if (v1 >= 0 || v1 < -100) - v = v1; - } - else if (v > 100) - { - if (v1 > 0) - v = v1; - } - else if (v > 0) - { - if (v1 == 1) - v = v1; - } - else if (v1 >= 0) - v = v1; - else - { - uint16_t moves0[MAX_MOVES]; - uint16_t *moves = moves0; - uint16_t *end = gen_moves(pos, moves); - bool found = false; - for (; moves < end; moves++) - { - if (is_en_passant(pos, *moves)) - continue; - struct pos pos1; - if (do_move(&pos1, pos, *moves)) - { - found = true; - break; - } - } - if (!found) - v = v1; // Forced to play the losing ep capture. - } - } - - return v; + v = -VALUE_MATE + 2 * probe_dtm_table(pos, 0, success); + return max(best, v); } -static unsigned dtz_to_wdl(int cnt50, int dtz) +static Value probe_dtm_win(Pos *pos, int *success) { - int wdl = 0; - if (dtz > 0) - wdl = (dtz + cnt50 <= 100? 2: 1); - else if (dtz < 0) - wdl = (-dtz + cnt50 <= 100? -2: -1); - return wdl + 2; + Value v, best = -VALUE_INFINITE; + + // Generate all moves + ExtMove *m = (pos->st-1)->endMoves; + ExtMove *end = pos_checkers() ? generate_evasions(pos, m) + : generate_non_evasions(pos, m); + pos->st->endMoves = end; + + // Perform a 1-ply search + for (; m < end; m++) { + Move move = m->move; + if (!is_legal(pos, move)) + continue; + do_move(pos, move, gives_check(pos, pos->st, move)); + if ( (ep_square() ? TB_probe_wdl(pos, success) + : probe_ab(pos, -1, 0, success)) < 0 + && *success) + v = -probe_dtm_loss(pos, success) - 1; + else + v = -VALUE_INFINITE; + undo_move(pos, move); + best = max(best, v); + if (*success == 0) return 0; + } + + return best; } -static uint16_t probe_root(const struct pos *pos, int *score, - unsigned *results) +Value TB_probe_dtm(Pos *pos, int wdl, int *success) { - int success; - int dtz = probe_dtz(pos, &success); - if (!success) - return 0; + assert(wdl != 0); - int16_t scores[MAX_MOVES]; - uint16_t moves0[MAX_MOVES]; - uint16_t *moves = moves0; - uint16_t *end = gen_moves(pos, moves); - size_t len = end - moves; - size_t num_draw = 0; - unsigned j = 0; - for (unsigned i = 0; i < len; i++) - { - struct pos pos1; - if (!do_move(&pos1, pos, moves[i])) - { - scores[i] = SCORE_ILLEGAL; - continue; - } - int v = 0; - if (dtz > 0 && is_mate(&pos1)) - v = 1; - else - { - if (pos1.rule50 != 0) - { - v = -probe_dtz(&pos1, &success); - if (v > 0) - v++; - else if (v < 0) - v--; - } - else - { - v = -probe_wdl(&pos1, &success); - v = wdl_to_dtz[v + 2]; - } - } - num_draw += (v == 0); - if (!success) - return 0; - scores[i] = v; - if (results != NULL) - { - unsigned res = 0; - res = TB_SET_WDL(res, dtz_to_wdl(pos->rule50, v)); - res = TB_SET_FROM(res, move_from(moves[i])); - res = TB_SET_TO(res, move_to(moves[i])); - res = TB_SET_PROMOTES(res, move_promotes(moves[i])); - res = TB_SET_EP(res, is_en_passant(pos, moves[i])); - res = TB_SET_DTZ(res, (v < 0? -v: v)); - results[j++] = res; - } - } - if (results != NULL) - results[j++] = TB_RESULT_FAILED; - if (score != NULL) - *score = dtz; + *success = 1; - // Now be a bit smart about filtering out moves. - if (dtz > 0) // winning (or 50-move rule draw) - { - int best = BEST_NONE; - uint16_t best_move = 0; - for (unsigned i = 0; i < len; i++) - { - int v = scores[i]; - if (v == SCORE_ILLEGAL) - continue; - if (v > 0 && v < best) - { - best = v; - best_move = moves[i]; - } - } - return (best == BEST_NONE? 0: best_move); - } - else if (dtz < 0) // losing (or 50-move rule draw) - { - int best = 0; - uint16_t best_move = 0; - for (unsigned i = 0; i < len; i++) - { - int v = scores[i]; - if (v == SCORE_ILLEGAL) - continue; - if (v < best) - { - best = v; - best_move = moves[i]; - } - } - return (best == 0? MOVE_CHECKMATE: best_move); - } - else // drawing - { - // Check for stalemate: - if (num_draw == 0) - return MOVE_STALEMATE; - - // Select a "random" move that preserves the draw. - // Uses calc_key as the PRNG. - size_t count = calc_key(pos, !pos->turn) % num_draw; - for (unsigned i = 0; i < len; i++) - { - int v = scores[i]; - if (v == SCORE_ILLEGAL) - continue; - if (v == 0) - { - if (count == 0) - return moves[i]; - count--; - } - } - return 0; - } + return wdl > 0 ? probe_dtm_win(pos, success) + : probe_dtm_loss(pos, success); } -bool tb_init_impl(const char *path) +#if 0 +// To be called only for non-drawn positions. +Value TB_probe_dtm2(Pos *pos, int wdl, int *success) { - if (sizeof(uint64_t) != 8 || // Paranoid check - sizeof(uint32_t) != 4 || - sizeof(uint16_t) != 2 || - sizeof(uint8_t) != 1) - return false; - king_attacks_init(); - knight_attacks_init(); - bishop_attacks_init(); - rook_attacks_init(); - pawn_attacks_init(); - if (path == NULL) - path = ""; - init_tablebases(path); - return true; -} + assert(wdl != 0); + + *success = 1; + Value v, bestCap = -VALUE_INFINITE, bestEp = -VALUE_INFINITE; + + ExtMove *end, *m = (pos->st-1)->endMoves; + + // Generate at least all legal captures including (under)promotions + if (!pos_checkers()) { + end = generate_captures(pos, m); + end = add_underprom_caps(pos, m, end); + } else + end = generate_evasions(pos, m); + pos->st->endMoves = end; + + // Resolve captures, letting bestCap keep track of the best non-ep + // capture and letting bestEp keep track of the best ep capture. + for (; m < end; m++) { + Move move = m->move; + if (!is_capture(pos, move) || !is_legal(pos, move)) + continue; + do_move(pos, move, gives_check(pos, pos->st, move)); + if (wdl < 0) + v = -probe_dtm_dc(pos, 1, success) + 1; + else if (probe_ab(pos, -1, 0, success) < 0 && *success) + v = -probe_dtm_dc(pos, 0, success) - 1; + else + v = -VALUE_MATE; + undo_move(pos, move); + if (type_of_m(move) == ENPASSANT) + bestEp = max(bestEp, v); + else + bestCap = max(bestCap, v); + if (*success == 0) + return 0; + } + + // If there are en passant captures, we have to determine the WDL value + // for the position without ep rights if it might be different. + if (bestEp > -VALUE_INFINITE && (bestEp < 0 || bestCap < 0)) { + assert(ep_square() != 0); + uint8_t s = pos->st->epSquare; + pos->st->epSquare = 0; + wdl = probe_ab(pos, -2, 2, success); + pos->st->epSquare = s; + if (*success == 0) + return 0; + if (wdl == 0) + return bestEp; + } -unsigned tb_probe_wdl_impl( - uint64_t white, - uint64_t black, - uint64_t kings, - uint64_t queens, - uint64_t rooks, - uint64_t bishops, - uint64_t knights, - uint64_t pawns, - unsigned ep, - bool turn) -{ - struct pos pos = - { - white, - black, - kings, - queens, - rooks, - bishops, - knights, - pawns, - 0, - (uint8_t)ep, - turn - }; - int success; - int v = probe_wdl(&pos, &success); - if (success == 0) - return TB_RESULT_FAILED; - return (unsigned)(v + 2); + bestCap = max(bestCap, bestEp); + int dtm = probe_dtm_table(pos, wdl > 0, success); + v = wdl > 0 ? VALUE_MATE - 2 * dtm + 1 : -VALUE_MATE + 2 * dtm; + return max(bestCap, v); } +#endif -unsigned tb_probe_root_impl( - uint64_t white, - uint64_t black, - uint64_t kings, - uint64_t queens, - uint64_t rooks, - uint64_t bishops, - uint64_t knights, - uint64_t pawns, - unsigned rule50, - unsigned ep, - bool turn, - unsigned *results) +static int WdlToDtz[] = { -1, -101, 0, 101, 1 }; + +// Probe the DTZ table for a particular position. +// If *success != 0, the probe was successful. +// The return value is from the point of view of the side to move: +// n < -100 : loss, but draw under 50-move rule +// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0) +// 0 : draw +// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0) +// 100 < n : win, but draw under 50-move rule +// +// If the position mate, -1 is returned instead of 0. +// +// The return value n can be off by 1: a return value -n can mean a loss +// in n+1 ply and a return value +n can mean a win in n+1 ply. This +// cannot happen for tables with positions exactly on the "edge" of +// the 50-move rule. +// +// This means that if dtz > 0 is returned, the position is certainly +// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine +// picks moves that preserve dtz + 50-move-counter <= 99. +// +// If n = 100 immediately after a capture or pawn move, then the position +// is also certainly a win, and during the whole phase until the next +// capture or pawn move, the inequality to be preserved is +// dtz + 50-movecounter <= 100. +// +// In short, if a move is available resulting in dtz + 50-move-counter <= 99, +// then do not accept moves leading to dtz + 50-move-counter == 100. +// +int probe_dtz(struct Pos *pos, int *success) { - struct pos pos = - { - white, - black, - kings, - queens, - rooks, - bishops, - knights, - pawns, - (uint8_t)rule50, - (uint8_t)ep, - turn - }; - int dtz; - if (!is_valid(&pos)) - return TB_RESULT_FAILED; - uint16_t move = probe_root(&pos, &dtz, results); - if (move == 0) - return TB_RESULT_FAILED; - if (move == MOVE_CHECKMATE) - return TB_RESULT_CHECKMATE; - if (move == MOVE_STALEMATE) - return TB_RESULT_STALEMATE; - unsigned res = 0; - res = TB_SET_WDL(res, dtz_to_wdl(rule50, dtz)); - res = TB_SET_DTZ(res, (dtz < 0? -dtz: dtz)); - res = TB_SET_FROM(res, move_from(move)); - res = TB_SET_TO(res, move_to(move)); - res = TB_SET_PROMOTES(res, move_promotes(move)); - res = TB_SET_EP(res, is_en_passant(&pos, move)); - return res; + int wdl = TB_probe_wdl(pos, success); + if (*success == 0) return 0; + + // If draw, then dtz = 0. + if (wdl == 0) return 0; + + // Check for winning capture or en passant capture as only best move. + if (*success == 2) + return WdlToDtz[wdl + 2]; + + ExtMove *m = (pos->st-1)->endMoves, *end = NULL; + + // If winning, check for a winning pawn move. + if (wdl > 0) { + // Generate at least all legal non-capturing pawn moves + // including non-capturing promotions. + // (The following calls in fact generate all moves.) + end = !pos_checkers() + ? generate_non_evasions(pos, m) + : generate_evasions(pos, m); + pos->st->endMoves = end; + + for (; m < end; m++) { + Move move = m->move; + if (type_of_p(moved_piece(move)) != PAWN || is_capture(pos, move) + || !is_legal(pos, move)) + continue; + do_move(pos, move, gives_check(pos, pos->st, move)); + int v = -TB_probe_wdl(pos, success); + undo_move(pos, move); + if (*success == 0) return 0; + if (v == wdl) + return WdlToDtz[wdl + 2]; + } + } + + // If we are here, we know that the best move is not an ep capture. + // In other words, the value of wdl corresponds to the WDL value of + // the position without ep rights. It is therefore safe to probe the + // DTZ table with the current value of wdl. + + int dtz = probe_dtz_table(pos, wdl, success); + if (*success >= 0) + return WdlToDtz[wdl + 2] + ((wdl > 0) ? dtz : -dtz); + + // *success < 0 means we need to probe DTZ for the other side to move. + int best; + if (wdl > 0) { + best = INT32_MAX; + // If wdl > 0, we have already generated all moves. + m = (pos->st-1)->endMoves; + } else { + // If (cursed) loss, the worst case is a losing capture or pawn move + // as the "best" move, leading to dtz of -1 or -101. + // In case of mate, this will cause -1 to be returned. + best = WdlToDtz[wdl + 2]; + // If wdl < 0, we still have to generate all moves. + if (!pos_checkers()) + end = generate_non_evasions(pos, m); + else + end = generate_evasions(pos, m); + pos->st->endMoves = end; + } + + for (; m < end; m++) { + Move move = m->move; + // We can skip pawn moves and captures. + // If wdl > 0, we already caught them. If wdl < 0, the initial value + // of best already takes account of them. + if (is_capture(pos, move) || type_of_p(moved_piece(move)) == PAWN + || !is_legal(pos, move)) + continue; + do_move(pos, move, gives_check(pos, pos->st, move)); + int v = -TB_probe_dtz(pos, success); + if ( v == 1 + && pos_checkers() + && generate_legal(pos, (pos->st-1)->endMoves) == (pos->st-1)->endMoves) + best = 1; + else if (wdl > 0) { + if (v > 0 && v + 1 < best) + best = v + 1; + } else { + if (v - 1 < best) + best = v - 1; + } + undo_move(pos, move); + if (*success == 0) return 0; + } + return best; } -#ifndef TB_NO_HELPER_API - -unsigned tb_pop_count(uint64_t bb) +// Use the DTZ tables to rank and score all root moves in the list. +// A return value of 0 means that not all probes were successful. +int TB_root_probe_dtz(Pos *pos, struct RootMoves *rm) { - return popcount(bb); -} + int v, success; + + // Obtain 50-move counter for the root position. + int cnt50 = pos_rule50_count(); + + // Check whether a position was repeated since the last zeroing move. + // In that case, we need to be careful and play DTZ-optimal moves if + // winning. + int rep = pos->hasRepeated; + + // The border between draw and win lies at rank 1 or rank 900, depending + // on whether the 50-move rule is used. + int bound = option_value(OPT_SYZ_50_MOVE) ? 900 : 1; + + // Probe, rank and score each move. + pos->st->endMoves = (pos->st-1)->endMoves; + for (int i = 0; i < rm->size; i++) { + struct RootMove *m = &rm->move[i]; + do_move(pos, m->pv[0], gives_check(pos, pos->st, m->pv[0])); + + // Calculate dtz for the current move counting from the root position. + if (pos_rule50_count() == 0) { + // If the move resets the 50-move counter, dtz is -101/-1/0/1/101. + v = -TB_probe_wdl(pos, &success); + v = WdlToDtz[v + 2]; + } else { + // Otherwise, take dtz for the new position and correct by 1 ply. + v = -TB_probe_dtz(pos, &success); + if (v > 0) v++; + else if (v < 0) v--; + } + // Make sure that a mating move gets value 1. + if (pos_checkers() && v == 2) { + if (generate_legal(pos, (pos->st-1)->endMoves) == (pos->st-1)->endMoves) + v = 1; + } -unsigned tb_lsb(uint64_t bb) -{ - return lsb(bb); + undo_move(pos, m->pv[0]); + if (!success) return 0; + + // Better moves are ranked higher. Guaranteed wins are ranked equally. + // Losing moves are ranked equally unless a 50-move draw is in sight. + // Note that moves ranked 900 have dtz + cnt50 == 100, which in rare + // cases may be insufficient to win as dtz may be one off (see the + // comments before TB_probe_dtz()). + int r = v > 0 ? (v + cnt50 <= 99 && !rep ? 1000 : 1000 - (v + cnt50)) + : v < 0 ? (-v * 2 + cnt50 < 100 ? -1000 : -1000 + (-v + cnt50)) + : 0; + m->tbRank = r; + + // Determine the score to be displayed for this move. Assign at least + // 1 cp to cursed wins and let it grow to 49 cp as the position gets + // closer to a real win. + m->tbScore = r >= bound ? VALUE_MATE - MAX_MATE_PLY - 1 + : r > 0 ? max( 3, r - 800) * PawnValueEg / 200 + : r == 0 ? VALUE_DRAW + : r > -bound ? min(-3, r + 800) * PawnValueEg / 200 + : -VALUE_MATE + MAX_MATE_PLY + 1; + } + + return 1; } -uint64_t tb_pop_lsb(uint64_t bb) +// Use the WDL tables to rank all root moves in the list. +// This is a fallback for the case that some or all DTZ tables are missing. +// A return value of 0 means that not all probes were successful. +int root_probe_wdl(struct Pos *pos, struct RootMoves *rm) { - return poplsb(bb); + static int WdlToRank[] = { -1000, -899, 0, 899, 1000 }; + static Value WdlToValue[] = { + -VALUE_MATE + MAX_MATE_PLY + 1, + VALUE_DRAW - 2, + VALUE_DRAW, + VALUE_DRAW + 2, + VALUE_MATE - MAX_MATE_PLY - 1 + }; + + int v, success; + int move50 = option_value(OPT_SYZ_50_MOVE); + + // Probe, rank and score each move. + pos->st->endMoves = (pos->st-1)->endMoves; + for (int i = 0; i < rm->size; i++) { + struct RootMove *m = &rm->move[i]; + do_move(pos, m->pv[0], gives_check(pos, pos->st, m->pv[0])); + v = -TB_probe_wdl(pos, &success); + undo_move(pos, m->pv[0]); + if (!success) return 0; + if (!move50) + v = v > 0 ? 2 : v < 0 ? -2 : 0; + m->tbRank = WdlToRank[v + 2]; + m->tbScore = WdlToValue[v + 2]; + } + + return 1; } -uint64_t tb_king_attacks(unsigned sq) +// Use the DTM tables to find mate scores. +// Either DTZ or WDL must have been probed successfully earlier. +// A return value of 0 means that not all probes were successful. +int TB_root_probe_dtm(Pos *pos, struct RootMoves *rm) { - return king_attacks(sq); -} + int success; + Value tmpScore[rm->size]; -uint64_t tb_queen_attacks(unsigned sq, uint64_t occ) -{ - return queen_attacks(sq, occ); -} + // Probe each move. + pos->st->endMoves = (pos->st-1)->endMoves; + for (int i = 0; i < rm->size; i++) { + struct RootMove *m = &rm->move[i]; -uint64_t tb_rook_attacks(unsigned sq, uint64_t occ) -{ - return rook_attacks(sq, occ); -} + // Use tbScore to find out if the position is won or lost. + int wdl = m->tbScore > PawnValueEg ? 2 + : m->tbScore < -PawnValueEg ? -2 : 0; -uint64_t tb_bishop_attacks(unsigned sq, uint64_t occ) -{ - return bishop_attacks(sq, occ); -} + if (wdl == 0) + tmpScore[i] = 0; + else { + // Probe and adjust mate score by 1 ply. + do_move(pos, m->pv[0], gives_check(pos, pos->st, m->pv[0])); + Value v = -TB_probe_dtm(pos, -wdl, &success); + tmpScore[i] = wdl > 0 ? v - 1 : v + 1; + undo_move(pos, m->pv[0]); + if (success == 0) + return 0; + } + } -uint64_t tb_knight_attacks(unsigned sq) -{ - return knight_attacks(sq); -} + // All probes were successful. Now adjust TB scores and ranks. + for (int i = 0; i < rm->size; i++) { + struct RootMove *m = &rm->move[i]; -uint64_t tb_pawn_attacks(unsigned sq, bool color) -{ - return pawn_attacks(sq, color); + m->tbScore = tmpScore[i]; + + // Let rank correspond to mate score, except for critical moves + // ranked 900, which we rank below all other mates for safety. + // By ranking mates above 1000 or below -1000, we let the search + // know it need not search those moves. + m->tbRank = m->tbRank == 900 ? 1001 : m->tbScore; + } + + return 1; } -#endif /* TB_NO_HELPER_API */ +// Use the DTM tables to complete a PV with mate score. +void TB_expand_mate(Pos *pos, struct RootMove *move) +{ + int success = 1, chk = 0; + Value v = move->score, w = 0; + int wdl = v > 0 ? 2 : -2; + ExtMove *m; + + if (move->pvSize == TB_MAX_PLY) + return; + + // First get to the end of the incomplete PV. + for (int i = 0; i < move->pvSize; i++) { + v = v > 0 ? -v - 1 : -v + 1; + wdl = -wdl; + pos->st->endMoves = (pos->st-1)->endMoves; + do_move(pos, move->pv[i], gives_check(pos, pos->st, move->pv[i])); + } + + // Now try to expand until the actual mate. + if (popcount(pieces()) <= TB_CardinalityDTM) + while (v != -VALUE_MATE && move->pvSize < TB_MAX_PLY) { + v = v > 0 ? -v - 1 : -v + 1; + wdl = -wdl; + pos->st->endMoves = generate_legal(pos, (pos->st-1)->endMoves); + for (m = (pos->st-1)->endMoves; m < pos->st->endMoves; m++) { + do_move(pos, m->move, gives_check(pos, pos->st, m->move)); + if (wdl < 0) + chk = TB_probe_wdl(pos, &success); // verify that m->move wins + w = success && (wdl > 0 || chk < 0) + ? TB_probe_dtm(pos, wdl, &success) + : 0; + undo_move(pos, m->move); + if (!success || v == w) break; + } + if (!success || v != w) + break; + move->pv[move->pvSize++] = m->move; + do_move(pos, m->move, gives_check(pos, pos->st, m->move)); + } + // Get back to the root position. + for (int i = move->pvSize - 1; i >= 0; i--) + undo_move(pos, move->pv[i]); +} diff --git a/src/tbprobe.h b/src/tbprobe.h index 96dcb85..b2d56f3 100644 --- a/src/tbprobe.h +++ b/src/tbprobe.h @@ -88,7 +88,7 @@ extern unsigned tb_probe_root_impl( /****************************************************************************/ #define TB_MAX_MOVES (192+1) - +#define TB_MAX_PLY 256 #define TB_CASTLING_K 0x1 /* White king-side. */ #define TB_CASTLING_Q 0x2 /* White queen-side. */ #define TB_CASTLING_k 0x4 /* Black king-side. */ @@ -172,10 +172,12 @@ extern unsigned TB_LARGEST; * initialized. If no tablebase files are found, then `true' is returned * and TB_LARGEST is set to zero. */ -static inline bool tb_init(const char *_path) -{ - return tb_init_impl(_path); -} +bool tb_init(const char *_path); + +/* + * Free any resources allocated by tb_init + */ +void tb_free(void); /* * Probe the Win-Draw-Loss (WDL) table. From e0bfd48fbc2e63070bf847a8b70582e46df1cb88 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Wed, 17 Apr 2019 06:10:31 -0700 Subject: [PATCH 22/75] Fix WDL value when a mate in 1 is possible at the 50-move threshold (fix ported from Cfish - see https://github.com/syzygy1/Cfish/commit/d4d1493a487ff3804c198ec2032b4bd91bec9088#diff-98bbf44ad0a296906914322f9b11cd63). --- src/tbprobe.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 6e50224..01aa085 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1565,8 +1565,13 @@ static int probe_dtz_no_ep(const struct pos *pos, int *success) int v = -probe_dtz(&pos1, success); if (*success == 0) return 0; - if (v > 0 && v + 1 < best) - best = v + 1; + if (v > 0 && v + 1 < best) { + // handle the case of mate in 1 + if (v == 1 && is_mate(&pos1)) { + v = 0; + } + best = v + 1; + } } assert(best != BEST_NONE); return best; From 4bad501611c3eff626cc1c160801f829b6b9a3e5 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 21 Apr 2019 15:52:56 -0700 Subject: [PATCH 23/75] Bug fixes and code cleanup. Changes include: 1. Expose new interfaces for DTZ and WDL probing with ranks/scores as in Cfish, as well as the old Fathom interface. 2. Put both tbprobe.c and tbchess.c under MIT license. 3. Require atomic support, remove this option from tbconfig.h. 4. Use stdendian for big/little endian support. 5. Add constants for mate scores, pawn scores etc. to tbconfig.h. Note: two test cases from Arasan unit tests still not passing due to Cfish bug. --- LICENSE | 2 +- src/apps/fathom.c | 86 ++- src/stdendian.h | 282 ++++++++++ src/tbchess.c | 347 ++++++------- src/tbconfig.h | 31 +- src/tbprobe.c | 1265 ++++++++++++++++++++++++++++++--------------- src/tbprobe.h | 77 +++ 7 files changed, 1463 insertions(+), 627 deletions(-) create mode 100644 src/stdendian.h diff --git a/LICENSE b/LICENSE index 9a37ffa..3229171 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ The MIT License (MIT) Copyright (c) 2015 basil00 -Modifications Copyright (c) 2016-2018 by Jon Dart +Modifications Copyright (c) 2016-2019 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/apps/fathom.c b/src/apps/fathom.c index 8ddb433..47d718b 100644 --- a/src/apps/fathom.c +++ b/src/apps/fathom.c @@ -260,6 +260,21 @@ static bool parse_FEN(struct pos *pos, const char *fen) return false; } +/* + * Test if the given move is an en passant capture. + */ +static bool is_en_passant(const struct pos *pos, uint64_t from, uint64_t to) +{ + uint64_t us = (pos->turn? pos->white: pos->black); + if (pos->ep == 0) + return false; + if (to != pos->ep) + return false; + if ((board(from) & us & pos->pawns) == 0) + return false; + return true; +} + /* * Test if the king is in check. */ @@ -288,16 +303,12 @@ static bool is_check(const struct pos *pos) /* * Convert a move into a string. */ -static void move_to_str(const struct pos *pos, unsigned move, char *str) -{ - uint64_t occ = pos->black | pos->white; - uint64_t us = (pos->turn? pos->white: pos->black); - unsigned from = TB_GET_FROM(move); - unsigned to = TB_GET_TO(move); +static void move_parts_to_str(const struct pos *pos, int from, int to, int promotes, char *str) { unsigned r = rank(from); unsigned f = file(from); - unsigned promotes = TB_GET_PROMOTES(move); - bool capture = (occ & board(to)) != 0 || (TB_GET_EP(move) != 0); + uint64_t occ = pos->black | pos->white; + uint64_t us = (pos->turn? pos->white: pos->black); + bool capture = (occ & board(to)) != 0 || is_en_passant(pos,from,to); uint64_t b = board(from), att = 0; if (b & pos->kings) *str++ = 'K'; @@ -359,6 +370,14 @@ static void move_to_str(const struct pos *pos, unsigned move, char *str) *str++ = '\0'; } +static void move_to_str(const struct pos *pos, unsigned move, char *str) +{ + unsigned from = TB_GET_FROM(move); + unsigned to = TB_GET_TO(move); + unsigned promotes = TB_GET_PROMOTES(move); + move_parts_to_str(pos, from, to, promotes, str); +} + /* * Do a move. Does not support castling. */ @@ -438,6 +457,7 @@ static void do_move(struct pos *pos, unsigned move) */ static void print_PV(struct pos *pos) { + struct pos temp = *pos; putchar('\n'); bool first = true, check = false; if (!pos->turn) @@ -453,19 +473,19 @@ static void print_PV(struct pos *pos) if (move == TB_RESULT_FAILED) { printf("{TB probe failed}\n"); - return; + break; } if (move == TB_RESULT_CHECKMATE) { printf("# %s\n", (pos->turn? "0-1": "1-0")); - return; + break; } if (check) putchar('+'); if (pos->rule50 >= 100 || move == TB_RESULT_STALEMATE) { printf(" 1/2-1/2\n"); - return; + break; } char str[32]; @@ -479,6 +499,8 @@ static void print_PV(struct pos *pos) do_move(pos, move); check = is_check(pos); } + // restore to root position + *pos = temp; } /* @@ -600,6 +622,10 @@ int main(int argc, char **argv) // (0) init: if (path == NULL) path = getenv("TB_PATH"); + if (path == NULL) { + fprintf(stderr, "Path not set"); + exit(EXIT_FAILURE); + } tb_init(path); if (TB_LARGEST == 0) { @@ -676,6 +702,44 @@ int main(int argc, char **argv) printf("\"]\n"); print_PV(pos); + struct TbRootMoves moves; + int result = tb_probe_root_dtz(pos->white, pos->black, pos->kings, + pos->queens, pos->rooks, pos->bishops, pos->knights, pos->pawns, + pos->rule50, pos->castling, pos->ep, pos->turn, false, true, &moves); + + + if (!result) { + fprintf(stderr,"DTZ proble failed\n"); + return -1; + } + printf("%d moves returned from DTZ probe\n",moves.size); + char str[20]; + for (int i = 0; i < moves.size; i++) { + struct TbRootMove *m = &(moves.moves[i]); + move_parts_to_str(pos, TB_MOVE_FROM(m->move), + TB_MOVE_TO(m->move), + TB_MOVE_PROMOTES(m->move),str); + + printf("%s rank = %d score=%d\n", str, m->tbRank, m->tbScore); + } + result = tb_probe_root_wdl(pos->white, pos->black, pos->kings, + pos->queens, pos->rooks, pos->bishops, pos->knights, pos->pawns, + pos->rule50, pos->castling, pos->ep, pos->turn, true, &moves); + + + if (!result) { + fprintf(stderr,"WDL proble failed\n"); + return -1; + } + printf("%d moves returned from WDL probe\n",moves.size); + for (int i = 0; i < moves.size; i++) { + struct TbRootMove *m = &(moves.moves[i]); + move_parts_to_str(pos, TB_MOVE_FROM(m->move), + TB_MOVE_TO(m->move), + TB_MOVE_PROMOTES(m->move),str); + + printf("%s rank = %d score=%d\n", str, m->tbRank, m->tbScore); + } return 0; } diff --git a/src/stdendian.h b/src/stdendian.h new file mode 100644 index 0000000..6f2bf98 --- /dev/null +++ b/src/stdendian.h @@ -0,0 +1,282 @@ +/* from https://gist.github.com/michaeljclark/3b4fd912f6fa8bb598b3 */ +/* modified to use functions not macros for bswap */ +/* and added a fix for Cygwin */ +/* + * stdendian.h + * + * This header defines the following endian macros as defined here: + * http://austingroupbugs.net/view.php?id=162 + * + * BYTE_ORDER this macro shall have a value equal to one + * of the *_ENDIAN macros in this header. + * LITTLE_ENDIAN if BYTE_ORDER == LITTLE_ENDIAN, the host + * byte order is from least significant to + * most significant. + * BIG_ENDIAN if BYTE_ORDER == BIG_ENDIAN, the host byte + * order is from most significant to least + * significant. + * + * The following are defined as macros: + * + * uint16_t bswap16(uint16_t x); + * uint32_t bswap32(uint32_t x); + * uint64_t bswap64(uint64_t x); + + * uint16_t htobe16(uint16_t x); + * uint16_t htole16(uint16_t x); + * uint16_t be16toh(uint16_t x); + * uint16_t le16toh(uint16_t x); + * + * uint32_t htobe32(uint32_t x); + * uint32_t htole32(uint32_t x); + * uint32_t be32toh(uint32_t x); + * uint32_t le32toh(uint32_t x); + * + * uint64_t htobe64(uint64_t x); + * uint64_t htole64(uint64_t x); + * uint64_t be64toh(uint64_t x); + * uint64_t le64toh(uint64_t x); + * + * The header defines the following macro for OpenCL compatibility + * https://www.khronos.org/registry/cl/sdk/2.0/docs/man/xhtml/preprocessorDirectives.html + * + * __ENDIAN_LITTLE__ if BYTE_ORDER == LITTLE_ENDIAN then this + * macro is present for OpenCL compatibility + * + * The implementation provides a uniform interface to endian macros using only + * system headers on recent Linux, Darwin, FreeBSD, Solaris and Windows systems. + * + * This approach is intended to avoid the need for preflight configure scripts. + * An alternative approach would be to test compiler CPU architecture marcros. + * + * This header has had *limited* testing on recent C11/C++11 compilers and is + * based on the austin bug tracker interface, manpages, and headers present in + * Linux, FreeBSD, Windows, Solaris and Darwin. + * + * The header uses __builtin_bswapXX intrinsic with GCC/Clang (__GNUC__) on + * platforms that do not provide bswap16, bswap32, bswap64 (Darwin) + * + * Public Domain. + */ + +/* requires C11 or C++11 */ +#if defined (__cplusplus) +#include +#elif !defined (__OPENCL_VERSION__) +#include +#endif + +/* Linux / GLIBC */ +#if defined(__linux__) || defined(__GLIBC__) || defined(__CYGWIN__) +#include +#include +#define __ENDIAN_DEFINED 1 +#define __BSWAP_DEFINED 1 +#define __HOSTSWAP_DEFINED 1 +// NDK defines _BYTE_ORDER etc +#ifndef _BYTE_ORDER +#define _BYTE_ORDER __BYTE_ORDER +#define _LITTLE_ENDIAN __LITTLE_ENDIAN +#define _BIG_ENDIAN __BIG_ENDIAN +#endif +#define bswap16(x) bswap_16(x) +#define bswap32(x) bswap_32(x) +#define bswap64(x) bswap_64(x) +#endif /* __linux__ || __GLIBC__ */ + +/* BSD */ +#if defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) || defined(__OpenBSD__) +#include +#define __ENDIAN_DEFINED 1 +#define __BSWAP_DEFINED 1 +#define __HOSTSWAP_DEFINED 1 +#endif /* BSD */ + +/* Solaris */ +#if defined (sun) +#include +/* sun headers don't set a value for _LITTLE_ENDIAN or _BIG_ENDIAN */ +#if defined(_LITTLE_ENDIAN) +#undef _LITTLE_ENDIAN +#define _LITTLE_ENDIAN 1234 +#define _BIG_ENDIAN 4321 +#define _BYTE_ORDER _LITTLE_ENDIAN +#elif defined(_BIG_ENDIAN) +#undef _BIG_ENDIAN +#define _LITTLE_ENDIAN 1234 +#define _BIG_ENDIAN 4321 +#define _BYTE_ORDER _BIG_ENDIAN +#endif +#define __ENDIAN_DEFINED 1 +#endif /* sun */ + +/* Windows */ +#if defined(_WIN32) || defined(_MSC_VER) +/* assumes all Microsoft targets are little endian */ +#define _LITTLE_ENDIAN 1234 +#define _BIG_ENDIAN 4321 +#define _BYTE_ORDER _LITTLE_ENDIAN +#define __ENDIAN_DEFINED 1 +#endif /* _MSC_VER */ + +/* OS X */ +#if defined(__APPLE__) +#include +#define _BYTE_ORDER BYTE_ORDER +#define _LITTLE_ENDIAN LITTLE_ENDIAN +#define _BIG_ENDIAN BIG_ENDIAN +#define __ENDIAN_DEFINED 1 +#endif /* __APPLE__ */ + +/* OpenCL */ +#if defined (__OPENCL_VERSION__) +#define _LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#if defined (__ENDIAN_LITTLE__) +#define _BYTE_ORDER _LITTLE_ENDIAN +#else +#define _BYTE_ORDER _BIG_ENDIAN +#endif +#define bswap16(x) as_ushort(as_uchar2(ushort(x)).s1s0) +#define bswap32(x) as_uint(as_uchar4(uint(x)).s3s2s1s0) +#define bswap64(x) as_ulong(as_uchar8(ulong(x)).s7s6s5s4s3s2s1s0) +#define __ENDIAN_DEFINED 1 +#define __BSWAP_DEFINED 1 +#endif + +/* Unknown */ +#if !__ENDIAN_DEFINED +#error Could not determine CPU byte order +#endif + +/* POSIX - http://austingroupbugs.net/view.php?id=162 */ +#ifndef BYTE_ORDER +#define BYTE_ORDER _BYTE_ORDER +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN _LITTLE_ENDIAN +#endif +#ifndef BIG_ENDIAN +#define BIG_ENDIAN _BIG_ENDIAN +#endif + +/* OpenCL compatibility - define __ENDIAN_LITTLE__ on little endian systems */ +#if _BYTE_ORDER == _LITTLE_ENDIAN +#if !defined (__ENDIAN_LITTLE__) +#define __ENDIAN_LITTLE__ 1 +#endif +#endif + +/* Byte swap macros */ +#if !__BSWAP_DEFINED + +#ifndef bswap16 +/* handle missing __builtin_bswap16 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52624 */ +#if defined __GNUC__ +#define bswap16(x) __builtin_bswap16(x) +#else +inline uint16_t bswap16(uint16_t x) { + return (uint16_t)((((uint16_t) (x) & 0xff00) >> 8) | \ + (((uint16_t) (x) & 0x00ff) << 8)); +} +#endif +#endif + +#ifndef bswap32 +#if defined __GNUC__ +#define bswap32(x) __builtin_bswap32(x) +#else +inline uint32_t bswap32(uint32_t x) { + return (( x & 0xff000000) >> 24) | \ + (( x & 0x00ff0000) >> 8) | \ + (( x & 0x0000ff00) << 8) | \ + (( x & 0x000000ff) << 24); +} +#endif +#endif + +#ifndef bswap64 +#if defined __GNUC__ +#define bswap64(x) __builtin_bswap64(x) +#else +inline uint64_t bswap64(uint64_t x) { + return (( x & 0xff00000000000000ull) >> 56) | \ + (( x & 0x00ff000000000000ull) >> 40) | \ + (( x & 0x0000ff0000000000ull) >> 24) | \ + (( x & 0x000000ff00000000ull) >> 8) | \ + (( x & 0x00000000ff000000ull) << 8) | \ + (( x & 0x0000000000ff0000ull) << 24) | \ + (( x & 0x000000000000ff00ull) << 40) | \ + (( x & 0x00000000000000ffull) << 56); +} +#endif +#endif + +#endif + +/* Host swap macros */ +#ifndef __HOSTSWAP_DEFINED +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define htobe16(x) bswap16((x)) +#define htole16(x) ((uint16_t)(x)) +#define be16toh(x) bswap16((x)) +#define le16toh(x) ((uint16_t)(x)) + +#define htobe32(x) bswap32((x)) +#define htole32(x) ((uint32_t((x)) +#define be32toh(x) bswap32((x)) +#define le32toh(x) ((uint32_t)(x)) + +#define htobe64(x) bswap64((x)) +#define htole64(x) ((uint64_t)(x)) +#define be64toh(x) bswap64((x)) +#define le64toh(x) ((uint64_t)(x)) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define htobe16(x) ((uint16_t)(x)) +#define htole16(x) bswap16((x)) +#define be16toh(x) ((uint16_t)(x)) +#define le16toh(x) bswap16((x)) + +#define htobe32(x) ((uint32_t)(x)) +#define htole32(x) bswap32((x)) +#define be32toh(x) ((uint32_t)(x)) +#define le64toh(x) bswap64((x)) + +#define htobe64(x) ((uint64_t)(x)) +#define htole64(x) bswap64((x)) +#define be64toh(x) ((uint64_t)(x)) +#define le32toh(x) bswap32((x)) +#endif +#endif + +/* + +#include +#include + +int main() +{ + +#if BYTE_ORDER == LITTLE_ENDIAN +printf("little endian\n"); +#endif + +#if BYTE_ORDER == BIG_ENDIAN +printf("big endian\n"); +#endif + +printf("bswap16(%04x) %04x\n", 0xf0e0, bswap16(0xf0e0)); +printf("htobe16(%04x) %04x\n", 0xf0e0, htobe16(0xf0e0)); +printf("htole16(%04x) %04x\n", 0xf0e0, htole16(0xf0e0)); + +printf("bswap32(%08x) %08x\n", 0xf0e0d0c0, bswap32(0xf0e0d0c0)); +printf("htobe32(%08x) %08x\n", 0xf0e0d0c0, htobe32(0xf0e0d0c0)); +printf("htole32(%08x) %08x\n", 0xf0e0d0c0, htole32(0xf0e0d0c0)); + +printf("bswap64(%016llx) %016llx\n", 0xf0e0d0c0b0a09080ULL, bswap64(0xf0e0d0c0b0a09080ULL)); +printf("htobe64(%016llx) %016llx\n", 0xf0e0d0c0b0a09080ULL, htobe64(0xf0e0d0c0b0a09080ULL)); +printf("htole64(%016llx) %016llx\n", 0xf0e0d0c0b0a09080ULL, htole64(0xf0e0d0c0b0a09080ULL)); +} + +*/ diff --git a/src/tbchess.c b/src/tbchess.c index 119ba0c..336a57c 100644 --- a/src/tbchess.c +++ b/src/tbchess.c @@ -1,3 +1,26 @@ +/* +Copyright (c) 2015 basil00 +Modifications Copyright (c) 2016-2019 by Jon Dart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + #define TB_PAWN 1 #define TB_KNIGHT 2 #define TB_BISHOP 3 @@ -43,51 +66,82 @@ #define BEST_NONE 0xFFFF #define SCORE_ILLEGAL 0x7FFF -#define rank(s) ((s) >> 3) -#define file(s) ((s) & 0x07) -#define board(s) ((uint64_t)1 << (s)) -#ifdef TB_CUSTOM_LSB -#define lsb(b) TB_CUSTOM_LSB(b) -#else -#if defined(__GNUC__) -static inline unsigned lsb(uint64_t b) { - assert(b != 0); - return __builtin_ffsll(b)-1; -} -#elif defined(_MSC_VER) -static inline unsigned lsb(uint64_t b) { - assert(b != 0); - DWORD index; -#ifdef _WIN64 - _BitScanForward64(&index,b); - return (unsigned)index; +// Note: WHITE, BLACK values are reverse of Stockfish +#ifdef __cplusplus +enum Color { BLACK, WHITE }; +enum PieceType { PAWN=1, KNIGHT, BISHOP, ROOK, QUEEN, KING }; +enum Piece { + W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, + B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; #else - if (b & 0xffffffffULL) { - _BitScanForward(&index,(unsigned long)(b & 0xffffffffULL)); - return (unsigned)index; - } - else { - _BitScanForward(&index,(unsigned long)(b >> 32)); - return 32 + (unsigned)index; - } +typedef enum Color { BLACK, WHITE } Color; +typedef enum PieceType { PAWN=1, KNIGHT, BISHOP, ROOK, QUEEN, KING } PieceType; +typedef enum Piece { + W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, + B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING +} Piece; #endif + +static inline Color ColorOfPiece(int piece) { + return (Color)(!(piece >> 3)); } -#else -/* not a compiler/architecture with recognized builtins */ -static uint32_t get_bit32(uint64_t x) { - return (uint32_t)(((int32_t)(x))&-((int32_t)(x))); + +static inline PieceType TypeOfPiece(int piece) { + return (PieceType)(piece & 7); } -static const unsigned MAGIC32 = 0xe89b2be; -static const uint32_t MagicTable32[32] = {31,0,9,1,10,20,13,2,7,11,21,23,17,14,3,25,30,8,19,12,6,22,16,24,29,18,5,15,28,4,27,26}; -static unsigned lsb(uint64_t b) { - if (b & 0xffffffffULL) - return MagicTable32[(get_bit32(b & 0xffffffffULL)*MAGIC32)>>27]; - else - return MagicTable32[(get_bit32(b >> 32)*MAGIC32)>>27]+32; + +typedef int32_t Value; + +typedef struct Pos +{ + uint64_t white; + uint64_t black; + uint64_t kings; + uint64_t queens; + uint64_t rooks; + uint64_t bishops; + uint64_t knights; + uint64_t pawns; + uint8_t rule50; + uint8_t ep; + bool turn; +} Pos; + +static inline uint64_t pieces_by_type(const Pos *pos, Color c, PieceType p) { + uint64_t mask = (c == WHITE) ? pos->white : pos->black; + switch(p) { + case PAWN: + return pos->pawns & mask; + case KNIGHT: + return pos->knights & mask; + case BISHOP: + return pos->bishops & mask; + case ROOK: + return pos->rooks & mask; + case QUEEN: + return pos->queens & mask; + case KING: + return pos->kings & mask; + default: + assert(0); + return 0; + } +} + +static const char piece_to_char[] = " PNBRQK pnbrqk"; + +// map upper-case characters to piece types +static PieceType char_to_piece_type(char c) { + for (int i = PAWN; i <= KING; i++) + if (c == piece_to_char[i]) { + return (PieceType)i; + } + return (PieceType)0; } -#endif -#endif +#define rank(s) ((s) >> 3) +#define file(s) ((s) & 0x07) +#define board(s) ((uint64_t)1 << (s)) #define square(r, f) (8 * (r) + (f)) #ifdef TB_KING_ATTACKS @@ -495,46 +549,10 @@ static void pawn_attacks_init(void) #endif /* TB_PAWN_ATTACKS */ -static void prt_str(const struct Pos *pos, char *str, bool mirror) -{ - uint64_t white = pos->white, black = pos->black; - int i; - if (mirror) - { - uint64_t tmp = white; - white = black; - black = tmp; - } - *str++ = 'K'; - for (i = popcount(white & pos->queens); i > 0; i--) - *str++ = 'Q'; - for (i = popcount(white & pos->rooks); i > 0; i--) - *str++ = 'R'; - for (i = popcount(white & pos->bishops); i > 0; i--) - *str++ = 'B'; - for (i = popcount(white & pos->knights); i > 0; i--) - *str++ = 'N'; - for (i = popcount(white & pos->pawns); i > 0; i--) - *str++ = 'P'; - *str++ = 'v'; - *str++ = 'K'; - for (i = popcount(black & pos->queens); i > 0; i--) - *str++ = 'Q'; - for (i = popcount(black & pos->rooks); i > 0; i--) - *str++ = 'R'; - for (i = popcount(black & pos->bishops); i > 0; i--) - *str++ = 'B'; - for (i = popcount(black & pos->knights); i > 0; i--) - *str++ = 'N'; - for (i = popcount(black & pos->pawns); i > 0; i--) - *str++ = 'P'; - *str++ = '\0'; -} - /* * Given a position, produce a 64-bit material signature key. */ -static uint64_t calc_key(const struct Pos *pos, bool mirror) +static uint64_t calc_key(const Pos *pos, bool mirror) { uint64_t white = pos->white, black = pos->black; if (mirror) @@ -555,6 +573,10 @@ static uint64_t calc_key(const struct Pos *pos, bool mirror) popcount(black & pos->pawns) * PRIME_BLACK_PAWN; } +// Produce a 64-bit material key corresponding to the material combination +// defined by pcs[16], where pcs[1], ..., pcs[6] are the number of white +// pawns, ..., kings and pcs[9], ..., pcs[14] are the number of black +// pawns, ..., kings. static uint64_t calc_key_from_pcs(int *pcs, int mirror) { mirror = (mirror? 8: 0); @@ -570,37 +592,22 @@ static uint64_t calc_key_from_pcs(int *pcs, int mirror) pcs[BLACK_PAWN ^ mirror] * PRIME_BLACK_PAWN; } -static uint64_t get_pieces(const struct Pos *pos, uint8_t code) +// Produce a 64-bit material key corresponding to the material combination +// piece[0], ..., piece[num - 1], where each value corresponds to a piece +// (1-6 for white pawn-king, 9-14 for black pawn-king). +static uint64_t calc_key_from_pieces(uint8_t *piece, int num) { - switch (code) - { - case WHITE_KING: - return pos->kings & pos->white; - case WHITE_QUEEN: - return pos->queens & pos->white; - case WHITE_ROOK: - return pos->rooks & pos->white; - case WHITE_BISHOP: - return pos->bishops & pos->white; - case WHITE_KNIGHT: - return pos->knights & pos->white; - case WHITE_PAWN: - return pos->pawns & pos->white; - case BLACK_KING: - return pos->kings & pos->black; - case BLACK_QUEEN: - return pos->queens & pos->black; - case BLACK_ROOK: - return pos->rooks & pos->black; - case BLACK_BISHOP: - return pos->bishops & pos->black; - case BLACK_KNIGHT: - return pos->knights & pos->black; - case BLACK_PAWN: - return pos->pawns & pos->black; - default: - return 0; // Dummy. + uint64_t key = 0; + static const uint64_t keys[16] = {0,PRIME_WHITE_PAWN,PRIME_WHITE_KNIGHT, + PRIME_WHITE_BISHOP,PRIME_WHITE_ROOK, + PRIME_WHITE_QUEEN,0,0,PRIME_BLACK_PAWN, + PRIME_BLACK_KNIGHT,PRIME_BLACK_BISHOP, + PRIME_BLACK_ROOK,PRIME_BLACK_QUEEN,0}; + for (int i = 0; i < num; i++) { + assert(piece[i]<16); + key += keys[piece[i]]; } + return key; } #define make_move(promote, from, to) \ @@ -612,11 +619,21 @@ static uint64_t get_pieces(const struct Pos *pos, uint8_t code) #define move_promotes(move) \ (((move) >> 12) & 0x7) +static inline int type_of_piece_moved(Pos *pos, TbMove move) { + for (int i = PAWN; i <= KING; i++) { + if ((pieces_by_type(pos,(Color)(pos->turn == WHITE),(PieceType)i) & board(move_from(move))) != 0) { + return i; + } + } + assert(0); + return 0; +} + #define MAX_MOVES TB_MAX_MOVES #define MOVE_STALEMATE 0xFFFF #define MOVE_CHECKMATE 0xFFFE -static uint16_t *add_move(uint16_t *moves, bool promotes, unsigned from, +static TbMove *add_move(TbMove *moves, bool promotes, unsigned from, unsigned to) { if (!promotes) @@ -632,10 +649,9 @@ static uint16_t *add_move(uint16_t *moves, bool promotes, unsigned from, } /* - * Generate all captures or promotions. + * Generate all captures, including all underpomotions */ -static uint16_t *gen_captures_or_promotions(const struct Pos *pos, - uint16_t *moves) +static TbMove *gen_captures(const Pos *pos, TbMove *moves) { uint64_t occ = pos->white | pos->black; uint64_t us = (pos->turn? pos->white: pos->black), @@ -700,71 +716,6 @@ static uint16_t *gen_captures_or_promotions(const struct Pos *pos, moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, to); } - if (pos->turn && rank(from) == 6) - { - unsigned to = from + 8; - if ((board(to) & occ) == 0) - moves = add_move(moves, true, from, to); - } - else if (!pos->turn && rank(from) == 1) - { - unsigned to = from - 8; - if ((board(to) & occ) == 0) - moves = add_move(moves, true, from, to); - } - } - return moves; -} - -/* - * Generate all non-capture pawn moves and promotions. - */ -static uint16_t *gen_pawn_quiets_or_promotions(const struct Pos *pos, - uint16_t *moves) -{ - uint64_t occ = pos->white | pos->black; - uint64_t us = (pos->turn? pos->white: pos->black); - uint64_t b, att; - - for (b = us & pos->pawns; b; b = poplsb(b)) - { - unsigned from = lsb(b); - unsigned next = from + (pos->turn? 8: -8); - att = 0; - if ((board(next) & occ) == 0) - { - att |= board(next); - unsigned next2 = from + (pos->turn? 16: -16); - if ((pos->turn? rank(from) == 1: rank(from) == 6) && - ((board(next2) & occ) == 0)) - att |= board(next2); - } - for (; att; att = poplsb(att)) - { - unsigned to = lsb(att); - moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, - to); - } - } - return moves; -} - -/* - * Generate all en passant captures. - */ -static uint16_t *gen_pawn_ep_captures(const struct Pos *pos, uint16_t *moves) -{ - if (pos->ep == 0) - return moves; - uint64_t ep = board(pos->ep); - unsigned to = pos->ep; - uint64_t us = (pos->turn? pos->white: pos->black); - uint64_t b; - for (b = us & pos->pawns; b; b = poplsb(b)) - { - unsigned from = lsb(b); - if ((pawn_attacks(from, pos->turn) & ep) != 0) - moves = add_move(moves, false, from, to); } return moves; } @@ -772,7 +723,7 @@ static uint16_t *gen_pawn_ep_captures(const struct Pos *pos, uint16_t *moves) /* * Generate all moves. */ -static uint16_t *gen_moves(const struct Pos *pos, uint16_t *moves) +static TbMove *gen_moves(const Pos *pos, TbMove *moves) { uint64_t occ = pos->white | pos->black; uint64_t us = (pos->turn? pos->white: pos->black), @@ -855,7 +806,7 @@ static uint16_t *gen_moves(const struct Pos *pos, uint16_t *moves) /* * Test if the given move is an en passant capture. */ -static bool is_en_passant(const struct Pos *pos, uint16_t move) +static bool is_en_passant(const Pos *pos, TbMove move) { uint16_t from = move_from(move); uint16_t to = move_to(move); @@ -869,11 +820,23 @@ static bool is_en_passant(const struct Pos *pos, uint16_t move) return true; } + +/* + * Test if the given move is a capture. + */ +static bool is_capture(const Pos *pos, TbMove move) +{ + uint16_t to = move_to(move); + uint64_t them = (pos->turn? pos->black: pos->white); + return (them & board(to)) != 0 || is_en_passant(pos,move); +} + + /* * Test if the given position is legal. * (Pawns on backrank? Can the king be captured?) */ -static bool is_legal(const struct Pos *pos) +static bool is_legal(const Pos *pos) { uint64_t occ = pos->white | pos->black; uint64_t us = (pos->turn? pos->black: pos->white), @@ -902,7 +865,7 @@ static bool is_legal(const struct Pos *pos) /* * Test if the king is in check. */ -static bool is_check(const struct Pos *pos) +static bool is_check(const Pos *pos) { uint64_t occ = pos->white | pos->black; uint64_t us = (pos->turn? pos->white: pos->black), @@ -927,7 +890,7 @@ static bool is_check(const struct Pos *pos) /* * Test if the position is valid. */ -static bool is_valid(const struct Pos *pos) +static bool is_valid(const Pos *pos) { if (popcount(pos->kings) != 2) return false; @@ -980,7 +943,7 @@ static bool is_valid(const struct Pos *pos) (((b) & (~board(to)) & (~board(from))) | \ ((((b) >> (from)) & 0x1) << (to))) -static bool do_move(struct Pos *pos, const struct Pos *pos0, uint16_t move) +static bool do_move(Pos *pos, const Pos *pos0, TbMove move) { unsigned from = move_from(move); unsigned to = move_to(move); @@ -1038,10 +1001,15 @@ static bool do_move(struct Pos *pos, const struct Pos *pos0, uint16_t move) return true; } +static bool legal_move(const Pos *pos, TbMove move) { + struct Pos pos1; + return do_move(&pos1, pos, move); +} + /* * Test if the king is in checkmate. */ -static bool is_mate(const struct Pos *pos) +static bool is_mate(const Pos *pos) { if (!is_check(pos)) return false; @@ -1050,11 +1018,26 @@ static bool is_mate(const struct Pos *pos) uint16_t *end = gen_moves(pos, moves); for (; moves < end; moves++) { - struct Pos pos1; + Pos pos1; if (do_move(&pos1, pos, *moves)) return false; } return true; } +/* + * Generate all legal moves. + */ +static TbMove *gen_legal(const Pos *pos, TbMove *moves) +{ + TbMove pl_moves[TB_MAX_MOVES]; + TbMove *end = gen_moves(pos, pl_moves); + TbMove *results = moves; + for (TbMove *m = pl_moves; m < end; m++) { + if (legal_move(pos,*m)) { + *results++ = *m; + } + } + return results; +} diff --git a/src/tbconfig.h b/src/tbconfig.h index abc1561..5836386 100644 --- a/src/tbconfig.h +++ b/src/tbconfig.h @@ -43,20 +43,6 @@ */ /* #define TB_CUSTOM_LSB(x) */ -/* - * Define TB_CUSTOM_BSWAP32 to override the internal bswap32 - * implementation. To do this supply a macro or function definition - * here: - */ -/* #define TB_CUSTOM_BSWAP32(x) */ - -/* - * Define TB_CUSTOM_BSWAP64 to override the internal bswap64 - * implementation. To do this supply a macro or function definition - * here: - */ -/* #define TB_CUSTOM_BSWAP64(x) */ - /* * Define TB_NO_STDINT if you do not want to use or it is not * available. @@ -92,11 +78,20 @@ */ /* #define TB_NO_HW_POP_COUNT */ -/** - * Define TB_USE_ATOMIC to use C++ 11 (or higher) feature - * (recommended if using C++ and compiler supports it). +/***************************************************************************/ +/* SCORING CONSTANTS */ +/***************************************************************************/ +/* + * Fathom can produce scores for tablebase moves. These depend on the + * value of a pawn, and the magnitude of mate scores. The following + * constants are representative values but will likely need + * modification to adapt to an engine's own internal score values. */ -/* #define TB_USE_ATOMIC */ +#define TB_VALUE_PAWN 100 /* value of pawn in endgame */ +#define TB_VALUE_MATE 32000 +#define TB_VALUE_INFINITE 32767 /* value above all normal score values */ +#define TB_VALUE_DRAW 0 +#define TB_MAX_MATE_PLY 255 /***************************************************************************/ /* ENGINE INTEGRATION CONFIG */ diff --git a/src/tbprobe.c b/src/tbprobe.c index fba24e1..f2deec0 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1,11 +1,32 @@ /* - Copyright (c) 2013-2018 Ronald de Man - Copyright (c) 2019 Jon Dart - This file may be redistributed and/or modified without restrictions. +Copyright (c) 2015 basil00 +Modifications Copyright (c) 2016-2019 by Jon Dart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ #include +#ifdef __cplusplus +#include +#else #include +#endif #include #include #include @@ -17,25 +38,36 @@ #endif #include "tbprobe.h" +#ifdef __cplusplus +using namespace std; +#endif + #define TB_PIECES 7 #define TB_HASHBITS (TB_PIECES < 7 ? 11 : 12) #define TB_MAX_PIECE (TB_PIECES < 7 ? 254 : 650) #define TB_MAX_PAWN (TB_PIECES < 7 ? 256 : 861) #ifndef _WIN32 +#include #include +#include +#include +#include #define SEP_CHAR ':' #define FD int #define FD_ERR -1 typedef size_t map_t; #else #include +#include #define SEP_CHAR ';' #define FD HANDLE #define FD_ERR INVALID_HANDLE_VALUE typedef HANDLE map_t; #endif +#define DECOMP64 + // Threading support #ifndef TB_NO_THREADS #if defined(__cplusplus) && (__cplusplus >= 201103L) @@ -43,6 +75,7 @@ typedef HANDLE map_t; #include #define LOCK_T std::mutex #define LOCK_INIT(x) +#define LOCK_DESTROY(x) #define LOCK(x) x.lock() #define UNLOCK(x) x.unlock() @@ -50,11 +83,13 @@ typedef HANDLE map_t; #ifndef _WIN32 #define LOCK_T pthread_mutex_t #define LOCK_INIT(x) pthread_mutex_init(&(x), NULL) +#define LOCK_DESTROY(x) pthread_mutex_destroy(&(x)) #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_DESTROY(x) CloseHandle(x) #define LOCK(x) WaitForSingleObject(x, INFINITE) #define UNLOCK(x) ReleaseMutex(x) #endif @@ -63,6 +98,7 @@ typedef HANDLE map_t; #else /* TB_NO_THREADS */ #define LOCK_T int #define LOCK_INIT(x) /* NOP */ +#define LOCK_DESTROY(x) /* NOP */ #define LOCK(x) /* NOP */ #define UNLOCK(x) /* NOP */ #endif @@ -95,13 +131,247 @@ static inline unsigned tb_software_popcount(uint64_t x) x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0full; return (x * 0x0101010101010101ull) >> 56; } + #define popcount(x) tb_software_popcount(x) #endif +// LSB (least-significant bit) implementation +#ifdef TB_CUSTOM_LSB +#define lsb(b) TB_CUSTOM_LSB(b) +#else +#if defined(__GNUC__) +static inline unsigned lsb(uint64_t b) { + assert(b != 0); + return __builtin_ffsll(b)-1; +} +#elif defined(_MSC_VER) +static inline unsigned lsb(uint64_t b) { + assert(b != 0); + DWORD index; +#ifdef _WIN64 + _BitScanForward64(&index,b); + return (unsigned)index; +#else + if (b & 0xffffffffULL) { + _BitScanForward(&index,(unsigned long)(b & 0xffffffffULL)); + return (unsigned)index; + } + else { + _BitScanForward(&index,(unsigned long)(b >> 32)); + return 32 + (unsigned)index; + } +#endif +} +#else +/* not a compiler/architecture with recognized builtins */ +static uint32_t get_bit32(uint64_t x) { + return (uint32_t)(((int32_t)(x))&-((int32_t)(x))); +} +static const unsigned MAGIC32 = 0xe89b2be; +static const uint32_t MagicTable32[32] = {31,0,9,1,10,20,13,2,7,11,21,23,17,14,3,25,30,8,19,12,6,22,16,24,29,18,5,15,28,4,27,26}; +static unsigned lsb(uint64_t b) { + if (b & 0xffffffffULL) + return MagicTable32[(get_bit32(b & 0xffffffffULL)*MAGIC32)>>27]; + else + return MagicTable32[(get_bit32(b >> 32)*MAGIC32)>>27]+32; +} +#endif +#endif + +#define max(a,b) a > b ? a : b +#define min(a,b) a < b ? a : b + +#include + +#if _BYTE_ORDER == _BIG_ENDIAN + +static uint64_t from_le_u64(uint64_t input) { + return bswap64(input); +} + +static uint32_t from_le_u32(uint32_t input) { + return bswap32(input); +} + +static uint16_t from_le_u16(uint16_t input) { + return bswap16(input); +} + +static uint64_t from_be_u64(uint64_t x) { + return x; +} + +static uint32_t from_be_u32(uint32_t x) { + return x; +} + +static uint16_t from_be_u16(uint16_t x) { + return x; +} + +#else + +static uint64_t from_le_u64(uint64_t x) { + return x; +} + +static uint32_t from_le_u32(uint32_t x) { + return x; +} + +static uint16_t from_le_u16(uint16_t x) { + return x; +} + +static uint64_t from_be_u64(uint64_t input) { + return bswap64(input); +} + +static uint32_t from_be_u32(uint32_t input) { + return bswap32(input); +} + +static uint16_t from_be_u16(const uint16_t input) { + return bswap16(input); +} + +#endif + +inline static uint32_t read_le_u32(void *p) +{ + return from_le_u32(*(uint32_t *)p); +} + +inline static uint16_t read_le_u16(void *p) +{ + return from_le_u16(*(uint16_t *)p); +} + +static size_t file_size(FD fd) { +#ifdef _WIN32 + LARGE_INTEGER fileSize; + if (GetFileSizeEx(fd, &fileSize)==0) { + return 0; + } + return (size_t)fileSize.QuadPart; +#else + struct stat buf; + if (fstat(fd,&buf)) { + return 0; + } else { + return buf.st_size; + } +#endif +} + +#ifndef TB_NO_THREADS +static LOCK_T tbMutex; +#endif +static int initialized = 0; +static int numPaths = 0; +static char *pathString = NULL; +static char **paths = NULL; + +static FD open_tb(const char *str, const char *suffix) +{ + int i; + FD fd; + char *file; + + for (i = 0; i < numPaths; i++) { + file = (char*)malloc(strlen(paths[i]) + strlen(str) + + strlen(suffix) + 2); + strcpy(file, paths[i]); +#ifdef _WIN32 + strcat(file,"\\"); +#else + strcat(file,"/"); +#endif + strcat(file, str); + strcat(file, suffix); +#ifndef _WIN32 + fd = open(file, O_RDONLY); +#else + fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif + free(file); + if (fd != FD_ERR) { + return fd; + } + } + return FD_ERR; +} + +static void close_tb(FD fd) +{ +#ifndef _WIN32 + close(fd); +#else + CloseHandle(fd); +#endif +} + +static void *map_file(FD fd, uint64_t *mapping) +{ +#ifndef _WIN32 + struct stat statbuf; + if (fstat(fd, &statbuf)) { + perror("fstat"); + close_tb(fd); + return NULL; + } + *mapping = statbuf.st_size; + void *data = mmap(NULL, statbuf.st_size, PROT_READ, + MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + perror("mmap"); + return NULL; + } +#else + DWORD size_low, size_high; + size_low = GetFileSize(fd, &size_high); + HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, + NULL); + if (map == NULL) { + fprintf(stderr,"CreateFileMapping() failed.\n"); + return NULL; + } + *mapping = (uint64)map; + void *data = (void *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); + if (data == NULL) { + fprintf(stderr,"MapViewOfFile() failed, name = %s%s, error = %lu.\n", name, suffix, GetLastError()); + } +#endif + return data; +} + +#ifndef _WIN32 +static void unmap_file(void *data, uint64_t size) +{ + if (!data) return; + if (!munmap(data, size)) { + perror("munmap"); + } +} +#else +static void unmap_file(void *data, uint64_t mapping) +{ + if (!data) return; + if (!UnmapViewOfFile(data)) { + fprintf(stderr, "unmap failed, error code %d", GetLastError()); + } + if (!CloseHandle((HANDLE)mapping)) { + fprintf(stderr, "CloseHandle failed, error code %d", GetLastError()); + } +} +#endif + #define poplsb(x) ((x) & ((x) - 1)) int TB_MaxCardinality = 0, TB_MaxCardinalityDTM = 0; -extern int TB_CardinalityDTM; +unsigned TB_LARGEST = 0; +//extern int TB_CardinalityDTM; static const char *tbSuffix[] = { ".rtbw", ".rtbm", ".rtbz" }; static uint32_t tbMagic[] = { 0x5d23e871, 0x88ac504b, 0xa50c66d7 }; @@ -109,35 +379,6 @@ static uint32_t tbMagic[] = { 0x5d23e871, 0x88ac504b, 0xa50c66d7 }; enum { WDL, DTM, DTZ }; enum { PIECE_ENC, FILE_ENC, RANK_ENC }; -typedef int32_t Value; - -struct RootMove { - uint16_t move; - uint16_t pv[TB_MAX_PLY]; - unsigned pvSize; - Value tbScore; -}; - -struct RootMoves { - unsigned size; - struct RootMove moves[TB_MAX_MOVES]; -}; - -struct Pos -{ - uint64_t white; - uint64_t black; - uint64_t kings; - uint64_t queens; - uint64_t rooks; - uint64_t bishops; - uint64_t knights; - uint64_t pawns; - uint8_t rule50; - uint8_t ep; - bool turn; -}; - // Attack and move generation code #include "tbchess.c" @@ -152,7 +393,7 @@ struct PairsData { uint8_t idxBits; uint8_t minLen; uint8_t constValue[2]; - uint64_t base[]; // must be base[1] in C++ + uint64_t base[1]; }; struct EncInfo { @@ -166,7 +407,11 @@ struct BaseEntry { uint64_t key; uint8_t *data[3]; map_t mapping[3]; +#ifdef __cplusplus + atomic ready[3]; +#else atomic_bool ready[3]; +#endif uint8_t num; bool symmetric, hasPawns, hasDtm, hasDtz; union { @@ -202,12 +447,6 @@ struct TbHashEntry { struct BaseEntry *ptr; }; -static LOCK_T tbMutex; -static int initialized = 0; -static int numPaths = 0; -static char *pathString = NULL; -static char **paths = NULL; - static int tbNumPiece, tbNumPawn; static int numWdl, numDtm, numDtz; @@ -219,13 +458,11 @@ static void init_indices(void); // Forward declarations. These functions without the tb_ // prefix take a pos structure as input. -static int probe_wdl(struct Pos *pos, int *success); -static int probe_dtz(struct Pos *pos, int *success); -static Value probe_dtm(struct Pos *pos, int wdl, int *success); -static int root_probe_wdl(struct Pos *pos, struct RootMoves *rm); -static int root_probe_dtz(struct Pos *pos, struct RootMoves *rm); -static int root_probe_dtm(struct Pos *pos, struct RootMoves *rm); -static void expand_mate(struct Pos *pos, struct RootMove *move); +static int probe_wdl(Pos *pos, int *success); +static int probe_dtz(Pos *pos, int *success); +static int root_probe_wdl(const Pos *pos, bool useRule50, struct TbRootMoves *rm); +static int root_probe_dtz(const Pos *pos, bool hasRepeated, bool useRule50, struct TbRootMoves *rm); +static uint16_t probe_root(Pos *pos, int *score, unsigned *results); unsigned tb_probe_wdl_impl( uint64_t white, @@ -239,7 +476,7 @@ unsigned tb_probe_wdl_impl( unsigned ep, bool turn) { - struct Pos pos = + Pos pos = { white, black, @@ -260,6 +497,16 @@ unsigned tb_probe_wdl_impl( return (unsigned)(v + 2); } +static unsigned dtz_to_wdl(int cnt50, int dtz) +{ + int wdl = 0; + if (dtz > 0) + wdl = (dtz + cnt50 <= 100? 2: 1); + else if (dtz < 0) + wdl = (-dtz + cnt50 <= 100? -2: -1); + return wdl + 2; +} + unsigned tb_probe_root_impl( uint64_t white, uint64_t black, @@ -274,7 +521,7 @@ unsigned tb_probe_root_impl( bool turn, unsigned *results) { - struct Pos pos = + Pos pos = { white, black, @@ -291,7 +538,7 @@ unsigned tb_probe_root_impl( int dtz; if (!is_valid(&pos)) return TB_RESULT_FAILED; - uint16_t move = probe_root(&pos, &dtz, results); + TbMove move = probe_root(&pos, &dtz, results); if (move == 0) return TB_RESULT_FAILED; if (move == MOVE_CHECKMATE) @@ -308,91 +555,95 @@ unsigned tb_probe_root_impl( return res; } +int tb_probe_root_dtz( + uint64_t white, + uint64_t black, + uint64_t kings, + uint64_t queens, + uint64_t rooks, + uint64_t bishops, + uint64_t knights, + uint64_t pawns, + unsigned rule50, + unsigned castling, + unsigned ep, + bool turn, + bool hasRepeated, + bool useRule50, + struct TbRootMoves *results) { + Pos pos = + { + white, + black, + kings, + queens, + rooks, + bishops, + knights, + pawns, + (uint8_t)rule50, + (uint8_t)ep, + turn + }; + return root_probe_dtz(&pos, hasRepeated, useRule50, results); +} + +int tb_probe_root_wdl( + uint64_t white, + uint64_t black, + uint64_t kings, + uint64_t queens, + uint64_t rooks, + uint64_t bishops, + uint64_t knights, + uint64_t pawns, + unsigned rule50, + unsigned castling, + unsigned ep, + bool turn, + bool useRule50, + struct TbRootMoves *results) { + Pos pos = + { + white, + black, + kings, + queens, + rooks, + bishops, + knights, + pawns, + (uint8_t)rule50, + (uint8_t)ep, + turn + }; + return root_probe_wdl(&pos, useRule50, results); +} // Given a position, produce a text string of the form KQPvKRP, where // "KQP" represents the white pieces if flip == false and the black pieces // if flip == true. -static void prt_str(Pos *pos, char *str, bool flip) +static void prt_str(const Pos *pos, char *str, bool flip) { - int color = !flip ? WHITE : BLACK; + int color = flip ? BLACK : WHITE; for (int pt = KING; pt >= PAWN; pt--) - for (int i = popcount(pieces_cp(color, pt)); i > 0; i--) - *str++ = PieceToChar[pt]; + for (int i = popcount(pieces_by_type(pos, (Color)color, (PieceType)pt)); i > 0; i--) + *str++ = piece_to_char[pt]; *str++ = 'v'; color ^= 1; for (int pt = KING; pt >= PAWN; pt--) - for (int i = popcount(pieces_cp(color, pt)); i > 0; i--) - *str++ = PieceToChar[pt]; + for (int i = popcount(pieces_by_type(pos, (Color)color, (PieceType)pt)); i > 0; i--) + *str++ = piece_to_char[pt]; *str++ = 0; } -// Produce a 64-bit material key corresponding to the material combination -// defined by pcs[16], where pcs[1], ..., pcs[6] are the number of white -// pawns, ..., kings and pcs[9], ..., pcs[14] are the number of black -// pawns, ..., kings. -static uint64_t calc_key_from_pcs(int *pcs, bool flip) -{ - uint64_t key = 0; - - int color = !flip ? 0 : 8; - for (int i = W_PAWN; i <= B_KING; i++) - key += matKey[i] * pcs[i ^ color]; - - return key; -} - -// Produce a 64-bit material key corresponding to the material combination -// piece[0], ..., piece[num - 1], where each value corresponds to a piece -// (1-6 for white pawn-king, 9-14 for black pawn-king). -static uint64_t calc_key_from_pieces(uint8_t *piece, int num) -{ - uint64_t key = 0; - - for (int i = 0; i < num; i++) - if (piece[i]) - key += matKey[piece[i]]; - - return key; -} - -static FD open_tb(const char *str, const char *suffix) -{ - int i; - FD fd; - char *file; - - for (i = 0; i < num_paths; i++) { - file = (char*)malloc(strlen(paths[i]) + strlen(str) + - strlen(suffix) + 2); - strcpy(file, paths[i]); -#ifdef _WIN32 - strcat(file,"\\"); -#else - strcat(file,"/"); -#endif - strcat(file, str); - strcat(file, suffix); -#ifndef _WIN32 - fd = open(file, O_RDONLY); -#else - fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); -#endif - free(file); - if (fd != FD_ERR) { - return fd; - } - } - return FD_ERR; -} - static bool test_tb(const char *str, const char *suffix) { FD fd = open_tb(str, suffix); if (fd != FD_ERR) { size_t size = file_size(fd); - close_file(fd); + close_tb(fd); if ((size & 63) != 16) { fprintf(stderr, "Incomplete tablebase file %s.%s\n", str, suffix); printf("info string Incomplete tablebase file %s.%s\n", str, suffix); @@ -414,12 +665,12 @@ static void *map_tb(const char *name, const char *suffix, map_t *mapping) exit(EXIT_FAILURE); } - close_file(fd); + close_tb(fd); return data; } -static void add_to_hash(void *ptr, uint64_t key) +static void add_to_hash(struct BaseEntry *ptr, uint64_t key) { int idx; @@ -431,7 +682,7 @@ static void add_to_hash(void *ptr, uint64_t key) tbHash[idx].ptr = ptr; } -#define pchr(i) PieceToChar[QUEEN - (i)] +#define pchr(i) piece_to_char[QUEEN - (i)] #define Swap(a,b) {int tmp=a;a=b;b=tmp;} static void init_tb(char *str) @@ -446,12 +697,13 @@ static void init_tb(char *str) for (char *s = str; *s; s++) if (*s == 'v') color = 8; - else - for (int i = PAWN; i <= KING; i++) - if (*s == PieceToChar[i]) { - pcs[i | color]++; - break; - } + else { + int piece_type = char_to_piece_type(*s); + if (piece_type) { + assert((piece_type | color) < 16); + pcs[piece_type | color]++; + } + } uint64_t key = calc_key_from_pcs(pcs, false); uint64_t key2 = calc_key_from_pcs(pcs, true); @@ -471,9 +723,13 @@ static void init_tb(char *str) numDtm += be->hasDtm = test_tb(str, tbSuffix[DTM]); numDtz += be->hasDtz = test_tb(str, tbSuffix[DTZ]); - TB_MaxCardinality = max(TB_MaxCardinality, be->num); + if (be->num > TB_MaxCardinality) { + TB_MaxCardinality = be->num; + } if (be->hasDtm) - TB_MaxCardinalityDTM = max(TB_MaxCardinalityDTM, be->num); + if (be->num > TB_MaxCardinalityDTM) { + TB_MaxCardinalityDTM = be->num; + } for (int type = 0; type < 3; type++) atomic_init(&be->ready[type], false); @@ -498,12 +754,12 @@ static void init_tb(char *str) #define PIECE(x) ((struct PieceEntry *)(x)) #define PAWN(x) ((struct PawnEntry *)(x)) -INLINE int num_tables(struct BaseEntry *be, const int type) +int num_tables(struct BaseEntry *be, const int type) { return be->hasPawns ? type == DTM ? 6 : 4 : 1; } -INLINE struct EncInfo *first_ei(struct BaseEntry *be, const int type) +struct EncInfo *first_ei(struct BaseEntry *be, const int type) { return be->hasPawns ? &PAWN(be)->ei[type == WDL ? 0 : type == DTM ? 8 : 20] @@ -514,7 +770,7 @@ static void free_tb_entry(struct BaseEntry *be) { for (int type = 0; type < 3; type++) { if (atomic_load_explicit(&be->ready[type], memory_order_relaxed)) { - unmap_file(be->data[type], be->mapping[type]); + unmap_file((void*)(be->data[type]), be->mapping[type]); int num = num_tables(be, type); struct EncInfo *ei = first_ei(be, type); for (int t = 0; t < num; t++) { @@ -527,26 +783,16 @@ static void free_tb_entry(struct BaseEntry *be) } } -void tb_free(void) -{ - tb_init(""); - free(pieceEntry); - free(pawnEntry); -} - -void tb_init(char *path) +bool tb_init(const char *path) { if (!initialized) { init_indices(); - initialized = 1; king_attacks_init(); knight_attacks_init(); bishop_attacks_init(); rook_attacks_init(); pawn_attacks_init(); - if (path == NULL) - path = ""; - init_tablebases(path); + initialized = 1; } // if pathString is set, we need to clean up first. @@ -567,9 +813,9 @@ void tb_init(char *path) // if path is an empty string or equals "", we are done. const char *p = path; - if (strlen(p) == 0 || !strcmp(p, "")) return; + if (strlen(p) == 0 || !strcmp(p, "")) return true; - pathString = malloc(strlen(p) + 1); + pathString = (char*)malloc(strlen(p) + 1); strcpy(pathString, p); numPaths = 0; for (int i = 0;; i++) { @@ -580,7 +826,7 @@ void tb_init(char *path) if (!pathString[i]) break; pathString[i] = 0; } - paths = malloc(numPaths * sizeof(*paths)); + paths = (char**)malloc(numPaths * sizeof(*paths)); for (int i = 0, j = 0; i < numPaths; i++) { while (!pathString[j]) j++; paths[i] = &pathString[j]; @@ -591,10 +837,11 @@ void tb_init(char *path) tbNumPiece = tbNumPawn = 0; TB_MaxCardinality = TB_MaxCardinalityDTM = 0; + TB_LARGEST = 0; if (!pieceEntry) { - pieceEntry = malloc(TB_MAX_PIECE * sizeof(*pieceEntry)); - pawnEntry = malloc(TB_MAX_PAWN * sizeof(*pawnEntry)); + pieceEntry = (struct PieceEntry*)malloc(TB_MAX_PIECE * sizeof(*pieceEntry)); + pawnEntry = (struct PawnEntry*)malloc(TB_MAX_PAWN * sizeof(*pawnEntry)); if (!pieceEntry || !pawnEntry) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); @@ -697,9 +944,24 @@ void tb_init(char *path) } finished: + /* TBD - assumes UCI printf("info string Found %d WDL, %d DTM and %d DTZ tablebase files.\n", numWdl, numDtm, numDtz); fflush(stdout); + */ + // Set TB_LARGEST, for backward compatibility with pre-7-man Fathom + TB_LARGEST = (unsigned)TB_MaxCardinality; + if ((unsigned)TB_MaxCardinalityDTM > TB_LARGEST) { + TB_LARGEST = TB_MaxCardinalityDTM; + } + return true; +} + +void tb_free(void) +{ + tb_init(""); + free(pieceEntry); + free(pawnEntry); } static const int8_t OffDiag[] = { @@ -928,7 +1190,7 @@ static void init_indices(void) } } -INLINE int leading_pawn(int *p, struct BaseEntry *be, const int enc) +int leading_pawn(int *p, struct BaseEntry *be, const int enc) { for (int i = 1; i < be->pawns[0]; i++) if (Flap[enc-1][p[0]] > Flap[enc-1][p[i]]) @@ -937,7 +1199,7 @@ INLINE int leading_pawn(int *p, struct BaseEntry *be, const int enc) return enc == FILE_ENC ? FileToFile[p[0] & 7] : (p[0] - 8) >> 3; } -INLINE size_t encode(int *p, struct EncInfo *ei, struct BaseEntry *be, +size_t encode(int *p, struct EncInfo *ei, struct BaseEntry *be, const int enc) { int n = be->num; @@ -1129,7 +1391,7 @@ static struct PairsData *setup_pairs(uint8_t **ptr, size_t tb_size, *flags = data[0]; if (data[0] & 0x80) { - d = malloc(sizeof(*d)); + d = (struct PairsData*)malloc(sizeof(struct PairsData)); d->idxBits = 0; d->constValue[0] = type == WDL ? data[1] : 0; d->constValue[1] = 0; @@ -1140,17 +1402,17 @@ static struct PairsData *setup_pairs(uint8_t **ptr, size_t tb_size, uint8_t blockSize = data[1]; uint8_t idxBits = data[2]; - uint32_t realNumBlocks = read_le_u32(&data[4]); + uint32_t realNumBlocks = read_le_u32(data+4); uint32_t numBlocks = realNumBlocks + data[3]; int maxLen = data[8]; int minLen = data[9]; int h = maxLen - minLen + 1; - uint32_t numSyms = read_le_u16(&data[10 + 2 * h]); - d = malloc(sizeof(*d) + h * sizeof(uint64_t) + numSyms); + uint32_t numSyms = (uint32_t)read_le_u16(data + 10 + 2 * h); + d = (struct PairsData*)malloc(sizeof(struct PairsData) + h * sizeof(uint64_t) + numSyms); d->blockSize = blockSize; d->idxBits = idxBits; - d->offset = (uint16_t *)&data[10]; - d->symLen = (uint8_t *)d + sizeof(*d) + h * sizeof(uint64_t); + d->offset = (uint16_t *)(&data[10]); + d->symLen = (uint8_t *)d + sizeof(struct PairsData) + h * sizeof(uint64_t); d->symPat = &data[12 + 2 * h]; d->minLen = minLen; *ptr = &data[12 + 2 * h + 3 * numSyms + (numSyms & 1)]; @@ -1169,9 +1431,13 @@ static struct PairsData *setup_pairs(uint8_t **ptr, size_t tb_size, d->base[h - 1] = 0; for (int i = h - 2; i >= 0; i--) d->base[i] = (d->base[i + 1] + read_le_u16((uint8_t *)(d->offset + i)) - read_le_u16((uint8_t *)(d->offset + i + 1))) / 2; +#ifdef DECOMP64 for (int i = 0; i < h; i++) d->base[i] <<= 64 - (minLen + i); - +#else + for (int i = 0; i < h; i++) + d->base[i] <<= 32 - (minLen + i); +#endif d->offset -= d->minLen; return d; @@ -1179,12 +1445,12 @@ static struct PairsData *setup_pairs(uint8_t **ptr, size_t tb_size, static bool init_table(struct BaseEntry *be, const char *str, int type) { - uint8_t *data = map_tb(str, tbSuffix[type], &be->mapping[type]); + uint8_t *data = (uint8_t*)map_tb(str, tbSuffix[type], &be->mapping[type]); if (!data) return false; if (read_le_u32(data) != tbMagic[type]) { fprintf(stderr, "Corrupted table.\n"); - unmap_file(data, be->mapping[type]); + unmap_file((void*)data, be->mapping[type]); return false; } @@ -1335,6 +1601,7 @@ static uint8_t *decompress_pairs(struct PairsData *d, size_t idx) uint8_t *symLen = d->symLen; uint32_t sym, bitCnt; +#ifdef DECOMP64 uint64_t code = from_be_u64(*(uint64_t *)ptr); ptr += 2; @@ -1354,7 +1621,32 @@ static uint8_t *decompress_pairs(struct PairsData *d, size_t idx) code |= (uint64_t)tmp << bitCnt; } } - +#else + uint32_t next = 0; + uint32_t data = *ptr++; + uint32_t code = from_be_u32(data); + bitCnt = 0; // number of bits in next + for (;;) { + int l = m; + while (code < base[l]) l++; + sym = offset[l] + ((code - base[l]) >> (32 - l)); + if (litIdx < (int)symLen[sym] + 1) break; + litIdx -= (int)symLen[sym] + 1; + code <<= l; + if (bitCnt < l) { + if (bitCnt) { + code |= (next >> (32 - l)); + l -= bitCnt; + } + data = *ptr++; + next = from_be_u32(data); + bitCnt = 32; + } + code |= (next >> (32 - l)); + next <<= l; + bitCnt -= l; + } +#endif uint8_t *symPat = d->symPat; while (symLen[sym] != 0) { uint8_t *w = symPat + (3 * sym); @@ -1374,20 +1666,25 @@ static uint8_t *decompress_pairs(struct PairsData *d, size_t idx) // pc[i] ^ flip, where 1 = white pawn, ..., 14 = black king and pc ^ flip // flips between white and black if flip == true. // Pieces of the same type are guaranteed to be consecutive. -INLINE int fill_squares(Pos *pos, uint8_t *pc, bool flip, int mirror, int *p, +inline static int fill_squares(const Pos *pos, uint8_t *pc, bool flip, int mirror, int *p, int i) { - Bitboard bb = pieces_cp((pc[i] >> 3) ^ flip, pc[i] & 7); + Color color = ColorOfPiece(pc[i]); + if (flip) color = (Color)(!(int)color); + uint64_t bb = pieces_by_type(pos, color, TypeOfPiece(pc[i])); + unsigned sq; do { - p[i++] = pop_lsb(&bb) ^ mirror; + sq = lsb(bb); + p[i++] = sq ^ mirror; + bb = poplsb(bb); } while (bb); return i; } -INLINE int probe_table(Pos *pos, int s, int *success, const int type) +int probe_table(const Pos *pos, int s, int *success, const int type) { // Obtain the position's material-signature key - uint64_t key = pos_material_key(); + uint64_t key = calc_key(pos,false); // Test for KvK if (type == WDL && key == 2ULL) @@ -1427,13 +1724,13 @@ INLINE int probe_table(Pos *pos, int s, int *success, const int type) bool bside, flip; if (!be->symmetric) { flip = key != be->key; - bside = (pos_stm() == WHITE) == flip; + bside = (pos->turn == WHITE) == flip; if (type == DTM && be->hasPawns && PAWN(be)->dtmSwitched) { flip = !flip; bside = !bside; } } else { - flip = pos_stm() != WHITE; + flip = pos->turn != WHITE; bside = false; } @@ -1481,9 +1778,9 @@ INLINE int probe_table(Pos *pos, int s, int *success, const int type) if (type == DTM) { if (!be->dtmLossOnly) - v = from_le_u16(be->hasPawns - ? PAWN(be)->dtmMap[PAWN(be)->dtmMapIdx[t][bside][s] + v] - : PIECE(be)->dtmMap[PIECE(be)->dtmMapIdx[bside][s] + v]); + v = (int)from_le_u16(be->hasPawns + ? PAWN(be)->dtmMap[PAWN(be)->dtmMapIdx[t][bside][s] + v] + : PIECE(be)->dtmMap[PIECE(be)->dtmMapIdx[bside][s] + v]); } else { if (flags & 2) { int m = WdlToMap[s + 2]; @@ -1492,9 +1789,9 @@ INLINE int probe_table(Pos *pos, int s, int *success, const int type) ? ((uint8_t *)PAWN(be)->dtzMap)[PAWN(be)->dtzMapIdx[t][m] + v] : ((uint8_t *)PIECE(be)->dtzMap)[PIECE(be)->dtzMapIdx[m] + v]; else - v = from_le_u16(be->hasPawns - ? ((uint16_t *)PAWN(be)->dtzMap)[PAWN(be)->dtzMapIdx[t][m] + v] - : ((uint16_t *)PIECE(be)->dtzMap)[PIECE(be)->dtzMapIdx[m] + v]); + v = (int)from_le_u16(be->hasPawns + ? ((uint16_t *)PAWN(be)->dtzMap)[PAWN(be)->dtzMapIdx[t][m] + v] + : ((uint16_t *)PIECE(be)->dtzMap)[PIECE(be)->dtzMapIdx[m] + v]); } if (!(flags & PAFlags[s + 2]) || (s & 1)) v *= 2; @@ -1503,58 +1800,39 @@ INLINE int probe_table(Pos *pos, int s, int *success, const int type) return v; } -static int probe_wdl_table(struct Pos *pos, int *success) +static int probe_wdl_table(const Pos *pos, int *success) { return probe_table(pos, 0, success, WDL); } -static int probe_dtm_table(struct Pos *pos, int won, int *success) +static int probe_dtm_table(const Pos *pos, int won, int *success) { return probe_table(pos, won, success, DTM); } -static int probe_dtz_table(struct Pos *pos, int wdl, int *success) +static int probe_dtz_table(const Pos *pos, int wdl, int *success) { return probe_table(pos, wdl, success, DTZ); } -// Add underpromotion captures to list of captures. -static ExtMove *add_underprom_caps(struct Pos *pos, ExtMove *m, ExtMove *end) -{ - ExtMove *extra = end; - - for (; m < end; m++) { - Move move = m->move; - if (type_of_m(move) == PROMOTION && piece_on(to_sq(move))) { - (*extra++).move = (Move)(move - (1 << 12)); - (*extra++).move = (Move)(move - (2 << 12)); - (*extra++).move = (Move)(move - (3 << 12)); - } - } - - return extra; -} - // probe_ab() is not called for positions with en passant captures. -static int probe_ab(Pos *pos, int alpha, int beta, int *success) +static int probe_ab(const Pos *pos, int alpha, int beta, int *success) { - assert(ep_square() == 0); + assert(pos->ep == 0); + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *m = moves0; // Generate (at least) all legal captures including (under)promotions. // It is OK to generate more, as long as they are filtered out below. - ExtMove *m = (pos->st-1)->endMoves; - ExtMove *end = !pos_checkers() - ? add_underprom_caps(pos, m, generate_captures(pos, m)) - : generate_evasions(pos, m); - pos->st->endMoves = end; - + TbMove *end = gen_captures(pos, m); for (; m < end; m++) { - Move move = m->move; - if (!is_capture(pos, move) || !is_legal(pos, move)) + Pos pos1; + TbMove move = *m; + if (!is_capture(pos, move)) continue; - do_move(pos, move, gives_check(pos, pos->st, move)); - int v = -probe_ab(pos, -beta, -alpha, success); - undo_move(pos, move); + if (!do_move(&pos1, pos, move)) + continue; // illegal move + int v = -probe_ab(&pos1, -beta, -alpha, success); if (*success == 0) return 0; if (v > alpha) { if (v >= beta) @@ -1583,17 +1861,14 @@ static int probe_ab(Pos *pos, int alpha, int beta, int *success) // 0 : draw // 1 : win, but draw under 50-move rule // 2 : win -int probe_wdl(struct Pos *pos, int *success) +int probe_wdl(Pos *pos, int *success) { *success = 1; // Generate (at least) all legal captures including (under)promotions. - ExtMove *m = (pos->st-1)->endMoves; - ExtMove *end = !pos_checkers() - ? add_underprom_caps(pos, m, generate_captures(pos, m)) - : generate_evasions(pos, m); - pos->st->endMoves = end; - + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *m = moves0; + TbMove *end = gen_captures(pos, m); int bestCap = -3, bestEp = -3; // We do capture resolution, letting bestCap keep track of the best @@ -1601,19 +1876,20 @@ int probe_wdl(struct Pos *pos, int *success) // better ep captures if they exist. for (; m < end; m++) { - Move move = m->move; - if (!is_capture(pos, move) || !is_legal(pos, move)) + Pos pos1; + TbMove move = *m; + if (!is_capture(pos, move)) continue; - do_move(pos, move, gives_check(pos, pos->st, move)); - int v = -probe_ab(pos, -2, -bestCap, success); - undo_move(pos, move); + if (!do_move(&pos1, pos, move)) + continue; // illegal move + int v = -probe_ab(&pos1, -2, -bestCap, success); if (*success == 0) return 0; if (v > bestCap) { if (v == 2) { *success = 2; return 2; } - if (type_of_m(move) != ENPASSANT) + if (!is_en_passant(pos,move)) bestCap = v; else if (v > bestEp) bestEp = v; @@ -1649,26 +1925,19 @@ int probe_wdl(struct Pos *pos, int *success) // Now handle the stalemate case. if (bestEp > -3 && v == 0) { + TbMove moves[TB_MAX_MOVES]; + TbMove *end = gen_moves(pos, moves); // Check for stalemate in the position with ep captures. - for (m = (pos->st-1)->endMoves; m < end; m++) { - Move move = m->move; - if (type_of_m(move) == ENPASSANT) continue; - if (is_legal(pos, move)) break; + for (m = moves; m < end; m++) { + if (!is_en_passant(pos,*m) && legal_move(pos, *m)) break; } - if (m == end && !pos_checkers()) { - end = generate_quiets(pos, end); - for (; m < end; m++) { - Move move = m->move; - if (is_legal(pos, move)) - break; - } - } - if (m == end) { // Stalemate detected. + if (m == end && !is_check(pos)) { + // stalemate score from tb (w/o e.p.), but an en-passant capture + // is possible. *success = 2; return bestEp; } } - // Stalemate / en passant not an issue, so v is the correct value. return v; @@ -1676,113 +1945,111 @@ int probe_wdl(struct Pos *pos, int *success) #if 0 // This will not be called for positions with en passant captures -static Value probe_dtm_dc(Pos *pos, int won, int *success) +static Value probe_dtm_dc(const Pos *pos, int won, int *success) { assert(ep_square() == 0); - Value v, bestCap = -VALUE_INFINITE; + Value v, bestCap = -TB_VALUE_INFINITE; - ExtMove *end, *m = (pos->st-1)->endMoves; + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *end, *m = moves0; // Generate at least all legal captures including (under)promotions - if (!pos_checkers()) { - end = generate_captures(pos, m); - end = add_underprom_caps(pos, m, end); - } else - end = generate_evasions(pos, m); - pos->st->endMoves = end; - + end = gen_captures(pos, m); + Pos pos1; for (; m < end; m++) { - Move move = m->move; - if (!is_capture(pos, move) || !is_legal(pos, move)) + TbMove move = m->move; + if (!is_capture(pos, move)) + continue; + if (!do_move(&pos1, pos, move)) continue; - do_move(pos, move, gives_check(pos, pos->st, move)); if (!won) - v = -probe_dtm_dc(pos, 1, success) + 1; - else if (probe_ab(pos, -1, 0, success) < 0 && *success) - v = -probe_dtm_dc(pos, 0, success) - 1; + v = -probe_dtm_dc(&pos1, 1, success) + 1; + else if (probe_ab(&pos1, -1, 0, success) < 0 && *success) + v = -probe_dtm_dc(&pos1, 0, success) - 1; else - v = -VALUE_INFINITE; - undo_move(pos, move); - bestCap = max(bestCap, v); + v = -TB_VALUE_INFINITE; + bestCap = max(bestCap,v); if (*success == 0) return 0; } int dtm = probe_dtm_table(pos, won, success); - v = won ? VALUE_MATE - 2 * dtm + 1 : -VALUE_MATE + 2 * dtm; + v = won ? TB_VALUE_MATE - 2 * dtm + 1 : -TB_VALUE_MATE + 2 * dtm; - return max(bestCap, v); + return max(bestCap,v); } #endif -static Value probe_dtm_win(Pos *pos, int *success); +static Value probe_dtm_win(const Pos *pos, int *success); // Probe a position known to lose by probing the DTM table and looking // at captures. -static Value probe_dtm_loss(Pos *pos, int *success) +static Value probe_dtm_loss(const Pos *pos, int *success) { - Value v, best = -VALUE_INFINITE, numEp = 0; + Value v, best = -TB_VALUE_INFINITE, numEp = 0; + TbMove moves0[TB_MAX_CAPTURES]; // Generate at least all legal captures including (under)promotions - ExtMove *end, *m = (pos->st-1)->endMoves; - end = pos_checkers() ? generate_evasions(pos, m) - : add_underprom_caps(pos, m, generate_captures(pos, m)); - pos->st->endMoves = end; + TbMove *end, *m = moves0; + end = gen_captures(pos, m); + Pos pos1; for (; m < end; m++) { - Move move = m->move; - if (!is_capture(pos, move) || !is_legal(pos, move)) + TbMove move = *m; + if (!is_capture(pos, move) || !legal_move(pos, move)) continue; - if (type_of_m(move) == ENPASSANT) + if (is_en_passant(pos, move)) numEp++; - do_move(pos, move, gives_check(pos, pos->st, move)); - v = -probe_dtm_win(pos, success) + 1; - undo_move(pos, move); - best = max(best, v); + do_move(&pos1, pos, move); + v = -probe_dtm_win(&pos1, success) + 1; + if (v > best) { + best = v; + } if (*success == 0) return 0; } // If there are en passant captures, the position without ep rights // may be a stalemate. If it is, we must avoid probing the DTM table. - if (numEp != 0 && generate_legal(pos, m) == m + numEp) + if (numEp != 0 && gen_legal(pos, m) == m + numEp) return best; - v = -VALUE_MATE + 2 * probe_dtm_table(pos, 0, success); - return max(best, v); + v = -TB_VALUE_MATE + 2 * probe_dtm_table(pos, 0, success); + return best > v ? best : v; } -static Value probe_dtm_win(Pos *pos, int *success) +static Value probe_dtm_win(const Pos *pos, int *success) { - Value v, best = -VALUE_INFINITE; + Value v, best = -TB_VALUE_INFINITE; // Generate all moves - ExtMove *m = (pos->st-1)->endMoves; - ExtMove *end = pos_checkers() ? generate_evasions(pos, m) - : generate_non_evasions(pos, m); - pos->st->endMoves = end; - + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *m = moves0; + TbMove *end = gen_moves(pos, m); // Perform a 1-ply search + Pos pos1; for (; m < end; m++) { - Move move = m->move; - if (!is_legal(pos, move)) + TbMove move = *m; + if (do_move(&pos1, pos, move)) { + // not legal continue; - do_move(pos, move, gives_check(pos, pos->st, move)); - if ( (ep_square() ? TB_probe_wdl(pos, success) - : probe_ab(pos, -1, 0, success)) < 0 + } + if ((pos1.ep > 0 ? probe_wdl(&pos1, success) + : probe_ab(&pos1, -1, 0, success)) < 0 && *success) - v = -probe_dtm_loss(pos, success) - 1; + v = -probe_dtm_loss(&pos1, success) - 1; else - v = -VALUE_INFINITE; - undo_move(pos, move); - best = max(best, v); + v = -TB_VALUE_INFINITE; + if (v > best) { + best = v; + } if (*success == 0) return 0; } return best; } -Value TB_probe_dtm(Pos *pos, int wdl, int *success) +Value TB_probe_dtm(const Pos *pos, int wdl, int *success) { assert(wdl != 0); @@ -1794,48 +2061,46 @@ Value TB_probe_dtm(Pos *pos, int wdl, int *success) #if 0 // To be called only for non-drawn positions. -Value TB_probe_dtm2(Pos *pos, int wdl, int *success) +Value TB_probe_dtm2(const Pos *pos, int wdl, int *success) { assert(wdl != 0); *success = 1; - Value v, bestCap = -VALUE_INFINITE, bestEp = -VALUE_INFINITE; + Value v, bestCap = -TB_VALUE_INFINITE, bestEp = -TB_VALUE_INFINITE; - ExtMove *end, *m = (pos->st-1)->endMoves; + TbMove moves0[TB_MAX_CAPTURES]; + TbMove *end, *m = moves0; // Generate at least all legal captures including (under)promotions - if (!pos_checkers()) { - end = generate_captures(pos, m); - end = add_underprom_caps(pos, m, end); - } else - end = generate_evasions(pos, m); - pos->st->endMoves = end; + end = gen_captures(pos, m); + Pos pos0 = *pos; // Resolve captures, letting bestCap keep track of the best non-ep // capture and letting bestEp keep track of the best ep capture. + Pos pos1; for (; m < end; m++) { - Move move = m->move; - if (!is_capture(pos, move) || !is_legal(pos, move)) + TbMove move = *m; + if (!is_capture(pos, move)) + continue; + if (!do_move(&pos1, pos, move)) continue; - do_move(pos, move, gives_check(pos, pos->st, move)); if (wdl < 0) - v = -probe_dtm_dc(pos, 1, success) + 1; - else if (probe_ab(pos, -1, 0, success) < 0 && *success) - v = -probe_dtm_dc(pos, 0, success) - 1; + v = -probe_dtm_dc(&pos1, 1, success) + 1; + else if (probe_ab(&pos1, -1, 0, success) < 0 && *success) + v = -probe_dtm_dc(&pos1, 0, success) - 1; else - v = -VALUE_MATE; - undo_move(pos, move); - if (type_of_m(move) == ENPASSANT) - bestEp = max(bestEp, v); + v = -TB_VALUE_MATE; + if (is_en_passant(&pos1, move)) + bestEp = max(bestEp,v); else - bestCap = max(bestCap, v); + bestCap = max(bestCap,v); if (*success == 0) return 0; } // If there are en passant captures, we have to determine the WDL value // for the position without ep rights if it might be different. - if (bestEp > -VALUE_INFINITE && (bestEp < 0 || bestCap < 0)) { + if (bestEp > -TB_VALUE_INFINITE && (bestEp < 0 || bestCap < 0)) { assert(ep_square() != 0); uint8_t s = pos->st->epSquare; pos->st->epSquare = 0; @@ -1847,10 +2112,10 @@ Value TB_probe_dtm2(Pos *pos, int wdl, int *success) return bestEp; } - bestCap = max(bestCap, bestEp); + bestCap = max(bestCap,v); int dtm = probe_dtm_table(pos, wdl > 0, success); - v = wdl > 0 ? VALUE_MATE - 2 * dtm + 1 : -VALUE_MATE + 2 * dtm; - return max(bestCap, v); + v = wdl > 0 ? TB_VALUE_MATE - 2 * dtm + 1 : -TB_VALUE_MATE + 2 * dtm; + return max(bestCap,v); } #endif @@ -1884,9 +2149,9 @@ static int WdlToDtz[] = { -1, -101, 0, 101, 1 }; // In short, if a move is available resulting in dtz + 50-move-counter <= 99, // then do not accept moves leading to dtz + 50-move-counter == 100. // -int probe_dtz(struct Pos *pos, int *success) +int probe_dtz(Pos *pos, int *success) { - int wdl = TB_probe_wdl(pos, success); + int wdl = probe_wdl(pos, success); if (*success == 0) return 0; // If draw, then dtz = 0. @@ -1896,26 +2161,24 @@ int probe_dtz(struct Pos *pos, int *success) if (*success == 2) return WdlToDtz[wdl + 2]; - ExtMove *m = (pos->st-1)->endMoves, *end = NULL; + TbMove moves[TB_MAX_MOVES]; + TbMove *m = moves, *end = NULL; + Pos pos1; // If winning, check for a winning pawn move. if (wdl > 0) { // Generate at least all legal non-capturing pawn moves // including non-capturing promotions. - // (The following calls in fact generate all moves.) - end = !pos_checkers() - ? generate_non_evasions(pos, m) - : generate_evasions(pos, m); - pos->st->endMoves = end; - - for (; m < end; m++) { - Move move = m->move; - if (type_of_p(moved_piece(move)) != PAWN || is_capture(pos, move) - || !is_legal(pos, move)) - continue; - do_move(pos, move, gives_check(pos, pos->st, move)); - int v = -TB_probe_wdl(pos, success); - undo_move(pos, move); + // (The following call in fact generates all moves.) + end = gen_legal(pos, moves); + + for (m = moves; m < end; m++) { + TbMove move = *m; + if (type_of_piece_moved(pos,move) != PAWN || is_capture(pos, move)) + continue; + if (!do_move(&pos1, pos, move)) + continue; // not legal + int v = -probe_wdl(&pos1, success); if (*success == 0) return 0; if (v == wdl) return WdlToDtz[wdl + 2]; @@ -1935,34 +2198,30 @@ int probe_dtz(struct Pos *pos, int *success) int best; if (wdl > 0) { best = INT32_MAX; - // If wdl > 0, we have already generated all moves. - m = (pos->st-1)->endMoves; } else { // If (cursed) loss, the worst case is a losing capture or pawn move // as the "best" move, leading to dtz of -1 or -101. // In case of mate, this will cause -1 to be returned. best = WdlToDtz[wdl + 2]; // If wdl < 0, we still have to generate all moves. - if (!pos_checkers()) - end = generate_non_evasions(pos, m); - else - end = generate_evasions(pos, m); - pos->st->endMoves = end; + end = gen_moves(pos, m); } + assert(end != NULL); - for (; m < end; m++) { - Move move = m->move; + for (m = moves; m < end; m++) { + TbMove move = *m; // We can skip pawn moves and captures. // If wdl > 0, we already caught them. If wdl < 0, the initial value // of best already takes account of them. - if (is_capture(pos, move) || type_of_p(moved_piece(move)) == PAWN - || !is_legal(pos, move)) + if (is_capture(pos, move) || type_of_piece_moved(pos, move) == PAWN) + continue; + if (!do_move(&pos1, pos, move)) { + // move was not legal continue; - do_move(pos, move, gives_check(pos, pos->st, move)); - int v = -TB_probe_dtz(pos, success); - if ( v == 1 - && pos_checkers() - && generate_legal(pos, (pos->st-1)->endMoves) == (pos->st-1)->endMoves) + } + int v = -probe_dtz(&pos1, success); + // Check for the case of mate in 1 + if (v == 1 && is_mate(&pos1)) best = 1; else if (wdl > 0) { if (v > 0 && v + 1 < best) @@ -1971,7 +2230,6 @@ int probe_dtz(struct Pos *pos, int *success) if (v - 1 < best) best = v - 1; } - undo_move(pos, move); if (*success == 0) return 0; } return best; @@ -1979,46 +2237,43 @@ int probe_dtz(struct Pos *pos, int *success) // Use the DTZ tables to rank and score all root moves in the list. // A return value of 0 means that not all probes were successful. -int TB_root_probe_dtz(Pos *pos, struct RootMoves *rm) +static int root_probe_dtz(const Pos *pos, bool hasRepeated, bool useRule50, struct TbRootMoves *rm) { int v, success; // Obtain 50-move counter for the root position. - int cnt50 = pos_rule50_count(); - - // Check whether a position was repeated since the last zeroing move. - // In that case, we need to be careful and play DTZ-optimal moves if - // winning. - int rep = pos->hasRepeated; + int cnt50 = pos->rule50; // The border between draw and win lies at rank 1 or rank 900, depending // on whether the 50-move rule is used. - int bound = option_value(OPT_SYZ_50_MOVE) ? 900 : 1; + int bound = useRule50 ? 900 : 1; // Probe, rank and score each move. - pos->st->endMoves = (pos->st-1)->endMoves; - for (int i = 0; i < rm->size; i++) { - struct RootMove *m = &rm->move[i]; - do_move(pos, m->pv[0], gives_check(pos, pos->st, m->pv[0])); + TbMove rootMoves[TB_MAX_MOVES]; + TbMove * end = gen_legal(pos,rootMoves); + rm->size = end-rootMoves; + Pos pos1; + for (unsigned i = 0; i < rm->size; i++) { + struct TbRootMove *m = &(rm->moves[i]); + m->move = rootMoves[i]; + do_move(&pos1, pos, m->move); // Calculate dtz for the current move counting from the root position. - if (pos_rule50_count() == 0) { + if (pos1.rule50 == 0) { // If the move resets the 50-move counter, dtz is -101/-1/0/1/101. - v = -TB_probe_wdl(pos, &success); + v = -probe_wdl(&pos1, &success); v = WdlToDtz[v + 2]; } else { // Otherwise, take dtz for the new position and correct by 1 ply. - v = -TB_probe_dtz(pos, &success); + v = -probe_dtz(&pos1, &success); if (v > 0) v++; else if (v < 0) v--; } // Make sure that a mating move gets value 1. - if (pos_checkers() && v == 2) { - if (generate_legal(pos, (pos->st-1)->endMoves) == (pos->st-1)->endMoves) - v = 1; + if (v == 2 && is_mate(&pos1)) { + v = 1; } - undo_move(pos, m->pv[0]); if (!success) return 0; // Better moves are ranked higher. Guaranteed wins are ranked equally. @@ -2026,7 +2281,7 @@ int TB_root_probe_dtz(Pos *pos, struct RootMoves *rm) // Note that moves ranked 900 have dtz + cnt50 == 100, which in rare // cases may be insufficient to win as dtz may be one off (see the // comments before TB_probe_dtz()). - int r = v > 0 ? (v + cnt50 <= 99 && !rep ? 1000 : 1000 - (v + cnt50)) + int r = v > 0 ? (v + cnt50 <= 99 && !hasRepeated ? 1000 : 1000 - (v + cnt50)) : v < 0 ? (-v * 2 + cnt50 < 100 ? -1000 : -1000 + (-v + cnt50)) : 0; m->tbRank = r; @@ -2034,42 +2289,43 @@ int TB_root_probe_dtz(Pos *pos, struct RootMoves *rm) // Determine the score to be displayed for this move. Assign at least // 1 cp to cursed wins and let it grow to 49 cp as the position gets // closer to a real win. - m->tbScore = r >= bound ? VALUE_MATE - MAX_MATE_PLY - 1 - : r > 0 ? max( 3, r - 800) * PawnValueEg / 200 - : r == 0 ? VALUE_DRAW - : r > -bound ? min(-3, r + 800) * PawnValueEg / 200 - : -VALUE_MATE + MAX_MATE_PLY + 1; + m->tbScore = r >= bound ? TB_VALUE_MATE - TB_MAX_MATE_PLY - 1 + : r > 0 ? max( 3, r - 800) * TB_VALUE_PAWN / 200 + : r == 0 ? TB_VALUE_DRAW + : r > -bound ? min(-3, r + 800) * TB_VALUE_PAWN / 200 + : -TB_VALUE_MATE + TB_MAX_MATE_PLY + 1; } - return 1; } // Use the WDL tables to rank all root moves in the list. // This is a fallback for the case that some or all DTZ tables are missing. // A return value of 0 means that not all probes were successful. -int root_probe_wdl(struct Pos *pos, struct RootMoves *rm) +int root_probe_wdl(const Pos *pos, bool useRule50, struct TbRootMoves *rm) { static int WdlToRank[] = { -1000, -899, 0, 899, 1000 }; static Value WdlToValue[] = { - -VALUE_MATE + MAX_MATE_PLY + 1, - VALUE_DRAW - 2, - VALUE_DRAW, - VALUE_DRAW + 2, - VALUE_MATE - MAX_MATE_PLY - 1 + -TB_VALUE_MATE + TB_MAX_MATE_PLY + 1, + TB_VALUE_DRAW - 2, + TB_VALUE_DRAW, + TB_VALUE_DRAW + 2, + TB_VALUE_MATE - TB_MAX_MATE_PLY - 1 }; int v, success; - int move50 = option_value(OPT_SYZ_50_MOVE); // Probe, rank and score each move. - pos->st->endMoves = (pos->st-1)->endMoves; - for (int i = 0; i < rm->size; i++) { - struct RootMove *m = &rm->move[i]; - do_move(pos, m->pv[0], gives_check(pos, pos->st, m->pv[0])); - v = -TB_probe_wdl(pos, &success); - undo_move(pos, m->pv[0]); + TbMove moves[TB_MAX_MOVES]; + TbMove *end = gen_legal(pos,moves); + rm->size = end-moves; + Pos pos1; + for (unsigned i = 0; i < rm->size; i++) { + struct TbRootMove *m = &rm->moves[i]; + m->move = moves[i]; + do_move(&pos1, pos, m->move); + v = -probe_wdl(&pos1, &success); if (!success) return 0; - if (!move50) + if (!useRule50) v = v > 0 ? 2 : v < 0 ? -2 : 0; m->tbRank = WdlToRank[v + 2]; m->tbScore = WdlToValue[v + 2]; @@ -2081,36 +2337,35 @@ int root_probe_wdl(struct Pos *pos, struct RootMoves *rm) // Use the DTM tables to find mate scores. // Either DTZ or WDL must have been probed successfully earlier. // A return value of 0 means that not all probes were successful. -int TB_root_probe_dtm(Pos *pos, struct RootMoves *rm) +int root_probe_dtm(const Pos *pos, struct TbRootMoves *rm) { int success; Value tmpScore[rm->size]; // Probe each move. - pos->st->endMoves = (pos->st-1)->endMoves; - for (int i = 0; i < rm->size; i++) { - struct RootMove *m = &rm->move[i]; + for (unsigned i = 0; i < rm->size; i++) { + Pos pos1; + struct TbRootMove *m = &rm->moves[i]; // Use tbScore to find out if the position is won or lost. - int wdl = m->tbScore > PawnValueEg ? 2 - : m->tbScore < -PawnValueEg ? -2 : 0; + int wdl = m->tbScore > TB_VALUE_PAWN ? 2 + : m->tbScore < -TB_VALUE_PAWN ? -2 : 0; if (wdl == 0) tmpScore[i] = 0; else { // Probe and adjust mate score by 1 ply. - do_move(pos, m->pv[0], gives_check(pos, pos->st, m->pv[0])); - Value v = -TB_probe_dtm(pos, -wdl, &success); + do_move(&pos1, pos, m->pv[0]); + Value v = -TB_probe_dtm(&pos1, -wdl, &success); tmpScore[i] = wdl > 0 ? v - 1 : v + 1; - undo_move(pos, m->pv[0]); if (success == 0) return 0; } } // All probes were successful. Now adjust TB scores and ranks. - for (int i = 0; i < rm->size; i++) { - struct RootMove *m = &rm->move[i]; + for (unsigned i = 0; i < rm->size; i++) { + struct TbRootMove *m = &rm->moves[i]; m->tbScore = tmpScore[i]; @@ -2125,47 +2380,227 @@ int TB_root_probe_dtm(Pos *pos, struct RootMoves *rm) } // Use the DTM tables to complete a PV with mate score. -void TB_expand_mate(Pos *pos, struct RootMove *move) +void tb_expand_mate(Pos *pos, struct TbRootMove *move, Value moveScore, unsigned cardinalityDTM) { int success = 1, chk = 0; - Value v = move->score, w = 0; + Value v = moveScore, w = 0; int wdl = v > 0 ? 2 : -2; - ExtMove *m; if (move->pvSize == TB_MAX_PLY) return; + Pos root = *pos; // First get to the end of the incomplete PV. for (int i = 0; i < move->pvSize; i++) { v = v > 0 ? -v - 1 : -v + 1; wdl = -wdl; - pos->st->endMoves = (pos->st-1)->endMoves; - do_move(pos, move->pv[i], gives_check(pos, pos->st, move->pv[i])); + Pos pos0 = *pos; + do_move(pos, &pos0, move->pv[i]); } // Now try to expand until the actual mate. - if (popcount(pieces()) <= TB_CardinalityDTM) - while (v != -VALUE_MATE && move->pvSize < TB_MAX_PLY) { + if (popcount(pos->white | pos->black) <= cardinalityDTM) { + while (v != -TB_VALUE_MATE && move->pvSize < TB_MAX_PLY) { v = v > 0 ? -v - 1 : -v + 1; wdl = -wdl; - pos->st->endMoves = generate_legal(pos, (pos->st-1)->endMoves); - for (m = (pos->st-1)->endMoves; m < pos->st->endMoves; m++) { - do_move(pos, m->move, gives_check(pos, pos->st, m->move)); + TbMove moves[TB_MAX_MOVES]; + TbMove *end = gen_legal(pos, moves); + TbMove *m = moves; + for (; m < end; m++) { + Pos pos1; + do_move(&pos1, pos, *m); if (wdl < 0) - chk = TB_probe_wdl(pos, &success); // verify that m->move wins + chk = probe_wdl(&pos1, &success); // verify that move wins w = success && (wdl > 0 || chk < 0) - ? TB_probe_dtm(pos, wdl, &success) + ? TB_probe_dtm(&pos1, wdl, &success) : 0; - undo_move(pos, m->move); if (!success || v == w) break; } if (!success || v != w) break; - move->pv[move->pvSize++] = m->move; - do_move(pos, m->move, gives_check(pos, pos->st, m->move)); + move->pv[move->pvSize++] = *m; + Pos pos0 = *pos; + do_move(pos, &pos0, *m); } - + } // Get back to the root position. - for (int i = move->pvSize - 1; i >= 0; i--) - undo_move(pos, move->pv[i]); + *pos = root; +} + +static const int wdl_to_dtz[] = +{ + -1, -101, 0, 101, 1 +}; + +// This supports the original Fathom root probe API +static uint16_t probe_root(Pos *pos, int *score, unsigned *results) +{ + int success; + int dtz = probe_dtz(pos, &success); + if (!success) + return 0; + + int16_t scores[MAX_MOVES]; + uint16_t moves0[MAX_MOVES]; + uint16_t *moves = moves0; + uint16_t *end = gen_moves(pos, moves); + size_t len = end - moves; + size_t num_draw = 0; + unsigned j = 0; + for (unsigned i = 0; i < len; i++) + { + Pos pos1; + if (!do_move(&pos1, pos, moves[i])) + { + scores[i] = SCORE_ILLEGAL; + continue; + } + int v = 0; + // print_move(pos,moves[i]); + if (dtz > 0 && is_mate(&pos1)) + v = 1; + else + { + if (pos1.rule50 != 0) + { + v = -probe_dtz(&pos1, &success); + if (v > 0) + v++; + else if (v < 0) + v--; + } + else + { + v = -probe_wdl(&pos1, &success); + v = wdl_to_dtz[v + 2]; + } + } + num_draw += (v == 0); + if (!success) + return 0; + scores[i] = v; + if (results != NULL) + { + unsigned res = 0; + res = TB_SET_WDL(res, dtz_to_wdl(pos->rule50, v)); + res = TB_SET_FROM(res, move_from(moves[i])); + res = TB_SET_TO(res, move_to(moves[i])); + res = TB_SET_PROMOTES(res, move_promotes(moves[i])); + res = TB_SET_EP(res, is_en_passant(pos, moves[i])); + res = TB_SET_DTZ(res, (v < 0? -v: v)); + results[j++] = res; + } + } + if (results != NULL) + results[j++] = TB_RESULT_FAILED; + if (score != NULL) + *score = dtz; + + // Now be a bit smart about filtering out moves. + if (dtz > 0) // winning (or 50-move rule draw) + { + int best = BEST_NONE; + uint16_t best_move = 0; + for (unsigned i = 0; i < len; i++) + { + int v = scores[i]; + if (v == SCORE_ILLEGAL) + continue; + if (v > 0 && v < best) + { + best = v; + best_move = moves[i]; + } + } + return (best == BEST_NONE? 0: best_move); + } + else if (dtz < 0) // losing (or 50-move rule draw) + { + int best = 0; + uint16_t best_move = 0; + for (unsigned i = 0; i < len; i++) + { + int v = scores[i]; + if (v == SCORE_ILLEGAL) + continue; + if (v < best) + { + best = v; + best_move = moves[i]; + } + } + return (best == 0? MOVE_CHECKMATE: best_move); + } + else // drawing + { + // Check for stalemate: + if (num_draw == 0) + return MOVE_STALEMATE; + + // Select a "random" move that preserves the draw. + // Uses calc_key as the PRNG. + size_t count = calc_key(pos, !pos->turn) % num_draw; + for (unsigned i = 0; i < len; i++) + { + int v = scores[i]; + if (v == SCORE_ILLEGAL) + continue; + if (v == 0) + { + if (count == 0) + return moves[i]; + count--; + } + } + return 0; + } +} + +#ifndef TB_NO_HELPER_API + +unsigned tb_pop_count(uint64_t bb) +{ + return popcount(bb); +} + +unsigned tb_lsb(uint64_t bb) +{ + return lsb(bb); +} + +uint64_t tb_pop_lsb(uint64_t bb) +{ + return poplsb(bb); +} + +uint64_t tb_king_attacks(unsigned sq) +{ + return king_attacks(sq); +} + +uint64_t tb_queen_attacks(unsigned sq, uint64_t occ) +{ + return queen_attacks(sq, occ); } + +uint64_t tb_rook_attacks(unsigned sq, uint64_t occ) +{ + return rook_attacks(sq, occ); +} + +uint64_t tb_bishop_attacks(unsigned sq, uint64_t occ) +{ + return bishop_attacks(sq, occ); +} + +uint64_t tb_knight_attacks(unsigned sq) +{ + return knight_attacks(sq); +} + +uint64_t tb_pawn_attacks(unsigned sq, bool color) +{ + return pawn_attacks(sq, color); +} + +#endif /* TB_NO_HELPER_API */ diff --git a/src/tbprobe.h b/src/tbprobe.h index b2d56f3..cc1cf65 100644 --- a/src/tbprobe.h +++ b/src/tbprobe.h @@ -88,6 +88,7 @@ extern unsigned tb_probe_root_impl( /****************************************************************************/ #define TB_MAX_MOVES (192+1) +#define TB_MAX_CAPTURES 64 #define TB_MAX_PLY 256 #define TB_CASTLING_K 0x1 /* White king-side. */ #define TB_CASTLING_Q 0x2 /* White queen-side. */ @@ -291,6 +292,82 @@ static inline unsigned tb_probe_root( _bishops, _knights, _pawns, _rule50, _ep, _turn, _results); } +typedef uint16_t TbMove; + +#define TB_MOVE_FROM(move) \ + (((move) >> 6) & 0x3F) +#define TB_MOVE_TO(move) \ + ((move) & 0x3F) +#define TB_MOVE_PROMOTES(move) \ + (((move) >> 12) & 0x7) + +struct TbRootMove { + TbMove move; + TbMove pv[TB_MAX_PLY]; + unsigned pvSize; + int32_t tbScore, tbRank; +}; + +struct TbRootMoves { + unsigned size; + struct TbRootMove moves[TB_MAX_MOVES]; +}; + +/* + * Use the DTZ tables to rank and score all root moves. + * INPUT: as for tb_probe_root + * OUTPUT: TbRootMoves structure is filled in. This contains + * an array of TbRootMove structures. + * Each structure instance contains a rank, a score, and a + * predicted principal variation. + * RETURN VALUE: + * non-zero if ok, 0 means not all probes were successful + * + */ +int tb_probe_root_dtz( + uint64_t _white, + uint64_t _black, + uint64_t _kings, + uint64_t _queens, + uint64_t _rooks, + uint64_t _bishops, + uint64_t _knights, + uint64_t _pawns, + unsigned _rule50, + unsigned _castling, + unsigned _ep, + bool _turn, + bool hasRepeated, + bool useRule50, + struct TbRootMoves *_results); + +/* +// Use the WDL tables to rank and score all root moves. +// This is a fallback for the case that some or all DTZ tables are missing. + * INPUT: as for tb_probe_root + * OUTPUT: TbRootMoves structure is filled in. This contains + * an array of TbRootMove structures. + * Each structure instance contains a rank, a score, and a + * predicted principal variation. + * RETURN VALUE: + * non-zero if ok, 0 means not all probes were successful + * + */ +int tb_probe_root_wdl(uint64_t _white, + uint64_t _black, + uint64_t _kings, + uint64_t _queens, + uint64_t _rooks, + uint64_t _bishops, + uint64_t _knights, + uint64_t _pawns, + unsigned _rule50, + unsigned _castling, + unsigned _ep, + bool _turn, + bool useRule50, + struct TbRootMoves *_results); + /****************************************************************************/ /* HELPER API */ /****************************************************************************/ From f0171d7f1342ce63f3eb9c070f8695adcd5e3f5f Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 21 Apr 2019 17:59:33 -0700 Subject: [PATCH 24/75] remove trailing whitespace --- src/tbprobe.c | 6 +++--- src/tbprobe.h | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index f2deec0..051feed 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -249,7 +249,7 @@ inline static uint16_t read_le_u16(void *p) static size_t file_size(FD fd) { #ifdef _WIN32 - LARGE_INTEGER fileSize; + LARGE_INTEGER fileSize; if (GetFileSizeEx(fd, &fileSize)==0) { return 0; } @@ -261,7 +261,7 @@ static size_t file_size(FD fd) { } else { return buf.st_size; } -#endif +#endif } #ifndef TB_NO_THREADS @@ -409,7 +409,7 @@ struct BaseEntry { map_t mapping[3]; #ifdef __cplusplus atomic ready[3]; -#else +#else atomic_bool ready[3]; #endif uint8_t num; diff --git a/src/tbprobe.h b/src/tbprobe.h index cc1cf65..406d566 100644 --- a/src/tbprobe.h +++ b/src/tbprobe.h @@ -8,10 +8,10 @@ * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -307,7 +307,7 @@ struct TbRootMove { unsigned pvSize; int32_t tbScore, tbRank; }; - + struct TbRootMoves { unsigned size; struct TbRootMove moves[TB_MAX_MOVES]; @@ -322,7 +322,7 @@ struct TbRootMoves { * predicted principal variation. * RETURN VALUE: * non-zero if ok, 0 means not all probes were successful - * + * */ int tb_probe_root_dtz( uint64_t _white, @@ -340,7 +340,7 @@ int tb_probe_root_dtz( bool hasRepeated, bool useRule50, struct TbRootMoves *_results); - + /* // Use the WDL tables to rank and score all root moves. // This is a fallback for the case that some or all DTZ tables are missing. @@ -351,7 +351,7 @@ int tb_probe_root_dtz( * predicted principal variation. * RETURN VALUE: * non-zero if ok, 0 means not all probes were successful - * + * */ int tb_probe_root_wdl(uint64_t _white, uint64_t _black, From 8ab09a899b0069a648d953b134e2fb3086d81fcb Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Mon, 22 Apr 2019 19:09:44 -0700 Subject: [PATCH 25/75] Fix test for KvK. Fix a signed/unsigned compare warning. --- src/tbprobe.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 051feed..2880273 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1687,7 +1687,8 @@ int probe_table(const Pos *pos, int s, int *success, const int type) uint64_t key = calc_key(pos,false); // Test for KvK - if (type == WDL && key == 2ULL) + // Note: Cfish has key == 2ULL for KvK but we have 0 + if (type == WDL && key == 0ULL) return 0; int hashIdx = key >> (64 - TB_HASHBITS); @@ -2391,7 +2392,7 @@ void tb_expand_mate(Pos *pos, struct TbRootMove *move, Value moveScore, unsigned Pos root = *pos; // First get to the end of the incomplete PV. - for (int i = 0; i < move->pvSize; i++) { + for (unsigned i = 0; i < move->pvSize; i++) { v = v > 0 ? -v - 1 : -v + 1; wdl = -wdl; Pos pos0 = *pos; From 19298c0f187287700e84a6fe570ce187f441f3e7 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Mon, 22 Apr 2019 19:11:01 -0700 Subject: [PATCH 26/75] optimize by default again --- src/apps/Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/apps/Makefile b/src/apps/Makefile index 2e60c35..0cd7b6d 100644 --- a/src/apps/Makefile +++ b/src/apps/Makefile @@ -7,14 +7,13 @@ ifeq ($(UNAME),Darwin) endif CC?=gcc STRIP=strip -#CFLAGS=-std=c99 -O2 -Wall -D TB_NO_THREADS -I.. -CFLAGS=-std=c99 -g -Wall -DDEBUG -DTB_NO_THREADS -I.. +CFLAGS=-std=c99 -O2 -Wall -D TB_NO_THREADS -I.. main: $(TARGET) fathom.linux: $(CC) $(CFLAGS) fathom.c ../tbprobe.c -o fathom.linux -# $(STRIP) fathom.linux + $(STRIP) fathom.linux fathom.macosx: $(CC) $(CFLAGS) fathom.c ../tbprobe.c -o fathom.macosx From e8b79f71cf28f52fdad2dcf3bf0da771869174ff Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Tue, 23 Apr 2019 07:41:33 -0700 Subject: [PATCH 27/75] Fixes for C++ and Windows/MSVC compilation. --- src/stdendian.h | 3 +++ src/tbprobe.c | 41 ++++++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/stdendian.h b/src/stdendian.h index 6f2bf98..cd0e317 100644 --- a/src/stdendian.h +++ b/src/stdendian.h @@ -1,3 +1,5 @@ +#ifndef _STDENDIAN_H_ +#define _STDENDIAN_H_ /* from https://gist.github.com/michaeljclark/3b4fd912f6fa8bb598b3 */ /* modified to use functions not macros for bswap */ /* and added a fix for Cygwin */ @@ -280,3 +282,4 @@ printf("htole64(%016llx) %016llx\n", 0xf0e0d0c0b0a09080ULL, htole64(0xf0e0d0c0b0 } */ +#endif diff --git a/src/tbprobe.c b/src/tbprobe.c index 2880273..8c907ba 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -46,6 +46,7 @@ using namespace std; #define TB_HASHBITS (TB_PIECES < 7 ? 11 : 12) #define TB_MAX_PIECE (TB_PIECES < 7 ? 254 : 650) #define TB_MAX_PAWN (TB_PIECES < 7 ? 256 : 861) +#define TB_MAX_SYMS 4096 #ifndef _WIN32 #include @@ -59,7 +60,6 @@ using namespace std; typedef size_t map_t; #else #include -#include #define SEP_CHAR ';' #define FD HANDLE #define FD_ERR INVALID_HANDLE_VALUE @@ -181,7 +181,7 @@ static unsigned lsb(uint64_t b) { #define max(a,b) a > b ? a : b #define min(a,b) a < b ? a : b -#include +#include "stdendian.h" #if _BYTE_ORDER == _BIG_ENDIAN @@ -312,7 +312,7 @@ static void close_tb(FD fd) #endif } -static void *map_file(FD fd, uint64_t *mapping) +static void *map_file(FD fd, map_t *mapping) { #ifndef _WIN32 struct stat statbuf; @@ -337,17 +337,17 @@ static void *map_file(FD fd, uint64_t *mapping) fprintf(stderr,"CreateFileMapping() failed.\n"); return NULL; } - *mapping = (uint64)map; + *mapping = (map_t)map; void *data = (void *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); if (data == NULL) { - fprintf(stderr,"MapViewOfFile() failed, name = %s%s, error = %lu.\n", name, suffix, GetLastError()); + fprintf(stderr,"MapViewOfFile() failed, error = %lu.\n", GetLastError()); } #endif return data; } #ifndef _WIN32 -static void unmap_file(void *data, uint64_t size) +static void unmap_file(void *data, map_t size) { if (!data) return; if (!munmap(data, size)) { @@ -355,7 +355,7 @@ static void unmap_file(void *data, uint64_t size) } } #else -static void unmap_file(void *data, uint64_t mapping) +static void unmap_file(void *data, map_t mapping) { if (!data) return; if (!UnmapViewOfFile(data)) { @@ -848,8 +848,10 @@ bool tb_init(const char *path) } } - for (int i = 0; i < (1 << TB_HASHBITS); i++) - tbHash[i] = (struct TbHashEntry){ 0 }; + for (int i = 0; i < (1 << TB_HASHBITS); i++) { + tbHash[i].key = 0; + tbHash[i].ptr = NULL; + } char str[16]; int i, j, k, l, m; @@ -1422,7 +1424,8 @@ static struct PairsData *setup_pairs(uint8_t **ptr, size_t tb_size, size[1] = 2ULL * numBlocks; size[2] = (size_t)realNumBlocks << blockSize; - char tmp[numSyms]; + assert(numSyms < TB_MAX_SYMS); + char tmp[TB_MAX_SYMS]; memset(tmp, 0, numSyms); for (uint32_t s = 0; s < numSyms; s++) if (!tmp[s]) @@ -1498,12 +1501,12 @@ static bool init_table(struct BaseEntry *be, const char *str, int type) : &PIECE(be)->dtmMapIdx; for (int t = 0; t < num; t++) { for (int i = 0; i < 2; i++) { - mapIdx[t][0][i] = (uint16_t *)data + 1 - map; + mapIdx[t][0][i] = (uint16_t)(data + 1 - (uint8_t*)map); data += 2 + 2 * read_le_u16(data); } if (split) { for (int i = 0; i < 2; i++) { - mapIdx[t][1][i] = (uint16_t *)data + 1 - map; + mapIdx[t][1][i] = (uint16_t)(data + 1 - (uint8_t*)map); data += 2 + 2 * read_le_u16(data); } } @@ -1521,13 +1524,13 @@ static bool init_table(struct BaseEntry *be, const char *str, int type) if (flags[t] & 2) { if (!(flags[t] & 16)) { for (int i = 0; i < 4; i++) { - mapIdx[t][i] = data + 1 - (uint8_t *)map; + mapIdx[t][i] = (uint16_t)(data + 1 - (uint8_t *)map); data += 1 + data[0]; } } else { data += (uintptr_t)data & 0x01; for (int i = 0; i < 4; i++) { - mapIdx[t][i] = (uint16_t *)data + 1 - (uint16_t *)map; + mapIdx[t][i] = (uint16_t)(data + 1 - (uint8_t *)map); data += 2 + 2 * read_le_u16(data); } } @@ -1577,7 +1580,7 @@ static uint8_t *decompress_pairs(struct PairsData *d, size_t idx) if (!d->idxBits) return d->constValue; - uint32_t mainIdx = idx >> d->idxBits; + uint32_t mainIdx = (uint32_t)(idx >> d->idxBits); int litIdx = (idx & (((size_t)1 << d->idxBits) - 1)) - ((size_t)1 << (d->idxBits - 1)); uint32_t block; memcpy(&block, d->indexTable + 6 * mainIdx, sizeof(block)); @@ -1610,7 +1613,7 @@ static uint8_t *decompress_pairs(struct PairsData *d, size_t idx) int l = m; while (code < base[l]) l++; sym = from_le_u16(offset[l]); - sym += (code - base[l]) >> (64 - l); + sym += (uint32_t)((code - base[l]) >> (64 - l)); if (litIdx < (int)symLen[sym] + 1) break; litIdx -= (int)symLen[sym] + 1; code <<= l; @@ -2252,7 +2255,7 @@ static int root_probe_dtz(const Pos *pos, bool hasRepeated, bool useRule50, stru // Probe, rank and score each move. TbMove rootMoves[TB_MAX_MOVES]; TbMove * end = gen_legal(pos,rootMoves); - rm->size = end-rootMoves; + rm->size = (unsigned)(end-rootMoves); Pos pos1; for (unsigned i = 0; i < rm->size; i++) { struct TbRootMove *m = &(rm->moves[i]); @@ -2318,7 +2321,7 @@ int root_probe_wdl(const Pos *pos, bool useRule50, struct TbRootMoves *rm) // Probe, rank and score each move. TbMove moves[TB_MAX_MOVES]; TbMove *end = gen_legal(pos,moves); - rm->size = end-moves; + rm->size = (unsigned)(end-moves); Pos pos1; for (unsigned i = 0; i < rm->size; i++) { struct TbRootMove *m = &rm->moves[i]; @@ -2341,7 +2344,7 @@ int root_probe_wdl(const Pos *pos, bool useRule50, struct TbRootMoves *rm) int root_probe_dtm(const Pos *pos, struct TbRootMoves *rm) { int success; - Value tmpScore[rm->size]; + Value tmpScore[TB_MAX_MOVES]; // Probe each move. for (unsigned i = 0; i < rm->size; i++) { From 965fc712b33c6c3231cb80da29fcac6b210979fc Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Tue, 23 Apr 2019 14:59:16 -0700 Subject: [PATCH 28/75] Fix gcc warnings. Return error if castling status != 0. --- src/apps/fathom.c | 4 ++-- src/tbchess.c | 10 +++++----- src/tbprobe.c | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/apps/fathom.c b/src/apps/fathom.c index 47d718b..b04845d 100644 --- a/src/apps/fathom.c +++ b/src/apps/fathom.c @@ -714,7 +714,7 @@ int main(int argc, char **argv) } printf("%d moves returned from DTZ probe\n",moves.size); char str[20]; - for (int i = 0; i < moves.size; i++) { + for (unsigned i = 0; i < moves.size; i++) { struct TbRootMove *m = &(moves.moves[i]); move_parts_to_str(pos, TB_MOVE_FROM(m->move), TB_MOVE_TO(m->move), @@ -732,7 +732,7 @@ int main(int argc, char **argv) return -1; } printf("%d moves returned from WDL probe\n",moves.size); - for (int i = 0; i < moves.size; i++) { + for (unsigned i = 0; i < moves.size; i++) { struct TbRootMove *m = &(moves.moves[i]); move_parts_to_str(pos, TB_MOVE_FROM(m->move), TB_MOVE_TO(m->move), diff --git a/src/tbchess.c b/src/tbchess.c index 336a57c..a11788d 100644 --- a/src/tbchess.c +++ b/src/tbchess.c @@ -298,7 +298,7 @@ static const uint64_t anti2board_table[15] = 0x0001020408102040ull, }; -static inline size_t diag2index(uint64_t b, unsigned d) +static inline size_t diag2index(uint64_t b) { b *= 0x0101010101010101ull; b >>= 56; @@ -306,9 +306,9 @@ static inline size_t diag2index(uint64_t b, unsigned d) return (size_t)b; } -static inline size_t anti2index(uint64_t b, unsigned a) +static inline size_t anti2index(uint64_t b) { - return diag2index(b, a); + return diag2index(b); } #define diag(s) square2diag_table[(s)] @@ -322,8 +322,8 @@ static uint64_t bishop_attacks(unsigned sq, uint64_t occ) unsigned d = diag(sq), a = anti(sq); uint64_t d_occ = occ & (diag2board(d) & ~BOARD_EDGE); uint64_t a_occ = occ & (anti2board(a) & ~BOARD_EDGE); - size_t d_idx = diag2index(d_occ, d); - size_t a_idx = anti2index(a_occ, a); + size_t d_idx = diag2index(d_occ); + size_t a_idx = anti2index(a_occ); uint64_t d_attacks = diag_attacks_table[sq][d_idx]; uint64_t a_attacks = anti_attacks_table[sq][a_idx]; return d_attacks | a_attacks; diff --git a/src/tbprobe.c b/src/tbprobe.c index 8c907ba..200c032 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -185,9 +185,11 @@ static unsigned lsb(uint64_t b) { #if _BYTE_ORDER == _BIG_ENDIAN +/* (unused) static uint64_t from_le_u64(uint64_t input) { return bswap64(input); } +*/ static uint32_t from_le_u32(uint32_t input) { return bswap32(input); @@ -205,15 +207,18 @@ static uint32_t from_be_u32(uint32_t x) { return x; } -static uint16_t from_be_u16(uint16_t x) { +/* (unused) + static uint16_t from_be_u16(uint16_t x) { return x; -} + }*/ #else +/* (unused) static uint64_t from_le_u64(uint64_t x) { return x; } +*/ static uint32_t from_le_u32(uint32_t x) { return x; @@ -231,9 +236,11 @@ static uint32_t from_be_u32(uint32_t input) { return bswap32(input); } +/* (unused) static uint16_t from_be_u16(const uint16_t input) { return bswap16(input); } +*/ #endif @@ -585,6 +592,7 @@ int tb_probe_root_dtz( (uint8_t)ep, turn }; + if (castling != 0) return 0; return root_probe_dtz(&pos, hasRepeated, useRule50, results); } @@ -617,6 +625,7 @@ int tb_probe_root_wdl( (uint8_t)ep, turn }; + if (castling != 0) return 0; return root_probe_wdl(&pos, useRule50, results); } @@ -1742,7 +1751,7 @@ int probe_table(const Pos *pos, int s, int *success, const int type) int p[TB_PIECES]; size_t idx; int t = 0; - uint8_t flags; + uint8_t flags = 0; // initialize to fix GCC warning if (!be->hasPawns) { if (type == DTZ) { From 2d258905badbc1a6291f22e39e9956fa734df4ed Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Tue, 23 Apr 2019 15:07:20 -0700 Subject: [PATCH 29/75] Fix Windows error reporting: use correct printf specifier, call GetLastError if CreateFileMapping fails --- src/tbprobe.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 200c032..4b9f9a7 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -341,13 +341,13 @@ static void *map_file(FD fd, map_t *mapping) HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, NULL); if (map == NULL) { - fprintf(stderr,"CreateFileMapping() failed.\n"); + fprintf(stderr,"CreateFileMapping() failed, error = %u.\n", GetLastError()); return NULL; } *mapping = (map_t)map; void *data = (void *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); if (data == NULL) { - fprintf(stderr,"MapViewOfFile() failed, error = %lu.\n", GetLastError()); + fprintf(stderr,"MapViewOfFile() failed, error = %u.\n", GetLastError()); } #endif return data; @@ -366,10 +366,10 @@ static void unmap_file(void *data, map_t mapping) { if (!data) return; if (!UnmapViewOfFile(data)) { - fprintf(stderr, "unmap failed, error code %d", GetLastError()); + fprintf(stderr, "unmap failed, error code %u\n", GetLastError()); } if (!CloseHandle((HANDLE)mapping)) { - fprintf(stderr, "CloseHandle failed, error code %d", GetLastError()); + fprintf(stderr, "CloseHandle failed, error code %u\n", GetLastError()); } } #endif From 85b96e176df44c9cd6a98f6c2b2d207d0ab3bc6f Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Tue, 30 Apr 2019 20:41:17 -0700 Subject: [PATCH 30/75] update README --- README.md | 64 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 52fd21b..18216b7 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ The tool will print out a PGN representation of the probe result, including: * WinningMoves: The list of all winning moves * DrawingMoves: The list of all drawing moves * LosingMoves: The list of all losing moves -* A pseudo "principle variation" of Syzygy vs. Syzygy for the input position. +* A pseudo "principal variation" of Syzygy vs. Syzygy for the input position. For more information, run the following command: @@ -37,37 +37,54 @@ Programming API Fathom provides a simple API. There are three main function calls: -* `tb_init` initializes the tablebase -* `tb_probe_wdl` probes the Win-Draw-Loss (WDL) table for a given position +* `tb_init` initializes the tablebases. +* `tb_free` releases any resources allocated by Fathom. +* `tb_probe_wdl` probes the Win-Draw-Loss (WDL) table for a given position. * `tb_probe_root` probes the Distance-To-Zero (DTZ) table for the given - position. - -All of the API functions use basic integer types, i.e. there is no need to -create and initialize data-structures. Fathom does not require the callee -to provide any additional functionality (e.g. move generation) unlike the -traditional `tbprobe` code. However, chess engines can opt to replace some -of the functionality of Fathom for better performance (see below). + position. It returns a recommended move, and also a list of unsigned + integers, each one encoding a possible move and its DTZ and WDL values. +* `tb_probe_root_dtz` probes the Distance-To-Zero (DTZ) at the root position. + It returns a score and a rank for each possible move. +* `tb_probe_root_wdl` probes the Win-Draw-Loss (WDL) at the root position. + it returns a score and a rank for each possible move. + +Fathom does not require the callee to provide any additional functionality +(e.g. move generation). A simple set of chess-related functions including move +generation is provided in file tbchess.c. However, chess engines can opt to +replace some of this functionality for better performance (see below). Chess Engines ------------- -Chess engines can be `tb_probe_wdl` to get the WDL value during search. The -`tb_probe_root` functional can be used to help pick the best move at the root. -Note that `tb_probe_root` is slower and therefore should only be used at the -root. +Chess engines can use `tb_probe_wdl` to get the WDL value during search. +This function is thread safe (unless TB_NO_THREADS is set). The various +"probe_root" functions are intended for probing only at the root node +and are not thread-safe. Chess engines can opt for a tighter integration of Fathom by configuring `tbconfig.h`. Specifically, the chess engines can define `TB_*_ATTACKS` -macros that replace the default definitions with the engine's own definitions, +macros that replace the default attack functions with the engine's own definitions, avoiding duplication of functionality. -Credits -------- +History and Credits +------------------- + +The Syzygy tablebases were created by Ronald de Man. This original version of Fathom +(basil00/Fathom) combined probing code from Ronald de Man, originally written for +Stockfish, with chess-related functions and other support code from Basil Falcinelli. +This repository was originaly a fork of that codebase, with additional modifications +by Jon Dart. -The Syzygy tablebases were created by Ronald de Man. Much of the probing code -`tbprobe.c` is a modified version of Ronald's `tbprobe.cpp` for Stockfish (all -Stockfish-specific code has been removed). The `tbcore.c` file is virtually -unchanged from Ronald's original version. +However, the current release of Fathom is not derived directly from the probing code +written for Stockfish, but from tbprobe.c, which is a component of the Cfish chess engine +(https://github.com/syzygy1/Cfish), a Stockfish derivative. tbprobe.c was written +by Ronald de Man and released for unrestricted distribution and use. + +Fathom replaces the Cfish board representation and move generation code used in tbprobe.c +with simpler code from the original Fathom source by Basil. The code has been reorganized +so that tbchess.c contains all move generation and most chess-related typedefs and +functions, while tbprobe.c contains all the tablebase probing code. The code replacement and +reorganization was done by Jon Dart. License ------- @@ -76,10 +93,7 @@ License (C) 2015 basil (new modifications) (C) 2016-2019 Jon Dart (additional modifications) -Ronald de Man's original code can be "redistributed and/or modified without -restrictions". - -The new modifications are released under the permissive MIT License: +This version of Fathom is released under the MIT License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From ec24b3f23e3d3893c9cd5b2d458e10b7bdb620f8 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Tue, 30 Apr 2019 20:44:50 -0700 Subject: [PATCH 31/75] small README fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18216b7..fb97460 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ For more information, run the following command: Programming API --------------- -Fathom provides a simple API. There are three main function calls: +Fathom provides a simple API. Following are the main function calls: * `tb_init` initializes the tablebases. * `tb_free` releases any resources allocated by Fathom. From d9d8c0a69470ae29a087b25a0aa1600caf5e254c Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 28 Jul 2019 07:14:42 -0700 Subject: [PATCH 32/75] add some asserts --- src/tbchess.c | 2 ++ src/tbprobe.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tbchess.c b/src/tbchess.c index a11788d..7c95137 100644 --- a/src/tbchess.c +++ b/src/tbchess.c @@ -659,6 +659,7 @@ static TbMove *gen_captures(const Pos *pos, TbMove *moves) uint64_t b, att; { unsigned from = lsb(pos->kings & us); + assert(from < 64); for (att = king_attacks(from) & them; att; att = poplsb(att)) { unsigned to = lsb(att); @@ -871,6 +872,7 @@ static bool is_check(const Pos *pos) uint64_t us = (pos->turn? pos->white: pos->black), them = (pos->turn? pos->black: pos->white); uint64_t king = pos->kings & us; + assert(king != 0); unsigned sq = lsb(king); uint64_t ratt = rook_attacks(sq, occ); uint64_t batt = bishop_attacks(sq, occ); diff --git a/src/tbprobe.c b/src/tbprobe.c index 4b9f9a7..17ccc02 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -2193,8 +2193,10 @@ int probe_dtz(Pos *pos, int *success) continue; // not legal int v = -probe_wdl(&pos1, success); if (*success == 0) return 0; - if (v == wdl) + if (v == wdl) { + assert(wdl < 3); return WdlToDtz[wdl + 2]; + } } } @@ -2275,6 +2277,7 @@ static int root_probe_dtz(const Pos *pos, bool hasRepeated, bool useRule50, stru if (pos1.rule50 == 0) { // If the move resets the 50-move counter, dtz is -101/-1/0/1/101. v = -probe_wdl(&pos1, &success); + assert(v < 3); v = WdlToDtz[v + 2]; } else { // Otherwise, take dtz for the new position and correct by 1 ply. From a330188275b49225524cec0cb4509a6f548ff2a6 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 28 Jul 2019 08:48:55 -0700 Subject: [PATCH 33/75] Fix mingw64 warnings (reported by Vivien Clauzon). --- src/tbprobe.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 17ccc02..685fed5 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -59,6 +59,9 @@ using namespace std; #define FD_ERR -1 typedef size_t map_t; #else +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #define SEP_CHAR ';' #define FD HANDLE @@ -341,13 +344,13 @@ static void *map_file(FD fd, map_t *mapping) HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, NULL); if (map == NULL) { - fprintf(stderr,"CreateFileMapping() failed, error = %u.\n", GetLastError()); + fprintf(stderr,"CreateFileMapping() failed, error = %lu.\n", GetLastError()); return NULL; } *mapping = (map_t)map; void *data = (void *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); if (data == NULL) { - fprintf(stderr,"MapViewOfFile() failed, error = %u.\n", GetLastError()); + fprintf(stderr,"MapViewOfFile() failed, error = %lu.\n", GetLastError()); } #endif return data; @@ -366,10 +369,10 @@ static void unmap_file(void *data, map_t mapping) { if (!data) return; if (!UnmapViewOfFile(data)) { - fprintf(stderr, "unmap failed, error code %u\n", GetLastError()); + fprintf(stderr, "unmap failed, error code %lu\n", GetLastError()); } if (!CloseHandle((HANDLE)mapping)) { - fprintf(stderr, "CloseHandle failed, error code %u\n", GetLastError()); + fprintf(stderr, "CloseHandle failed, error code %lu\n", GetLastError()); } } #endif From 97fb5ca91fd742220197c12a71e49ee72bf73641 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 11 Aug 2019 20:33:41 -0700 Subject: [PATCH 34/75] minor README changes --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fb97460..be9d06a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ are: * To make it easy to create stand-alone applications that use the Syzygy tablebases; +Fathom is compilable under either C99 or C++ and supports a variety of +platforms, including at least Windows, Linux, and MacOS. + Tool ---- @@ -70,7 +73,7 @@ History and Credits ------------------- The Syzygy tablebases were created by Ronald de Man. This original version of Fathom -(basil00/Fathom) combined probing code from Ronald de Man, originally written for +(https://github.com/basil00/Fathom) combined probing code from Ronald de Man, originally written for Stockfish, with chess-related functions and other support code from Basil Falcinelli. This repository was originaly a fork of that codebase, with additional modifications by Jon Dart. From 2c8c19875756179715b19dae710a9eb472a8c468 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Thu, 25 Jun 2020 14:22:21 -0700 Subject: [PATCH 35/75] Place PieceType and other definitions within an anonymous C++ namespace (fixes ODR warning when compiling Arasan with Fathom). --- LICENSE | 2 +- README.md | 2 +- src/tbchess.c | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 3229171..3aa7375 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ The MIT License (MIT) Copyright (c) 2015 basil00 -Modifications Copyright (c) 2016-2019 by Jon Dart +Modifications Copyright (c) 2016-2020 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index be9d06a..dca3763 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ License (C) 2013-2015 Ronald de Man (original code) (C) 2015 basil (new modifications) -(C) 2016-2019 Jon Dart (additional modifications) +(C) 2016-2020 Jon Dart (additional modifications) This version of Fathom is released under the MIT License: diff --git a/src/tbchess.c b/src/tbchess.c index 7c95137..bca4d1a 100644 --- a/src/tbchess.c +++ b/src/tbchess.c @@ -1,6 +1,6 @@ /* Copyright (c) 2015 basil00 -Modifications Copyright (c) 2016-2019 by Jon Dart +Modifications Copyright (c) 2016-2020 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -68,6 +68,7 @@ SOFTWARE. // Note: WHITE, BLACK values are reverse of Stockfish #ifdef __cplusplus +namespace { enum Color { BLACK, WHITE }; enum PieceType { PAWN=1, KNIGHT, BISHOP, ROOK, QUEEN, KING }; enum Piece { @@ -1043,3 +1044,7 @@ static TbMove *gen_legal(const Pos *pos, TbMove *moves) return results; } +#ifdef __cplusplus +}; +#endif + From 6126bcd92886a9ea07f9d63cd010de84ad30cb52 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 5 Jul 2020 10:26:05 -0700 Subject: [PATCH 36/75] fix some warnings --- src/tbprobe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 685fed5..de61362 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -115,10 +115,10 @@ typedef HANDLE map_t; #define TB_SOFTWARE_POP_COUNT #elif defined (__GNUC__) && defined(__x86_64__) && defined(__SSE4_2__) #include -#define popcount(x) _mm_popcnt_u64((x)) +#define popcount(x) (int)_mm_popcnt_u64((x)) #elif defined(_MSC_VER) && (_MSC_VER >= 1500) && defined(_M_AMD64) #include -#define popcount(x) _mm_popcnt_u64((x)) +#define popcount(x) (int)_mm_popcnt_u64((x)) #else #define TB_SOFTWARE_POP_COUNT #endif From 9f011e480509611caf2aead4d3a55fee08ef1763 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 5 Jul 2020 13:45:50 -0700 Subject: [PATCH 37/75] support MSVC compilation in Unicode mode (-D_UNICODE). --- src/tbprobe.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tbprobe.c b/src/tbprobe.c index de61362..cb2786e 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -301,9 +301,17 @@ static FD open_tb(const char *str, const char *suffix) strcat(file, suffix); #ifndef _WIN32 fd = open(file, O_RDONLY); +#else +#ifdef _UNICODE + wchar_t ucode_name[4096]; + size_t len; + mbstowcs_s(&len, ucode_name, strlen(file)+1, file, _TRUNCATE); + fd = CreateFile(ucode_name, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #else fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif #endif free(file); if (fd != FD_ERR) { From 401f7205120eece3bb63e5cef8c4d6815c7e2822 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Fri, 10 Jul 2020 10:14:45 -0700 Subject: [PATCH 38/75] Pass in correct length of Unicode buffer to mbstowcs_s --- src/tbprobe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index cb2786e..9c54030 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -305,7 +305,7 @@ static FD open_tb(const char *str, const char *suffix) #ifdef _UNICODE wchar_t ucode_name[4096]; size_t len; - mbstowcs_s(&len, ucode_name, strlen(file)+1, file, _TRUNCATE); + mbstowcs_s(&len, ucode_name, 4096, file, _TRUNCATE); fd = CreateFile(ucode_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #else From 4c0792208f551d30614ddf1192ed3c00c41c971a Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 22 Aug 2020 12:47:13 -0700 Subject: [PATCH 39/75] some edits to history/credits section. Add some further advice about overriding tbconfig.h. --- README.md | 89 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index dca3763..ab82d4e 100644 --- a/README.md +++ b/README.md @@ -53,41 +53,84 @@ Fathom provides a simple API. Following are the main function calls: Fathom does not require the callee to provide any additional functionality (e.g. move generation). A simple set of chess-related functions including move -generation is provided in file tbchess.c. However, chess engines can opt to +generation is provided in file `tbchess.c`. However, chess engines can opt to replace some of this functionality for better performance (see below). -Chess Engines +Chess engines ------------- -Chess engines can use `tb_probe_wdl` to get the WDL value during search. -This function is thread safe (unless TB_NO_THREADS is set). The various -"probe_root" functions are intended for probing only at the root node -and are not thread-safe. - -Chess engines can opt for a tighter integration of Fathom by configuring -`tbconfig.h`. Specifically, the chess engines can define `TB_*_ATTACKS` -macros that replace the default attack functions with the engine's own definitions, -avoiding duplication of functionality. +Chess engines can use `tb_probe_wdl` to get the WDL value during +search. This function is thread safe (unless TB_NO_THREADS is +set). The various "probe_root" functions are intended for probing only +at the root node and are not thread-safe. + +Chess engines and other clients can modify some features of Fathom and +override some of its internal functions by configuring +`tbconfig.h`. `tbconfig.h` is included in Fathom's code with angle +brackets. This allows a client of Fathom to override tbconfig.h by +placing its own modified copy in its include path before the Fathom +source directory. + +One option provided by `tbconfig.h` is to define macros that replace +some aspects of Fathom's functionality, such as calculating piece +attacks, avoiding duplication of functionality. If doing this, +however, be careful with including typedefs or defines from your own +code into `tbconfig.h`, since these may clash with internal definitions +used by Fathom. I recommend instead interfacing to external +functions via a small module, with an interface something like this: + +``` +#ifndef _TB_ATTACK_INTERFACE +#define _TB_ATTACK_INTERFACE + +#ifdef __cplusplus +#include +#else +#include +#endif + +extern tb_knight_attacks(unsigned square); +extern tb_king_attacks(unsigned square); +extern tb_root_attacks(unsigned square, uint64_t occ); +extern tb_bishop_attacks(unsigned square, uinit64_t occ); +extern tb_queen_attacks(unsigned square, uint64_t occ); +extern tb_pawn_attacks(unsigned square, uint64_t occ); + +#endif +``` + +You can add if wanted other function definitions such as a popcnt +function based on the chess engine's native popcnt support. + +`tbconfig.h` can then reference these functions safety because the +interface depends only on types defined in standard headers. The +implementation, however, can use any types from the chess engine or +other client that are necessary. (A good optimizer with link-time +optimization will inline the implementation code even though it is not +visible in the interface). History and Credits ------------------- -The Syzygy tablebases were created by Ronald de Man. This original version of Fathom +The Syzygy tablebases were created by Ronald de Man. The original version of Fathom (https://github.com/basil00/Fathom) combined probing code from Ronald de Man, originally written for Stockfish, with chess-related functions and other support code from Basil Falcinelli. -This repository was originaly a fork of that codebase, with additional modifications -by Jon Dart. - -However, the current release of Fathom is not derived directly from the probing code -written for Stockfish, but from tbprobe.c, which is a component of the Cfish chess engine -(https://github.com/syzygy1/Cfish), a Stockfish derivative. tbprobe.c was written +That codebase is no longer being maintained. This repository was originaly a fork of +that codebase, with additional modifications by Jon Dart. + +However, the current Fathom code in this repository is no longer +derived directly from the probing code written for Stockfish, but +instead derives from tbprobe.c, which is a component of the Cfish +chess engine (https://github.com/syzygy1/Cfish), a Stockfish +derivative. tbprobe.c includes 7-man tablebase support. It was written by Ronald de Man and released for unrestricted distribution and use. -Fathom replaces the Cfish board representation and move generation code used in tbprobe.c -with simpler code from the original Fathom source by Basil. The code has been reorganized -so that tbchess.c contains all move generation and most chess-related typedefs and -functions, while tbprobe.c contains all the tablebase probing code. The code replacement and -reorganization was done by Jon Dart. +This fork of Fathom replaces the Cfish board representation and move +generation code used in tbprobe.c with simpler code from the original +Fathom source by Basil. The code has been reorganized so that +`tbchess.c` contains all move generation and most chess-related typedefs +and functions, while `tbprobe.c` contains all the tablebase probing +code. The code replacement and reorganization was done by Jon Dart. License ------- From da175accf7297b6f259dac2e5d306324fae128f4 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 22 Aug 2020 12:59:06 -0700 Subject: [PATCH 40/75] Fix typo. Add mention that chess code was MIT-licensed. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ab82d4e..6dc19e3 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ functions via a small module, with an interface something like this: extern tb_knight_attacks(unsigned square); extern tb_king_attacks(unsigned square); extern tb_root_attacks(unsigned square, uint64_t occ); -extern tb_bishop_attacks(unsigned square, uinit64_t occ); +extern tb_bishop_attacks(unsigned square, uint64_t occ); extern tb_queen_attacks(unsigned square, uint64_t occ); extern tb_pawn_attacks(unsigned square, uint64_t occ); @@ -126,7 +126,7 @@ derivative. tbprobe.c includes 7-man tablebase support. It was written by Ronald de Man and released for unrestricted distribution and use. This fork of Fathom replaces the Cfish board representation and move -generation code used in tbprobe.c with simpler code from the original +generation code used in tbprobe.c with simpler, MIT-licensed code from the original Fathom source by Basil. The code has been reorganized so that `tbchess.c` contains all move generation and most chess-related typedefs and functions, while `tbprobe.c` contains all the tablebase probing From 44234cde16ea4be40c363e7076c43b2442272d11 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 22 Aug 2020 13:06:44 -0700 Subject: [PATCH 41/75] restore original copyright from Ronald de Man from source tbprobe.c --- src/tbprobe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 9c54030..064bc85 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1,6 +1,7 @@ /* +Copyright (c) 2013-2018 Ronald de Man Copyright (c) 2015 basil00 -Modifications Copyright (c) 2016-2019 by Jon Dart +Modifications Copyright (c) 2016-2020 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c6cf6e8f2f4275e91e03c3612b8d17fe64253c5e Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 22 Aug 2020 13:13:37 -0700 Subject: [PATCH 42/75] Bug fix: ensure TB_LARGEST is set zero if Fathom is re-initialized with an empty string or "". --- src/tbprobe.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 064bc85..d94c526 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -832,9 +832,13 @@ bool tb_init(const char *path) numWdl = numDtm = numDtz = 0; } + TB_LARGEST = 0; + // if path is an empty string or equals "", we are done. const char *p = path; - if (strlen(p) == 0 || !strcmp(p, "")) return true; + if (strlen(p) == 0 || !strcmp(p, "")) { + return true; + } pathString = (char*)malloc(strlen(p) + 1); strcpy(pathString, p); @@ -858,7 +862,6 @@ bool tb_init(const char *path) tbNumPiece = tbNumPawn = 0; TB_MaxCardinality = TB_MaxCardinalityDTM = 0; - TB_LARGEST = 0; if (!pieceEntry) { pieceEntry = (struct PieceEntry*)malloc(TB_MAX_PIECE * sizeof(*pieceEntry)); From b2e158a98fdbb03d811265ac6cde0d6b7808d1c0 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Mon, 21 Dec 2020 20:11:48 -0800 Subject: [PATCH 43/75] update copyrights in LICENSE file --- LICENSE | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 3aa7375..0db8b4f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,8 @@ The MIT License (MIT) +Copyright (c) 2013-2018 Ronald de Man Copyright (c) 2015 basil00 -Modifications Copyright (c) 2016-2020 by Jon Dart +Copyright (c) 2016-2020 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 85b69687088a8baf33f911cf0ce31611906fdd12 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Wed, 24 Mar 2021 12:12:51 -0700 Subject: [PATCH 44/75] support Emscripten (emcc) compile --- src/stdendian.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/stdendian.h b/src/stdendian.h index cd0e317..847db07 100644 --- a/src/stdendian.h +++ b/src/stdendian.h @@ -113,9 +113,10 @@ #define __ENDIAN_DEFINED 1 #endif /* sun */ -/* Windows */ -#if defined(_WIN32) || defined(_MSC_VER) -/* assumes all Microsoft targets are little endian */ +/* Windows (also Emscripten) */ +#if defined(_WIN32) || defined(_MSC_VER) || defined(__EMSCRIPTEN__) +/* assumes all Microsoft targets are little endian. */ +/* Emscripten (emcc) also currently assumes little endian. */ #define _LITTLE_ENDIAN 1234 #define _BIG_ENDIAN 4321 #define _BYTE_ORDER _LITTLE_ENDIAN From 8d94ef296506ec729605c385403090b41f052a05 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 11 Sep 2021 17:00:07 -0700 Subject: [PATCH 45/75] mark unreferenced functions as unused, if using C++17 or above --- src/tbprobe.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tbprobe.c b/src/tbprobe.c index d94c526..09928e5 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -2368,6 +2368,9 @@ int root_probe_wdl(const Pos *pos, bool useRule50, struct TbRootMoves *rm) // Use the DTM tables to find mate scores. // Either DTZ or WDL must have been probed successfully earlier. // A return value of 0 means that not all probes were successful. +#if defined(__cplusplus) && __cplusplus >= 201703L +[[maybe_unused]] +#endif int root_probe_dtm(const Pos *pos, struct TbRootMoves *rm) { int success; @@ -2411,6 +2414,9 @@ int root_probe_dtm(const Pos *pos, struct TbRootMoves *rm) } // Use the DTM tables to complete a PV with mate score. +#if defined(__cplusplus) && __cplusplus >= 201703L +[[maybe_unused]] +#endif void tb_expand_mate(Pos *pos, struct TbRootMove *move, Value moveScore, unsigned cardinalityDTM) { int success = 1, chk = 0; From 6a5436603c78e4e06dbfb445617d262db698e547 Mon Sep 17 00:00:00 2001 From: Tearth Date: Tue, 23 Aug 2022 21:09:24 +0200 Subject: [PATCH 46/75] Fix false munmap error --- src/tbprobe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 09928e5..80cdb4c 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -369,7 +369,7 @@ static void *map_file(FD fd, map_t *mapping) static void unmap_file(void *data, map_t size) { if (!data) return; - if (!munmap(data, size)) { + if (munmap(data, size) != 0) { perror("munmap"); } } From c135b094eb8227a282bbfb4d01f8de7907b0a9e6 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 23 Oct 2022 10:48:56 -0700 Subject: [PATCH 47/75] Add option to use __builtin_popcount, as suggested in PR#5. --- src/tbprobe.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 80cdb4c..12092ba 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -121,13 +121,22 @@ typedef HANDLE map_t; #include #define popcount(x) (int)_mm_popcnt_u64((x)) #else +// try to use a builtin +#if defined (__has_builtin) +#if __has_builtin(__builtin_popcountll) +#define popcount(x) __builtin_popcountll((x)) +#else +#define TB_SOFTWARE_POP_COUNT +#endif +#else #define TB_SOFTWARE_POP_COUNT #endif +#endif #ifdef TB_SOFTWARE_POP_COUNT -// Not a recognized compiler/architecture that has popcount: -// fall back to a software popcount. This one is still reasonably -// fast. +// Not a recognized compiler/architecture that has popcount, and +// no builtin available: fall back to a software popcount. This one +// is still reasonably fast. static inline unsigned tb_software_popcount(uint64_t x) { x = x - ((x >> 1) & 0x5555555555555555ull); @@ -135,7 +144,6 @@ static inline unsigned tb_software_popcount(uint64_t x) x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0full; return (x * 0x0101010101010101ull) >> 56; } - #define popcount(x) tb_software_popcount(x) #endif @@ -370,7 +378,7 @@ static void unmap_file(void *data, map_t size) { if (!data) return; if (munmap(data, size) != 0) { - perror("munmap"); + perror("munmap"); } } #else From 0eb8c27fba5d72d0079af68df560755bd3202eec Mon Sep 17 00:00:00 2001 From: Andreas Matthies Date: Mon, 24 Oct 2022 10:38:14 +0200 Subject: [PATCH 48/75] Fix mapping offset for wide dtz tables. --- src/tbprobe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 12092ba..547db05 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1556,7 +1556,7 @@ static bool init_table(struct BaseEntry *be, const char *str, int type) if (flags[t] & 2) { if (!(flags[t] & 16)) { for (int i = 0; i < 4; i++) { - mapIdx[t][i] = (uint16_t)(data + 1 - (uint8_t *)map); + mapIdx[t][i] = ((uint16_t *)data + 1 - (uint16_t *)map); data += 1 + data[0]; } } else { From fcbcb50f3857ed639338e576ac3656edc42ef2c3 Mon Sep 17 00:00:00 2001 From: Andreas Matthies Date: Mon, 24 Oct 2022 11:48:47 +0200 Subject: [PATCH 49/75] Murphys law strikes again, I edited the wrong branch. This fixes it. --- src/tbprobe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 547db05..1ffa7cc 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1556,13 +1556,13 @@ static bool init_table(struct BaseEntry *be, const char *str, int type) if (flags[t] & 2) { if (!(flags[t] & 16)) { for (int i = 0; i < 4; i++) { - mapIdx[t][i] = ((uint16_t *)data + 1 - (uint16_t *)map); + mapIdx[t][i] = (uint16_t)(data + 1 - (uint8_t *)map); data += 1 + data[0]; } } else { data += (uintptr_t)data & 0x01; for (int i = 0; i < 4; i++) { - mapIdx[t][i] = (uint16_t)(data + 1 - (uint8_t *)map); + mapIdx[t][i] = (uint16_t)((uint16_t*)data + 1 - (uint16_t *)map); data += 2 + 2 * read_le_u16(data); } } From 61327b50fc4eda7196012c4ac46bf832101b350d Mon Sep 17 00:00:00 2001 From: Serge Rogatch Date: Sat, 26 Nov 2022 15:35:10 +0100 Subject: [PATCH 50/75] Fix the compilation for C++20 --- src/tbprobe.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 1ffa7cc..9c800c8 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -39,10 +39,6 @@ SOFTWARE. #endif #include "tbprobe.h" -#ifdef __cplusplus -using namespace std; -#endif - #define TB_PIECES 7 #define TB_HASHBITS (TB_PIECES < 7 ? 11 : 12) #define TB_MAX_PIECE (TB_PIECES < 7 ? 254 : 650) @@ -70,6 +66,13 @@ typedef size_t map_t; typedef HANDLE map_t; #endif +// This must be after the inclusion of Windows headers, because otherwise +// std::byte conflicts with "byte" in rpcndr.h . The error occurs if C++ +// standard is at lest 17, as std::byte was introduced in C++17. +#ifdef __cplusplus +using namespace std; +#endif + #define DECOMP64 // Threading support From 8e9fd4d0cf1319b6f1f902c401a3ea21a32fd51b Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Tue, 27 Dec 2022 16:32:09 -0800 Subject: [PATCH 51/75] Change sprintf to snprintf (fixes clang compiler warnings). Update copyright. --- src/tbprobe.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 9c800c8..af6f592 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1,7 +1,7 @@ /* Copyright (c) 2013-2018 Ronald de Man Copyright (c) 2015 basil00 -Modifications Copyright (c) 2016-2020 by Jon Dart +Modifications Copyright (c) 2016-2022 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -892,33 +892,33 @@ bool tb_init(const char *path) int i, j, k, l, m; for (i = 0; i < 5; i++) { - sprintf(str, "K%cvK", pchr(i)); + snprintf(str, 16, "K%cvK", pchr(i)); init_tb(str); } for (i = 0; i < 5; i++) for (j = i; j < 5; j++) { - sprintf(str, "K%cvK%c", pchr(i), pchr(j)); + snprintf(str, 16, "K%cvK%c", pchr(i), pchr(j)); init_tb(str); } for (i = 0; i < 5; i++) for (j = i; j < 5; j++) { - sprintf(str, "K%c%cvK", pchr(i), pchr(j)); + snprintf(str, 16, "K%c%cvK", pchr(i), pchr(j)); init_tb(str); } for (i = 0; i < 5; i++) for (j = i; j < 5; j++) for (k = 0; k < 5; k++) { - sprintf(str, "K%c%cvK%c", pchr(i), pchr(j), pchr(k)); + snprintf(str, 16, "K%c%cvK%c", pchr(i), pchr(j), pchr(k)); init_tb(str); } for (i = 0; i < 5; i++) for (j = i; j < 5; j++) for (k = j; k < 5; k++) { - sprintf(str, "K%c%c%cvK", pchr(i), pchr(j), pchr(k)); + snprintf(str, 16, "K%c%c%cvK", pchr(i), pchr(j), pchr(k)); init_tb(str); } @@ -930,7 +930,7 @@ bool tb_init(const char *path) for (j = i; j < 5; j++) for (k = i; k < 5; k++) for (l = (i == k) ? j : k; l < 5; l++) { - sprintf(str, "K%c%cvK%c%c", pchr(i), pchr(j), pchr(k), pchr(l)); + snprintf(str, 16, "K%c%cvK%c%c", pchr(i), pchr(j), pchr(k), pchr(l)); init_tb(str); } @@ -938,7 +938,7 @@ bool tb_init(const char *path) for (j = i; j < 5; j++) for (k = j; k < 5; k++) for (l = 0; l < 5; l++) { - sprintf(str, "K%c%c%cvK%c", pchr(i), pchr(j), pchr(k), pchr(l)); + snprintf(str, 16, "K%c%c%cvK%c", pchr(i), pchr(j), pchr(k), pchr(l)); init_tb(str); } @@ -946,7 +946,7 @@ bool tb_init(const char *path) for (j = i; j < 5; j++) for (k = j; k < 5; k++) for (l = k; l < 5; l++) { - sprintf(str, "K%c%c%c%cvK", pchr(i), pchr(j), pchr(k), pchr(l)); + snprintf(str, 16, "K%c%c%c%cvK", pchr(i), pchr(j), pchr(k), pchr(l)); init_tb(str); } @@ -958,7 +958,7 @@ bool tb_init(const char *path) for (k = j; k < 5; k++) for (l = k; l < 5; l++) for (m = l; m < 5; m++) { - sprintf(str, "K%c%c%c%c%cvK", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); + snprintf(str, 16, "K%c%c%c%c%cvK", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); init_tb(str); } @@ -967,7 +967,7 @@ bool tb_init(const char *path) for (k = j; k < 5; k++) for (l = k; l < 5; l++) for (m = 0; m < 5; m++) { - sprintf(str, "K%c%c%c%cvK%c", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); + snprintf(str, 16, "K%c%c%c%cvK%c", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); init_tb(str); } @@ -976,7 +976,7 @@ bool tb_init(const char *path) for (k = j; k < 5; k++) for (l = 0; l < 5; l++) for (m = l; m < 5; m++) { - sprintf(str, "K%c%c%cvK%c%c", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); + snprintf(str, 16, "K%c%c%cvK%c%c", pchr(i), pchr(j), pchr(k), pchr(l), pchr(m)); init_tb(str); } From 03882f25149661595d82ea1ed393d744f03c907f Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Thu, 29 Dec 2022 09:48:59 -0800 Subject: [PATCH 52/75] Use madvise to avoid prefetch from tb memory-mapped file region. Also use FILE_FLAG_RANDOM_ACCESS for opening Windows files, as in Stockfish. --- src/apps/Makefile | 2 +- src/tbprobe.c | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/apps/Makefile b/src/apps/Makefile index 0cd7b6d..dec430e 100644 --- a/src/apps/Makefile +++ b/src/apps/Makefile @@ -7,7 +7,7 @@ ifeq ($(UNAME),Darwin) endif CC?=gcc STRIP=strip -CFLAGS=-std=c99 -O2 -Wall -D TB_NO_THREADS -I.. +CFLAGS=-std=gnu99 -O2 -Wall -DTB_NO_THREADS -I.. main: $(TARGET) diff --git a/src/tbprobe.c b/src/tbprobe.c index af6f592..acf691e 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -318,11 +318,14 @@ static FD open_tb(const char *str, const char *suffix) wchar_t ucode_name[4096]; size_t len; mbstowcs_s(&len, ucode_name, 4096, file, _TRUNCATE); + /* use FILE_FLAG_RANDOM_ACCESS because we are likely to access this file + randomly, so prefetch is not helpful. See + https://github.com/official-stockfish/Stockfish/pull/1829 */ fd = CreateFile(ucode_name, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); #else fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); #endif #endif free(file); @@ -358,6 +361,12 @@ static void *map_file(FD fd, map_t *mapping) perror("mmap"); return NULL; } +#ifdef POSIX_MADV_RANDOM + /* Advise the kernel that we are likely to access this data + region randomly, so prefetch is not helpful. See + https://github.com/official-stockfish/Stockfish/pull/1829 */ + posix_madvise(data, statbuf.st_size, POSIX_MADV_RANDOM); +#endif #else DWORD size_low, size_high; size_low = GetFileSize(fd, &size_high); From a817a9b806735de7c2fcc0e781f44195969634ba Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 10 Jun 2023 12:30:01 -0700 Subject: [PATCH 53/75] fix a shadow warning --- src/apps/Makefile | 2 +- src/tbprobe.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/apps/Makefile b/src/apps/Makefile index dec430e..63871a3 100644 --- a/src/apps/Makefile +++ b/src/apps/Makefile @@ -7,7 +7,7 @@ ifeq ($(UNAME),Darwin) endif CC?=gcc STRIP=strip -CFLAGS=-std=gnu99 -O2 -Wall -DTB_NO_THREADS -I.. +CFLAGS=-std=gnu99 -O2 -Wall -Wshadow -DTB_NO_THREADS -I.. main: $(TARGET) diff --git a/src/tbprobe.c b/src/tbprobe.c index acf691e..aba80b6 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1,7 +1,7 @@ /* Copyright (c) 2013-2018 Ronald de Man Copyright (c) 2015 basil00 -Modifications Copyright (c) 2016-2022 by Jon Dart +Modifications Copyright (c) 2016-2023 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -1974,12 +1974,12 @@ int probe_wdl(Pos *pos, int *success) // Now handle the stalemate case. if (bestEp > -3 && v == 0) { TbMove moves[TB_MAX_MOVES]; - TbMove *end = gen_moves(pos, moves); + TbMove *end2 = gen_moves(pos, moves); // Check for stalemate in the position with ep captures. - for (m = moves; m < end; m++) { + for (m = moves; m < end2; m++) { if (!is_en_passant(pos,*m) && legal_move(pos, *m)) break; } - if (m == end && !is_check(pos)) { + if (m == end2 && !is_check(pos)) { // stalemate score from tb (w/o e.p.), but an en-passant capture // is possible. *success = 2; From 294bf60b89fddae3d8fc13930ab5a74a01f3fd7f Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 13 Aug 2023 01:57:24 -0700 Subject: [PATCH 54/75] update copyright dates --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0db8b4f..a83f6f0 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ The MIT License (MIT) Copyright (c) 2013-2018 Ronald de Man Copyright (c) 2015 basil00 -Copyright (c) 2016-2020 by Jon Dart +Copyright (c) 2016-2023 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From aeb9d696c230335a0ebafa36a931427d4a2698ad Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 13 Aug 2023 08:23:22 -0700 Subject: [PATCH 55/75] Update README to reference LICENSE file. --- README.md | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 6dc19e3..132f0df 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Fathom is a stand-alone Syzygy tablebase probing tool. The aims of Fathom are: * To make it easy to integrate the Syzygy tablebases into existing chess - engines; + engines * To make it easy to create stand-alone applications that use the Syzygy - tablebases; + tablebases Fathom is compilable under either C99 or C++ and supports a variety of platforms, including at least Windows, Linux, and MacOS. @@ -135,27 +135,6 @@ code. The code replacement and reorganization was done by Jon Dart. License ------- -(C) 2013-2015 Ronald de Man (original code) -(C) 2015 basil (new modifications) -(C) 2016-2020 Jon Dart (additional modifications) - -This version of Fathom is released under the MIT License: - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +This version of Fathom is released under the MIT License. See the LICENSE file for +details. From da00c954eb56d64bde1576f344e59587fbe3806e Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Thu, 4 Apr 2024 19:41:46 -0700 Subject: [PATCH 56/75] Fix C++20 warning - don't call init to initialize atomic variables, use the constructor --- LICENSE | 2 +- src/tbprobe.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index a83f6f0..fce9757 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ The MIT License (MIT) Copyright (c) 2013-2018 Ronald de Man Copyright (c) 2015 basil00 -Copyright (c) 2016-2023 by Jon Dart +Copyright (c) 2016-2024 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/tbprobe.c b/src/tbprobe.c index aba80b6..e8d4628 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1,7 +1,7 @@ /* Copyright (c) 2013-2018 Ronald de Man Copyright (c) 2015 basil00 -Modifications Copyright (c) 2016-2023 by Jon Dart +Modifications Copyright (c) 2016-2024 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -447,7 +447,11 @@ struct BaseEntry { uint8_t *data[3]; map_t mapping[3]; #ifdef __cplusplus +#if __cplusplus >= 202002L + atomic ready[3]({false, false, false}); +#else atomic ready[3]; +#endif #else atomic_bool ready[3]; #endif @@ -772,8 +776,10 @@ static void init_tb(char *str) TB_MaxCardinalityDTM = be->num; } + #if !defined(__cplusplus) for (int type = 0; type < 3; type++) atomic_init(&be->ready[type], false); + #endif if (!be->hasPawns) { int j = 0; From 0c62f4f91380e97d695064897018055ad8c95cf8 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Wed, 10 Apr 2024 11:41:57 -0700 Subject: [PATCH 57/75] use more portable initialization for ready array --- src/tbprobe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index e8d4628..7952972 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -448,7 +448,7 @@ struct BaseEntry { map_t mapping[3]; #ifdef __cplusplus #if __cplusplus >= 202002L - atomic ready[3]({false, false, false}); + atomic ready[3]{false, false, false}; #else atomic ready[3]; #endif From 9cda72d6c02a012f0b27686652c4ab0853d61d9c Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Thu, 11 Apr 2024 14:16:10 -0700 Subject: [PATCH 58/75] call atomic_init for C++ versions below 20 --- src/tbprobe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 7952972..f4c833d 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -776,10 +776,10 @@ static void init_tb(char *str) TB_MaxCardinalityDTM = be->num; } - #if !defined(__cplusplus) +#if !defined(__cplusplus) || (__cplusplus < 202002L) for (int type = 0; type < 3; type++) atomic_init(&be->ready[type], false); - #endif +#endif if (!be->hasPawns) { int j = 0; From fcd8f26c71e110186290a8adf3100b33d8ff8a2f Mon Sep 17 00:00:00 2001 From: Isak Ellmer Date: Thu, 25 Apr 2024 20:59:07 +0200 Subject: [PATCH 59/75] Add top level Makefile for compiling a shared object --- Makefile | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..521e2c3 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +MKDIR_P = mkdir -p +RM = rm +INSTALL = install + +CC = gcc +CFLAGS = -std=gnu99 -O2 -Wall -Wshadow -Isrc +LDFLAGS = $(CFLAGS) + +PREFIX = /usr/local +LIBDIR = $(PREFIX)/lib64 +INCDIR = $(PREFIX)/include + +libfathom.so: obj/tbprobe.o + $(CC) $(LDFLAGS) -shared $^ -o $@ + +obj/tbprobe.o: src/tbprobe.c src/tbchess.c src/stdendian.h src/tbprobe.h + @$(MKDIR_P) obj + $(CC) $(CFLAGS) -fPIC -c $< -o $@ + +clean: + $(RM) -f libfathom.so + $(RM) -rf obj + +install: libfathom.so + $(MKDIR_P) $(DESTDIR)$(LIBDIR) + $(INSTALL) -m 0644 libfathom.so $(DESTDIR)$(LIBDIR) + $(INSTALL) -m 0644 src/{stdendian,tbconfig,tbprobe}.h $(DESTDIR)$(INCDIR) + +uninstall: + $(RM) -f $(DESTDIR)$(LIBDIR)/libfathom.so + $(RM) -f $(DESTDIR)$(INCDIR)/{stdendian,tbconfig,tbprobe}.h + +.PHONY: clean install uninstall From cb805af48877d64bcccc8bb215cc74b967f282f5 Mon Sep 17 00:00:00 2001 From: Isak Ellmer Date: Sat, 27 Apr 2024 10:26:53 +0200 Subject: [PATCH 60/75] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 521e2c3..0eabfb5 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ MKDIR_P = mkdir -p RM = rm INSTALL = install -CC = gcc +CC ?= gcc CFLAGS = -std=gnu99 -O2 -Wall -Wshadow -Isrc LDFLAGS = $(CFLAGS) From 1b914d637fe0fb4afc0d27cbeada5caf2bc6a30b Mon Sep 17 00:00:00 2001 From: Isak Ellmer Date: Sat, 27 Apr 2024 10:36:54 +0200 Subject: [PATCH 61/75] Add build instructions --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 132f0df..a5b731b 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,16 @@ are: Fathom is compilable under either C99 or C++ and supports a variety of platforms, including at least Windows, Linux, and MacOS. +Building the library +-------------------- +It is possible to build and install Fathom as a shared library on Unix-like +systems. Simple run the commands + + make + make install + +in the top level directory. + Tool ---- From de5cbdd7347db290f969f77c436855992803aef9 Mon Sep 17 00:00:00 2001 From: Isak Ellmer Date: Sun, 28 Apr 2024 13:34:36 +0200 Subject: [PATCH 62/75] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5b731b..94ebbf9 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ platforms, including at least Windows, Linux, and MacOS. Building the library -------------------- It is possible to build and install Fathom as a shared library on Unix-like -systems. Simple run the commands +systems. Simply run the commands make make install From 15d669f99583502be8b090bf5c574e4c898c3bed Mon Sep 17 00:00:00 2001 From: Isak Ellmer Date: Tue, 6 Aug 2024 14:10:31 +0200 Subject: [PATCH 63/75] Fix install bug if $(DESTDIR)$(INCDIR) does not exist --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 0eabfb5..1873033 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ clean: install: libfathom.so $(MKDIR_P) $(DESTDIR)$(LIBDIR) + $(MKDIR_P) $(DESTDIR)$(INCDIR) $(INSTALL) -m 0644 libfathom.so $(DESTDIR)$(LIBDIR) $(INSTALL) -m 0644 src/{stdendian,tbconfig,tbprobe}.h $(DESTDIR)$(INCDIR) From 67d7ae7f6cc35991cb22b663a97fa99b6008d377 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Wed, 4 Dec 2024 16:31:11 -0800 Subject: [PATCH 64/75] Correct statement about thread safety for probe_root* functions. Fixes issue from PR#10. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94ebbf9..f284c83 100644 --- a/README.md +++ b/README.md @@ -70,9 +70,9 @@ Chess engines ------------- Chess engines can use `tb_probe_wdl` to get the WDL value during -search. This function is thread safe (unless TB_NO_THREADS is +search. This function is thread safe (unless TB_NO_THREADS is set). The various "probe_root" functions are intended for probing only -at the root node and are not thread-safe. +at the root node, but are also theread-safe. Chess engines and other clients can modify some features of Fathom and override some of its internal functions by configuring From a56c63ef3dd8dad3432866ddaf0a084108565eed Mon Sep 17 00:00:00 2001 From: Isak Ellmer Date: Thu, 5 Dec 2024 20:30:37 +0100 Subject: [PATCH 65/75] Fix race condition caused by init_table failure --- src/tbprobe.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index f4c833d..0ce5021 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -488,6 +488,11 @@ struct PawnEntry { struct TbHashEntry { uint64_t key; struct BaseEntry *ptr; +#ifdef __cplusplus + atomic error; +#else + atomic_bool error; +#endif }; static int tbNumPiece, tbNumPawn; @@ -725,6 +730,7 @@ static void add_to_hash(struct BaseEntry *ptr, uint64_t key) tbHash[idx].key = key; tbHash[idx].ptr = ptr; + atomic_init(&tbHash[idx].error, false); } #define pchr(i) piece_to_char[QUEEN - (i)] @@ -1747,7 +1753,7 @@ int probe_table(const Pos *pos, int s, int *success, const int type) int hashIdx = key >> (64 - TB_HASHBITS); while (tbHash[hashIdx].key && tbHash[hashIdx].key != key) hashIdx = (hashIdx + 1) & ((1 << TB_HASHBITS) - 1); - if (!tbHash[hashIdx].ptr) { + if (!tbHash[hashIdx].ptr || atomic_load_explicit(&tbHash[hashIdx].error, memory_order_relaxed)) { *success = 0; return 0; } @@ -1761,11 +1767,16 @@ int probe_table(const Pos *pos, int s, int *success, const int type) // Use double-checked locking to reduce locking overhead if (!atomic_load_explicit(&be->ready[type], memory_order_acquire)) { LOCK(tbMutex); + if (atomic_load_explicit(&tbHash[hashIdx].error, memory_order_relaxed)) { + *success = 0; + UNLOCK(tbMutex); + return 0; + } if (!atomic_load_explicit(&be->ready[type], memory_order_relaxed)) { char str[16]; prt_str(pos, str, be->key != key); if (!init_table(be, str, type)) { - tbHash[hashIdx].ptr = NULL; // mark as deleted + atomic_store_explicit(&tbHash[hashIdx].error, true, memory_order_relaxed); *success = 0; UNLOCK(tbMutex); return 0; From 6abe4611ec10d06644ca7dfc3826056945b3b722 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sun, 8 Dec 2024 13:24:26 -0800 Subject: [PATCH 66/75] make probe_table static --- src/tbprobe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index f4c833d..7391236 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1734,7 +1734,7 @@ inline static int fill_squares(const Pos *pos, uint8_t *pc, bool flip, int mirro return i; } -int probe_table(const Pos *pos, int s, int *success, const int type) +static int probe_table(const Pos *pos, int s, int *success, const int type) { // Obtain the position's material-signature key uint64_t key = calc_key(pos,false); From d1e5bcd63102d6a9235cce114e291bcf441ecbef Mon Sep 17 00:00:00 2001 From: Gabe <66077254+MrBrain295@users.noreply.github.com> Date: Wed, 3 Sep 2025 18:04:21 -0500 Subject: [PATCH 67/75] Fix typo in `tbprobe.h` --- src/tbprobe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tbprobe.h b/src/tbprobe.h index 406d566..82850e7 100644 --- a/src/tbprobe.h +++ b/src/tbprobe.h @@ -169,7 +169,7 @@ extern unsigned TB_LARGEST; * The tablebase PATH string. * * RETURN: - * - true=succes, false=failed. The TB_LARGEST global will also be + * - true=success, false=failed. The TB_LARGEST global will also be * initialized. If no tablebase files are found, then `true' is returned * and TB_LARGEST is set to zero. */ From d9522c9b6dd16d915f3b862e9d3e452c71bc41ea Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Sat, 6 Sep 2025 22:50:43 +0200 Subject: [PATCH 68/75] tb_init verifies if already initialized by checking if pieceEntry is NULL. tb_free DID free that memory but did not set the variable to NULL hence confusing the init part. --- src/tbprobe.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tbprobe.c b/src/tbprobe.c index 88f54f8..cba5f0e 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -1019,7 +1019,9 @@ void tb_free(void) { tb_init(""); free(pieceEntry); + pieceEntry = NULL; free(pawnEntry); + pawnEntry = NULL; } static const int8_t OffDiag[] = { From cc65d5da327ddd41be68b863d9049112dac43ae1 Mon Sep 17 00:00:00 2001 From: Cristian Date: Tue, 23 Sep 2025 21:47:56 -0700 Subject: [PATCH 69/75] customize assert, do not define bool if C++, fix typedef typo --- src/tbprobe.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index cba5f0e..2ecdc26 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -22,7 +22,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef TB_ASSERT +#define assert(x) TB_ASSERT(x) +#else #include +#endif + #ifdef __cplusplus #include #else @@ -32,11 +37,15 @@ SOFTWARE. #include #include #include + +#ifndef __cplusplus #ifdef TB_NO_STDBOOL -#typedef uint8 bool +typedef uint8 bool #else #include #endif +#endif + #include "tbprobe.h" #define TB_PIECES 7 From bdf3ac4cabaa563308bb024269f800a9cd3ecc65 Mon Sep 17 00:00:00 2001 From: Cristian Date: Wed, 24 Sep 2025 09:52:09 -0700 Subject: [PATCH 70/75] fix error message --- src/tbprobe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 2ecdc26..ae62d6b 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -704,8 +704,8 @@ static bool test_tb(const char *str, const char *suffix) size_t size = file_size(fd); close_tb(fd); if ((size & 63) != 16) { - fprintf(stderr, "Incomplete tablebase file %s.%s\n", str, suffix); - printf("info string Incomplete tablebase file %s.%s\n", str, suffix); + fprintf(stderr, "Incomplete tablebase file %s%s\n", str, suffix); + printf("info string Incomplete tablebase file %s%s\n", str, suffix); fd = FD_ERR; } } From b7f7d4735fdf3a4719a156278bcea747dfa774f2 Mon Sep 17 00:00:00 2001 From: Cristian Date: Wed, 24 Sep 2025 17:35:55 -0700 Subject: [PATCH 71/75] fix c++20 crashes --- src/tbprobe.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index ae62d6b..d4e46c1 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -905,8 +905,25 @@ bool tb_init(const char *path) TB_MaxCardinality = TB_MaxCardinalityDTM = 0; if (!pieceEntry) { +#if defined(__cplusplus) && (__cplusplus >= 202002L) + /* Fix crash with -std=c++20 by being consistent with BaseEntry initialization: + + #if __cplusplus >= 202002L + atomic ready[3]{false, false, false}; + #else + ... + + C++ initialization does not work with malloc. + Also: with new, if the allocation fails we get std::bad_alloc exception, + which is better library behavior than just calling exit. + */ + + pieceEntry = new PieceEntry[TB_MAX_PIECE](); + pawnEntry = new PawnEntry[TB_MAX_PAWN](); +#else pieceEntry = (struct PieceEntry*)malloc(TB_MAX_PIECE * sizeof(*pieceEntry)); pawnEntry = (struct PawnEntry*)malloc(TB_MAX_PAWN * sizeof(*pawnEntry)); +#endif if (!pieceEntry || !pawnEntry) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); @@ -1027,9 +1044,14 @@ bool tb_init(const char *path) void tb_free(void) { tb_init(""); +#if defined __cplusplus && __cplusplus >= 202002L + delete[] pieceEntry; + delete[] pawnEntry; +#else free(pieceEntry); - pieceEntry = NULL; free(pawnEntry); +#endif + pieceEntry = NULL; pawnEntry = NULL; } From eb5fe56292ebe9fcbbc6f624d9767660da1ad9d6 Mon Sep 17 00:00:00 2001 From: cristivlas Date: Fri, 26 Sep 2025 12:05:05 -0700 Subject: [PATCH 72/75] revert non-essential changes --- src/tbprobe.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index d4e46c1..9b23263 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -22,12 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifdef TB_ASSERT -#define assert(x) TB_ASSERT(x) -#else #include -#endif - #ifdef __cplusplus #include #else @@ -37,15 +32,11 @@ SOFTWARE. #include #include #include - -#ifndef __cplusplus #ifdef TB_NO_STDBOOL -typedef uint8 bool +#typedef uint8 bool #else #include #endif -#endif - #include "tbprobe.h" #define TB_PIECES 7 @@ -704,8 +695,8 @@ static bool test_tb(const char *str, const char *suffix) size_t size = file_size(fd); close_tb(fd); if ((size & 63) != 16) { - fprintf(stderr, "Incomplete tablebase file %s%s\n", str, suffix); - printf("info string Incomplete tablebase file %s%s\n", str, suffix); + fprintf(stderr, "Incomplete tablebase file %s.%s\n", str, suffix); + printf("info string Incomplete tablebase file %s.%s\n", str, suffix); fd = FD_ERR; } } From 3288b4b97e7968e5d0160d3b246b7b6c210e1158 Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Sat, 27 Sep 2025 14:39:23 -0700 Subject: [PATCH 73/75] port stdendian fix from https://github.com/jdart1/arasan-chess --- src/stdendian.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stdendian.h b/src/stdendian.h index 847db07..9f598b5 100644 --- a/src/stdendian.h +++ b/src/stdendian.h @@ -113,10 +113,10 @@ #define __ENDIAN_DEFINED 1 #endif /* sun */ -/* Windows (also Emscripten) */ -#if defined(_WIN32) || defined(_MSC_VER) || defined(__EMSCRIPTEN__) +/* Windows */ /* assumes all Microsoft targets are little endian. */ /* Emscripten (emcc) also currently assumes little endian. */ +#if defined(_WIN32) || defined(_MSC_VER) || defined(__EMSCRIPTEN__) #define _LITTLE_ENDIAN 1234 #define _BIG_ENDIAN 4321 #define _BYTE_ORDER _LITTLE_ENDIAN @@ -227,7 +227,7 @@ inline uint64_t bswap64(uint64_t x) { #define le16toh(x) ((uint16_t)(x)) #define htobe32(x) bswap32((x)) -#define htole32(x) ((uint32_t((x)) +#define htole32(x) ((uint32_t)(x)) #define be32toh(x) bswap32((x)) #define le32toh(x) ((uint32_t)(x)) From f08df228fcc62883a577ce66d4d72473f608aadd Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Tue, 7 Oct 2025 06:16:51 -0700 Subject: [PATCH 74/75] Update copyright. Fix a README typo. --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index fce9757..274394e 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ The MIT License (MIT) Copyright (c) 2013-2018 Ronald de Man Copyright (c) 2015 basil00 -Copyright (c) 2016-2024 by Jon Dart +Copyright (c) 2016-2025 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f284c83..4fabd10 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Chess engines Chess engines can use `tb_probe_wdl` to get the WDL value during search. This function is thread safe (unless TB_NO_THREADS is set). The various "probe_root" functions are intended for probing only -at the root node, but are also theread-safe. +at the root node, but are also thread-safe. Chess engines and other clients can modify some features of Fathom and override some of its internal functions by configuring From c9c6fef0dddc05d2e242c183acf5833149ab676d Mon Sep 17 00:00:00 2001 From: Jon Dart Date: Tue, 23 Dec 2025 17:45:52 -0800 Subject: [PATCH 75/75] Fix possible integer alignment issues --- src/tbprobe.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/tbprobe.c b/src/tbprobe.c index 9b23263..5eac014 100644 --- a/src/tbprobe.c +++ b/src/tbprobe.c @@ -261,12 +261,23 @@ static uint16_t from_be_u16(const uint16_t input) { inline static uint32_t read_le_u32(void *p) { - return from_le_u32(*(uint32_t *)p); + // input may be unaligned, so read into stack allocated + // buffer, which should be aligned + // (prevents runtime errors from glibc) + unsigned char buffer[4]; + memcpy(buffer, p, 4); + return from_le_u32(*(uint32_t *)buffer); } inline static uint16_t read_le_u16(void *p) { - return from_le_u16(*(uint16_t *)p); + // input may be unaligned, so read into stack allocated + // buffer, which should be aligned + // (prevents runtime errors from glibc) + unsigned char buffer[2]; + buffer[0] = *((unsigned char*)p); + buffer[1] = *(((unsigned char*)p)+1); + return from_le_u16(*(uint16_t *)buffer); } static size_t file_size(FD fd) {