X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsyzygy%2Ftbprobe.cpp;h=0805975b1e381a630e9e9e1ee8f55ec60cb56ff2;hp=43fc0f1c210b288cdb48ed7c26eea72edbc157ed;hb=2c237da54647bb7526f3512bea183eb44919cdda;hpb=c0bb0415394179e9c771cc96a9da6724fc14c167 diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 43fc0f1c..0805975b 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (c) 2013 Ronald de Man - Copyright (C) 2016 Marco Costalba, Lucas Braesch + Copyright (C) 2016-2017 Marco Costalba, Lucas Braesch Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -65,10 +65,10 @@ inline Square operator^(Square s, int i) { return Square(int(s) ^ i); } // like captures and pawn moves but we can easily recover the correct dtz of the // previous move if we know the position's WDL score. int dtz_before_zeroing(WDLScore wdl) { - return wdl == WDLWin ? 1 : - wdl == WDLCursedWin ? 101 : - wdl == WDLCursedLoss ? -101 : - wdl == WDLLoss ? -1 : 0; + return wdl == WDLWin ? 1 : + wdl == WDLCursedWin ? 101 : + wdl == WDLBlessedLoss ? -101 : + wdl == WDLLoss ? -1 : 0; } // Return the sign of a number (-1, 0, 1) @@ -125,18 +125,39 @@ struct PairsData { int groupLen[TBPIECES+1]; // Number of pieces in a given group: KRKN -> (3, 1) }; -// Helper struct to avoid to manually define entry copy c'tor as we should -// because default one is not compatible with std::atomic_bool. +// Helper struct to avoid manually defining entry copy constructor as we +// should because the default one is not compatible with std::atomic_bool. struct Atomic { Atomic() = default; Atomic(const Atomic& e) { ready = e.ready.load(); } // MSVC 2013 wants assignment within body std::atomic_bool ready; }; -struct WDLEntry : public Atomic { - WDLEntry(const std::string& code); - ~WDLEntry(); +// We define types for the different parts of the WLDEntry and DTZEntry with +// corresponding specializations for pieces or pawns. + +struct WLDEntryPiece { + PairsData* precomp; +}; + +struct WDLEntryPawn { + uint8_t pawnCount[2]; // [Lead color / other color] + WLDEntryPiece file[2][4]; // [wtm / btm][FILE_A..FILE_D] +}; + +struct DTZEntryPiece { + PairsData* precomp; + uint16_t map_idx[4]; // WDLWin, WDLLoss, WDLCursedWin, WDLBlessedLoss + uint8_t* map; +}; + +struct DTZEntryPawn { + uint8_t pawnCount[2]; + DTZEntryPiece file[4]; + uint8_t* map; +}; +struct TBEntry : public Atomic { void* baseAddress; uint64_t mapping; Key key; @@ -144,46 +165,24 @@ struct WDLEntry : public Atomic { int pieceCount; bool hasPawns; bool hasUniquePieces; +}; + +// Now the main types: WDLEntry and DTZEntry +struct WDLEntry : public TBEntry { + WDLEntry(const std::string& code); + ~WDLEntry(); union { - struct { - PairsData* precomp; - } pieceTable[2]; // [wtm / btm] - - struct { - uint8_t pawnCount[2]; // [Lead color / other color] - struct { - PairsData* precomp; - } file[2][4]; // [wtm / btm][FILE_A..FILE_D] - } pawnTable; + WLDEntryPiece pieceTable[2]; // [wtm / btm] + WDLEntryPawn pawnTable; }; }; -struct DTZEntry : public Atomic { +struct DTZEntry : public TBEntry { DTZEntry(const WDLEntry& wdl); ~DTZEntry(); - - void* baseAddress; - uint64_t mapping; - Key key; - Key key2; - int pieceCount; - bool hasPawns; - bool hasUniquePieces; union { - struct { - PairsData* precomp; - uint16_t map_idx[4]; // WDLWin, WDLLoss, WDLCursedWin, WDLCursedLoss - uint8_t* map; - } pieceTable; - - struct { - uint8_t pawnCount[2]; - struct { - PairsData* precomp; - uint16_t map_idx[4]; - } file[4]; - uint8_t* map; - } pawnTable; + DTZEntryPiece pieceTable; + DTZEntryPawn pawnTable; }; }; @@ -229,17 +228,23 @@ template inline void swap_byte(T& x) { char tmp, *c = (char*)&x; - if (Half) // Fix a MSVC 2015 warning - for (int i = 0; i < Half; ++i) - tmp = c[i], c[i] = c[End - i], c[End - i] = tmp; + for (int i = 0; i < Half; ++i) + tmp = c[i], c[i] = c[End - i], c[End - i] = tmp; } +template<> inline void swap_byte(uint8_t&) {} template T number(void* addr) { const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; const bool IsLittleEndian = (Le.c[0] == 4); - T v = *((T*)addr); + T v; + + if ((uintptr_t)addr & (alignof(T) - 1)) // Unaligned pointer (very rare) + std::memcpy(&v, addr, sizeof(T)); + else + v = *((T*)addr); + if (LE != IsLittleEndian) swap_byte(v); return v; @@ -526,14 +531,14 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // // I(k) = k * d->span + d->span / 2 (1) - // First step is to get the 'k' of the I(k) nearest to our idx, using defintion (1) + // First step is to get the 'k' of the I(k) nearest to our idx, using definition (1) uint32_t k = idx / d->span; // Then we read the corresponding SparseIndex[] entry uint32_t block = number(&d->sparseIndex[k].block); int offset = number(&d->sparseIndex[k].offset); - // Now compute the difference idx - I(k). From defintion of k we know that + // Now compute the difference idx - I(k). From definition of k we know that // // idx = k * d->span + idx % d->span (2) // @@ -655,7 +660,7 @@ int map_score(DTZEntry* entry, File f, int value, WDLScore wdl) { if ( (wdl == WDLWin && !(flags & TBFlag::WinPlies)) || (wdl == WDLLoss && !(flags & TBFlag::LossPlies)) || wdl == WDLCursedWin - || wdl == WDLCursedLoss) + || wdl == WDLBlessedLoss) value *= 2; return value + 1; @@ -668,7 +673,7 @@ int map_score(DTZEntry* entry, File f, int value, WDLScore wdl) { // idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk] // template::type> -T do_probe_table(const Position& pos, Entry* entry, WDLScore wdl, ProbeState* result) { +T do_probe_table(const Position& pos, Entry* entry, WDLScore wdl, ProbeState* result) { const bool IsWDL = std::is_same::value; @@ -973,8 +978,8 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { d->flags = *data++; if (d->flags & TBFlag::SingleValue) { - d->blocksNum = d->span = - d->blockLengthSize = d->sparseIndexSize = 0; // Broken MSVC zero-init + d->blocksNum = d->blockLengthSize = 0; + d->span = d->sparseIndexSize = 0; // Broken MSVC zero-init d->minSymLen = *data++; // Here we store the single value return data; } @@ -1061,10 +1066,10 @@ void do_init(Entry& e, T& p, uint8_t* data) { enum { Split = 1, HasPawns = 2 }; - uint8_t flags = *data++; + assert(e.hasPawns == !!(*data & HasPawns)); + assert((e.key != e.key2) == !!(*data & Split)); - assert(e.hasPawns == !!(flags & HasPawns)); - assert((e.key != e.key2) == !!(flags & Split)); + data++; // First byte stores flags const int Sides = IsWDL && (e.key != e.key2) ? 2 : 1; const File MaxFile = e.hasPawns ? FILE_D : FILE_A; @@ -1102,7 +1107,7 @@ void do_init(Entry& e, T& p, uint8_t* data) { for (File f = FILE_A; f <= MaxFile; ++f) for (int i = 0; i < Sides; i++) { (d = item(p, i, f).precomp)->sparseIndex = (SparseEntry*)data; - data += d->sparseIndexSize * sizeof(SparseEntry) ; + data += d->sparseIndexSize * sizeof(SparseEntry); } for (File f = FILE_A; f <= MaxFile; ++f) @@ -1201,7 +1206,7 @@ WDLScore search(Position& pos, ProbeState* result) { moveCount++; - pos.do_move(move, st, pos.gives_check(move)); + pos.do_move(move, st); value = -search(pos, result); pos.undo_move(move); @@ -1285,8 +1290,9 @@ void Tablebases::init(const std::string& paths) { for (int idx = 0; idx < 10; idx++) for (Square s1 = SQ_A1; s1 <= SQ_D4; ++s1) if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0 + { for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - if ((StepAttacksBB[KING][s1] | s1) & s2) + if ((PseudoAttacks[KING][s1] | s1) & s2) continue; // Illegal position else if (!off_A1H8(s1) && off_A1H8(s2) > 0) @@ -1297,6 +1303,7 @@ void Tablebases::init(const std::string& paths) { else MapKK[idx][s2] = code++; + } // Legal positions with both kings on diagonal are encoded as last ones for (auto p : bothOnDiagonal) @@ -1436,7 +1443,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { return 0; if (*result != CHANGE_STM) - return (dtz + 100 * (wdl == WDLCursedLoss || wdl == WDLCursedWin)) * sign_of(wdl); + return (dtz + 100 * (wdl == WDLBlessedLoss || wdl == WDLCursedWin)) * sign_of(wdl); // DTZ stores results for the other side, so we need to do a 1-ply search and // find the winning move that minimizes DTZ. @@ -1447,7 +1454,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { { bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN; - pos.do_move(move, st, pos.gives_check(move)); + pos.do_move(move, st); // For zeroing moves we want the dtz of the move _before_ doing it, // otherwise we will get the dtz of the next move sequence. Search the @@ -1521,7 +1528,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& // Probe each move for (size_t i = 0; i < rootMoves.size(); ++i) { Move move = rootMoves[i].pv[0]; - pos.do_move(move, st, pos.gives_check(move)); + pos.do_move(move, st); int v = 0; if (pos.checkers() && dtz > 0) { @@ -1559,12 +1566,12 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& // Use 50-move counter to determine whether the root position is // won, lost or drawn. - int wdl = 0; + WDLScore wdl = WDLDraw; if (dtz > 0) - wdl = (dtz + cnt50 <= 100) ? 2 : 1; + wdl = (dtz + cnt50 <= 100) ? WDLWin : WDLCursedWin; else if (dtz < 0) - wdl = (-dtz + cnt50 <= 100) ? -2 : -1; + wdl = (-dtz + cnt50 <= 100) ? WDLLoss : WDLBlessedLoss; // Determine the score to report to the user. score = WDL_to_value[wdl + 2]; @@ -1572,9 +1579,9 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& // If the position is winning or losing, but too few moves left, adjust the // score to show how close it is to winning or losing. // NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci(). - if (wdl == 1 && dtz <= 100) + if (wdl == WDLCursedWin && dtz <= 100) score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200); - else if (wdl == -1 && dtz >= -100) + else if (wdl == WDLBlessedLoss && dtz >= -100) score = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200); // Now be a bit smart about filtering out moves. @@ -1657,7 +1664,7 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Val // Probe each move for (size_t i = 0; i < rootMoves.size(); ++i) { Move move = rootMoves[i].pv[0]; - pos.do_move(move, st, pos.gives_check(move)); + pos.do_move(move, st); WDLScore v = -Tablebases::probe_wdl(pos, &result); pos.undo_move(move);