pax_global_header00006660000000000000000000000064134212100720014502gustar00rootroot0000000000000052 comment=0439ca1b01163ef842ebb1ad635549bc2f930ae8 fathom-1.0+git.20190120.0439ca/000077500000000000000000000000001342121007200151765ustar00rootroot00000000000000fathom-1.0+git.20190120.0439ca/LICENSE000066400000000000000000000021451342121007200162050ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 basil00 Modifications Copyright (c) 2016-2018 by Jon Dart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal 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.20190120.0439ca/README.md000066400000000000000000000077251342121007200164700ustar00rootroot00000000000000Fathom ====== 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; 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 "principle variation" of Syzygy vs. Syzygy for the input position. For more information, run the following command: fathom --help Pre-compiled versions of `fathom` (for all platforms) are available from here: * https://github.com/basil00/Fathom/releases Programming API --------------- Fathom provides a simple API. There are three main function calls: * `tb_init` initializes the tablebase * `tb_probe_wdl` probes the Win-Draw-Loss (WDL) table for a given position * `tb_probe_root` probes the Distance-To-Zero (DTZ) table for the given position. All of the API functions use basic integer types, i.e. there is no need to create and initialize data-structures. Fathom does not require the callee to provide any additional functionality (e.g. move generation) unlike the traditional `tbprobe` code. However, chess engines can opt to replace some of the functionality of Fathom for better performance (see below). Chess Engines ------------- Chess engines can be `tb_probe_wdl` to get the WDL value during search. The `tb_probe_root` functional can be used to help pick the best move at the root. Note that `tb_probe_root` is slower and therefore should only be used at the root. Chess engines can opt for a tighter integration of Fathom by configuring `tbconfig.h`. Specifically, the chess engines can define `TB_*_ATTACKS` macros that replace the default definitions with the engine's own definitions, avoiding duplication of functionality. Credits ------- The Syzygy tablebases were created by Ronald de Man. Much of the probing code `tbprobe.c` is a modified version of Ronald's `tbprobe.cpp` for Stockfish (all Stockfish-specific code has been removed). The `tbcore.c` file is virtually unchanged from Ronald's original version. License ------- (C) 2013-2015 Ronald de Man (original code) (C) 2015 basil (new modifications) (C) 2016-2019 Jon Dart (additional modifications) Ronald de Man's original code can be "redistributed and/or modified without restrictions". The new modifications are released under the permissive MIT License: 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.20190120.0439ca/src/000077500000000000000000000000001342121007200157655ustar00rootroot00000000000000fathom-1.0+git.20190120.0439ca/src/apps/000077500000000000000000000000001342121007200167305ustar00rootroot00000000000000fathom-1.0+git.20190120.0439ca/src/apps/Makefile000066400000000000000000000014241342121007200203710ustar00rootroot00000000000000UNAME=$(shell uname -s) ifeq ($(UNAME),Linux) TARGET=fathom.linux endif ifeq ($(UNAME),Darwin) TARGET=fathom.macosx endif CC=clang 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.20190120.0439ca/src/apps/fathom.c000066400000000000000000000461751342121007200203670ustar00rootroot00000000000000/* * 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 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_to_str(const struct pos *pos, unsigned move, char *str) { uint64_t occ = pos->black | pos->white; uint64_t us = (pos->turn? pos->white: pos->black); unsigned from = TB_GET_FROM(move); unsigned to = TB_GET_TO(move); unsigned r = rank(from); unsigned f = file(from); unsigned promotes = TB_GET_PROMOTES(move); bool capture = (occ & board(to)) != 0 || (TB_GET_EP(move) != 0); uint64_t 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'; } /* * 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) { 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"); return; } if (move == TB_RESULT_CHECKMATE) { printf("# %s\n", (pos->turn? "0-1": "1-0")); return; } if (check) putchar('+'); if (pos->rule50 >= 100 || move == TB_RESULT_STALEMATE) { printf(" 1/2-1/2\n"); return; } 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); } } /* * 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"); 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); return 0; } fathom-1.0+git.20190120.0439ca/src/tbconfig.h000066400000000000000000000125461342121007200177410ustar00rootroot00000000000000/* * 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_CUSTOM_BSWAP32 to override the internal bswap32 * implementation. To do this supply a macro or function definition * here: */ /* #define TB_CUSTOM_BSWAP32(x) */ /* * Define TB_CUSTOM_BSWAP64 to override the internal bswap64 * implementation. To do this supply a macro or function definition * here: */ /* #define TB_CUSTOM_BSWAP64(x) */ /* * Define TB_NO_STDINT if you do not want to use or it is not * available. */ /* #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 */ /** * Define TB_USE_ATOMIC to use C++ 11 (or higher) feature * (recommended if using C++ and compiler supports it). */ /* #define TB_USE_ATOMIC */ /***************************************************************************/ /* ENGINE INTEGRATION CONFIG */ /***************************************************************************/ /* * 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.20190120.0439ca/src/tbcore.c000066400000000000000000001273661342121007200174260ustar00rootroot00000000000000/* * Copyright (c) 2011-2015 Ronald de Man * Copyright (c) 2016-2017 Jon Dart * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* tbcore.c contains engine-independent routines of the tablebase probing code. This file should not need to much adaptation to add tablebase probing to a particular engine, provided the engine is written in C or C++. */ #include #include #ifndef TB_NO_STDINT #include #endif #include #include #include #include #ifndef _WIN32 #include #include #endif #include "tbcore.h" #if !defined(DECOMP64) && defined(_LP64) // use 64-bit decompression if OS is 64-bit // (appears not to work so commented out for now) //#define DECOMP64 #endif #define TBMAX_PIECE 254 #define TBMAX_PAWN 256 #define HSHMAX 5 // for variants where kings can connect and/or captured // #define CONNECTED_KINGS #define Swap(a,b) {int tmp=a;a=b;b=tmp;} #define TB_PAWN 1 #define TB_KNIGHT 2 #define TB_BISHOP 3 #define TB_ROOK 4 #define TB_QUEEN 5 #define TB_KING 6 #define TB_WPAWN TB_PAWN #define TB_BPAWN (TB_PAWN | 8) #ifndef TB_NO_THREADS static LOCK_T TB_MUTEX; #endif #ifdef TB_CUSTOM_BSWAP32 #define internal_bswap32(x) TB_CUSTOM_BSWAP32(x) #else #define internal_bswap32(x) __builtin_bswap32(x) #endif #ifdef TB_CUSTOM_BSWAP64 #define internal_bswap64(x) TB_CUSTOM_BSWAP64(x) #else #define internal_bswap64(x) __builtin_bswap64(x) #endif static int initialized = 0; static int num_paths = 0; static char *path_string = NULL; static char **paths = NULL; static int TBnum_piece, TBnum_pawn; static struct TBEntry_piece TB_piece[TBMAX_PIECE]; static struct TBEntry_pawn TB_pawn[TBMAX_PAWN]; static struct TBHashEntry TB_hash[1 << TBHASHBITS][HSHMAX]; #define DTZ_ENTRIES 64 static struct DTZTableEntry DTZ_table[DTZ_ENTRIES]; static void init_indices(void); static uint64_t calc_key_from_pcs(int *pcs, int mirror); static void free_wdl_entry(struct TBEntry *entry); static void free_dtz_entry(struct TBEntry *entry); static FD open_tb(const char *str, const char *suffix) { int i; FD fd; char *file; for (i = 0; i < num_paths; i++) { file = (char*)malloc(strlen(paths[i]) + strlen(str) + strlen(suffix) + 2); strcpy(file, paths[i]); #ifdef _WIN32 strcat(file,"\\"); #else strcat(file,"/"); #endif strcat(file, str); strcat(file, suffix); #ifndef _WIN32 fd = open(file, O_RDONLY); #else fd = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); #endif free(file); if (fd != FD_ERR) { return fd; } } return FD_ERR; } static void close_tb(FD fd) { #ifndef _WIN32 close(fd); #else CloseHandle(fd); #endif } static char *map_file(const char *name, const char *suffix, uint64 *mapping) { FD fd = open_tb(name, suffix); if (fd == FD_ERR) return NULL; #ifndef _WIN32 struct stat statbuf; if (fstat(fd, &statbuf)) { perror("fstat"); close_tb(fd); return NULL; } *mapping = statbuf.st_size; char *data = (char *)mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); if (data == (char *)(-1)) { fprintf(stderr,"Could not mmap() %s.\n", name); exit(1); } #else DWORD size_low, size_high; size_low = GetFileSize(fd, &size_high); // *size = ((uint64)size_high) << 32 | ((uint64)size_low); HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, NULL); if (map == NULL) { fprintf(stderr,"CreateFileMapping() failed.\n"); exit(1); } *mapping = (uint64)map; char *data = (char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); if (data == NULL) { fprintf(stderr,"MapViewOfFile() failed, name = %s%s, error = %lu.\n", name, suffix, GetLastError()); exit(1); } #endif close_tb(fd); return data; } #ifndef _WIN32 static void unmap_file(char *data, uint64 size) { if (!data) return; if (!munmap(data, size)) { perror("munmap"); } } #else static void unmap_file(char *data, uint64 mapping) { if (!data) return; if (!UnmapViewOfFile(data)) { fprintf(stderr, "unmap failed, error code %d", GetLastError()); } if (!CloseHandle((HANDLE)mapping)) { fprintf(stderr, "CloseHandle failed, error code %d", GetLastError()); } } #endif static void add_to_hash(struct TBEntry *ptr, uint64 key) { int i, hshidx; hshidx = key >> (64 - TBHASHBITS); i = 0; while (i < HSHMAX && TB_hash[hshidx][i].ptr) i++; if (i == HSHMAX) { fprintf(stderr,"HSHMAX too low!\n"); exit(1); } else { TB_hash[hshidx][i].key = key; TB_hash[hshidx][i].ptr = ptr; } } static char pchr[] = {'K', 'Q', 'R', 'B', 'N', 'P'}; static void init_tb(char *str) { FD fd; struct TBEntry *entry; int i, j, pcs[16]; uint64 key, key2; int color; char *s; fd = open_tb(str, WDLSUFFIX); if (fd == FD_ERR) return; close_tb(fd); for (i = 0; i < 16; i++) pcs[i] = 0; color = 0; for (s = str; *s; s++) switch (*s) { case 'P': pcs[TB_PAWN | color]++; break; case 'N': pcs[TB_KNIGHT | color]++; break; case 'B': pcs[TB_BISHOP | color]++; break; case 'R': pcs[TB_ROOK | color]++; break; case 'Q': pcs[TB_QUEEN | color]++; break; case 'K': pcs[TB_KING | color]++; break; case 'v': color = 0x08; break; } key = calc_key_from_pcs(pcs, 0); key2 = calc_key_from_pcs(pcs, 1); if (pcs[TB_WPAWN] + pcs[TB_BPAWN] == 0) { if (TBnum_piece == TBMAX_PIECE) { fprintf(stderr,"TBMAX_PIECE limit too low!\n"); exit(1); } entry = (struct TBEntry *)&TB_piece[TBnum_piece++]; } else { if (TBnum_pawn == TBMAX_PAWN) { fprintf(stderr,"TBMAX_PAWN limit too low!\n"); exit(1); } entry = (struct TBEntry *)&TB_pawn[TBnum_pawn++]; } entry->key = key; entry->ready = 0; entry->num = 0; for (i = 0; i < 16; i++) entry->num += pcs[i]; entry->symmetric = (key == key2); entry->has_pawns = (pcs[TB_WPAWN] + pcs[TB_BPAWN] > 0); if (entry->num > TB_LARGEST) TB_LARGEST = entry->num; if (entry->has_pawns) { struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; ptr->pawns[0] = pcs[TB_WPAWN]; ptr->pawns[1] = pcs[TB_BPAWN]; if (pcs[TB_BPAWN] > 0 && (pcs[TB_WPAWN] == 0 || pcs[TB_BPAWN] < pcs[TB_WPAWN])) { ptr->pawns[0] = pcs[TB_BPAWN]; ptr->pawns[1] = pcs[TB_WPAWN]; } } else { struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; for (i = 0, j = 0; i < 16; i++) if (pcs[i] == 1) j++; if (j >= 3) ptr->enc_type = 0; else if (j == 2) ptr->enc_type = 2; else { /* only for suicide */ j = 16; for (i = 0; i < 16; i++) { if (pcs[i] < j && pcs[i] > 1) j = pcs[i]; ptr->enc_type = 1 + j; } } } add_to_hash(entry, key); if (key2 != key) add_to_hash(entry, key2); } void init_tablebases(const char *path) { char str[16]; int i, j, k, l; if (initialized) { free(path_string); free(paths); struct TBEntry *entry; for (i = 0; i < TBnum_piece; i++) { entry = (struct TBEntry *)&TB_piece[i]; free_wdl_entry(entry); } for (i = 0; i < TBnum_pawn; i++) { entry = (struct TBEntry *)&TB_pawn[i]; free_wdl_entry(entry); } for (i = 0; i < DTZ_ENTRIES; i++) if (DTZ_table[i].entry) free_dtz_entry(DTZ_table[i].entry); } else { init_indices(); initialized = 1; } const char *p = path; if (strlen(p) == 0 || !strcmp(p, "")) return; path_string = (char *)malloc(strlen(p) + 1); strcpy(path_string, p); num_paths = 0; for (i = 0;; i++) { if (path_string[i] != SEP_CHAR) num_paths++; while (path_string[i] && path_string[i] != SEP_CHAR) i++; if (!path_string[i]) break; path_string[i] = 0; } paths = (char **)malloc(num_paths * sizeof(char *)); for (i = j = 0; i < num_paths; i++) { while (!path_string[j]) j++; paths[i] = &path_string[j]; while (path_string[j]) j++; } LOCK_INIT(TB_MUTEX); TBnum_piece = TBnum_pawn = 0; TB_LARGEST = 0; for (i = 0; i < (1 << TBHASHBITS); i++) for (j = 0; j < HSHMAX; j++) { TB_hash[i][j].key = 0ULL; TB_hash[i][j].ptr = NULL; } for (i = 0; i < DTZ_ENTRIES; i++) DTZ_table[i].entry = NULL; for (i = 1; i < 6; i++) { sprintf(str, "K%cvK", pchr[i]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) { sprintf(str, "K%cvK%c", pchr[i], pchr[j]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) { sprintf(str, "K%c%cvK", pchr[i], pchr[j]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = 1; k < 6; k++) { sprintf(str, "K%c%cvK%c", pchr[i], pchr[j], pchr[k]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = j; k < 6; k++) { sprintf(str, "K%c%c%cvK", pchr[i], pchr[j], pchr[k]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = i; k < 6; k++) for (l = (i == k) ? j : k; l < 6; l++) { sprintf(str, "K%c%cvK%c%c", pchr[i], pchr[j], pchr[k], pchr[l]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = j; k < 6; k++) for (l = 1; l < 6; l++) { sprintf(str, "K%c%c%cvK%c", pchr[i], pchr[j], pchr[k], pchr[l]); init_tb(str); } for (i = 1; i < 6; i++) for (j = i; j < 6; j++) for (k = j; k < 6; k++) for (l = k; l < 6; l++) { sprintf(str, "K%c%c%c%cvK", pchr[i], pchr[j], pchr[k], pchr[l]); init_tb(str); } // printf("Found %d tablebases.\n", TBnum_piece + TBnum_pawn); } static const signed char offdiag[] = { 0,-1,-1,-1,-1,-1,-1,-1, 1, 0,-1,-1,-1,-1,-1,-1, 1, 1, 0,-1,-1,-1,-1,-1, 1, 1, 1, 0,-1,-1,-1,-1, 1, 1, 1, 1, 0,-1,-1,-1, 1, 1, 1, 1, 1, 0,-1,-1, 1, 1, 1, 1, 1, 1, 0,-1, 1, 1, 1, 1, 1, 1, 1, 0 }; static const ubyte triangle[] = { 6, 0, 1, 2, 2, 1, 0, 6, 0, 7, 3, 4, 4, 3, 7, 0, 1, 3, 8, 5, 5, 8, 3, 1, 2, 4, 5, 9, 9, 5, 4, 2, 2, 4, 5, 9, 9, 5, 4, 2, 1, 3, 8, 5, 5, 8, 3, 1, 0, 7, 3, 4, 4, 3, 7, 0, 6, 0, 1, 2, 2, 1, 0, 6 }; static const ubyte invtriangle[] = { 1, 2, 3, 10, 11, 19, 0, 9, 18, 27 }; static const ubyte invdiag[] = { 0, 9, 18, 27, 36, 45, 54, 63, 7, 14, 21, 28, 35, 42, 49, 56 }; static const ubyte flipdiag[] = { 0, 8, 16, 24, 32, 40, 48, 56, 1, 9, 17, 25, 33, 41, 49, 57, 2, 10, 18, 26, 34, 42, 50, 58, 3, 11, 19, 27, 35, 43, 51, 59, 4, 12, 20, 28, 36, 44, 52, 60, 5, 13, 21, 29, 37, 45, 53, 61, 6, 14, 22, 30, 38, 46, 54, 62, 7, 15, 23, 31, 39, 47, 55, 63 }; static const ubyte lower[] = { 28, 0, 1, 2, 3, 4, 5, 6, 0, 29, 7, 8, 9, 10, 11, 12, 1, 7, 30, 13, 14, 15, 16, 17, 2, 8, 13, 31, 18, 19, 20, 21, 3, 9, 14, 18, 32, 22, 23, 24, 4, 10, 15, 19, 22, 33, 25, 26, 5, 11, 16, 20, 23, 25, 34, 27, 6, 12, 17, 21, 24, 26, 27, 35 }; static const ubyte diag[] = { 0, 0, 0, 0, 0, 0, 0, 8, 0, 1, 0, 0, 0, 0, 9, 0, 0, 0, 2, 0, 0, 10, 0, 0, 0, 0, 0, 3, 11, 0, 0, 0, 0, 0, 0, 12, 4, 0, 0, 0, 0, 0, 13, 0, 0, 5, 0, 0, 0, 14, 0, 0, 0, 0, 6, 0, 15, 0, 0, 0, 0, 0, 0, 7 }; static const ubyte flap[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 12, 18, 18, 12, 6, 0, 1, 7, 13, 19, 19, 13, 7, 1, 2, 8, 14, 20, 20, 14, 8, 2, 3, 9, 15, 21, 21, 15, 9, 3, 4, 10, 16, 22, 22, 16, 10, 4, 5, 11, 17, 23, 23, 17, 11, 5, 0, 0, 0, 0, 0, 0, 0, 0 }; static const ubyte ptwist[] = { 0, 0, 0, 0, 0, 0, 0, 0, 47, 35, 23, 11, 10, 22, 34, 46, 45, 33, 21, 9, 8, 20, 32, 44, 43, 31, 19, 7, 6, 18, 30, 42, 41, 29, 17, 5, 4, 16, 28, 40, 39, 27, 15, 3, 2, 14, 26, 38, 37, 25, 13, 1, 0, 12, 24, 36, 0, 0, 0, 0, 0, 0, 0, 0 }; static const ubyte invflap[] = { 8, 16, 24, 32, 40, 48, 9, 17, 25, 33, 41, 49, 10, 18, 26, 34, 42, 50, 11, 19, 27, 35, 43, 51 }; static const ubyte invptwist[] = { 52, 51, 44, 43, 36, 35, 28, 27, 20, 19, 12, 11, 53, 50, 45, 42, 37, 34, 29, 26, 21, 18, 13, 10, 54, 49, 46, 41, 38, 33, 30, 25, 22, 17, 14, 9, 55, 48, 47, 40, 39, 32, 31, 24, 23, 16, 15, 8 }; static const ubyte file_to_file[] = { 0, 1, 2, 3, 3, 2, 1, 0 }; #ifndef CONNECTED_KINGS static const short KK_idx[10][64] = { { -1, -1, -1, 0, 1, 2, 3, 4, -1, -1, -1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 }, { 58, -1, -1, -1, 59, 60, 61, 62, 63, -1, -1, -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,101,102,103,104,105,106,107, 108,109,110,111,112,113,114,115}, {116,117, -1, -1, -1,118,119,120, 121,122, -1, -1, -1,123,124,125, 126,127,128,129,130,131,132,133, 134,135,136,137,138,139,140,141, 142,143,144,145,146,147,148,149, 150,151,152,153,154,155,156,157, 158,159,160,161,162,163,164,165, 166,167,168,169,170,171,172,173 }, {174, -1, -1, -1,175,176,177,178, 179, -1, -1, -1,180,181,182,183, 184, -1, -1, -1,185,186,187,188, 189,190,191,192,193,194,195,196, 197,198,199,200,201,202,203,204, 205,206,207,208,209,210,211,212, 213,214,215,216,217,218,219,220, 221,222,223,224,225,226,227,228 }, {229,230, -1, -1, -1,231,232,233, 234,235, -1, -1, -1,236,237,238, 239,240, -1, -1, -1,241,242,243, 244,245,246,247,248,249,250,251, 252,253,254,255,256,257,258,259, 260,261,262,263,264,265,266,267, 268,269,270,271,272,273,274,275, 276,277,278,279,280,281,282,283 }, {284,285,286,287,288,289,290,291, 292,293, -1, -1, -1,294,295,296, 297,298, -1, -1, -1,299,300,301, 302,303, -1, -1, -1,304,305,306, 307,308,309,310,311,312,313,314, 315,316,317,318,319,320,321,322, 323,324,325,326,327,328,329,330, 331,332,333,334,335,336,337,338 }, { -1, -1,339,340,341,342,343,344, -1, -1,345,346,347,348,349,350, -1, -1,441,351,352,353,354,355, -1, -1, -1,442,356,357,358,359, -1, -1, -1, -1,443,360,361,362, -1, -1, -1, -1, -1,444,363,364, -1, -1, -1, -1, -1, -1,445,365, -1, -1, -1, -1, -1, -1, -1,446 }, { -1, -1, -1,366,367,368,369,370, -1, -1, -1,371,372,373,374,375, -1, -1, -1,376,377,378,379,380, -1, -1, -1,447,381,382,383,384, -1, -1, -1, -1,448,385,386,387, -1, -1, -1, -1, -1,449,388,389, -1, -1, -1, -1, -1, -1,450,390, -1, -1, -1, -1, -1, -1, -1,451 }, {452,391,392,393,394,395,396,397, -1, -1, -1, -1,398,399,400,401, -1, -1, -1, -1,402,403,404,405, -1, -1, -1, -1,406,407,408,409, -1, -1, -1, -1,453,410,411,412, -1, -1, -1, -1, -1,454,413,414, -1, -1, -1, -1, -1, -1,455,415, -1, -1, -1, -1, -1, -1, -1,456 }, {457,416,417,418,419,420,421,422, -1,458,423,424,425,426,427,428, -1, -1, -1, -1, -1,429,430,431, -1, -1, -1, -1, -1,432,433,434, -1, -1, -1, -1, -1,435,436,437, -1, -1, -1, -1, -1,459,438,439, -1, -1, -1, -1, -1, -1,460,440, -1, -1, -1, -1, -1, -1, -1,461 } }; #else static const short PP_idx[10][64] = { { 0, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, -1, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61 }, { 62, -1, -1, 63, 64, 65, -1, 66, -1, 67, 68, 69, 70, 71, 72, -1, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, -1, 97, 98, 99,100,101,102,103, -1,104,105,106,107,108,109, -1, 110, -1,111,112,113,114, -1,115 }, {116, -1, -1, -1,117, -1, -1,118, -1,119,120,121,122,123,124, -1, -1,125,126,127,128,129,130, -1, 131,132,133,134,135,136,137,138, -1,139,140,141,142,143,144,145, -1,146,147,148,149,150,151, -1, -1,152,153,154,155,156,157, -1, 158, -1, -1,159,160, -1, -1,161 }, {162, -1, -1, -1, -1, -1, -1,163, -1,164, -1,165,166,167,168, -1, -1,169,170,171,172,173,174, -1, -1,175,176,177,178,179,180, -1, -1,181,182,183,184,185,186, -1, -1, -1,187,188,189,190,191, -1, -1,192,193,194,195,196,197, -1, 198, -1, -1, -1, -1, -1, -1,199 }, {200, -1, -1, -1, -1, -1, -1,201, -1,202, -1, -1,203, -1,204, -1, -1, -1,205,206,207,208, -1, -1, -1,209,210,211,212,213,214, -1, -1, -1,215,216,217,218,219, -1, -1, -1,220,221,222,223, -1, -1, -1,224, -1,225,226, -1,227, -1, 228, -1, -1, -1, -1, -1, -1,229 }, {230, -1, -1, -1, -1, -1, -1,231, -1,232, -1, -1, -1, -1,233, -1, -1, -1,234, -1,235,236, -1, -1, -1, -1,237,238,239,240, -1, -1, -1, -1, -1,241,242,243, -1, -1, -1, -1,244,245,246,247, -1, -1, -1,248, -1, -1, -1, -1,249, -1, 250, -1, -1, -1, -1, -1, -1,251 }, { -1, -1, -1, -1, -1, -1, -1,259, -1,252, -1, -1, -1, -1,260, -1, -1, -1,253, -1, -1,261, -1, -1, -1, -1, -1,254,262, -1, -1, -1, -1, -1, -1, -1,255, -1, -1, -1, -1, -1, -1, -1, -1,256, -1, -1, -1, -1, -1, -1, -1, -1,257, -1, -1, -1, -1, -1, -1, -1, -1,258 }, { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,268, -1, -1, -1,263, -1, -1,269, -1, -1, -1, -1, -1,264,270, -1, -1, -1, -1, -1, -1, -1,265, -1, -1, -1, -1, -1, -1, -1, -1,266, -1, -1, -1, -1, -1, -1, -1, -1,267, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,274, -1, -1, -1, -1, -1,271,275, -1, -1, -1, -1, -1, -1, -1,272, -1, -1, -1, -1, -1, -1, -1, -1,273, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,277, -1, -1, -1, -1, -1, -1, -1,276, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } }; static const ubyte test45[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static const ubyte mtwist[] = { 15, 63, 55, 47, 40, 48, 56, 12, 62, 11, 39, 31, 24, 32, 8, 57, 54, 38, 7, 23, 16, 4, 33, 49, 46, 30, 22, 3, 0, 17, 25, 41, 45, 29, 21, 2, 1, 18, 26, 42, 53, 37, 6, 20, 19, 5, 34, 50, 61, 10, 36, 28, 27, 35, 9, 58, 14, 60, 52, 44, 43, 51, 59, 13 }; #endif static int binomial[5][64]; static int pawnidx[5][24]; static int pfactor[5][4]; #ifdef CONNECTED_KINGS static int multidx[5][10]; static int mfactor[5]; #endif static void init_indices(void) { int i, j, k; // binomial[k-1][n] = Bin(n, k) for (i = 0; i < 5; i++) for (j = 0; j < 64; j++) { int f = j; int l = 1; for (k = 1; k <= i; k++) { f *= (j - k); l *= (k + 1); } binomial[i][j] = f / l; } for (i = 0; i < 5; i++) { int s = 0; for (j = 0; j < 6; j++) { pawnidx[i][j] = s; s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; } pfactor[i][0] = s; s = 0; for (; j < 12; j++) { pawnidx[i][j] = s; s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; } pfactor[i][1] = s; s = 0; for (; j < 18; j++) { pawnidx[i][j] = s; s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; } pfactor[i][2] = s; s = 0; for (; j < 24; j++) { pawnidx[i][j] = s; s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; } pfactor[i][3] = s; } #ifdef CONNECTED_KINGS for (i = 0; i < 5; i++) { int s = 0; for (j = 0; j < 10; j++) { multidx[i][j] = s; s += (i == 0) ? 1 : binomial[i - 1][mtwist[invtriangle[j]]]; } mfactor[i] = s; } #endif } #ifndef CONNECTED_KINGS static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor) { uint64 idx; int i, j, k, m, l, p; int n = ptr->num; assert(n>=0 && n<7); if (pos[0] & 0x04) { for (i = 0; i < n; i++) pos[i] ^= 0x07; } if (pos[0] & 0x20) { for (i = 0; i < n; i++) pos[i] ^= 0x38; } for (i = 0; i < n; i++) if (offdiag[pos[i]]) break; if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0) for (i = 0; i < n; i++) pos[i] = flipdiag[pos[i]]; switch (ptr->enc_type) { case 0: /* 111 */ i = (pos[1] > pos[0]); j = (pos[2] > pos[0]) + (pos[2] > pos[1]); if (offdiag[pos[0]]) idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j); else if (offdiag[pos[1]]) idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j; else if (offdiag[pos[2]]) idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]]; else idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j); i = 3; break; case 1: /* K3 */ j = (pos[2] > pos[0]) + (pos[2] > pos[1]); idx = KK_idx[triangle[pos[0]]][pos[1]]; if (idx < 441) idx = idx + 441 * (pos[2] - j); else { idx = 441*62 + (idx - 441) + 21 * lower[pos[2]]; if (!offdiag[pos[2]]) idx -= j * 21; } i = 3; break; default: /* K2 */ idx = KK_idx[triangle[pos[0]]][pos[1]]; i = 2; break; } idx *= factor[0]; for (; i < n;) { int t = norm[i]; for (j = i; j < i + t; j++) for (k = j + 1; k < i + t; k++) if (pos[j] > pos[k]) Swap(pos[j], pos[k]); int s = 0; for (m = i; m < i + t; m++) { p = pos[m]; for (l = 0, j = 0; l < i; l++) j += (p > pos[l]); assert(m-i >= 0 && m-i < 5); assert(p-j >= 0 && p-j < 64); s += binomial[m - i][p - j]; } idx += ((uint64)s) * ((uint64)factor[i]); i += t; } return idx; } #else static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor) { uint64 idx; int i, j, k, m, l, p; int n = ptr->num; assert(n>=0 && n<7); if (ptr->enc_type < 3) { if (pos[0] & 0x04) { for (i = 0; i < n; i++) pos[i] ^= 0x07; } if (pos[0] & 0x20) { for (i = 0; i < n; i++) pos[i] ^= 0x38; } for (i = 0; i < n; i++) if (offdiag[pos[i]]) break; if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0) for (i = 0; i < n; i++) pos[i] = flipdiag[pos[i]]; switch (ptr->enc_type) { case 0: /* 111 */ i = (pos[1] > pos[0]); j = (pos[2] > pos[0]) + (pos[2] > pos[1]); if (offdiag[pos[0]]) idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j); else if (offdiag[pos[1]]) idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j; else if (offdiag[pos[2]]) idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]]; else idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j); i = 3; break; case 2: /* 11 */ i = (pos[1] > pos[0]); if (offdiag[pos[0]]) idx = triangle[pos[0]] * 63 + (pos[1] - i); else if (offdiag[pos[1]]) idx = 6*63 + diag[pos[0]] * 28 + lower[pos[1]]; else idx = 6*63 + 4*28 + (diag[pos[0]]) * 7 + (diag[pos[1]] - i); i = 2; break; } } else if (ptr->enc_type == 3) { /* 2, e.g. KKvK */ if (triangle[pos[0]] > triangle[pos[1]]) Swap(pos[0], pos[1]); if (pos[0] & 0x04) for (i = 0; i < n; i++) pos[i] ^= 0x07; if (pos[0] & 0x20) for (i = 0; i < n; i++) pos[i] ^= 0x38; if (offdiag[pos[0]] > 0 || (offdiag[pos[0]] == 0 && offdiag[pos[1]] > 0)) for (i = 0; i < n; i++) pos[i] = flipdiag[pos[i]]; if (test45[pos[1]] && triangle[pos[0]] == triangle[pos[1]]) { Swap(pos[0], pos[1]); for (i = 0; i < n; i++) pos[i] = flipdiag[pos[i] ^ 0x38]; } idx = PP_idx[triangle[pos[0]]][pos[1]]; i = 2; } else { /* 3 and higher, e.g. KKKvK and KKKKvK */ for (i = 1; i < norm[0]; i++) if (triangle[pos[0]] > triangle[pos[i]]) Swap(pos[0], pos[i]); if (pos[0] & 0x04) for (i = 0; i < n; i++) pos[i] ^= 0x07; if (pos[0] & 0x20) for (i = 0; i < n; i++) pos[i] ^= 0x38; if (offdiag[pos[0]] > 0) for (i = 0; i < n; i++) pos[i] = flipdiag[pos[i]]; for (i = 1; i < norm[0]; i++) for (j = i + 1; j < norm[0]; j++) if (mtwist[pos[i]] > mtwist[pos[j]]) Swap(pos[i], pos[j]); idx = multidx[norm[0] - 1][triangle[pos[0]]]; for (i = 1; i < norm[0]; i++) idx += binomial[i - 1][mtwist[pos[i]]]; } idx *= factor[0]; for (; i < n;) { int t = norm[i]; for (j = i; j < i + t; j++) for (k = j + 1; k < i + t; k++) if (pos[j] > pos[k]) Swap(pos[j], pos[k]); int s = 0; for (m = i; m < i + t; m++) { p = pos[m]; for (l = 0, j = 0; l < i; l++) j += (p > pos[l]); s += binomial[m - i][p - j]; } idx += ((uint64)s) * ((uint64)factor[i]); i += t; } return idx; } #endif // determine file of leftmost pawn and sort pawns static int pawn_file(struct TBEntry_pawn *ptr, int *pos) { int i; for (i = 1; i < ptr->pawns[0]; i++) if (flap[pos[0]] > flap[pos[i]]) Swap(pos[0], pos[i]); return file_to_file[pos[0] & 0x07]; } static uint64 encode_pawn(struct TBEntry_pawn *ptr, ubyte *norm, int *pos, int *factor) { uint64 idx; int i, j, k, m, s, t; int n = ptr->num; if (pos[0] & 0x04) for (i = 0; i < n; i++) pos[i] ^= 0x07; for (i = 1; i < ptr->pawns[0]; i++) for (j = i + 1; j < ptr->pawns[0]; j++) if (ptwist[pos[i]] < ptwist[pos[j]]) Swap(pos[i], pos[j]); t = ptr->pawns[0] - 1; idx = pawnidx[t][flap[pos[0]]]; for (i = t; i > 0; i--) idx += binomial[t - i][ptwist[pos[i]]]; idx *= factor[0]; // remaining pawns i = ptr->pawns[0]; t = i + ptr->pawns[1]; if (t > i) { for (j = i; j < t; j++) for (k = j + 1; k < t; k++) if (pos[j] > pos[k]) Swap(pos[j], pos[k]); s = 0; for (m = i; m < t; m++) { int p = pos[m]; for (k = 0, j = 0; k < i; k++) j += (p > pos[k]); s += binomial[m - i][p - j - 8]; } idx += ((uint64)s) * ((uint64)factor[i]); i = t; } for (; i < n;) { t = norm[i]; for (j = i; j < i + t; j++) for (k = j + 1; k < i + t; k++) if (pos[j] > pos[k]) Swap(pos[j], pos[k]); s = 0; for (m = i; m < i + t; m++) { int p = pos[m]; for (k = 0, j = 0; k < i; k++) j += (p > pos[k]); s += binomial[m - i][p - j]; } idx += ((uint64)s) * ((uint64)factor[i]); i += t; } return idx; } static ubyte decompress_pairs(struct PairsData *d, uint64 index); // place k like pieces on n squares static int subfactor(int k, int n) { int i, f, l; f = n; l = 1; for (i = 1; i < k; i++) { f *= n - i; l *= i + 1; } return f / l; } static uint64 calc_factors_piece(int *factor, int num, int order, ubyte *norm, ubyte enc_type) { int i, k, n; uint64 f; #ifndef CONNECTED_KINGS static int pivfac[] = { 31332, 28056, 462 }; #else static int pivfac[] = { 31332, 0, 518, 278 }; #endif n = 64 - norm[0]; f = 1; for (i = norm[0], k = 0; i < num || k == order; k++) { if (k == order) { factor[0] = (int)f; #ifndef CONNECTED_KINGS f *= pivfac[enc_type]; #else if (enc_type < 4) f *= pivfac[enc_type]; else f *= mfactor[enc_type - 2]; #endif } else { factor[i] = (int)f; f *= subfactor(norm[i], n); n -= norm[i]; i += norm[i]; } } return f; } static uint64 calc_factors_pawn(int *factor, int num, int order, int order2, ubyte *norm, int file) { int i, k, n; uint64 f; i = norm[0]; if (order2 < 0x0f) i += norm[i]; n = 64 - i; f = 1; for (k = 0; i < num || k == order || k == order2; k++) { if (k == order) { factor[0] = (int)f; f *= pfactor[norm[0] - 1][file]; } else if (k == order2) { factor[norm[0]] = (int)f; f *= subfactor(norm[norm[0]], 48 - norm[0]); } else { factor[i] = (int)f; f *= subfactor(norm[i], n); n -= norm[i]; i += norm[i]; } } return f; } static void set_norm_piece(struct TBEntry_piece *ptr, ubyte *norm, ubyte *pieces) { int i, j; for (i = 0; i < ptr->num; i++) norm[i] = 0; switch (ptr->enc_type) { case 0: norm[0] = 3; break; case 2: norm[0] = 2; break; default: norm[0] = ptr->enc_type - 1; break; } for (i = norm[0]; i < ptr->num; i += norm[i]) for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) norm[i]++; } static void set_norm_pawn(struct TBEntry_pawn *ptr, ubyte *norm, ubyte *pieces) { int i, j; for (i = 0; i < ptr->num; i++) norm[i] = 0; norm[0] = ptr->pawns[0]; if (ptr->pawns[1]) norm[ptr->pawns[0]] = ptr->pawns[1]; for (i = ptr->pawns[0] + ptr->pawns[1]; i < ptr->num; i += norm[i]) for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) norm[i]++; } static void setup_pieces_piece(struct TBEntry_piece *ptr, unsigned char *data, uint64 *tb_size) { int i; int order; for (i = 0; i < ptr->num; i++) ptr->pieces[0][i] = data[i + 1] & 0x0f; order = data[0] & 0x0f; set_norm_piece(ptr, ptr->norm[0], ptr->pieces[0]); tb_size[0] = calc_factors_piece(ptr->factor[0], ptr->num, order, ptr->norm[0], ptr->enc_type); for (i = 0; i < ptr->num; i++) ptr->pieces[1][i] = data[i + 1] >> 4; order = data[0] >> 4; set_norm_piece(ptr, ptr->norm[1], ptr->pieces[1]); tb_size[1] = calc_factors_piece(ptr->factor[1], ptr->num, order, ptr->norm[1], ptr->enc_type); } static void setup_pieces_piece_dtz(struct DTZEntry_piece *ptr, unsigned char *data, uint64 *tb_size) { int i; int order; for (i = 0; i < ptr->num; i++) ptr->pieces[i] = data[i + 1] & 0x0f; order = data[0] & 0x0f; set_norm_piece((struct TBEntry_piece *)ptr, ptr->norm, ptr->pieces); tb_size[0] = calc_factors_piece(ptr->factor, ptr->num, order, ptr->norm, ptr->enc_type); } static void setup_pieces_pawn(struct TBEntry_pawn *ptr, unsigned char *data, uint64 *tb_size, int f) { int i, j; int order, order2; j = 1 + (ptr->pawns[1] > 0); order = data[0] & 0x0f; order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; for (i = 0; i < ptr->num; i++) ptr->file[f].pieces[0][i] = data[i + j] & 0x0f; set_norm_pawn(ptr, ptr->file[f].norm[0], ptr->file[f].pieces[0]); tb_size[0] = calc_factors_pawn(ptr->file[f].factor[0], ptr->num, order, order2, ptr->file[f].norm[0], f); order = data[0] >> 4; order2 = ptr->pawns[1] ? (data[1] >> 4) : 0x0f; for (i = 0; i < ptr->num; i++) ptr->file[f].pieces[1][i] = data[i + j] >> 4; set_norm_pawn(ptr, ptr->file[f].norm[1], ptr->file[f].pieces[1]); tb_size[1] = calc_factors_pawn(ptr->file[f].factor[1], ptr->num, order, order2, ptr->file[f].norm[1], f); } static void setup_pieces_pawn_dtz(struct DTZEntry_pawn *ptr, unsigned char *data, uint64 *tb_size, int f) { int i, j; int order, order2; j = 1 + (ptr->pawns[1] > 0); order = data[0] & 0x0f; order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; for (i = 0; i < ptr->num; i++) ptr->file[f].pieces[i] = data[i + j] & 0x0f; set_norm_pawn((struct TBEntry_pawn *)ptr, ptr->file[f].norm, ptr->file[f].pieces); tb_size[0] = calc_factors_pawn(ptr->file[f].factor, ptr->num, order, order2, ptr->file[f].norm, f); } static void calc_symlen(struct PairsData *d, int s, char *tmp) { int s1, s2; int w = *(int *)(d->sympat + 3 * s); s2 = (w >> 12) & 0x0fff; if (s2 == 0x0fff) d->symlen[s] = 0; else { s1 = w & 0x0fff; if (!tmp[s1]) calc_symlen(d, s1, tmp); if (!tmp[s2]) calc_symlen(d, s2, tmp); d->symlen[s] = d->symlen[s1] + d->symlen[s2] + 1; } tmp[s] = 1; } static struct PairsData *setup_pairs(unsigned char *data, uint64 tb_size, uint64 *size, unsigned char **next, ubyte *flags, int wdl) { struct PairsData *d; int i; *flags = data[0]; if (data[0] & 0x80) { d = (struct PairsData *)malloc(sizeof(struct PairsData)); d->idxbits = 0; if (wdl) d->min_len = data[1]; else d->min_len = 0; *next = data + 2; size[0] = size[1] = size[2] = 0; return d; } int blocksize = data[1]; int idxbits = data[2]; int real_num_blocks = *(uint32 *)(&data[4]); int num_blocks = real_num_blocks + *(ubyte *)(&data[3]); int max_len = data[8]; int min_len = data[9]; int h = max_len - min_len + 1; int num_syms = *(ushort *)(&data[10 + 2 * h]); d = (struct PairsData *)malloc(sizeof(struct PairsData) + (h - 1) * sizeof(base_t) + num_syms); d->blocksize = blocksize; d->idxbits = idxbits; d->offset = (ushort *)(&data[10]); d->symlen = ((ubyte *)d) + sizeof(struct PairsData) + (h - 1) * sizeof(base_t); d->sympat = &data[12 + 2 * h]; d->min_len = min_len; *next = &data[12 + 2 * h + 3 * num_syms + (num_syms & 1)]; int num_indices = (int)((tb_size + (1ULL << idxbits) - 1) >> idxbits); size[0] = 6ULL * num_indices; size[1] = 2ULL * num_blocks; size[2] = (1ULL << blocksize) * real_num_blocks; // char tmp[num_syms]; char tmp[4096]; for (i = 0; i < num_syms; i++) tmp[i] = 0; for (i = 0; i < num_syms; i++) if (!tmp[i]) calc_symlen(d, i, tmp); d->base[h - 1] = 0; for (i = h - 2; i >= 0; i--) d->base[i] = (d->base[i + 1] + d->offset[i] - d->offset[i + 1]) / 2; #ifdef DECOMP64 for (i = 0; i < h; i++) d->base[i] <<= 64 - (min_len + i); #else for (i = 0; i < h; i++) d->base[i] <<= 32 - (min_len + i); #endif d->offset -= d->min_len; return d; } static int init_table_wdl(struct TBEntry *entry, char *str) { ubyte *next; int f, s; uint64 tb_size[8]; uint64 size[8 * 3]; ubyte flags; // first mmap the table into memory entry->data = map_file(str, WDLSUFFIX, &entry->mapping); if (!entry->data) { fprintf(stderr,"Could not find %s" WDLSUFFIX "\n", str); return 0; } ubyte *data = (ubyte *)entry->data; if (((uint32 *)data)[0] != WDL_MAGIC) { fprintf(stderr,"Corrupted table.\n"); unmap_file(entry->data, entry->mapping); entry->data = 0; return 0; } int split = data[4] & 0x01; int files = data[4] & 0x02 ? 4 : 1; data += 5; if (!entry->has_pawns) { struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; setup_pieces_piece(ptr, data, &tb_size[0]); data += ptr->num + 1; data += ((uintptr_t)data) & 0x01; ptr->precomp[0] = setup_pairs(data, tb_size[0], &size[0], &next, &flags, 1); data = next; if (split) { ptr->precomp[1] = setup_pairs(data, tb_size[1], &size[3], &next, &flags, 1); data = next; } else ptr->precomp[1] = NULL; ptr->precomp[0]->indextable = (char *)data; data += size[0]; if (split) { ptr->precomp[1]->indextable = (char *)data; data += size[3]; } ptr->precomp[0]->sizetable = (ushort *)data; data += size[1]; if (split) { ptr->precomp[1]->sizetable = (ushort *)data; data += size[4]; } data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->precomp[0]->data = data; data += size[2]; if (split) { data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->precomp[1]->data = data; } } else { struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; s = 1 + (ptr->pawns[1] > 0); for (f = 0; f < 4; f++) { setup_pieces_pawn((struct TBEntry_pawn *)ptr, data, &tb_size[2 * f], f); data += ptr->num + s; } data += ((uintptr_t)data) & 0x01; for (f = 0; f < files; f++) { ptr->file[f].precomp[0] = setup_pairs(data, tb_size[2 * f], &size[6 * f], &next, &flags, 1); data = next; if (split) { ptr->file[f].precomp[1] = setup_pairs(data, tb_size[2 * f + 1], &size[6 * f + 3], &next, &flags, 1); data = next; } else ptr->file[f].precomp[1] = NULL; } for (f = 0; f < files; f++) { ptr->file[f].precomp[0]->indextable = (char *)data; data += size[6 * f]; if (split) { ptr->file[f].precomp[1]->indextable = (char *)data; data += size[6 * f + 3]; } } for (f = 0; f < files; f++) { ptr->file[f].precomp[0]->sizetable = (ushort *)data; data += size[6 * f + 1]; if (split) { ptr->file[f].precomp[1]->sizetable = (ushort *)data; data += size[6 * f + 4]; } } for (f = 0; f < files; f++) { data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->file[f].precomp[0]->data = data; data += size[6 * f + 2]; if (split) { data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->file[f].precomp[1]->data = data; data += size[6 * f + 5]; } } } return 1; } static int init_table_dtz(struct TBEntry *entry) { ubyte *data = (ubyte *)entry->data; ubyte *next; int f, s; uint64 tb_size[4]; uint64 size[4 * 3]; if (!data) return 0; if (((uint32 *)data)[0] != DTZ_MAGIC) { fprintf(stderr,"Corrupted table.\n"); return 0; } int files = data[4] & 0x02 ? 4 : 1; data += 5; if (!entry->has_pawns) { struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; setup_pieces_piece_dtz(ptr, data, &tb_size[0]); data += ptr->num + 1; data += ((uintptr_t)data) & 0x01; ptr->precomp = setup_pairs(data, tb_size[0], &size[0], &next, &(ptr->flags), 0); data = next; ptr->map = data; if (ptr->flags & 2) { int i; for (i = 0; i < 4; i++) { ptr->map_idx[i] = (ushort)(data + 1 - ptr->map); data += 1 + data[0]; } data += ((uintptr_t)data) & 0x01; } ptr->precomp->indextable = (char *)data; data += size[0]; ptr->precomp->sizetable = (ushort *)data; data += size[1]; data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->precomp->data = data; data += size[2]; } else { struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; s = 1 + (ptr->pawns[1] > 0); for (f = 0; f < 4; f++) { setup_pieces_pawn_dtz(ptr, data, &tb_size[f], f); data += ptr->num + s; } data += ((uintptr_t)data) & 0x01; for (f = 0; f < files; f++) { ptr->file[f].precomp = setup_pairs(data, tb_size[f], &size[3 * f], &next, &(ptr->flags[f]), 0); data = next; } ptr->map = data; for (f = 0; f < files; f++) { if (ptr->flags[f] & 2) { int i; for (i = 0; i < 4; i++) { ptr->map_idx[f][i] = (ushort)(data + 1 - ptr->map); data += 1 + data[0]; } } } data += ((uintptr_t)data) & 0x01; for (f = 0; f < files; f++) { ptr->file[f].precomp->indextable = (char *)data; data += size[3 * f]; } for (f = 0; f < files; f++) { ptr->file[f].precomp->sizetable = (ushort *)data; data += size[3 * f + 1]; } for (f = 0; f < files; f++) { data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); ptr->file[f].precomp->data = data; data += size[3 * f + 2]; } } return 1; } static ubyte decompress_pairs(struct PairsData *d, uint64 idx) { if (!d->idxbits) return d->min_len; uint32 mainidx = (uint32)(idx >> d->idxbits); int litidx = (int)(idx & (((uint64)1 << d->idxbits) - 1)) - ((uint64)1 << (d->idxbits - 1)); uint32 block = *(uint32 *)(d->indextable + 6 * mainidx); litidx += *(ushort *)(d->indextable + 6 * mainidx + 4); if (litidx < 0) { do { litidx += d->sizetable[--block] + 1; } while (litidx < 0); } else { while (litidx > d->sizetable[block]) litidx -= d->sizetable[block++] + 1; } uint32 *ptr = (uint32 *)(d->data + (block << d->blocksize)); int m = d->min_len; ushort *offset = d->offset; base_t *base = d->base - m; ubyte *symlen = d->symlen; int sym, bitcnt; #ifdef DECOMP64 uint64 code = internal_bswap64(*((uint64 *)ptr)); ptr += 2; bitcnt = 0; // number of "empty bits" in code for (;;) { int l = m; while (code < base[l]) l++; sym = offset[l] + ((code - base[l]) >> (64 - l)); if (litidx < (int)symlen[sym] + 1) break; litidx -= (int)symlen[sym] + 1; code <<= l; bitcnt += l; if (bitcnt >= 32) { bitcnt -= 32; uint32 data = *ptr++; code |= ((uint64)(internal_bswap32(data))) << bitcnt; } } #else uint32 next = 0; uint32 data = *ptr++; uint32 code = internal_bswap32(data); bitcnt = 0; // number of bits in next for (;;) { int l = m; while (code < base[l]) l++; sym = offset[l] + ((code - base[l]) >> (32 - l)); if (litidx < (int)symlen[sym] + 1) break; litidx -= (int)symlen[sym] + 1; code <<= l; if (bitcnt < l) { if (bitcnt) { code |= (next >> (32 - l)); l -= bitcnt; } data = *ptr++; next = internal_bswap32(data); bitcnt = 32; } code |= (next >> (32 - l)); next <<= l; bitcnt -= l; } #endif ubyte *sympat = d->sympat; while (symlen[sym] != 0) { int w = *(int *)(sympat + 3 * sym); int s1 = w & 0x0fff; if (litidx < (int)symlen[s1] + 1) sym = s1; else { litidx -= (int)symlen[s1] + 1; sym = (w >> 12) & 0x0fff; } } return *(sympat + 3 * sym); } void load_dtz_table(char *str, uint64 key1, uint64 key2) { int i; struct TBEntry *ptr, *ptr3; struct TBHashEntry *ptr2; DTZ_table[0].key1 = key1; DTZ_table[0].key2 = key2; DTZ_table[0].entry = NULL; // find corresponding WDL entry ptr2 = TB_hash[key1 >> (64 - TBHASHBITS)]; for (i = 0; i < HSHMAX; i++) if (ptr2[i].key == key1) break; if (i == HSHMAX) return; ptr = ptr2[i].ptr; ptr3 = (struct TBEntry *)malloc(ptr->has_pawns ? sizeof(struct DTZEntry_pawn) : sizeof(struct DTZEntry_piece)); ptr3->data = map_file(str, DTZSUFFIX, &ptr3->mapping); ptr3->key = ptr->key; ptr3->num = ptr->num; ptr3->symmetric = ptr->symmetric; ptr3->has_pawns = ptr->has_pawns; if (ptr3->has_pawns) { struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr3; entry->pawns[0] = ((struct TBEntry_pawn *)ptr)->pawns[0]; entry->pawns[1] = ((struct TBEntry_pawn *)ptr)->pawns[1]; } else { struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr3; entry->enc_type = ((struct TBEntry_piece *)ptr)->enc_type; } if (!init_table_dtz(ptr3)) free(ptr3); else DTZ_table[0].entry = ptr3; } static void free_wdl_entry(struct TBEntry *entry) { unmap_file(entry->data, entry->mapping); entry->data = NULL; entry->mapping = 0; if (!entry->has_pawns) { struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; free(ptr->precomp[0]); if (ptr->precomp[1]) free(ptr->precomp[1]); ptr->precomp[0] = ptr->precomp[1] = NULL; } else { struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; int f; for (f = 0; f < 4; f++) { free(ptr->file[f].precomp[0]); if (ptr->file[f].precomp[1]) free(ptr->file[f].precomp[1]); ptr->file[f].precomp[0] = ptr->file[f].precomp[1] = NULL; } } } static void free_dtz_entry(struct TBEntry *entry) { unmap_file(entry->data, entry->mapping); entry->data = NULL; entry->mapping = 0; if (!entry->has_pawns) { struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; free(ptr->precomp); ptr->precomp = NULL; } else { struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; int f; for (f = 0; f < 4; f++) { free(ptr->file[f].precomp); ptr->file[f].precomp = NULL; } } free(entry); } static int wdl_to_map[5] = { 1, 3, 0, 2, 0 }; static ubyte pa_flags[5] = { 8, 0, 0, 0, 4 }; fathom-1.0+git.20190120.0439ca/src/tbcore.h000066400000000000000000000070011342121007200174120ustar00rootroot00000000000000/* Copyright (c) 2011-2015 Ronald de Man Copyright 2017-2018 Jon Dart */ #ifndef TBCORE_H #define TBCORE_H #if defined(__cplusplus) && defined(TB_USE_ATOMIC) #include #endif #ifndef _WIN32 #include #define SEP_CHAR ':' #define FD int #define FD_ERR -1 #else #include #define SEP_CHAR ';' #define FD HANDLE #define FD_ERR INVALID_HANDLE_VALUE #endif #ifndef TB_NO_THREADS #if defined(__cplusplus) && (__cplusplus >= 201103L) #include #define LOCK_T std::mutex #define LOCK_INIT(x) #define LOCK(x) x.lock() #define UNLOCK(x) x.unlock() #else #ifndef _WIN32 #define LOCK_T pthread_mutex_t #define LOCK_INIT(x) pthread_mutex_init(&(x), NULL) #define LOCK(x) pthread_mutex_lock(&(x)) #define UNLOCK(x) pthread_mutex_unlock(&(x)) #else #define LOCK_T HANDLE #define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0) #define LOCK(x) WaitForSingleObject(x, INFINITE) #define UNLOCK(x) ReleaseMutex(x) #endif #endif #else /* TB_NO_THREADS */ #define LOCK_T int #define LOCK_INIT(x) /* NOP */ #define LOCK(x) /* NOP */ #define UNLOCK(x) /* NOP */ #endif #define WDLSUFFIX ".rtbw" #define DTZSUFFIX ".rtbz" #define WDLDIR "RTBWDIR" #define DTZDIR "RTBZDIR" #define TBPIECES 6 #define WDL_MAGIC 0x5d23e871 #define DTZ_MAGIC 0xa50c66d7 #define TBHASHBITS 10 typedef unsigned long long uint64; typedef unsigned int uint32; typedef unsigned char ubyte; typedef unsigned short ushort; struct TBHashEntry; #ifdef DECOMP64 typedef uint64 base_t; #else typedef uint32 base_t; #endif struct PairsData { char *indextable; ushort *sizetable; ubyte *data; ushort *offset; ubyte *symlen; ubyte *sympat; int blocksize; int idxbits; int min_len; base_t base[1]; // C++ complains about base[]... }; struct TBEntry { char *data; uint64 key; uint64 mapping; ubyte ready; ubyte num; ubyte symmetric; ubyte has_pawns; } #ifdef __GNUC__ __attribute__((__may_alias__)); #else ; #endif struct TBEntry_piece { char *data; uint64 key; uint64 mapping; #if defined(__cplusplus) && defined(TB_USE_ATOMIC) std::atomic ready; #else ubyte ready; #endif ubyte num; ubyte symmetric; ubyte has_pawns; ubyte enc_type; struct PairsData *precomp[2]; int factor[2][TBPIECES]; ubyte pieces[2][TBPIECES]; ubyte norm[2][TBPIECES]; }; struct TBEntry_pawn { char *data; uint64 key; uint64 mapping; #if defined(__cplusplus) && defined(TB_USE_ATOMIC) std::atomic ready; #else ubyte ready; #endif ubyte num; ubyte symmetric; ubyte has_pawns; ubyte pawns[2]; struct { struct PairsData *precomp[2]; int factor[2][TBPIECES]; ubyte pieces[2][TBPIECES]; ubyte norm[2][TBPIECES]; } file[4]; }; struct DTZEntry_piece { char *data; uint64 key; uint64 mapping; ubyte ready; ubyte num; ubyte symmetric; ubyte has_pawns; ubyte enc_type; struct PairsData *precomp; int factor[TBPIECES]; ubyte pieces[TBPIECES]; ubyte norm[TBPIECES]; ubyte flags; // accurate, mapped, side ushort map_idx[4]; ubyte *map; }; struct DTZEntry_pawn { char *data; uint64 key; uint64 mapping; ubyte ready; ubyte num; ubyte symmetric; ubyte has_pawns; ubyte pawns[2]; struct { struct PairsData *precomp; int factor[TBPIECES]; ubyte pieces[TBPIECES]; ubyte norm[TBPIECES]; } file[4]; ubyte flags[4]; ushort map_idx[4][4]; ubyte *map; }; struct TBHashEntry { uint64 key; struct TBEntry *ptr; }; struct DTZTableEntry { uint64 key1; uint64 key2; struct TBEntry *entry; }; #endif fathom-1.0+git.20190120.0439ca/src/tbprobe.c000066400000000000000000001552411342121007200175760ustar00rootroot00000000000000/* * tbprobe.c * Copyright (c) 2013-2016 Ronald de Man * Copyright (c) 2016-2017, 2019 Jon Dart * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #if defined(_MSC_VER) #include #endif #include "tbprobe.h" #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 #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 (faster than GCC's builtin). static inline unsigned tb_software_popcount(uint64_t x) { x = x - ((x >> 1) & 0x5555555555555555ull); x = (x & 0x3333333333333333ull) + ((x >> 2) & 0x3333333333333333ull); x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0full; return (x * 0x0101010101010101ull) >> 56; } #define popcount(x) tb_software_popcount(x) #endif #define poplsb(x) ((x) & ((x) - 1)) #define make_move(promote, from, to) \ ((((promote) & 0x7) << 12) | (((from) & 0x3F) << 6) | ((to) & 0x3F)) #define move_from(move) \ (((move) >> 6) & 0x3F) #define move_to(move) \ ((move) & 0x3F) #define move_promotes(move) \ (((move) >> 12) & 0x7) #define MAX_MOVES TB_MAX_MOVES #define MOVE_STALEMATE 0xFFFF #define MOVE_CHECKMATE 0xFFFE 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; }; static bool do_move(struct pos *pos, const struct pos *pos0, uint16_t move); static int probe_dtz(const struct pos *pos, int *success); unsigned TB_LARGEST = 0; #include "tbcore.c" #define rank(s) ((s) >> 3) #define file(s) ((s) & 0x07) #define board(s) ((uint64_t)1 << (s)) #ifdef TB_CUSTOM_LSB #define lsb(b) TB_CUSTOM_LSB(b) #else #if defined(__GNUC__) static inline unsigned lsb(uint64_t b) { assert(b != 0); return __builtin_ffsll(b)-1; } #elif defined(_MSC_VER) static inline unsigned lsb(uint64_t b) { assert(b != 0); DWORD index; #ifdef _WIN64 _BitScanForward64(&index,b); return (unsigned)index; #else if (b & 0xffffffffULL) { _BitScanForward(&index,(unsigned long)(b & 0xffffffffULL)); return (unsigned)index; } else { _BitScanForward(&index,(unsigned long)(b >> 32)); return 32 + (unsigned)index; } #endif } #else /* not a compiler/architecture with recognized builtins */ static uint32_t get_bit32(uint64_t x) { return (uint32_t)(((int32_t)(x))&-((int32_t)(x))); } static const unsigned MAGIC32 = 0xe89b2be; static const uint32_t MagicTable32[32] = {31,0,9,1,10,20,13,2,7,11,21,23,17,14,3,25,30,8,19,12,6,22,16,24,29,18,5,15,28,4,27,26}; static unsigned lsb(uint64_t b) { if (b & 0xffffffffULL) return MagicTable32[(get_bit32(b & 0xffffffffULL)*MAGIC32)>>27]; else return MagicTable32[(get_bit32(b >> 32)*MAGIC32)>>27]+32; } #endif #endif #define square(r, f) (8 * (r) + (f)) #ifdef TB_KING_ATTACKS #define king_attacks(s) TB_KING_ATTACKS(s) #define king_attacks_init() /* NOP */ #else /* TB_KING_ATTACKS */ static uint64_t king_attacks_table[64]; #define king_attacks(s) king_attacks_table[(s)] static void king_attacks_init(void) { for (unsigned s = 0; s < 64; s++) { unsigned r = rank(s); unsigned f = file(s); uint64_t b = 0; if (r != 0 && f != 0) b |= board(square(r-1, f-1)); if (r != 0) b |= board(square(r-1, f)); if (r != 0 && f != 7) b |= board(square(r-1, f+1)); if (f != 7) b |= board(square(r, f+1)); if (r != 7 && f != 7) b |= board(square(r+1, f+1)); if (r != 7) b |= board(square(r+1, f)); if (r != 7 && f != 0) b |= board(square(r+1, f-1)); if (f != 0) b |= board(square(r, f-1)); king_attacks_table[s] = b; } } #endif /* TB_KING_ATTACKS */ #ifdef TB_KNIGHT_ATTACKS #define knight_attacks(s) TB_KNIGHT_ATTACKS(s) #define knight_attacks_init() /* NOP */ #else /* TB_KNIGHT_ATTACKS */ static uint64_t knight_attacks_table[64]; #define knight_attacks(s) knight_attacks_table[(s)] static void knight_attacks_init(void) { for (unsigned s = 0; s < 64; s++) { int r1, r = rank(s); int f1, f = file(s); uint64_t b = 0; r1 = r-1; f1 = f-2; if (r1 >= 0 && f1 >= 0) b |= board(square(r1, f1)); r1 = r-1; f1 = f+2; if (r1 >= 0 && f1 <= 7) b |= board(square(r1, f1)); r1 = r-2; f1 = f-1; if (r1 >= 0 && f1 >= 0) b |= board(square(r1, f1)); r1 = r-2; f1 = f+1; if (r1 >= 0 && f1 <= 7) b |= board(square(r1, f1)); r1 = r+1; f1 = f-2; if (r1 <= 7 && f1 >= 0) b |= board(square(r1, f1)); r1 = r+1; f1 = f+2; if (r1 <= 7 && f1 <= 7) b |= board(square(r1, f1)); r1 = r+2; f1 = f-1; if (r1 <= 7 && f1 >= 0) b |= board(square(r1, f1)); r1 = r+2; f1 = f+1; if (r1 <= 7 && f1 <= 7) b |= board(square(r1, f1)); knight_attacks_table[s] = b; } } #endif /* TB_KNIGHT_ATTACKS */ #ifdef TB_BISHOP_ATTACKS #define bishop_attacks(s, occ) TB_BISHOP_ATTACKS(s, occ) #define bishop_attacks_init() /* NOP */ #else /* TB_BISHOP_ATTACKS */ static uint64_t diag_attacks_table[64][64]; static uint64_t anti_attacks_table[64][64]; static const unsigned square2diag_table[64] = { 0, 1, 2, 3, 4, 5, 6, 7, 14, 0, 1, 2, 3, 4, 5, 6, 13, 14, 0, 1, 2, 3, 4, 5, 12, 13, 14, 0, 1, 2, 3, 4, 11, 12, 13, 14, 0, 1, 2, 3, 10, 11, 12, 13, 14, 0, 1, 2, 9, 10, 11, 12, 13, 14, 0, 1, 8, 9, 10, 11, 12, 13, 14, 0 }; static const unsigned square2anti_table[64] = { 8, 9, 10, 11, 12, 13, 14, 0, 9, 10, 11, 12, 13, 14, 0, 1, 10, 11, 12, 13, 14, 0, 1, 2, 11, 12, 13, 14, 0, 1, 2, 3, 12, 13, 14, 0, 1, 2, 3, 4, 13, 14, 0, 1, 2, 3, 4, 5, 14, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 7 }; static const uint64_t diag2board_table[15] = { 0x8040201008040201ull, 0x0080402010080402ull, 0x0000804020100804ull, 0x0000008040201008ull, 0x0000000080402010ull, 0x0000000000804020ull, 0x0000000000008040ull, 0x0000000000000080ull, 0x0100000000000000ull, 0x0201000000000000ull, 0x0402010000000000ull, 0x0804020100000000ull, 0x1008040201000000ull, 0x2010080402010000ull, 0x4020100804020100ull, }; static const uint64_t anti2board_table[15] = { 0x0102040810204080ull, 0x0204081020408000ull, 0x0408102040800000ull, 0x0810204080000000ull, 0x1020408000000000ull, 0x2040800000000000ull, 0x4080000000000000ull, 0x8000000000000000ull, 0x0000000000000001ull, 0x0000000000000102ull, 0x0000000000010204ull, 0x0000000001020408ull, 0x0000000102040810ull, 0x0000010204081020ull, 0x0001020408102040ull, }; static inline size_t diag2index(uint64_t b, unsigned d) { b *= 0x0101010101010101ull; b >>= 56; b >>= 1; return (size_t)b; } static inline size_t anti2index(uint64_t b, unsigned a) { return diag2index(b, a); } #define diag(s) square2diag_table[(s)] #define anti(s) square2anti_table[(s)] #define diag2board(d) diag2board_table[(d)] #define anti2board(a) anti2board_table[(a)] static uint64_t bishop_attacks(unsigned sq, uint64_t occ) { occ &= ~board(sq); unsigned d = diag(sq), a = anti(sq); uint64_t d_occ = occ & (diag2board(d) & ~BOARD_EDGE); uint64_t a_occ = occ & (anti2board(a) & ~BOARD_EDGE); size_t d_idx = diag2index(d_occ, d); size_t a_idx = anti2index(a_occ, a); uint64_t d_attacks = diag_attacks_table[sq][d_idx]; uint64_t a_attacks = anti_attacks_table[sq][a_idx]; return d_attacks | a_attacks; } static void bishop_attacks_init(void) { for (unsigned idx = 0; idx < 64; idx++) { unsigned idx1 = idx << 1; for (unsigned s = 0; s < 64; s++) { int r = rank(s); int f = file(s); uint64_t b = 0; for (int i = -1; f + i >= 0 && r + i >= 0; i--) { unsigned occ = (1 << (f + i)); b |= board(square(r + i, f + i)); if (idx1 & occ) break; } for (int i = 1; f + i <= 7 && r + i <= 7; i++) { unsigned occ = (1 << (f + i)); b |= board(square(r + i, f + i)); if (idx1 & occ) break; } diag_attacks_table[s][idx] = b; } } for (unsigned idx = 0; idx < 64; idx++) { unsigned idx1 = idx << 1; for (unsigned s = 0; s < 64; s++) { int r = rank(s); int f = file(s); uint64_t b = 0; for (int i = -1; f + i >= 0 && r - i <= 7; i--) { unsigned occ = (1 << (f + i)); b |= board(square(r - i, f + i)); if (idx1 & occ) break; } for (int i = 1; f + i <= 7 && r - i >= 0; i++) { unsigned occ = (1 << (f + i)); b |= board(square(r - i, f + i)); if (idx1 & occ) break; } anti_attacks_table[s][idx] = b; } } } #endif /* TB_BISHOP_ATTACKS */ #ifdef TB_ROOK_ATTACKS #define rook_attacks(s, occ) TB_ROOK_ATTACKS(s, occ) #define rook_attacks_init() /* NOP */ #else /* TB_ROOK_ATTACKS */ static uint64_t rank_attacks_table[64][64]; static uint64_t file_attacks_table[64][64]; static inline size_t rank2index(uint64_t b, unsigned r) { b >>= (8 * r); b >>= 1; return (size_t)b; } static inline size_t file2index(uint64_t b, unsigned f) { b >>= f; b *= 0x0102040810204080ull; b >>= 56; b >>= 1; return (size_t)b; } #define rank2board(r) (0xFFull << (8 * (r))) #define file2board(f) (0x0101010101010101ull << (f)) static uint64_t rook_attacks(unsigned sq, uint64_t occ) { occ &= ~board(sq); unsigned r = rank(sq), f = file(sq); uint64_t r_occ = occ & (rank2board(r) & ~BOARD_RANK_EDGE); uint64_t f_occ = occ & (file2board(f) & ~BOARD_FILE_EDGE); size_t r_idx = rank2index(r_occ, r); size_t f_idx = file2index(f_occ, f); uint64_t r_attacks = rank_attacks_table[sq][r_idx]; uint64_t f_attacks = file_attacks_table[sq][f_idx]; return r_attacks | f_attacks; } static void rook_attacks_init(void) { for (unsigned idx = 0; idx < 64; idx++) { unsigned idx1 = idx << 1, occ; for (int f = 0; f <= 7; f++) { uint64_t b = 0; if (f > 0) { int i = f-1; do { occ = (1 << i); b |= board(square(0, i)); i--; } while (!(idx1 & occ) && i >= 0); } if (f < 7) { int i = f+1; do { occ = (1 << i); b |= board(square(0, i)); i++; } while (!(idx1 & occ) && i <= 7); } for (int r = 0; r <= 7; r++) { rank_attacks_table[square(r, f)][idx] = b; b <<= 8; } } } for (unsigned idx = 0; idx < 64; idx++) { unsigned idx1 = idx << 1, occ; for (int r = 0; r <= 7; r++) { uint64_t b = 0; if (r > 0) { int i = r-1; do { occ = (1 << i); b |= board(square(i, 0)); i--; } while (!(idx1 & occ) && i >= 0); } if (r < 7) { int i = r+1; do { occ = (1 << i); b |= board(square(i, 0)); i++; } while (!(idx1 & occ) && i <= 7); } for (int f = 0; f <= 7; f++) { file_attacks_table[square(r, f)][idx] = b; b <<= 1; } } } } #endif /* TB_ROOK_ATTACKS */ #ifdef TB_QUEEN_ATTACKS #define queen_attacks(s, occ) TB_QUEEN_ATTACKS(s, occ) #else /* TB_QUEEN_ATTACKS */ #define queen_attacks(s, occ) \ (rook_attacks((s), (occ)) | bishop_attacks((s), (occ))) #endif /* TB_QUEEN_ATTACKS */ #ifdef TB_PAWN_ATTACKS #define pawn_attacks(s, c) TB_PAWN_ATTACKS(s, c) #define pawn_attacks_init() /* NOP */ #else /* TB_PAWN_ATTACKS */ static uint64_t pawn_attacks_table[2][64]; #define pawn_attacks(s, c) pawn_attacks_table[(c)][(s)] static void pawn_attacks_init(void) { for (unsigned s = 0; s < 64; s++) { int r = rank(s); int f = file(s); uint64_t b = 0; if (r != 7) { if (f != 0) b |= board(square(r+1, f-1)); if (f != 7) b |= board(square(r+1, f+1)); } pawn_attacks_table[1][s] = b; b = 0; if (r != 0) { if (f != 0) b |= board(square(r-1, f-1)); if (f != 7) b |= board(square(r-1, f+1)); } pawn_attacks_table[0][s] = b; } } #endif /* TB_PAWN_ATTACKS */ static void prt_str(const struct pos *pos, char *str, bool mirror) { uint64_t white = pos->white, black = pos->black; int i; if (mirror) { uint64_t tmp = white; white = black; black = tmp; } *str++ = 'K'; for (i = popcount(white & pos->queens); i > 0; i--) *str++ = 'Q'; for (i = popcount(white & pos->rooks); i > 0; i--) *str++ = 'R'; for (i = popcount(white & pos->bishops); i > 0; i--) *str++ = 'B'; for (i = popcount(white & pos->knights); i > 0; i--) *str++ = 'N'; for (i = popcount(white & pos->pawns); i > 0; i--) *str++ = 'P'; *str++ = 'v'; *str++ = 'K'; for (i = popcount(black & pos->queens); i > 0; i--) *str++ = 'Q'; for (i = popcount(black & pos->rooks); i > 0; i--) *str++ = 'R'; for (i = popcount(black & pos->bishops); i > 0; i--) *str++ = 'B'; for (i = popcount(black & pos->knights); i > 0; i--) *str++ = 'N'; for (i = popcount(black & pos->pawns); i > 0; i--) *str++ = 'P'; *str++ = '\0'; } /* * Given a position, produce a 64-bit material signature key. */ static uint64_t calc_key(const struct pos *pos, bool mirror) { uint64_t white = pos->white, black = pos->black; if (mirror) { uint64_t tmp = white; white = black; black = tmp; } return popcount(white & pos->queens) * PRIME_WHITE_QUEEN + popcount(white & pos->rooks) * PRIME_WHITE_ROOK + popcount(white & pos->bishops) * PRIME_WHITE_BISHOP + popcount(white & pos->knights) * PRIME_WHITE_KNIGHT + popcount(white & pos->pawns) * PRIME_WHITE_PAWN + popcount(black & pos->queens) * PRIME_BLACK_QUEEN + popcount(black & pos->rooks) * PRIME_BLACK_ROOK + popcount(black & pos->bishops) * PRIME_BLACK_BISHOP + popcount(black & pos->knights) * PRIME_BLACK_KNIGHT + popcount(black & pos->pawns) * PRIME_BLACK_PAWN; } static uint64_t calc_key_from_pcs(int *pcs, int mirror) { mirror = (mirror? 8: 0); return pcs[WHITE_QUEEN ^ mirror] * PRIME_WHITE_QUEEN + pcs[WHITE_ROOK ^ mirror] * PRIME_WHITE_ROOK + pcs[WHITE_BISHOP ^ mirror] * PRIME_WHITE_BISHOP + pcs[WHITE_KNIGHT ^ mirror] * PRIME_WHITE_KNIGHT + pcs[WHITE_PAWN ^ mirror] * PRIME_WHITE_PAWN + pcs[BLACK_QUEEN ^ mirror] * PRIME_BLACK_QUEEN + pcs[BLACK_ROOK ^ mirror] * PRIME_BLACK_ROOK + pcs[BLACK_BISHOP ^ mirror] * PRIME_BLACK_BISHOP + pcs[BLACK_KNIGHT ^ mirror] * PRIME_BLACK_KNIGHT + pcs[BLACK_PAWN ^ mirror] * PRIME_BLACK_PAWN; } static uint64_t get_pieces(const struct pos *pos, uint8_t code) { switch (code) { case WHITE_KING: return pos->kings & pos->white; case WHITE_QUEEN: return pos->queens & pos->white; case WHITE_ROOK: return pos->rooks & pos->white; case WHITE_BISHOP: return pos->bishops & pos->white; case WHITE_KNIGHT: return pos->knights & pos->white; case WHITE_PAWN: return pos->pawns & pos->white; case BLACK_KING: return pos->kings & pos->black; case BLACK_QUEEN: return pos->queens & pos->black; case BLACK_ROOK: return pos->rooks & pos->black; case BLACK_BISHOP: return pos->bishops & pos->black; case BLACK_KNIGHT: return pos->knights & pos->black; case BLACK_PAWN: return pos->pawns & pos->black; default: return 0; // Dummy. } } static int probe_wdl_table(const struct pos *pos, int *success) { struct TBEntry *ptr; struct TBHashEntry *ptr2; uint64_t idx; uint64_t key; int i; uint8_t res; int p[TBPIECES]; // Obtain the position's material signature key. key = calc_key(pos, false); // Test for KvK. if (key == KEY_KvK) return 0; ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; for (i = 0; i < HSHMAX; i++) { if (ptr2[i].key == key) break; } if (i == HSHMAX) { *success = 0; return 0; } ptr = ptr2[i].ptr; if (!ptr->ready) { LOCK(TB_MUTEX); if (!ptr->ready) { char str[16]; prt_str(pos, str, ptr->key != key); if (!init_table_wdl(ptr, str)) { ptr2[i].key = 0ULL; *success = 0; UNLOCK(TB_MUTEX); return 0; } // Memory barrier to ensure ptr->ready = 1 is not reordered. #if !defined(__cplusplus) || !defined(TB_USE_ATOMIC) #ifdef __GNUC__ __asm__ __volatile__ ("" ::: "memory"); #elif defined(_MSC_VER) MemoryBarrier(); #endif #endif ptr->ready = 1; } UNLOCK(TB_MUTEX); } int bside, mirror, cmirror; if (!ptr->symmetric) { if (key != ptr->key) { cmirror = 8; mirror = 0x38; bside = pos->turn; } else { cmirror = mirror = 0; bside = !pos->turn; } } else { cmirror = (pos->turn? 0: 8); mirror = (pos->turn? 0: 0x38); bside = 0; } // p[i] is to contain the square 0-63 (A1-H8) for a piece of type // pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king. // Pieces of the same type are guaranteed to be consecutive. if (!ptr->has_pawns) { struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr; uint8_t *pc = entry->pieces[bside]; for (i = 0; i < entry->num; ) { uint64_t bb = get_pieces(pos, pc[i] ^ cmirror); do { p[i++] = lsb(bb); bb = poplsb(bb); } while (bb); } idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]); res = decompress_pairs(entry->precomp[bside], idx); } else { struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr; int k = entry->file[0].pieces[0][0] ^ cmirror; uint64_t bb = get_pieces(pos, k); i = 0; do { p[i++] = lsb(bb) ^ mirror; bb = poplsb(bb); } while (bb); int f = pawn_file(entry, p); uint8_t *pc = entry->file[f].pieces[bside]; for (; i < entry->num; ) { bb = get_pieces(pos, pc[i] ^ cmirror); do { p[i++] = lsb(bb) ^ mirror; bb = poplsb(bb); } while (bb); } idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]); res = decompress_pairs(entry->file[f].precomp[bside], idx); } return ((int)res) - 2; } static int probe_dtz_table(const struct pos *pos, int wdl, int *success) { struct TBEntry *ptr; uint64_t idx; int i, res; int p[TBPIECES]; // Obtain the position's material signature key. uint64_t key = calc_key(pos, false); if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) { for (i = 1; i < DTZ_ENTRIES; i++) { if (DTZ_table[i].key1 == key) break; } if (i < DTZ_ENTRIES) { struct DTZTableEntry table_entry = DTZ_table[i]; for (; i > 0; i--) DTZ_table[i] = DTZ_table[i - 1]; DTZ_table[0] = table_entry; } else { struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)]; for (i = 0; i < HSHMAX; i++) { if (ptr2[i].key == key) break; } if (i == HSHMAX) { *success = 0; return 0; } ptr = ptr2[i].ptr; char str[16]; const bool mirror = (ptr->key != key); prt_str(pos, str, mirror); if (DTZ_table[DTZ_ENTRIES - 1].entry) free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry); for (i = DTZ_ENTRIES - 1; i > 0; i--) DTZ_table[i] = DTZ_table[i - 1]; uint64_t key1 = calc_key(pos, mirror); uint64_t key2 = calc_key(pos, !mirror); load_dtz_table(str, key1, key2); } } ptr = DTZ_table[0].entry; if (!ptr) { *success = 0; return 0; } int bside, mirror, cmirror; if (!ptr->symmetric) { if (key != ptr->key) { cmirror = 8; mirror = 0x38; bside = pos->turn; } else { cmirror = mirror = 0; bside = !pos->turn; } } else { cmirror = (pos->turn? 0: 8); mirror = (pos->turn? 0: 0x38); bside = 0; } if (!ptr->has_pawns) { struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr; if ((entry->flags & 1) != bside && !entry->symmetric) { *success = -1; return 0; } uint8_t *pc = entry->pieces; for (i = 0; i < entry->num;) { uint64_t bb = get_pieces(pos, pc[i] ^ cmirror); do { p[i++] = lsb(bb); bb = poplsb(bb); } while (bb); } idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor); res = decompress_pairs(entry->precomp, idx); if (entry->flags & 2) res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res]; if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1)) res *= 2; } else { struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr; int k = entry->file[0].pieces[0] ^ cmirror; uint64_t bb = get_pieces(pos, k); i = 0; do { p[i++] = lsb(bb) ^ mirror; bb = poplsb(bb); } while (bb); int f = pawn_file((struct TBEntry_pawn *)entry, p); if ((entry->flags[f] & 1) != bside) { *success = -1; return 0; } uint8_t *pc = entry->file[f].pieces; for (; i < entry->num;) { bb = get_pieces(pos, pc[i] ^ cmirror); do { p[i++] = lsb(bb) ^ mirror; bb = poplsb(bb); } while (bb); } idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor); res = decompress_pairs(entry->file[f].precomp, idx); if (entry->flags[f] & 2) res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res]; if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1)) res *= 2; } return res; } static uint16_t *add_move(uint16_t *moves, bool promotes, unsigned from, unsigned to) { if (!promotes) *moves++ = make_move(TB_PROMOTES_NONE, from, to); else { *moves++ = make_move(TB_PROMOTES_QUEEN, from, to); *moves++ = make_move(TB_PROMOTES_KNIGHT, from, to); *moves++ = make_move(TB_PROMOTES_ROOK, from, to); *moves++ = make_move(TB_PROMOTES_BISHOP, from, to); } return moves; } /* * Generate all captures or promotions. */ static uint16_t *gen_captures_or_promotions(const struct pos *pos, uint16_t *moves) { uint64_t occ = pos->white | pos->black; uint64_t us = (pos->turn? pos->white: pos->black), them = (pos->turn? pos->black: pos->white); uint64_t b, att; { unsigned from = lsb(pos->kings & us); for (att = king_attacks(from) & them; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->queens; b; b = poplsb(b)) { unsigned from = lsb(b); for (att = queen_attacks(from, occ) & them; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->rooks; b; b = poplsb(b)) { unsigned from = lsb(b); for (att = rook_attacks(from, occ) & them; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->bishops; b; b = poplsb(b)) { unsigned from = lsb(b); for (att = bishop_attacks(from, occ) & them; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->knights; b; b = poplsb(b)) { unsigned from = lsb(b); for (att = knight_attacks(from) & them; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->pawns; b; b = poplsb(b)) { unsigned from = lsb(b); att = pawn_attacks(from, pos->turn); if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) { unsigned to = pos->ep; moves = add_move(moves, false, from, to); } for (att = att & them; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, to); } if (pos->turn && rank(from) == 6) { unsigned to = from + 8; if ((board(to) & occ) == 0) moves = add_move(moves, true, from, to); } else if (!pos->turn && rank(from) == 1) { unsigned to = from - 8; if ((board(to) & occ) == 0) moves = add_move(moves, true, from, to); } } return moves; } /* * Generate all non-capture pawn moves and promotions. */ static uint16_t *gen_pawn_quiets_or_promotions(const struct pos *pos, uint16_t *moves) { uint64_t occ = pos->white | pos->black; uint64_t us = (pos->turn? pos->white: pos->black); uint64_t b, att; for (b = us & pos->pawns; b; b = poplsb(b)) { unsigned from = lsb(b); unsigned next = from + (pos->turn? 8: -8); att = 0; if ((board(next) & occ) == 0) { att |= board(next); unsigned next2 = from + (pos->turn? 16: -16); if ((pos->turn? rank(from) == 1: rank(from) == 6) && ((board(next2) & occ) == 0)) att |= board(next2); } for (; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, to); } } return moves; } /* * Generate all en passant captures. */ static uint16_t *gen_pawn_ep_captures(const struct pos *pos, uint16_t *moves) { if (pos->ep == 0) return moves; uint64_t ep = board(pos->ep); unsigned to = pos->ep; uint64_t us = (pos->turn? pos->white: pos->black); uint64_t b; for (b = us & pos->pawns; b; b = poplsb(b)) { unsigned from = lsb(b); if ((pawn_attacks(from, pos->turn) & ep) != 0) moves = add_move(moves, false, from, to); } return moves; } /* * Generate all moves. */ static uint16_t *gen_moves(const struct pos *pos, uint16_t *moves) { uint64_t occ = pos->white | pos->black; uint64_t us = (pos->turn? pos->white: pos->black), them = (pos->turn? pos->black: pos->white); uint64_t b, att; { unsigned from = lsb(pos->kings & us); for (att = king_attacks(from) & ~us; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->queens; b; b = poplsb(b)) { unsigned from = lsb(b); for (att = queen_attacks(from, occ) & ~us; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->rooks; b; b = poplsb(b)) { unsigned from = lsb(b); for (att = rook_attacks(from, occ) & ~us; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->bishops; b; b = poplsb(b)) { unsigned from = lsb(b); for (att = bishop_attacks(from, occ) & ~us; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->knights; b; b = poplsb(b)) { unsigned from = lsb(b); for (att = knight_attacks(from) & ~us; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, false, from, to); } } for (b = us & pos->pawns; b; b = poplsb(b)) { unsigned from = lsb(b); unsigned next = from + (pos->turn? 8: -8); att = pawn_attacks(from, pos->turn); if (pos->ep != 0 && ((att & board(pos->ep)) != 0)) { unsigned to = pos->ep; moves = add_move(moves, false, from, to); } att &= them; if ((board(next) & occ) == 0) { att |= board(next); unsigned next2 = from + (pos->turn? 16: -16); if ((pos->turn? rank(from) == 1: rank(from) == 6) && ((board(next2) & occ) == 0)) att |= board(next2); } for (; att; att = poplsb(att)) { unsigned to = lsb(att); moves = add_move(moves, (rank(to) == 7 || rank(to) == 0), from, to); } } return moves; } /* * Test if the given move is an en passant capture. */ static bool is_en_passant(const struct pos *pos, uint16_t move) { uint16_t from = move_from(move); uint16_t to = move_to(move); uint64_t us = (pos->turn? pos->white: pos->black); if (pos->ep == 0) return false; if (to != pos->ep) return false; if ((board(from) & us & pos->pawns) == 0) return false; return true; } /* * Test if the given position is legal. * (Pawns on backrank? Can the king be captured?) */ static bool is_legal(const struct pos *pos) { uint64_t occ = pos->white | pos->black; uint64_t us = (pos->turn? pos->black: pos->white), them = (pos->turn? pos->white: pos->black); uint64_t king = pos->kings & us; if (!king) return false; unsigned sq = lsb(king); if (king_attacks(sq) & (pos->kings & them)) return false; uint64_t ratt = rook_attacks(sq, occ); uint64_t batt = bishop_attacks(sq, occ); if (ratt & (pos->rooks & them)) return false; if (batt & (pos->bishops & them)) return false; if ((ratt | batt) & (pos->queens & them)) return false; if (knight_attacks(sq) & (pos->knights & them)) return false; if (pawn_attacks(sq, !pos->turn) & (pos->pawns & them)) return false; return true; } /* * Test if the king is in check. */ static bool is_check(const struct pos *pos) { uint64_t occ = pos->white | pos->black; uint64_t us = (pos->turn? pos->white: pos->black), them = (pos->turn? pos->black: pos->white); uint64_t king = pos->kings & us; unsigned sq = lsb(king); uint64_t ratt = rook_attacks(sq, occ); uint64_t batt = bishop_attacks(sq, occ); if (ratt & (pos->rooks & them)) return true; if (batt & (pos->bishops & them)) return true; if ((ratt | batt) & (pos->queens & them)) return true; if (knight_attacks(sq) & (pos->knights & them)) return true; if (pawn_attacks(sq, pos->turn) & (pos->pawns & them)) return true; return false; } /* * Test if the king is in checkmate. */ static bool is_mate(const struct pos *pos) { if (!is_check(pos)) return false; uint16_t moves0[MAX_MOVES]; uint16_t *moves = moves0; uint16_t *end = gen_moves(pos, moves); for (; moves < end; moves++) { struct pos pos1; if (do_move(&pos1, pos, *moves)) return false; } return true; } /* * Test if the position is valid. */ static bool is_valid(const struct pos *pos) { if (popcount(pos->kings) != 2) return false; if (popcount(pos->kings & pos->white) != 1) return false; if (popcount(pos->kings & pos->black) != 1) return false; if ((pos->white & pos->black) != 0) return false; if ((pos->kings & pos->queens) != 0) return false; if ((pos->kings & pos->rooks) != 0) return false; if ((pos->kings & pos->bishops) != 0) return false; if ((pos->kings & pos->knights) != 0) return false; if ((pos->kings & pos->pawns) != 0) return false; if ((pos->queens & pos->rooks) != 0) return false; if ((pos->queens & pos->bishops) != 0) return false; if ((pos->queens & pos->knights) != 0) return false; if ((pos->queens & pos->pawns) != 0) return false; if ((pos->rooks & pos->bishops) != 0) return false; if ((pos->rooks & pos->knights) != 0) return false; if ((pos->rooks & pos->pawns) != 0) return false; if ((pos->bishops & pos->knights) != 0) return false; if ((pos->bishops & pos->pawns) != 0) return false; if ((pos->knights & pos->pawns) != 0) return false; if (pos->pawns & BOARD_FILE_EDGE) return false; if ((pos->white | pos->black) != (pos->kings | pos->queens | pos->rooks | pos->bishops | pos->knights | pos->pawns)) return false; return is_legal(pos); } #define do_bb_move(b, from, to) \ (((b) & (~board(to)) & (~board(from))) | \ ((((b) >> (from)) & 0x1) << (to))) static bool do_move(struct pos *pos, const struct pos *pos0, uint16_t move) { unsigned from = move_from(move); unsigned to = move_to(move); unsigned promotes = move_promotes(move); pos->turn = !pos0->turn; pos->white = do_bb_move(pos0->white, from, to); pos->black = do_bb_move(pos0->black, from, to); pos->kings = do_bb_move(pos0->kings, from, to); pos->queens = do_bb_move(pos0->queens, from, to); pos->rooks = do_bb_move(pos0->rooks, from, to); pos->bishops = do_bb_move(pos0->bishops, from, to); pos->knights = do_bb_move(pos0->knights, from, to); pos->pawns = do_bb_move(pos0->pawns, from, to); pos->ep = 0; if (promotes != TB_PROMOTES_NONE) { pos->pawns &= ~board(to); // Promotion switch (promotes) { case TB_PROMOTES_QUEEN: pos->queens |= board(to); break; case TB_PROMOTES_ROOK: pos->rooks |= board(to); break; case TB_PROMOTES_BISHOP: pos->bishops |= board(to); break; case TB_PROMOTES_KNIGHT: pos->knights |= board(to); break; } pos->rule50 = 0; } else if ((board(from) & pos0->pawns) != 0) { pos->rule50 = 0; // Pawn move if (rank(from) == 1 && rank(to) == 3 && (pawn_attacks(from+8, true) & pos0->pawns & pos0->black) != 0) pos->ep = from+8; else if (rank(from) == 6 && rank(to) == 4 && (pawn_attacks(from-8, false) & pos0->pawns & pos0->white) != 0) pos->ep = from-8; else if (to == pos0->ep) { unsigned ep_to = (pos0->turn? to-8: to+8); uint64_t ep_mask = ~board(ep_to); pos->white &= ep_mask; pos->black &= ep_mask; pos->pawns &= ep_mask; } } else if ((board(to) & (pos0->white | pos0->black)) != 0) pos->rule50 = 0; // Capture else pos->rule50 = pos0->rule50 + 1; // Normal move if (!is_legal(pos)) return false; return true; } static int probe_ab(const struct pos *pos, int alpha, int beta, int *success) { int v; uint16_t moves0[64]; uint16_t *moves = moves0; uint16_t *end = gen_captures_or_promotions(pos, moves); for (; moves < end; moves++) { if (is_en_passant(pos, *moves)) continue; struct pos pos1; if (!do_move(&pos1, pos, *moves)) continue; v = -probe_ab(&pos1, -beta, -alpha, success); if (*success == 0) return 0; if (v > alpha) { if (v >= beta) { *success = 2; return v; } alpha = v; } } v = probe_wdl_table(pos, success); if (*success == 0) return 0; if (alpha >= v) { *success = 1 + (alpha > 0); return alpha; } else { *success = 1; return v; } } static int probe_wdl(const struct pos *pos, int *success) { *success = 1; int v = probe_ab(pos, -2, 2, success); if (*success == 0) return 0; // If en passant is not possible, we are done. if (pos->ep == 0) return v; // Now handle en passant. int v1 = -3; uint16_t moves0[2]; // Max=2 possible en-passant captures. uint16_t *moves = moves0; uint16_t *end = gen_pawn_ep_captures(pos, moves); for (; moves < end; moves++) { struct pos pos1; if (!do_move(&pos1, pos, *moves)) continue; int v0 = -probe_ab(&pos1, -2, 2, success); if (*success == 0) return 0; if (v0 > v1) v1 = v0; } if (v1 > -3) { if (v1 >= v) v = v1; else if (v == 0) { // Check whether there is at least one legal non-ep move. uint16_t moves0[MAX_MOVES]; uint16_t *moves = moves0; uint16_t *end = gen_moves(pos, moves); bool found = false; for (; moves < end; moves++) { if (is_en_passant(pos, *moves)) continue; struct pos pos1; if (do_move(&pos1, pos, *moves)) { found = true; break; } } if (!found) v = v1; // Forced to play the losing ep capture. } } return v; } static int probe_dtz_no_ep(const struct pos *pos, int *success) { int wdl, dtz; wdl = probe_ab(pos, -2, 2, success); if (wdl == 0) return 0; if (*success == 2) return wdl == 2 ? 1 : 101; uint16_t moves0[MAX_MOVES]; uint16_t *moves = moves0, *end = NULL; if (wdl > 0) { // Generate at least all legal non-capturing pawn moves // including non-capturing promotions. end = gen_pawn_quiets_or_promotions(pos, moves); for (; moves < end; moves++) { struct pos pos1; if (!do_move(&pos1, pos, *moves)) continue; int v = (pos1.ep == 0? -probe_ab(&pos1, -2, -wdl + 1, success): -probe_wdl(&pos1, success)); if (*success == 0) return 0; if (v == wdl) return (v == 2? 1: 101); } } dtz = 1 + probe_dtz_table(pos, wdl, success); if (*success >= 0) { if (wdl & 1) dtz += 100; return (wdl >= 0? dtz: -dtz); } if (wdl > 0) { int best = BEST_NONE; moves = moves0; end = gen_moves(pos, moves); for (; moves < end; moves++) { struct pos pos1; if (!do_move(&pos1, pos, *moves)) continue; if (pos1.rule50 == 0) continue; int v = -probe_dtz(&pos1, success); if (*success == 0) return 0; if (v > 0 && v + 1 < best) best = v + 1; } assert(best != BEST_NONE); return best; } else { int best = -1; end = gen_moves(pos, moves); for (; moves < end; moves++) { int v; struct pos pos1; if (!do_move(&pos1, pos, *moves)) continue; if (pos1.rule50 == 0) { if (wdl == -2) v = -1; else { v = probe_ab(&pos1, 1, 2, success); v = (v == 2) ? 0 : -101; } } else v = -probe_dtz(&pos1, success) - 1; if (*success == 0) return 0; if (v < best) best = v; } return best; } } static const int wdl_to_dtz[] = { -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 * * The return value n can be off by 1: a return value -n can mean a loss * in n+1 ply and a return value +n can mean a win in n+1 ply. This * cannot happen for tables with positions exactly on the "edge" of * the 50-move rule. * * This implies that if dtz > 0 is returned, the position is certainly * a win if dtz + 50-move-counter <= 99. Care must be taken that the engine * picks moves that preserve dtz + 50-move-counter <= 99. * * If n = 100 immediately after a capture or pawn move, then the position * is also certainly a win, and during the whole phase until the next * capture or pawn move, the inequality to be preserved is * dtz + 50-movecounter <= 100. * * In short, if a move is available resulting in dtz + 50-move-counter <= 99, * then do not accept moves leading to dtz + 50-move-counter == 100. */ static int probe_dtz(const struct pos *pos, int *success) { *success = 1; int v = probe_dtz_no_ep(pos, success); if (*success == 0) return 0; if (pos->ep == 0) return v; int v1 = -3; uint16_t moves0[2]; // Max=2 possible en-passant captures. uint16_t *moves = moves0; uint16_t *end = gen_pawn_ep_captures(pos, moves); for (; moves < end; moves++) { struct pos pos1; if (!do_move(&pos1, pos, *moves)) continue; int v0 = -probe_ab(&pos1, -2, 2, success); if (*success == 0) return 0; if (v0 > v1) v1 = v0; } if (v1 > -3) { v1 = wdl_to_dtz[v1 + 2]; if (v < -100) { if (v1 >= 0) v = v1; } else if (v < 0) { if (v1 >= 0 || v1 < -100) v = v1; } else if (v > 100) { if (v1 > 0) v = v1; } else if (v > 0) { if (v1 == 1) v = v1; } else if (v1 >= 0) v = v1; else { uint16_t moves0[MAX_MOVES]; uint16_t *moves = moves0; uint16_t *end = gen_moves(pos, moves); bool found = false; for (; moves < end; moves++) { if (is_en_passant(pos, *moves)) continue; struct pos pos1; if (do_move(&pos1, pos, *moves)) { found = true; break; } } if (!found) v = v1; // Forced to play the losing ep capture. } } return v; } 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; } static uint16_t probe_root(const struct 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++) { struct pos pos1; if (!do_move(&pos1, pos, moves[i])) { scores[i] = SCORE_ILLEGAL; continue; } int v = 0; if (dtz > 0 && is_mate(&pos1)) v = 1; else { if (pos1.rule50 != 0) { v = -probe_dtz(&pos1, &success); if (v > 0) v++; else if (v < 0) v--; } else { v = -probe_wdl(&pos1, &success); v = wdl_to_dtz[v + 2]; } } num_draw += (v == 0); if (!success) return 0; scores[i] = v; if (results != NULL) { unsigned res = 0; res = TB_SET_WDL(res, dtz_to_wdl(pos->rule50, v)); res = TB_SET_FROM(res, move_from(moves[i])); res = TB_SET_TO(res, move_to(moves[i])); res = TB_SET_PROMOTES(res, move_promotes(moves[i])); res = TB_SET_EP(res, is_en_passant(pos, moves[i])); res = TB_SET_DTZ(res, (v < 0? -v: v)); results[j++] = res; } } if (results != NULL) results[j++] = TB_RESULT_FAILED; if (score != NULL) *score = dtz; // 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; } } bool tb_init_impl(const char *path) { if (sizeof(uint64_t) != 8 || // Paranoid check sizeof(uint32_t) != 4 || sizeof(uint16_t) != 2 || sizeof(uint8_t) != 1) return false; king_attacks_init(); knight_attacks_init(); bishop_attacks_init(); rook_attacks_init(); pawn_attacks_init(); if (path == NULL) path = ""; init_tablebases(path); return true; } unsigned tb_probe_wdl_impl( uint64_t white, uint64_t black, uint64_t kings, uint64_t queens, uint64_t rooks, uint64_t bishops, uint64_t knights, uint64_t pawns, unsigned ep, bool turn) { struct pos pos = { white, black, kings, queens, rooks, bishops, knights, pawns, 0, (uint8_t)ep, turn }; int success; int v = probe_wdl(&pos, &success); if (success == 0) return TB_RESULT_FAILED; return (unsigned)(v + 2); } unsigned tb_probe_root_impl( uint64_t white, uint64_t black, uint64_t kings, uint64_t queens, uint64_t rooks, uint64_t bishops, uint64_t knights, uint64_t pawns, unsigned rule50, unsigned ep, bool turn, unsigned *results) { struct pos pos = { white, black, kings, queens, rooks, bishops, knights, pawns, (uint8_t)rule50, (uint8_t)ep, turn }; int dtz; if (!is_valid(&pos)) return TB_RESULT_FAILED; uint16_t move = probe_root(&pos, &dtz, results); if (move == 0) return TB_RESULT_FAILED; if (move == MOVE_CHECKMATE) return TB_RESULT_CHECKMATE; if (move == MOVE_STALEMATE) return TB_RESULT_STALEMATE; unsigned res = 0; res = TB_SET_WDL(res, dtz_to_wdl(rule50, dtz)); res = TB_SET_DTZ(res, (dtz < 0? -dtz: dtz)); res = TB_SET_FROM(res, move_from(move)); res = TB_SET_TO(res, move_to(move)); res = TB_SET_PROMOTES(res, move_promotes(move)); res = TB_SET_EP(res, is_en_passant(&pos, move)); return res; } #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.20190120.0439ca/src/tbprobe.h000066400000000000000000000247061342121007200176040ustar00rootroot00000000000000/* * 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_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. */ static inline bool tb_init(const char *_path) { return tb_init_impl(_path); } /* * 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); } /****************************************************************************/ /* 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