pax_global_header00006660000000000000000000000064135241570250014516gustar00rootroot0000000000000052 comment=97fb5ca91fd742220197c12a71e49ee72bf73641 fathom-1.0+git.20190811.97fb5c/000077500000000000000000000000001352415702500153155ustar00rootroot00000000000000fathom-1.0+git.20190811.97fb5c/LICENSE000066400000000000000000000021451352415702500163240ustar00rootroot00000000000000The MIT License (MIT) 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. fathom-1.0+git.20190811.97fb5c/README.md000066400000000000000000000121111352415702500165700ustar00rootroot00000000000000Fathom ====== 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; * 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 ---- Fathom includes a stand-alone command-line syzygy probing tool `fathom`. To probe a position, simply run the command: fathom --path= "FEN-string" The tool will print out a PGN representation of the probe result, including: * Result: "1-0" (white wins), "1/2-1/2" (draw), or "0-1" (black wins) * The Win-Draw-Loss (WDL) value for the next move: "Win", "Draw", "Loss", "CursedWin" (win but 50-move draw) or "BlessedLoss" (loss but 50-move draw) * The Distance-To-Zero (DTZ) value (in plys) for the next move * WinningMoves: The list of all winning moves * DrawingMoves: The list of all drawing moves * LosingMoves: The list of all losing moves * A pseudo "principal variation" of Syzygy vs. Syzygy for the input position. For more information, run the following command: fathom --help Programming API --------------- Fathom provides a simple API. Following are the main function calls: * `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. 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 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. History and Credits ------------------- The Syzygy tablebases were created by Ronald de Man. This 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 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 ------- (C) 2013-2015 Ronald de Man (original code) (C) 2015 basil (new modifications) (C) 2016-2019 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. fathom-1.0+git.20190811.97fb5c/src/000077500000000000000000000000001352415702500161045ustar00rootroot00000000000000fathom-1.0+git.20190811.97fb5c/src/apps/000077500000000000000000000000001352415702500170475ustar00rootroot00000000000000fathom-1.0+git.20190811.97fb5c/src/apps/Makefile000066400000000000000000000014231352415702500205070ustar00rootroot00000000000000UNAME=$(shell uname -s) ifeq ($(UNAME),Linux) TARGET=fathom.linux endif ifeq ($(UNAME),Darwin) TARGET=fathom.macosx endif CC?=gcc STRIP=strip 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 fathom.macosx: $(CC) $(CFLAGS) fathom.c ../tbprobe.c -o fathom.macosx $(STRIP) fathom.macosx fathom.windows: CC=x86_64-w64-mingw32-gcc fathom.windows: STRIP=x86_64-w64-mingw32-strip fathom.windows: $(CC) $(CFLAGS) fathom.c ../tbprobe.c -o fathom.exe $(STRIP) fathom.exe INSTALL=Fathom-1.0 PACKAGE=Fathom-1.0.zip release: mkdir $(INSTALL) cp fathom.linux $(INSTALL) cp fathom.macosx $(INSTALL) cp fathom.exe $(INSTALL) zip -r $(PACKAGE) $(INSTALL) rm -rf $(INSTALL) fathom-1.0+git.20190811.97fb5c/src/apps/fathom.c000066400000000000000000000524071352415702500205010ustar00rootroot00000000000000/* * fathom.c * (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"), * 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 #include #include #include #include #include "tbprobe.h" #define BOARD_RANK_1 0x00000000000000FFull #define BOARD_FILE_A 0x8080808080808080ull #define square(r, f) (8 * (r) + (f)) #define rank(s) ((s) >> 3) #define file(s) ((s) & 0x07) #define board(s) ((uint64_t)1 << (s)) static const char *wdl_to_str[5] = { "0-1", "1/2-1/2", "1/2-1/2", "1/2-1/2", "1-0" }; 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 castling; uint8_t rule50; uint8_t ep; bool turn; uint16_t move; }; /* * Parse a FEN string. */ static bool parse_FEN(struct pos *pos, const char *fen) { uint64_t white = 0, black = 0; uint64_t kings, queens, rooks, bishops, knights, pawns; kings = queens = rooks = bishops = knights = pawns = 0; bool turn; unsigned rule50 = 0, move = 1; unsigned ep = 0; unsigned castling = 0; char c; int r, f; if (fen == NULL) goto fen_parse_error; for (r = 7; r >= 0; r--) { for (f = 0; f <= 7; f++) { unsigned s = (r * 8) + f; uint64_t b = board(s); c = *fen++; switch (c) { case 'k': kings |= b; black |= b; continue; case 'K': kings |= b; white |= b; continue; case 'q': queens |= b; black |= b; continue; case 'Q': queens |= b; white |= b; continue; case 'r': rooks |= b; black |= b; continue; case 'R': rooks |= b; white |= b; continue; case 'b': bishops |= b; black |= b; continue; case 'B': bishops |= b; white |= b; continue; case 'n': knights |= b; black |= b; continue; case 'N': knights |= b; white |= b; continue; case 'p': pawns |= b; black |= b; continue; case 'P': pawns |= b; white |= b; continue; default: break; } if (c >= '1' && c <= '8') { unsigned jmp = (unsigned)c - '0'; f += jmp-1; continue; } goto fen_parse_error; } if (r == 0) break; c = *fen++; if (c != '/') goto fen_parse_error; } c = *fen++; if (c != ' ') goto fen_parse_error; c = *fen++; if (c != 'w' && c != 'b') goto fen_parse_error; turn = (c == 'w'); c = *fen++; if (c != ' ') goto fen_parse_error; c = *fen++; if (c != '-') { do { switch (c) { case 'K': castling |= TB_CASTLING_K; break; case 'Q': castling |= TB_CASTLING_Q; break; case 'k': castling |= TB_CASTLING_k; break; case 'q': castling |= TB_CASTLING_q; break; default: goto fen_parse_error; } c = *fen++; } while (c != ' '); fen--; } c = *fen++; if (c != ' ') goto fen_parse_error; c = *fen++; if (c >= 'a' && c <= 'h') { unsigned file = c - 'a'; c = *fen++; if (c != '3' && c != '6') goto fen_parse_error; unsigned rank = c - '1'; ep = square(rank, file); if (rank == 2 && turn) goto fen_parse_error; if (rank == 5 && !turn) goto fen_parse_error; if (rank == 2 && ((tb_pawn_attacks(ep, true) & (black & pawns)) == 0)) ep = 0; if (rank == 5 && ((tb_pawn_attacks(ep, false) & (white & pawns)) == 0)) ep = 0; } else if (c != '-') goto fen_parse_error; c = *fen++; if (c != ' ') goto fen_parse_error; char clk[4]; clk[0] = *fen++; if (clk[0] < '0' || clk[0] > '9') goto fen_parse_error; clk[1] = *fen++; if (clk[1] != ' ') { if (clk[1] < '0' || clk[1] > '9') goto fen_parse_error; clk[2] = *fen++; if (clk[2] != ' ') { if (clk[2] < '0' || clk[2] > '9') goto fen_parse_error; c = *fen++; if (c != ' ') goto fen_parse_error; clk[3] = '\0'; } else clk[2] = '\0'; } else clk[1] = '\0'; rule50 = atoi(clk); move = atoi(fen); pos->white = white; pos->black = black; pos->kings = kings; pos->queens = queens; pos->rooks = rooks; pos->bishops = bishops; pos->knights = knights; pos->pawns = pawns; pos->castling = castling; pos->rule50 = rule50; pos->ep = ep; pos->turn = turn; pos->move = move; return true; fen_parse_error: 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. */ 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 = tb_lsb(king); uint64_t ratt = tb_rook_attacks(sq, occ); uint64_t batt = tb_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 (tb_knight_attacks(sq) & (pos->knights & them)) return true; if (tb_pawn_attacks(sq, pos->turn) & (pos->pawns & them)) return true; return false; } /* * Convert a move into a string. */ 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); 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'; else if (b & pos->queens) { *str++ = 'Q'; att = tb_queen_attacks(to, occ) & us & pos->queens; } else if (b & pos->rooks) { *str++ = 'R'; att = tb_rook_attacks(to, occ) & us & pos->rooks; } else if (b & pos->bishops) { *str++ = 'B'; att = tb_bishop_attacks(to, occ) & us & pos->bishops; } else if (b & pos->knights) { *str++ = 'N'; att = tb_knight_attacks(to) & us & pos->knights; } else att = tb_pawn_attacks(to, !pos->turn) & us & pos->pawns; if ((b & pos->pawns) && capture) *str++ = 'a' + f; else if (tb_pop_count(att) > 1) { if (tb_pop_count(att & (BOARD_FILE_A >> f)) == 1) *str++ = 'a' + f; else if (tb_pop_count(att & (BOARD_RANK_1 << (8*r))) == 1) *str++ = '1' + r; else { *str++ = 'a' + f; *str++ = '1' + r; } } if (capture) *str++ = 'x'; *str++ = 'a' + file(to); *str++ = '1' + rank(to); if (promotes != TB_PROMOTES_NONE) { *str++ = '='; switch (promotes) { case TB_PROMOTES_QUEEN: *str++ = 'Q'; break; case TB_PROMOTES_ROOK: *str++ = 'R'; break; case TB_PROMOTES_BISHOP: *str++ = 'B'; break; case TB_PROMOTES_KNIGHT: *str++ = 'N'; break; } } *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. */ #define do_bb_move(b, from, to) \ (((b) & (~board(to)) & (~board(from))) | \ ((((b) >> (from)) & 0x1) << (to))) static void do_move(struct pos *pos, unsigned move) { unsigned from = TB_GET_FROM(move); unsigned to = TB_GET_TO(move); unsigned promotes = TB_GET_PROMOTES(move); bool turn = !pos->turn; uint64_t white = do_bb_move(pos->white, from, to); uint64_t black = do_bb_move(pos->black, from, to); uint64_t kings = do_bb_move(pos->kings, from, to); uint64_t queens = do_bb_move(pos->queens, from, to); uint64_t rooks = do_bb_move(pos->rooks, from, to); uint64_t bishops = do_bb_move(pos->bishops, from, to); uint64_t knights = do_bb_move(pos->knights, from, to); uint64_t pawns = do_bb_move(pos->pawns, from, to); unsigned ep = 0; unsigned rule50 = pos->rule50; if (promotes != TB_PROMOTES_NONE) { pawns &= ~board(to); switch (promotes) { case TB_PROMOTES_QUEEN: queens |= board(to); break; case TB_PROMOTES_ROOK: rooks |= board(to); break; case TB_PROMOTES_BISHOP: bishops |= board(to); break; case TB_PROMOTES_KNIGHT: knights |= board(to); break; } rule50 = 0; } else if ((board(from) & pos->pawns) != 0) { rule50 = 0; if (rank(from) == 1 && rank(to) == 3 && (tb_pawn_attacks(from+8, true) & pos->pawns & pos->black) != 0) ep = from+8; else if (rank(from) == 6 && rank(to) == 4 && (tb_pawn_attacks(from-8, false) & pos->pawns & pos->white) != 0) ep = from-8; else if (TB_GET_EP(move)) { unsigned ep_to = (pos->turn? to-8: to+8); uint64_t ep_mask = ~board(ep_to); white &= ep_mask; black &= ep_mask; pawns &= ep_mask; } } else if ((board(to) & (pos->white | pos->black)) != 0) rule50 = 0; else rule50++; pos->white = white; pos->black = black; pos->kings = kings; pos->queens = queens; pos->rooks = rooks; pos->bishops = bishops; pos->knights = knights; pos->pawns = pawns; pos->ep = ep; pos->rule50 = rule50; pos->turn = turn; pos->move += turn; } /* * Print the pseudo "PV" for the given position. */ static void print_PV(struct pos *pos) { struct pos temp = *pos; putchar('\n'); bool first = true, check = false; if (!pos->turn) { first = false; printf("%u...", pos->move); } while (true) { unsigned move = tb_probe_root(pos->white, pos->black, pos->kings, pos->queens, pos->rooks, pos->bishops, pos->knights, pos->pawns, pos->rule50, pos->castling, pos->ep, pos->turn, NULL); if (move == TB_RESULT_FAILED) { printf("{TB probe failed}\n"); break; } if (move == TB_RESULT_CHECKMATE) { printf("# %s\n", (pos->turn? "0-1": "1-0")); break; } if (check) putchar('+'); if (pos->rule50 >= 100 || move == TB_RESULT_STALEMATE) { printf(" 1/2-1/2\n"); break; } char str[32]; move_to_str(pos, move, str); if (!first) putchar(' '); first = false; if (pos->turn) printf("%u. ", pos->move); printf("%s", str); do_move(pos, move); check = is_check(pos); } // restore to root position *pos = temp; } /* * Print a list of moves that match the WDL value. */ static bool print_moves(struct pos *pos, unsigned *results, bool prev, unsigned wdl) { for (unsigned i = 0; results[i] != TB_RESULT_FAILED; i++) { if (TB_GET_WDL(results[i]) != wdl) continue; if (prev) printf(", "); prev = true; char str[32]; move_to_str(pos, results[i], str); printf("%s", str); } return prev; } /* * Print the help message. */ static void print_help(const char *prog) { printf("\n"); printf("usage: %s [--help] [--path=PATH] [--test] FEN\n\n", prog); printf("WHERE:\n"); printf("\tFEN\n"); printf("\t\tThe position (as a FEN string) to be probed.\n"); printf("\t--help\n"); printf("\t\tPrint this helpful message.\n"); printf("\t--path=PATH\n"); printf("\t\tSet the tablebase PATH string.\n"); printf("\t--test\n"); printf("\t\tPrint the result only. Useful for scripts.\n"); printf("\n"); printf("DESCRIPTION:\n"); printf("\tThis program is a stand-alone Syzygy tablebase probe tool. " "The\n"); printf("\tprogram takes as input a FEN string representation of a " "chess\n"); printf("\tposition and outputs a PGN representation of the probe " "result.\n"); printf("\n"); printf("\tIn addition to the standard fields, the output PGN " "represents the\n"); printf("\tfollowing information:\n"); printf("\t- Result: \"1-0\" (white wins), \"1/2-1/2\" (draw), or " "\"0-1\" (black wins)\n"); printf("\t- The Win-Draw-Loss (WDL) value for the next move: \"Win\", " "\"Draw\",\n"); printf("\t \"Loss\", \"CursedWin\" (win but 50-move draw) or " "\"BlessedLoss\" (loss\n"); printf("\t but 50-move draw)\n"); printf("\t- The Distance-To-Zero (DTZ) value (in plys) for the next " "move\n"); printf("\t- WinningMoves: The list of all winning moves\n"); printf("\t- DrawingMoves: The list of all drawing moves\n"); printf("\t- LosingMoves: The list of all losing moves\n"); printf("\n"); printf("\tThe PGN contains a pseudo \"principle variation\" of " "Syzygy vs. Syzygy\n"); printf("\tfor the input position. Each PV move is rational with " "respect to\n"); printf("\tpreserving the WDL value. The PV does not represent the " "shortest\n"); printf("\tmate nor the most natural human moves.\n"); printf("\n"); } /* * Main: */ #define OPTION_HELP 0 #define OPTION_PATH 1 #define OPTION_TEST 2 int main(int argc, char **argv) { static struct option long_options[] = { {"help", 0, 0, OPTION_HELP}, {"path", 1, 0, OPTION_PATH}, {"test", 0, 0, OPTION_TEST}, {NULL, 0, 0, 0} }; char *path = NULL; bool test = false; while (true) { int idx; int opt = getopt_long(argc, argv, "", long_options, &idx); if (opt < 0) break; switch (opt) { case OPTION_PATH: path = (char*)malloc(sizeof(char)*(strlen(optarg)+1)); assert(path != NULL); strcpy(path,optarg); break; case OPTION_TEST: test = true; break; case OPTION_HELP: default: usage: print_help(argv[0]); return EXIT_SUCCESS; } } if (optind != argc-1) goto usage; const char *fen = argv[optind]; // (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) { fprintf(stderr, "error: unable to initialize tablebase; no tablebase " "files found\n"); exit(EXIT_FAILURE); } // (1) parse the FEN: struct pos pos0; struct pos *pos = &pos0; if (!parse_FEN(pos, fen)) { fprintf(stderr, "error: unable to parse FEN string \"%s\"\n", fen); exit(EXIT_FAILURE); } // (2) probe the TB: if (tb_pop_count(pos->white | pos->black) > TB_LARGEST) { fprintf(stderr, "error: unable to probe tablebase; FEN string \"%s\" " "has too many pieces (max=%u)\n", fen, TB_LARGEST); exit(EXIT_FAILURE); } unsigned results[TB_MAX_MOVES]; unsigned res = tb_probe_root(pos->white, pos->black, pos->kings, pos->queens, pos->rooks, pos->bishops, pos->knights, pos->pawns, pos->rule50, pos->castling, pos->ep, pos->turn, results); if (res == TB_RESULT_FAILED) { fprintf(stderr, "error: unable to probe tablebase; position " "invalid, illegal or not in tablebase\n"); exit(EXIT_FAILURE); } // (3) Output: unsigned wdl = TB_GET_WDL(res); if (test) { printf("%s\n", wdl_to_str[(pos->turn? wdl: 4-wdl)]); return 0; } const char *wdl_to_name_str[5] = { "Loss", "BlessedLoss", "Draw", "CursedWin", "Win" }; printf("[Event \"\"]\n"); printf("[Site \"\"]\n"); printf("[Date \"??\"]\n"); printf("[Round \"-\"]\n"); printf("[White \"Syzygy\"]\n"); printf("[Black \"Syzygy\"]\n"); printf("[Result \"%s\"]\n", wdl_to_str[(pos->turn? wdl: 4-wdl)]); printf("[FEN \"%s\"]\n", fen); printf("[WDL \"%s\"]\n", wdl_to_name_str[wdl]); printf("[DTZ \"%u\"]\n", TB_GET_DTZ(res)); printf("[WinningMoves \""); bool prev = false; print_moves(pos, results, prev, TB_WIN); printf("\"]\n"); printf("[DrawingMoves \""); prev = false; prev = print_moves(pos, results, prev, TB_CURSED_WIN); prev = print_moves(pos, results, prev, TB_DRAW); prev = print_moves(pos, results, prev, TB_BLESSED_LOSS); printf("\"]\n"); printf("[LosingMoves \""); prev = false; print_moves(pos, results, prev, TB_LOSS); 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 (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), 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 (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), TB_MOVE_PROMOTES(m->move),str); printf("%s rank = %d score=%d\n", str, m->tbRank, m->tbScore); } return 0; } fathom-1.0+git.20190811.97fb5c/src/stdendian.h000066400000000000000000000212031352415702500202240ustar00rootroot00000000000000#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 */ /* * 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)); } */ #endif fathom-1.0+git.20190811.97fb5c/src/tbchess.c000066400000000000000000000750221352415702500177110ustar00rootroot00000000000000/* 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 #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 // 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 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)); } static inline PieceType TypeOfPiece(int piece) { return (PieceType)(piece & 7); } 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; } #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 #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) { b *= 0x0101010101010101ull; b >>= 56; b >>= 1; return (size_t)b; } static inline size_t anti2index(uint64_t b) { return diag2index(b); } #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); 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; } 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 */ /* * Given a position, produce a 64-bit material signature key. */ static uint64_t calc_key(const 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; } // 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); 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; } // 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; 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) \ ((((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) 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 TbMove *add_move(TbMove *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, including all underpomotions */ 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), them = (pos->turn? pos->black: pos->white); 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); 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); } } return moves; } /* * Generate all 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), 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 Pos *pos, TbMove 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 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 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 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; assert(king != 0); 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 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(Pos *pos, const Pos *pos0, TbMove 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; } 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 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++) { 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; } fathom-1.0+git.20190811.97fb5c/src/tbconfig.h000066400000000000000000000130201352415702500200440ustar00rootroot00000000000000/* * tbconfig.h * (C) 2015 basil, all rights reserved, * 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"), * 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. */ #ifndef TBCONFIG_H #define TBCONFIG_H /****************************************************************************/ /* BUILD CONFIG: */ /****************************************************************************/ /* * Define TB_CUSTOM_POP_COUNT to override the internal popcount * implementation. To do this supply a macro or function definition * here: */ /* #define TB_CUSTOM_POP_COUNT(x) */ /* * Define TB_CUSTOM_LSB to override the internal lsb * implementation. To do this supply a macro or function definition * here: */ /* #define TB_CUSTOM_LSB(x) */ /* * Define TB_NO_STDINT if you do not want to use or it is not * available. */ /* #define TB_NO_STDINT */ /* * Define TB_NO_STDBOOL if you do not want to use or it is not * available or unnecessary (e.g. C++). */ /* #define TB_NO_STDBOOL */ /* * Define TB_NO_THREADS if your program is not multi-threaded. */ /* #define TB_NO_THREADS */ /* * Define TB_NO_HELPER_API if you do not need the helper API. */ /* #define TB_NO_HELPER_API */ /* * 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 */ /***************************************************************************/ /* 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_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 */ /***************************************************************************/ /* * If you are integrating tbprobe into an engine, you can replace some of * tbprobe's built-in functionality with that already provided by the engine. * This is OPTIONAL. If no definition are provided then tbprobe will use its * own internal defaults. That said, for engines it is generally a good idea * to avoid redundancy. */ /* * Define TB_KING_ATTACKS(square) to return the king attacks bitboard for a * king at `square'. */ /* #define TB_KING_ATTACKS(square) */ /* * Define TB_KNIGHT_ATTACKS(square) to return the knight attacks bitboard for * a knight at `square'. */ /* #define TB_KNIGHT_ATTACKS(square) */ /* * Define TB_ROOK_ATTACKS(square, occ) to return the rook attacks bitboard * for a rook at `square' assuming the given `occ' occupancy bitboard. */ /* #define TB_ROOK_ATTACKS(square, occ) */ /* * Define TB_BISHOP_ATTACKS(square, occ) to return the bishop attacks bitboard * for a bishop at `square' assuming the given `occ' occupancy bitboard. */ /* #define TB_BISHOP_ATTACKS(square, occ) */ /* * Define TB_QUEEN_ATTACKS(square, occ) to return the queen attacks bitboard * for a queen at `square' assuming the given `occ' occupancy bitboard. * NOTE: If no definition is provided then tbprobe will use: * TB_ROOK_ATTACKS(square, occ) | TB_BISHOP_ATTACKS(square, occ) */ /* #define TB_QUEEN_ATTACKS(square, occ) */ /* * Define TB_PAWN_ATTACKS(square, color) to return the pawn attacks bitboard * for a `color' pawn at `square'. * NOTE: This definition must work for pawns on ranks 1 and 8. For example, * a white pawn on e1 attacks d2 and f2. A black pawn on e1 attacks * nothing. Etc. * NOTE: This definition must not include en passant captures. */ /* #define TB_PAWN_ATTACKS(square, color) */ #endif fathom-1.0+git.20190811.97fb5c/src/tbprobe.c000066400000000000000000002112601352415702500177070ustar00rootroot00000000000000/* 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 #include #ifdef TB_NO_STDBOOL #typedef uint8 bool #else #include #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) #define TB_MAX_SYMS 4096 #ifndef _WIN32 #include #include #include #include #include #define SEP_CHAR ':' #define FD int #define FD_ERR -1 typedef size_t map_t; #else #ifndef NOMINMAX #define NOMINMAX #endif #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) #include #define LOCK_T std::mutex #define LOCK_INIT(x) #define LOCK_DESTROY(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_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 #endif #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 // population count implementation #undef TB_SOFTWARE_POP_COUNT #if defined(TB_CUSTOM_POP_COUNT) #define popcount(x) TB_CUSTOM_POP_COUNT(x) #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)) #elif defined(_MSC_VER) && (_MSC_VER >= 1500) && defined(_M_AMD64) #include #define popcount(x) _mm_popcnt_u64((x)) #else #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. 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 // 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 "stdendian.h" #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); } 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; } /* (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; } 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); } /* (unused) 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, map_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, 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 = %lu.\n", GetLastError()); } #endif return data; } #ifndef _WIN32 static void unmap_file(void *data, map_t size) { if (!data) return; if (!munmap(data, size)) { perror("munmap"); } } #else static void unmap_file(void *data, map_t mapping) { if (!data) return; if (!UnmapViewOfFile(data)) { fprintf(stderr, "unmap failed, error code %lu\n", GetLastError()); } if (!CloseHandle((HANDLE)mapping)) { fprintf(stderr, "CloseHandle failed, error code %lu\n", GetLastError()); } } #endif #define poplsb(x) ((x) & ((x) - 1)) int TB_MaxCardinality = 0, TB_MaxCardinalityDTM = 0; unsigned TB_LARGEST = 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 }; // 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[1]; }; struct EncInfo { struct PairsData *precomp; size_t factor[TB_PIECES]; uint8_t pieces[TB_PIECES]; uint8_t norm[TB_PIECES]; }; 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 { bool kk_enc; uint8_t pawns[2]; }; bool dtmLossOnly; }; 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; }; 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; }; struct TbHashEntry { uint64_t key; struct BaseEntry *ptr; }; static int tbNumPiece, tbNumPawn; static int numWdl, numDtm, numDtz; static struct PieceEntry *pieceEntry; static struct PawnEntry *pawnEntry; static struct TbHashEntry tbHash[1 << TB_HASHBITS]; static void init_indices(void); // Forward declarations. These functions without the tb_ // prefix take a pos structure as input. 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, 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) { 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); } 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, 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) { 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; TbMove 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 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 }; if (castling != 0) return 0; 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 }; if (castling != 0) return 0; 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(const Pos *pos, char *str, bool flip) { int color = flip ? BLACK : WHITE; for (int pt = KING; pt >= PAWN; 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_by_type(pos, (Color)color, (PieceType)pt)); i > 0; i--) *str++ = piece_to_char[pt]; *str++ = 0; } 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_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); fd = FD_ERR; } } return fd != FD_ERR; } static void *map_tb(const char *name, const char *suffix, map_t *mapping) { 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_tb(fd); return data; } static void add_to_hash(struct BaseEntry *ptr, uint64_t key) { int idx; idx = key >> (64 - TB_HASHBITS); while (tbHash[idx].ptr) idx = (idx + 1) & ((1 << TB_HASHBITS) - 1); tbHash[idx].key = key; tbHash[idx].ptr = ptr; } #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) { 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 { 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); 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]); if (be->num > TB_MaxCardinality) { TB_MaxCardinality = be->num; } if (be->hasDtm) if (be->num > TB_MaxCardinalityDTM) { 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); } #define PIECE(x) ((struct PieceEntry *)(x)) #define PAWN(x) ((struct PawnEntry *)(x)) int num_tables(struct BaseEntry *be, const int type) { return be->hasPawns ? type == DTM ? 6 : 4 : 1; } struct EncInfo *first_ei(struct BaseEntry *be, const int type) { return be->hasPawns ? &PAWN(be)->ei[type == WDL ? 0 : type == DTM ? 8 : 20] : &PIECE(be)->ei[type == WDL ? 0 : type == DTM ? 2 : 4]; } 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((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++) { free(ei[t].precomp); if (type != DTZ) free(ei[num + t].precomp); } atomic_store_explicit(&be->ready[type], false, memory_order_relaxed); } } } bool tb_init(const char *path) { if (!initialized) { init_indices(); king_attacks_init(); knight_attacks_init(); bishop_attacks_init(); rook_attacks_init(); pawn_attacks_init(); initialized = 1; } // 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 true; pathString = (char*)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 = (char**)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; TB_LARGEST = 0; if (!pieceEntry) { 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); } } 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; 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: /* 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[] = { 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 } }; 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 } }; 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 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 }; 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 init_indices(void) { 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; } } } } 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; } size_t encode(int *p, struct EncInfo *ei, struct BaseEntry *be, const int enc) { 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; } } 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; } static size_t encode_piece(int *p, struct EncInfo *ei, struct BaseEntry *be) { return encode(p, ei, be, PIECE_ENC); } static size_t encode_pawn_f(int *p, struct EncInfo *ei, struct BaseEntry *be) { return encode(p, ei, be, FILE_ENC); } static size_t encode_pawn_r(int *p, struct EncInfo *ei, struct BaseEntry *be) { return encode(p, ei, be, RANK_ENC); } // Count number of placements of k like pieces on n squares static size_t subfactor(size_t k, size_t n) { 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; } 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]; } } return f; } 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 = (struct PairsData*)malloc(sizeof(struct PairsData)); 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 = (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(struct PairsData) + 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; 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]) 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; #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; } static bool init_table(struct BaseEntry *be, const char *str, int 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((void*)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 - (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 - (uint8_t*)map); data += 2 + 2 * read_le_u16(data); } } } } 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] = (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); data += 2 + 2 * read_le_u16(data); } } } } 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]; } } 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]; } } 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 true; } static uint8_t *decompress_pairs(struct PairsData *d, size_t idx) { if (!d->idxBits) return d->constValue; 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)); block = from_le_u32(block); uint16_t idxOffset = *(uint16_t *)(d->indexTable + 6 * mainIdx + 4); litIdx += from_le_u16(idxOffset); 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; #ifdef DECOMP64 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 += (uint32_t)((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; } } #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); 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 &symPat[3 * sym]; } // 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 static int fill_squares(const Pos *pos, uint8_t *pc, bool flip, int mirror, int *p, int i) { 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 { sq = lsb(bb); p[i++] = sq ^ mirror; bb = poplsb(bb); } while (bb); return i; } 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); // Test for KvK // Note: Cfish has key == 2ULL for KvK but we have 0 if (type == WDL && key == 0ULL) 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); } UNLOCK(tbMutex); } bool bside, flip; if (!be->symmetric) { flip = key != be->key; bside = (pos->turn == WHITE) == flip; if (type == DTM && be->hasPawns && PAWN(be)->dtmSwitched) { flip = !flip; bside = !bside; } } else { flip = pos->turn != WHITE; bside = false; } struct EncInfo *ei = first_ei(be, type); int p[TB_PIECES]; size_t idx; int t = 0; uint8_t flags = 0; // initialize to fix GCC warning if (!be->hasPawns) { if (type == DTZ) { flags = PIECE(be)->dtzFlags; if ((flags & 1) != bside && !be->symmetric) { *success = -1; return 0; } } 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; } } 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 = (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]; 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 = (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; } return v; } static int probe_wdl_table(const Pos *pos, int *success) { return probe_table(pos, 0, success, WDL); } static int probe_dtm_table(const Pos *pos, int won, int *success) { return probe_table(pos, won, success, DTM); } static int probe_dtz_table(const Pos *pos, int wdl, int *success) { return probe_table(pos, wdl, success, DTZ); } // probe_ab() is not called for positions with en passant captures. static int probe_ab(const Pos *pos, int alpha, int beta, int *success) { 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. TbMove *end = gen_captures(pos, m); for (; m < end; m++) { Pos pos1; TbMove move = *m; if (!is_capture(pos, move)) continue; 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) return v; alpha = v; } } int v = probe_wdl_table(pos, success); return alpha >= v ? alpha : v; } // 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(Pos *pos, int *success) { *success = 1; // Generate (at least) all legal captures including (under)promotions. 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 // capture without ep rights and letting bestEp keep track of still // better ep captures if they exist. for (; m < end; m++) { Pos pos1; TbMove move = *m; if (!is_capture(pos, move)) continue; 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 (!is_en_passant(pos,move)) bestCap = v; else if (v > bestEp) bestEp = v; } } int v = probe_wdl_table(pos, success); if (*success == 0) return 0; // 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). if (bestEp > bestCap) { if (bestEp > v) { // ep capture (possibly cursed losing) is best. *success = 2; return bestEp; } 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) { TbMove moves[TB_MAX_MOVES]; TbMove *end = gen_moves(pos, moves); // Check for stalemate in the position with ep captures. for (m = moves; m < end; m++) { if (!is_en_passant(pos,*m) && legal_move(pos, *m)) break; } 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; } #if 0 // This will not be called for positions with en passant captures static Value probe_dtm_dc(const Pos *pos, int won, int *success) { assert(ep_square() == 0); Value v, bestCap = -TB_VALUE_INFINITE; TbMove moves0[TB_MAX_CAPTURES]; TbMove *end, *m = moves0; // Generate at least all legal captures including (under)promotions end = gen_captures(pos, m); Pos pos1; for (; m < end; m++) { TbMove move = m->move; if (!is_capture(pos, move)) continue; if (!do_move(&pos1, pos, move)) continue; if (!won) 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 = -TB_VALUE_INFINITE; bestCap = max(bestCap,v); if (*success == 0) return 0; } int dtm = probe_dtm_table(pos, won, success); v = won ? TB_VALUE_MATE - 2 * dtm + 1 : -TB_VALUE_MATE + 2 * dtm; return max(bestCap,v); } #endif 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(const Pos *pos, int *success) { Value v, best = -TB_VALUE_INFINITE, numEp = 0; TbMove moves0[TB_MAX_CAPTURES]; // Generate at least all legal captures including (under)promotions TbMove *end, *m = moves0; end = gen_captures(pos, m); Pos pos1; for (; m < end; m++) { TbMove move = *m; if (!is_capture(pos, move) || !legal_move(pos, move)) continue; if (is_en_passant(pos, move)) numEp++; 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 && gen_legal(pos, m) == m + numEp) return best; v = -TB_VALUE_MATE + 2 * probe_dtm_table(pos, 0, success); return best > v ? best : v; } static Value probe_dtm_win(const Pos *pos, int *success) { Value v, best = -TB_VALUE_INFINITE; // Generate all moves 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++) { TbMove move = *m; if (do_move(&pos1, pos, move)) { // not legal continue; } if ((pos1.ep > 0 ? probe_wdl(&pos1, success) : probe_ab(&pos1, -1, 0, success)) < 0 && *success) v = -probe_dtm_loss(&pos1, success) - 1; else v = -TB_VALUE_INFINITE; if (v > best) { best = v; } if (*success == 0) return 0; } return best; } Value TB_probe_dtm(const Pos *pos, int wdl, int *success) { assert(wdl != 0); *success = 1; return wdl > 0 ? probe_dtm_win(pos, success) : probe_dtm_loss(pos, success); } #if 0 // To be called only for non-drawn positions. Value TB_probe_dtm2(const Pos *pos, int wdl, int *success) { assert(wdl != 0); *success = 1; Value v, bestCap = -TB_VALUE_INFINITE, bestEp = -TB_VALUE_INFINITE; TbMove moves0[TB_MAX_CAPTURES]; TbMove *end, *m = moves0; // Generate at least all legal captures including (under)promotions 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++) { TbMove move = *m; if (!is_capture(pos, move)) continue; if (!do_move(&pos1, pos, move)) continue; if (wdl < 0) 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 = -TB_VALUE_MATE; if (is_en_passant(&pos1, move)) 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 > -TB_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; } bestCap = max(bestCap,v); int dtm = probe_dtm_table(pos, wdl > 0, success); v = wdl > 0 ? TB_VALUE_MATE - 2 * dtm + 1 : -TB_VALUE_MATE + 2 * dtm; return max(bestCap,v); } #endif 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(Pos *pos, int *success) { int wdl = 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]; 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 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) { assert(wdl < 3); 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; } 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. end = gen_moves(pos, m); } assert(end != NULL); 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_piece_moved(pos, move) == PAWN) continue; if (!do_move(&pos1, pos, move)) { // move was not legal continue; } 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) best = v + 1; } else { if (v - 1 < best) best = v - 1; } if (*success == 0) return 0; } return best; } // 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. 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; // The border between draw and win lies at rank 1 or rank 900, depending // on whether the 50-move rule is used. int bound = useRule50 ? 900 : 1; // Probe, rank and score each move. TbMove rootMoves[TB_MAX_MOVES]; TbMove * end = gen_legal(pos,rootMoves); rm->size = (unsigned)(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 (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. v = -probe_dtz(&pos1, &success); if (v > 0) v++; else if (v < 0) v--; } // Make sure that a mating move gets value 1. if (v == 2 && is_mate(&pos1)) { v = 1; } 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 && !hasRepeated ? 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 ? 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(const Pos *pos, bool useRule50, struct TbRootMoves *rm) { static int WdlToRank[] = { -1000, -899, 0, 899, 1000 }; static Value WdlToValue[] = { -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; // Probe, rank and score each move. TbMove moves[TB_MAX_MOVES]; TbMove *end = gen_legal(pos,moves); rm->size = (unsigned)(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 (!useRule50) v = v > 0 ? 2 : v < 0 ? -2 : 0; m->tbRank = WdlToRank[v + 2]; m->tbScore = WdlToValue[v + 2]; } return 1; } // 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 root_probe_dtm(const Pos *pos, struct TbRootMoves *rm) { int success; Value tmpScore[TB_MAX_MOVES]; // Probe each move. 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 > 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(&pos1, pos, m->pv[0]); Value v = -TB_probe_dtm(&pos1, -wdl, &success); tmpScore[i] = wdl > 0 ? v - 1 : v + 1; if (success == 0) return 0; } } // All probes were successful. Now adjust TB scores and ranks. for (unsigned i = 0; i < rm->size; i++) { struct TbRootMove *m = &rm->moves[i]; 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; } // Use the DTM tables to complete a PV with mate score. void tb_expand_mate(Pos *pos, struct TbRootMove *move, Value moveScore, unsigned cardinalityDTM) { int success = 1, chk = 0; Value v = moveScore, w = 0; int wdl = v > 0 ? 2 : -2; if (move->pvSize == TB_MAX_PLY) return; Pos root = *pos; // First get to the end of the incomplete PV. for (unsigned i = 0; i < move->pvSize; i++) { v = v > 0 ? -v - 1 : -v + 1; wdl = -wdl; Pos pos0 = *pos; do_move(pos, &pos0, move->pv[i]); } // Now try to expand until the actual mate. 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; 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 = probe_wdl(&pos1, &success); // verify that move wins w = success && (wdl > 0 || chk < 0) ? TB_probe_dtm(&pos1, wdl, &success) : 0; if (!success || v == w) break; } if (!success || v != w) break; move->pv[move->pvSize++] = *m; Pos pos0 = *pos; do_move(pos, &pos0, *m); } } // Get back to the root position. *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 */ fathom-1.0+git.20190811.97fb5c/src/tbprobe.h000066400000000000000000000310161352415702500177130ustar00rootroot00000000000000/* * tbprobe.h * (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 * 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. */ #ifndef TBPROBE_H #define TBPROBE_H #include #ifdef __cplusplus extern "C" { #endif #ifndef TB_NO_STDINT #include #else typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned uint32_t; typedef long long unsigned uint64_t; typedef char int8_t; typedef short int16_t; typedef int int32_t; typedef long long int64_t; #endif #ifndef TB_NO_STDBOOL #include #else #ifndef __cplusplus typedef uint8_t bool; #define true 1 #define false 0 #endif #endif /* * Internal definitions. Do not call these functions directly. */ extern bool tb_init_impl(const char *_path); extern 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); extern 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); /****************************************************************************/ /* MAIN API */ /****************************************************************************/ #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. */ #define TB_CASTLING_k 0x4 /* Black king-side. */ #define TB_CASTLING_q 0x8 /* Black queen-side. */ #define TB_LOSS 0 /* LOSS */ #define TB_BLESSED_LOSS 1 /* LOSS but 50-move draw */ #define TB_DRAW 2 /* DRAW */ #define TB_CURSED_WIN 3 /* WIN but 50-move draw */ #define TB_WIN 4 /* WIN */ #define TB_PROMOTES_NONE 0 #define TB_PROMOTES_QUEEN 1 #define TB_PROMOTES_ROOK 2 #define TB_PROMOTES_BISHOP 3 #define TB_PROMOTES_KNIGHT 4 #define TB_RESULT_WDL_MASK 0x0000000F #define TB_RESULT_TO_MASK 0x000003F0 #define TB_RESULT_FROM_MASK 0x0000FC00 #define TB_RESULT_PROMOTES_MASK 0x00070000 #define TB_RESULT_EP_MASK 0x00080000 #define TB_RESULT_DTZ_MASK 0xFFF00000 #define TB_RESULT_WDL_SHIFT 0 #define TB_RESULT_TO_SHIFT 4 #define TB_RESULT_FROM_SHIFT 10 #define TB_RESULT_PROMOTES_SHIFT 16 #define TB_RESULT_EP_SHIFT 19 #define TB_RESULT_DTZ_SHIFT 20 #define TB_GET_WDL(_res) \ (((_res) & TB_RESULT_WDL_MASK) >> TB_RESULT_WDL_SHIFT) #define TB_GET_TO(_res) \ (((_res) & TB_RESULT_TO_MASK) >> TB_RESULT_TO_SHIFT) #define TB_GET_FROM(_res) \ (((_res) & TB_RESULT_FROM_MASK) >> TB_RESULT_FROM_SHIFT) #define TB_GET_PROMOTES(_res) \ (((_res) & TB_RESULT_PROMOTES_MASK) >> TB_RESULT_PROMOTES_SHIFT) #define TB_GET_EP(_res) \ (((_res) & TB_RESULT_EP_MASK) >> TB_RESULT_EP_SHIFT) #define TB_GET_DTZ(_res) \ (((_res) & TB_RESULT_DTZ_MASK) >> TB_RESULT_DTZ_SHIFT) #define TB_SET_WDL(_res, _wdl) \ (((_res) & ~TB_RESULT_WDL_MASK) | \ (((_wdl) << TB_RESULT_WDL_SHIFT) & TB_RESULT_WDL_MASK)) #define TB_SET_TO(_res, _to) \ (((_res) & ~TB_RESULT_TO_MASK) | \ (((_to) << TB_RESULT_TO_SHIFT) & TB_RESULT_TO_MASK)) #define TB_SET_FROM(_res, _from) \ (((_res) & ~TB_RESULT_FROM_MASK) | \ (((_from) << TB_RESULT_FROM_SHIFT) & TB_RESULT_FROM_MASK)) #define TB_SET_PROMOTES(_res, _promotes) \ (((_res) & ~TB_RESULT_PROMOTES_MASK) | \ (((_promotes) << TB_RESULT_PROMOTES_SHIFT) & TB_RESULT_PROMOTES_MASK)) #define TB_SET_EP(_res, _ep) \ (((_res) & ~TB_RESULT_EP_MASK) | \ (((_ep) << TB_RESULT_EP_SHIFT) & TB_RESULT_EP_MASK)) #define TB_SET_DTZ(_res, _dtz) \ (((_res) & ~TB_RESULT_DTZ_MASK) | \ (((_dtz) << TB_RESULT_DTZ_SHIFT) & TB_RESULT_DTZ_MASK)) #define TB_RESULT_CHECKMATE TB_SET_WDL(0, TB_WIN) #define TB_RESULT_STALEMATE TB_SET_WDL(0, TB_DRAW) #define TB_RESULT_FAILED 0xFFFFFFFF /* * The tablebase can be probed for any position where #pieces <= TB_LARGEST. */ extern unsigned TB_LARGEST; /* * Initialize the tablebase. * * PARAMETERS: * - path: * The tablebase PATH string. * * RETURN: * - true=succes, 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. */ bool tb_init(const char *_path); /* * Free any resources allocated by tb_init */ void tb_free(void); /* * Probe the Win-Draw-Loss (WDL) table. * * PARAMETERS: * - white, black, kings, queens, rooks, bishops, knights, pawns: * The current position (bitboards). * - rule50: * The 50-move half-move clock. * - castling: * Castling rights. Set to zero if no castling is possible. * - ep: * The en passant square (if exists). Set to zero if there is no en passant * square. * - turn: * true=white, false=black * * RETURN: * - One of {TB_LOSS, TB_BLESSED_LOSS, TB_DRAW, TB_CURSED_WIN, TB_WIN}. * Otherwise returns TB_RESULT_FAILED if the probe failed. * * NOTES: * - Engines should use this function during search. * - This function is thread safe assuming TB_NO_THREADS is disabled. */ static inline unsigned tb_probe_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) { if (_castling != 0) return TB_RESULT_FAILED; if (_rule50 != 0) return TB_RESULT_FAILED; return tb_probe_wdl_impl(_white, _black, _kings, _queens, _rooks, _bishops, _knights, _pawns, _ep, _turn); } /* * Probe the Distance-To-Zero (DTZ) table. * * PARAMETERS: * - white, black, kings, queens, rooks, bishops, knights, pawns: * The current position (bitboards). * - rule50: * The 50-move half-move clock. * - castling: * Castling rights. Set to zero if no castling is possible. * - ep: * The en passant square (if exists). Set to zero if there is no en passant * square. * - turn: * true=white, false=black * - results (OPTIONAL): * Alternative results, one for each possible legal move. The passed array * must be TB_MAX_MOVES in size. * If alternative results are not desired then set results=NULL. * * RETURN: * - A TB_RESULT value comprising: * 1) The WDL value (TB_GET_WDL) * 2) The suggested move (TB_GET_FROM, TB_GET_TO, TB_GET_PROMOTES, TB_GET_EP) * 3) The DTZ value (TB_GET_DTZ) * The suggested move is guaranteed to preserved the WDL value. * * Otherwise: * 1) TB_RESULT_STALEMATE is returned if the position is in stalemate. * 2) TB_RESULT_CHECKMATE is returned if the position is in checkmate. * 3) TB_RESULT_FAILED is returned if the probe failed. * * If results!=NULL, then a TB_RESULT for each legal move will be generated * and stored in the results array. The results array will be terminated * by TB_RESULT_FAILED. * * NOTES: * - Engines can use this function to probe at the root. This function should * not be used during search. * - DTZ tablebases can suggest unnatural moves, especially for losing * positions. Engines may prefer to traditional search combined with WDL * move filtering using the alternative results array. * - This function is NOT thread safe. For engines this function should only * be called once at the root per search. */ static inline unsigned tb_probe_root( 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, unsigned *_results) { if (_castling != 0) return TB_RESULT_FAILED; return tb_probe_root_impl(_white, _black, _kings, _queens, _rooks, _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 */ /****************************************************************************/ /* * The HELPER API provides some useful additional functions. It is optional * and can be disabled by defining TB_NO_HELPER_API. Engines should disable * the HELPER API. */ #ifndef TB_NO_HELPER_API extern unsigned tb_pop_count(uint64_t _bb); extern unsigned tb_lsb(uint64_t _bb); extern uint64_t tb_pop_lsb(uint64_t _bb); extern uint64_t tb_king_attacks(unsigned _square); extern uint64_t tb_queen_attacks(unsigned _square, uint64_t _occ); extern uint64_t tb_rook_attacks(unsigned _square, uint64_t _occ); extern uint64_t tb_bishop_attacks(unsigned _square, uint64_t _occ); extern uint64_t tb_knight_attacks(unsigned _square); extern uint64_t tb_pawn_attacks(unsigned _square, bool _color); #endif #ifdef __cplusplus } #endif #endif