From: Steinar H. Gunderson Date: Fri, 12 Apr 2019 20:33:19 +0000 (+0200) Subject: Merge branch 'master' of /srv/git.sesse.net/www/stockfish X-Git-Url: https://git.sesse.net/?p=stockfish;a=commitdiff_plain;h=9eba3583586893cb01501378eb0906d11ebce8c9;hp=8c0a2733924f59316003c576ee2064ded490678b Merge branch 'master' of /srv/git.sesse.net/www/stockfish --- diff --git a/.travis.yml b/.travis.yml index ded29e47..ea5bda1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: cpp sudo: required -dist: trusty +dist: xenial matrix: include: @@ -9,21 +9,21 @@ matrix: addons: apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-7', 'g++-7-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl'] + packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl'] env: - - COMPILER=g++-7 + - COMPILER=g++-8 - COMP=gcc - os: linux compiler: clang addons: apt: - sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-5.0'] - packages: ['clang-5.0', 'llvm-5.0-dev', 'g++-multilib', 'valgrind', 'expect', 'curl'] + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0'] + packages: ['clang-6.0', 'llvm-6.0-dev', 'g++-multilib', 'valgrind', 'expect', 'curl'] env: - - COMPILER=clang++-5.0 + - COMPILER=clang++-6.0 - COMP=clang - - LDFLAGS=-fuse-ld=gold + - LDFLAGS=-fuse-ld=lld - os: osx compiler: gcc @@ -34,7 +34,7 @@ matrix: - os: osx compiler: clang env: - - COMPILER=clang++ V='Apple LLVM 6.0' # Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) + - COMPILER=clang++ V='Apple LLVM 9.4.1' # Apple LLVM version 9.1.0 (clang-902.0.39.2) - COMP=clang branches: @@ -69,6 +69,6 @@ script: # # Sanitizer # - # Use g++-7 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc - - if [[ "$COMPILER" == "g++-7" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi - - if [[ "$COMPILER" == "g++-7" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi + # Use g++-8 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc + - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi + - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi diff --git a/AUTHORS b/AUTHORS index 04fc7d0f..603b0fb3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,11 +1,11 @@ -# List of authors for Stockfish, updated just after version 9 +# List of authors for Stockfish, updated for version 10 Tord Romstad (romstad) Marco Costalba (mcostalba) Joona Kiiski (zamar) Gary Linscott (glinscott) -absimaldata +Aditya (absimaldata) Ajith Chandy Jose (ajithcj) Alain Savard (Rocky640) Alexander Kure @@ -20,9 +20,9 @@ Balint Pfliegel Ben Koshy (BKSpurgeon) Bill Henry (VoyagerOne) braich +Bojun Guo (noobpwnftw) Brian Sheppard (SapphireBrand) Bryan Cross (crossbr) -Bujun Guo (noobpwnftw) Chris Cain (ceebo) Dan Schmidt Daniel Dugovic (ddugovic) @@ -31,7 +31,6 @@ David Zar Daylen Yang (daylen) DiscanX Eelco de Groot -ElbertoOne erbsenzaehler Ernesto Gatti Fabian Beuke (madnight) @@ -55,10 +54,12 @@ Hongzhi Cheng Ivan Ivec (IIvec) Jacques B. (Timshel) Jan Ondruš (hxim) +Jared Kish (Kurtbusch) Jarrod Torriero (DU-jdto) Jean-Francois Romang (jromang) Jerry Donald Watson (jerrydonaldwatson) Jonathan Calovski (Mysseno) +Jonathan D. (SFisGOD) Joost VandeVondele (vondele) Jörg Oster (joergoster) Joseph Ellis (jhellis3) @@ -70,10 +71,11 @@ Ken Takusagawa kinderchocolate Kiran Panditrao (Krgp) Kojirion -Leonardo Ljubičić (GM) +Leonardo Ljubičić (ICCF World Champion) Leonid Pechenik (lp--) Linus Arver loco-loco +Lub van den Berg (ElbertoOne) Luca Brivio (lucabrivio) Lucas Braesch (lucasart) Lyudmil Antonov (lantonov) @@ -86,7 +88,7 @@ Michael Chaly (Vizvezdenec) Michel Van den Bergh (vdbergh) Miguel Lahoz (miguel-l) Mikael Bäckman (mbootsector) -Mike Whiteley (protonspring) +Michael Whiteley (protonspring) Miroslav Fontán (Hexik) Moez Jellouli (MJZ1977) Mohammed Li (tthsqe12) @@ -111,6 +113,7 @@ Ron Britvich (Britvich) Ronald de Man (syzygy1) Ryan Schmitt Ryan Takker +Sebastian Buchwald (UniQP) Sergei Antonov (saproj) sf-x shane31 diff --git a/appveyor.yml b/appveyor.yml index c81869b5..21f3bbe3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ branches: - appveyor # Operating system (build VM template) -os: Visual Studio 2015 +os: Visual Studio 2017 # Build platform, i.e. x86, x64, AnyCPU. This setting is optional. platform: @@ -51,7 +51,7 @@ before_build: $b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1 $bench = $b -match '\D+(\d+)' | % { $matches[1] } Write-Host "Reference bench:" $bench - $g = "Visual Studio 14 2015" + $g = "Visual Studio 15 2017" If (${env:PLATFORM} -eq 'x64') { $g = $g + ' Win64' } cmake -G "${g}" . Write-Host "Generated files for: " $g diff --git a/src/Makefile b/src/Makefile index c314e7d8..1314092e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -551,7 +551,8 @@ GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)` $(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $< #LDFLAGS += -Wl,-Bstatic -Wl,-\( -lprotobuf -lgrpc++_unsecure -lgrpc_unsecure -lgrpc -lz -Wl,-\) -Wl,-Bdynamic -ldl -LDFLAGS += /usr/lib/x86_64-linux-gnu/libprotobuf.a /usr/lib/libgrpc++_unsecure.a /usr/lib/libgrpc_unsecure.a /usr/lib/libgrpc.a /usr/lib/x86_64-linux-gnu/libcares.a -ldl -lz +LDFLAGS += /usr/lib/x86_64-linux-gnu/libprotobuf.a /usr/lib/x86_64-linux-gnu/libgrpc++_unsecure.a /usr/lib/x86_64-linux-gnu/libgrpc_unsecure.a /usr/lib/x86_64-linux-gnu/libgrpc.a /usr/lib/x86_64-linux-gnu/libcares.a -ldl -lz +#LDFLAGS += /usr/lib/x86_64-linux-gnu/libprotobuf.a /usr/lib/libgrpc++_unsecure.a /usr/lib/libgrpc_unsecure.a /usr/lib/libgrpc.a /usr/lib/x86_64-linux-gnu/libcares.a -ldl -lz client: $(CLIOBJS) $(CXX) -o $@ $(CLIOBJS) $(LDFLAGS) diff --git a/src/bitboard.h b/src/bitboard.h index 559c70f6..f8440a23 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -135,6 +135,10 @@ constexpr bool more_than_one(Bitboard b) { return b & (b - 1); } +inline bool opposite_colors(Square s1, Square s2) { + return bool(DarkSquares & s1) != bool(DarkSquares & s2); +} + /// rank_bb() and file_bb() return a bitboard representing all the squares on /// the given file or rank. @@ -177,6 +181,16 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) { } +/// double_pawn_attacks_bb() returns the pawn attacks for the given color +/// from the squares in the given bitboard. + +template +constexpr Bitboard double_pawn_attacks_bb(Bitboard b) { + return C == WHITE ? shift(b) & shift(b) + : shift(b) & shift(b); +} + + /// adjacent_files_bb() returns a bitboard representing all the squares on the /// adjacent files of the given one. diff --git a/src/endgame.cpp b/src/endgame.cpp index 55a4caba..66ee54d8 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -45,14 +45,14 @@ namespace { // Table used to drive the king towards a corner square of the // right color in KBN vs K endgames. constexpr int PushToCorners[SQUARE_NB] = { - 200, 190, 180, 170, 160, 150, 140, 130, - 190, 180, 170, 160, 150, 140, 130, 140, - 180, 170, 155, 140, 140, 125, 140, 150, - 170, 160, 140, 120, 110, 140, 150, 160, - 160, 150, 140, 110, 120, 140, 160, 170, - 150, 140, 125, 140, 140, 155, 170, 180, - 140, 130, 140, 150, 160, 170, 180, 190, - 130, 140, 150, 160, 170, 180, 190, 200 + 6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160, + 6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480, + 5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800, + 5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120, + 5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440, + 4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760, + 4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080, + 4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400 }; // Tables used to drive a piece towards or away from another piece @@ -120,7 +120,7 @@ Value Endgame::operator()(const Position& pos) const { /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the -/// defending king towards a corner square of the right color. +/// defending king towards a corner square that our bishop attacks. template<> Value Endgame::operator()(const Position& pos) const { @@ -131,19 +131,14 @@ Value Endgame::operator()(const Position& pos) const { Square loserKSq = pos.square(weakSide); Square bishopSq = pos.square(strongSide); - // kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a - // bishop that cannot reach the above squares, we flip the kings in order - // to drive the enemy toward corners A8 or H1. - if (opposite_colors(bishopSq, SQ_A1)) - { - winnerKSq = ~winnerKSq; - loserKSq = ~loserKSq; - } + // If our Bishop does not attack A1/H8, we flip the enemy king square + // to drive to opposite corners (A8/H1). Value result = VALUE_KNOWN_WIN + PushClose[distance(winnerKSq, loserKSq)] - + PushToCorners[loserKSq]; + + PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq]; + assert(abs(result) < VALUE_MATE_IN_MAX_PLY); return strongSide == pos.side_to_move() ? result : -result; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index aeaa4336..333d04ac 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -152,26 +152,25 @@ namespace { }; // Assorted bonuses and penalties - constexpr Score BishopPawns = S( 3, 8); - constexpr Score CloseEnemies = S( 7, 0); + constexpr Score BishopPawns = S( 3, 7); + constexpr Score CloseEnemies = S( 8, 0); constexpr Score CorneredBishop = S( 50, 50); - constexpr Score Hanging = S( 62, 34); - constexpr Score KingProtector = S( 6, 7); - constexpr Score KnightOnQueen = S( 20, 12); - constexpr Score LongDiagonalBishop = S( 44, 0); - constexpr Score MinorBehindPawn = S( 16, 0); - constexpr Score Overload = S( 12, 6); - constexpr Score PawnlessFlank = S( 18, 94); - constexpr Score RestrictedPiece = S( 7, 6); - constexpr Score RookOnPawn = S( 10, 28); - constexpr Score SliderOnQueen = S( 49, 21); - constexpr Score ThreatByKing = S( 21, 84); - constexpr Score ThreatByPawnPush = S( 48, 42); - constexpr Score ThreatByRank = S( 14, 3); - constexpr Score ThreatBySafePawn = S(169, 99); - constexpr Score TrappedRook = S( 98, 5); - constexpr Score WeakQueen = S( 51, 10); - constexpr Score WeakUnopposedPawn = S( 14, 20); + constexpr Score Hanging = S( 69, 36); + constexpr Score KingProtector = S( 7, 8); + constexpr Score KnightOnQueen = S( 16, 12); + constexpr Score LongDiagonalBishop = S( 45, 0); + constexpr Score MinorBehindPawn = S( 18, 3); + constexpr Score PawnlessFlank = S( 17, 95); + constexpr Score RestrictedPiece = S( 7, 7); + constexpr Score RookOnPawn = S( 10, 32); + constexpr Score SliderOnQueen = S( 59, 18); + constexpr Score ThreatByKing = S( 24, 89); + constexpr Score ThreatByPawnPush = S( 48, 39); + constexpr Score ThreatByRank = S( 13, 0); + constexpr Score ThreatBySafePawn = S(173, 94); + constexpr Score TrappedRook = S( 96, 4); + constexpr Score WeakQueen = S( 49, 15); + constexpr Score WeakUnopposedPawn = S( 12, 23); #undef S @@ -275,6 +274,7 @@ namespace { kingRing[Us] |= shift(kingRing[Us]); kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); + kingRing[Us] &= ~double_pawn_attacks_bb(pos.pieces(Us, PAWN)); kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; } } @@ -382,7 +382,7 @@ namespace { { File kf = file_of(pos.square(Us)); if ((kf < FILE_E) == (file_of(s) < kf)) - score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us)); + score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.castling_rights(Us)); } } @@ -513,7 +513,7 @@ namespace { Score score = SCORE_ZERO; // Non-pawn enemies - nonPawnEnemies = pos.pieces(Them) ^ pos.pieces(Them, PAWN); + nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(Them, PAWN); // Squares strongly protected by the enemy, either because they defend the // square with a pawn, or because they defend the square twice and we don't. @@ -553,16 +553,14 @@ namespace { if (weak & attackedBy[Us][KING]) score += ThreatByKing; - score += Hanging * popcount(weak & ~attackedBy[Them][ALL_PIECES]); - - b = weak & nonPawnEnemies & attackedBy[Them][ALL_PIECES]; - score += Overload * popcount(b); + b = ~attackedBy[Them][ALL_PIECES] + | (nonPawnEnemies & attackedBy2[Us]); + score += Hanging * popcount(weak & b); } // Bonus for restricting their piece moves restricted = attackedBy[Them][ALL_PIECES] - & ~attackedBy[Them][PAWN] - & ~attackedBy2[Them] + & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; score += RestrictedPiece * popcount(restricted); diff --git a/src/evaluate.h b/src/evaluate.h index d54f4ab9..cccdd25d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,7 +29,7 @@ class Position; namespace Eval { -constexpr Value Tempo = Value(20); // Must be visible to search +constexpr Value Tempo = Value(28); // Must be visible to search std::string trace(const Position& pos); diff --git a/src/material.h b/src/material.h index 56de9ad2..b472c3fd 100644 --- a/src/material.h +++ b/src/material.h @@ -58,7 +58,7 @@ struct Entry { Key key; const EndgameBase* evaluationFunction; const EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each - // side (e.g. KPKP, KBPsKs) + // side (e.g. KPKP, KBPsK) int16_t value; uint8_t factor[COLOR_NB]; Phase gamePhase; diff --git a/src/misc.cpp b/src/misc.cpp index 4d2abd5a..bca48876 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -258,7 +258,7 @@ int best_group(size_t idx) { return -1; } - while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength) + while (byteOffset < returnLength) { if (ptr->Relationship == RelationNumaNode) nodes++; @@ -269,6 +269,7 @@ int best_group(size_t idx) { threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; } + assert(ptr->Size); byteOffset += ptr->Size; ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size); } diff --git a/src/movegen.cpp b/src/movegen.cpp index 833cd2fc..76a27d9f 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -29,7 +29,7 @@ namespace { ExtMove* generate_castling(const Position& pos, ExtMove* moveList) { constexpr CastlingRight Cr = Us | Cs; - constexpr bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO); + constexpr bool KingSide = (Cs == KING_SIDE); if (pos.castling_impeded(Cr) || !pos.can_castle(Cr)) return moveList; @@ -278,7 +278,7 @@ namespace { *moveList++ = make_move(ksq, pop_lsb(&b)); } - if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us)) + if (Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us)) { if (pos.is_chess960()) { diff --git a/src/pawns.cpp b/src/pawns.cpp index 7d39a4a0..edd670b8 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -67,7 +67,7 @@ namespace { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); - Bitboard b, neighbours, stoppers, doubled, supported, phalanx; + Bitboard b, neighbours, stoppers, doubled, support, phalanx; Bitboard lever, leverPush; Square s; bool opposed, backward; @@ -102,7 +102,7 @@ namespace { doubled = ourPawns & (s - Up); neighbours = ourPawns & adjacent_files_bb(f); phalanx = neighbours & rank_bb(s); - supported = neighbours & rank_bb(s - Up); + support = neighbours & rank_bb(s - Up); // A pawn is backward when it is behind all pawns of the same color // on the adjacent files and cannot be safely advanced. @@ -114,22 +114,22 @@ namespace { // which could become passed after one or two pawn pushes when are // not attacked more times than defended. if ( !(stoppers ^ lever ^ leverPush) - && popcount(supported) >= popcount(lever) - 1 + && popcount(support) >= popcount(lever) - 1 && popcount(phalanx) >= popcount(leverPush)) e->passedPawns[Us] |= s; else if ( stoppers == SquareBB[s + Up] && relative_rank(Us, s) >= RANK_5) { - b = shift(supported) & ~theirPawns; + b = shift(support) & ~theirPawns; while (b) if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)])) e->passedPawns[Us] |= s; } // Score this pawn - if (supported | phalanx) - score += Connected[opposed][bool(phalanx)][popcount(supported)][relative_rank(Us, s)]; + if (support | phalanx) + score += Connected[opposed][bool(phalanx)][popcount(support)][relative_rank(Us, s)]; else if (!neighbours) score -= Isolated, e->weakUnopposed[Us] += !opposed; @@ -137,7 +137,7 @@ namespace { else if (backward) score -= Backward, e->weakUnopposed[Us] += !opposed; - if (doubled && !supported) + if (doubled && !support) score -= Doubled; } @@ -214,10 +214,10 @@ Value Entry::evaluate_shelter(const Position& pos, Square ksq) { for (File f = File(center - 1); f <= File(center + 1); ++f) { b = ourPawns & file_bb(f); - int ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : 0; + Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; b = theirPawns & file_bb(f); - int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; + Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; int d = std::min(f, ~f); safety += ShelterStrength[d][ourRank]; @@ -237,7 +237,7 @@ Score Entry::do_king_safety(const Position& pos) { Square ksq = pos.square(Us); kingSquares[Us] = ksq; - castlingRights[Us] = pos.can_castle(Us); + castlingRights[Us] = pos.castling_rights(Us); int minKingPawnDistance = 0; Bitboard pawns = pos.pieces(Us, PAWN); diff --git a/src/pawns.h b/src/pawns.h index 4d8b2936..df220eab 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -51,7 +51,7 @@ struct Entry { template Score king_safety(const Position& pos) { - return kingSquares[Us] == pos.square(Us) && castlingRights[Us] == pos.can_castle(Us) + return kingSquares[Us] == pos.square(Us) && castlingRights[Us] == pos.castling_rights(Us) ? kingSafety[Us] : (kingSafety[Us] = do_king_safety(pos)); } diff --git a/src/position.cpp b/src/position.cpp index cf172126..d3afddeb 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -474,7 +474,7 @@ const string Position::fen() const { if (can_castle(BLACK_OOO)) ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q'); - if (!can_castle(WHITE) && !can_castle(BLACK)) + if (!can_castle(ANY_CASTLING)) ss << '-'; ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") diff --git a/src/position.h b/src/position.h index e1eacf26..d94ef185 100644 --- a/src/position.h +++ b/src/position.h @@ -97,8 +97,8 @@ public: template Square square(Color c) const; // Castling - int can_castle(Color c) const; - int can_castle(CastlingRight cr) const; + int castling_rights(Color c) const; + bool can_castle(CastlingRight cr) const; bool castling_impeded(CastlingRight cr) const; Square castling_rook_square(CastlingRight cr) const; @@ -260,11 +260,11 @@ inline Square Position::ep_square() const { return st->epSquare; } -inline int Position::can_castle(CastlingRight cr) const { +inline bool Position::can_castle(CastlingRight cr) const { return st->castlingRights & cr; } -inline int Position::can_castle(Color c) const { +inline int Position::castling_rights(Color c) const { return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); } diff --git a/src/psqt.cpp b/src/psqt.cpp index 4703da35..a4a5e0a0 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -37,15 +37,7 @@ namespace PSQT { // second half of the files. constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { }, - { // Pawn - { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, - { S(-11,-3), S( 7, -1), S( 7, 7), S(17, 2) }, - { S(-16,-2), S( -3, 2), S( 23, 6), S(23,-1) }, - { S(-14, 7), S( -7, -4), S( 20,-8), S(24, 2) }, - { S( -5,13), S( -2, 10), S( -1,-1), S(12,-8) }, - { S(-11,16), S(-12, 6), S( -2, 1), S( 4,16) }, - { S( -2, 1), S( 20,-12), S(-10, 6), S(-2,25) } - }, + { }, { // Knight { S(-169,-105), S(-96,-74), S(-80,-46), S(-79,-18) }, { S( -79, -70), S(-39,-56), S(-24,-15), S( -9, 6) }, @@ -57,24 +49,24 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { S(-200, -98), S(-80,-89), S(-53,-53), S(-32,-16) } }, { // Bishop - { S(-49,-58), S(- 7,-31), S(-10,-37), S(-34,-19) }, - { S(-24,-34), S( 9, -9), S( 15,-14), S( 1, 4) }, - { S( -9,-23), S( 22, 0), S( -3, -3), S( 12, 16) }, - { S( 4,-26), S( 9, -3), S( 18, -5), S( 40, 16) }, - { S( -8,-26), S( 27, -4), S( 13, -7), S( 30, 14) }, - { S(-17,-24), S( 14, -2), S( -6, 0), S( 6, 13) }, - { S(-19,-34), S(-13,-10), S( 7,-12), S(-11, 6) }, - { S(-47,-55), S( -7,-32), S(-17,-36), S(-29,-17) } + { S(-44,-63), S( -4,-30), S(-11,-35), S(-28, -8) }, + { S(-18,-38), S( 7,-13), S( 14,-14), S( 3, 0) }, + { S( -8,-18), S( 24, 0), S( -3, -7), S( 15, 13) }, + { S( 1,-26), S( 8, -3), S( 26, 1), S( 37, 16) }, + { S( -7,-24), S( 30, -6), S( 23,-10), S( 28, 17) }, + { S(-17,-26), S( 4, 2), S( -1, 1), S( 8, 16) }, + { S(-21,-34), S(-19,-18), S( 10, -7), S( -6, 9) }, + { S(-48,-51), S( -3,-40), S(-12,-39), S(-25,-20) } }, { // Rook - { S(-24, 0), S(-15, 3), S( -8, 0), S( 0, 3) }, - { S(-18,-7), S( -5,-5), S( -1,-5), S( 1,-1) }, - { S(-19, 6), S(-10,-7), S( 1, 3), S( 0, 3) }, - { S(-21, 0), S( -7, 4), S( -4,-2), S(-4, 1) }, - { S(-21,-7), S(-12, 5), S( -1,-5), S( 4,-7) }, - { S(-23, 3), S(-10, 2), S( 1,-1), S( 6, 3) }, - { S(-11,-1), S( 8, 7), S( 9,11), S(12,-1) }, - { S(-25, 6), S(-18, 4), S(-11, 6), S( 2, 2) } + { S(-24, -2), S(-13,-6), S( -7, -3), S( 2,-2) }, + { S(-18,-10), S(-10,-7), S( -5, 1), S( 9, 0) }, + { S(-21, 10), S( -7,-4), S( 3, 2), S(-1,-2) }, + { S(-13, -5), S( -5, 2), S( -4, -8), S(-6, 8) }, + { S(-24, -8), S(-12, 5), S( -1, 4), S( 6,-9) }, + { S(-24, 3), S( -4,-2), S( 4,-10), S(10, 7) }, + { S( -8, 1), S( 6, 2), S( 10, 17), S(12,-8) }, + { S(-22, 12), S(-24,-6), S( -6, 13), S( 4, 7) } }, { // Queen { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, @@ -98,6 +90,17 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { } }; +constexpr Score PBonus[RANK_NB][FILE_NB] = + { // Pawn + { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }, + { S( 0,-11), S( -3, -4), S( 13, -1), S( 19, -4), S( 16, 17), S( 13, 7), S( 4, 4), S( -4,-13) }, + { S(-16, -8), S(-12, -6), S( 20, -3), S( 21, 0), S( 25,-11), S( 29, 3), S( 0, 0), S(-27, -1) }, + { S(-11, 3), S(-17, 6), S( 11,-10), S( 21, 1), S( 32, -6), S( 19,-11), S( -5, 0), S(-14, -2) }, + { S( 4, 13), S( 6, 7), S( -8, 3), S( 3, -5), S( 8,-15), S( -2, -1), S(-19, 9), S( -5, 13) }, + { S( -5, 25), S(-19, 20), S( 7, 16), S( 8, 12), S( -7, 21), S( -2, 3), S(-10, -4), S(-16, 15) }, + { S(-10, 6), S( 9, -5), S( -7, 16), S(-12, 27), S( -7, 15), S( -8, 11), S( 16, -7), S( -8, 4) } + }; + #undef S Score psq[PIECE_NB][SQUARE_NB]; @@ -117,7 +120,8 @@ void init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) { File f = std::min(file_of(s), ~file_of(s)); - psq[ pc][ s] = score + Bonus[pc][rank_of(s)][f]; + psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] + : Bonus[pc][rank_of(s)][f]); psq[~pc][~s] = -psq[pc][s]; } } diff --git a/src/search.cpp b/src/search.cpp index f4e1da97..4a541c5b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -254,7 +254,7 @@ void MainThread::search() { && !Skill(Options["Skill Level"]).enabled() && rootMoves[0].pv[0] != MOVE_NONE) { - std::map votes; + std::map votes; Value minScore = this->rootMoves[0].score; // Find out minimum score and reset votes for moves which can be voted @@ -265,12 +265,13 @@ void MainThread::search() { } // Vote according to score and depth + auto square = [](int64_t x) { return x * x; }; for (Thread* th : Threads) - votes[th->rootMoves[0].pv[0]] += int(th->rootMoves[0].score - minScore) - + int(th->completedDepth); + votes[th->rootMoves[0].pv[0]] += 200 + (square(th->rootMoves[0].score - minScore + 1) + * int64_t(th->completedDepth)); // Select best thread - int bestVote = votes[this->rootMoves[0].pv[0]]; + int64_t bestVote = votes[this->rootMoves[0].pv[0]]; for (Thread* th : Threads) { if (votes[th->rootMoves[0].pv[0]] > bestVote) @@ -302,7 +303,12 @@ void MainThread::search() { void Thread::search() { - Stack stack[MAX_PLY+7], *ss = stack+4; // To reference from (ss-4) to (ss+2) + // To allow access to (ss-5) up to (ss+2), the stack must be oversized. + // The former is needed to allow update_continuation_histories(ss-1, ...), + // which accesses its argument at ss-4, also near the root. + // The latter is needed for statScores and killer initialization. + Stack stack[MAX_PLY+8], *ss = stack+5; + Move pv[MAX_PLY+1]; Value bestValue, alpha, beta, delta; Move lastBestMove = MOVE_NONE; Depth lastBestMoveDepth = DEPTH_ZERO; @@ -311,9 +317,10 @@ void Thread::search() { Color us = rootPos.side_to_move(); bool failedLow; - std::memset(ss-4, 0, 7 * sizeof(Stack)); - for (int i = 4; i > 0; i--) + std::memset(ss-5, 0, 8 * sizeof(Stack)); + for (int i = 5; i > 0; i--) (ss-i)->continuationHistory = &this->continuationHistory[NO_PIECE][0]; // Use as sentinel + ss->pv = pv; bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; @@ -448,7 +455,7 @@ void Thread::search() { { beta = std::min(bestValue + delta, VALUE_INFINITE); if (mainThread) - ++failedHighCnt; + ++failedHighCnt; } else break; @@ -492,10 +499,8 @@ void Thread::search() { && !Threads.stop && !Threads.stopOnPonderhit) { - const int F[] = { failedLow, - bestValue - mainThread->previousScore }; - - int improvingFactor = std::max(246, std::min(832, 306 + 119 * F[0] - 6 * F[1])); + double fallingEval = (306 + 119 * failedLow + 6 * (mainThread->previousScore - bestValue)) / 581.0; + fallingEval = std::max(0.5, std::min(1.5, fallingEval)); // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = 1.0; @@ -509,7 +514,7 @@ void Thread::search() { // Stop the search if we have only one legal move, or if available time elapsed if ( rootMoves.size() == 1 - || Time.elapsed() > Time.optimum() * bestMoveInstability * improvingFactor / 581) + || Time.elapsed() > Time.optimum() * bestMoveInstability * fallingEval) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". @@ -655,9 +660,10 @@ namespace { if (!pos.capture_or_promotion(ttMove)) update_quiet_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth)); - // Extra penalty for a quiet TT move in previous ply when it gets refuted - if ((ss-1)->moveCount == 1 && !pos.captured_piece()) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); + // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted + if ( ((ss-1)->moveCount == 1 || (ss-1)->currentMove == (ss-1)->killers[0]) + && !pos.captured_piece()) + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); } // Penalty for a quiet ttMove that fails low else if (!pos.capture_or_promotion(ttMove)) @@ -758,15 +764,16 @@ namespace { } // Step 7. Razoring (~2 Elo) - if ( depth < 2 * ONE_PLY - && eval <= alpha - RazorMargin) + if ( !rootNode // The required rootNode PV handling is not available in qsearch + && depth < 2 * ONE_PLY + && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); improving = ss->staticEval >= (ss-2)->staticEval || (ss-2)->staticEval == VALUE_NONE; // Step 8. Futility pruning: child node (~30 Elo) - if ( !rootNode + if ( !PvNode && depth < 7 * ONE_PLY && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins @@ -828,8 +835,8 @@ namespace { && depth >= 5 * ONE_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY) { - Value rbeta = std::min(beta + 216 - 48 * improving, VALUE_INFINITE); - MovePicker mp(pos, ttMove, rbeta - ss->staticEval, &thisThread->captureHistory); + Value raisedBeta = std::min(beta + 216 - 48 * improving, VALUE_INFINITE); + MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory); int probCutCount = 0; while ( (move = mp.next_move()) != MOVE_NONE @@ -846,15 +853,15 @@ namespace { pos.do_move(move, st); // Perform a preliminary qsearch to verify that the move holds - value = -qsearch(pos, ss+1, -rbeta, -rbeta+1); + value = -qsearch(pos, ss+1, -raisedBeta, -raisedBeta+1); // If the qsearch held perform the regular search - if (value >= rbeta) - value = -search(pos, ss+1, -rbeta, -rbeta+1, depth - 4 * ONE_PLY, !cutNode); + if (value >= raisedBeta) + value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4 * ONE_PLY, !cutNode); pos.undo_move(move); - if (value >= rbeta) + if (value >= raisedBeta) return value; } } @@ -930,26 +937,26 @@ moves_loop: // When in check, search starts from here if ( depth >= 8 * ONE_PLY && move == ttMove && !rootNode - && !excludedMove // Recursive singular search is not allowed + && !excludedMove // Avoid recursive singular search && ttValue != VALUE_NONE && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3 * ONE_PLY && pos.legal(move)) { - Value rBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE); + Value reducedBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE); ss->excludedMove = move; - value = search(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode); + value = search(pos, ss, reducedBeta - 1, reducedBeta, depth / 2, cutNode); ss->excludedMove = MOVE_NONE; - if (value < rBeta) + if (value < reducedBeta) extension = ONE_PLY; } else if ( givesCheck // Check extension (~2 Elo) && pos.see_ge(move)) extension = ONE_PLY; - else if ( pos.can_castle(us) // Extension for king moves that change castling rights - && type_of(movedPiece) == KING) + // Extension if castling + else if (type_of(move) == CASTLING) extension = ONE_PLY; // Calculate new depth for this move @@ -962,7 +969,7 @@ moves_loop: // When in check, search starts from here { if ( !captureOrPromotion && !givesCheck - && (!pos.advanced_pawn_push(move) || pos.non_pawn_material() >= Value(5000))) + && !pos.advanced_pawn_push(move)) { // Move count based pruning (~30 Elo) if (moveCountPruning) @@ -975,7 +982,7 @@ moves_loop: // When in check, search starts from here int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; // Countermoves based pruning (~20 Elo) - if ( lmrDepth < 3 + ((ss-1)->statScore > 0) + if ( lmrDepth < 3 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) continue; @@ -1187,14 +1194,15 @@ moves_loop: // When in check, search starts from here update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth + ONE_PLY)); - // Extra penalty for a quiet TT move in previous ply when it gets refuted - if ((ss-1)->moveCount == 1 && !pos.captured_piece()) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); + // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted + if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0])) + && !pos.captured_piece()) + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); + } // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 * ONE_PLY || PvNode) - && !pos.captured_piece() - && is_ok((ss-1)->currentMove)) + && !pos.captured_piece()) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth)); if (PvNode) @@ -1395,21 +1403,15 @@ moves_loop: // When in check, search starts from here if (value > alpha) { + bestMove = move; + if (PvNode) // Update pv even in fail-high case update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha here! - { alpha = value; - bestMove = move; - } - else // Fail high - { - tte->save(posKey, value_to_tt(value, ss->ply), BOUND_LOWER, - ttDepth, move, ss->staticEval); - - return value; - } + else + break; // Fail high } } } @@ -1420,7 +1422,8 @@ moves_loop: // When in check, search starts from here return mated_in(ss->ply); // Plies to mate from the root tte->save(posKey, value_to_tt(bestValue, ss->ply), - PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, + bestValue >= beta ? BOUND_LOWER : + PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, ttDepth, bestMove, ss->staticEval); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 03cffca1..94d73714 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -224,8 +224,9 @@ public: exit(1); } #else + // Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored. HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr); if (fd == INVALID_HANDLE_VALUE) return *baseAddress = nullptr, nullptr; @@ -1107,10 +1108,10 @@ void* mapped(TBTable& e, const Position& pos) { static Mutex mutex; - // Use 'aquire' to avoid a thread reads 'ready' == true while another is - // still working, this could happen due to compiler reordering. + // Use 'acquire' to avoid a thread reading 'ready' == true while + // another is still working. (compiler reordering may cause this). if (e.ready.load(std::memory_order_acquire)) - return e.baseAddress; // Could be nullptr if file does not exsist + return e.baseAddress; // Could be nullptr if file does not exist std::unique_lock lk(mutex); @@ -1277,7 +1278,7 @@ void Tablebases::init(const std::string& paths) { continue; // First on diagonal, second above else if (!off_A1H8(s1) && !off_A1H8(s2)) - bothOnDiagonal.push_back(std::make_pair(idx, s2)); + bothOnDiagonal.emplace_back(idx, s2); else MapKK[idx][s2] = code++; diff --git a/src/thread.cpp b/src/thread.cpp index 5e990fdc..f88e359b 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -118,7 +118,7 @@ void Thread::idle_loop() { } /// ThreadPool::set() creates/destroys threads to match the requested number. -/// Created and launched threads will go immediately to sleep in idle_loop. +/// Created and launched threads will immediately go to sleep in idle_loop. /// Upon resizing, threads are recreated to allow for binding if necessary. void ThreadPool::set(size_t requested) { @@ -136,10 +136,10 @@ void ThreadPool::set(size_t requested) { while (size() < requested) push_back(new Thread(size())); clear(); - } - // Reallocate the hash with the new threadpool size - TT.resize(Options["Hash"]); + // Reallocate the hash with the new threadpool size + TT.resize(Options["Hash"]); + } } /// ThreadPool::clear() sets threadPool data to initial values. diff --git a/src/tt.cpp b/src/tt.cpp index 53e78595..653d081f 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -24,6 +24,7 @@ #include "bitboard.h" #include "misc.h" +#include "thread.h" #include "tt.h" #include "uci.h" @@ -58,6 +59,8 @@ void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) { void TranspositionTable::resize(size_t mbSize) { + Threads.main()->wait_for_search_finished(); + clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); free(mem); @@ -82,9 +85,9 @@ void TranspositionTable::clear() { std::vector threads; - for (size_t idx = 0; idx < Options["Threads"]; idx++) + for (size_t idx = 0; idx < Options["Threads"]; ++idx) { - threads.push_back(std::thread([this, idx]() { + threads.emplace_back([this, idx]() { // Thread binding gives faster search on systems with a first-touch policy if (Options["Threads"] > 8) @@ -97,7 +100,7 @@ void TranspositionTable::clear() { stride : clusterCount - start; std::memset(&table[start], 0, len * sizeof(Cluster)); - })); + }); } for (std::thread& th: threads) @@ -145,12 +148,9 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { int TranspositionTable::hashfull() const { int cnt = 0; - for (int i = 0; i < 1000 / ClusterSize; i++) - { - const TTEntry* tte = &table[i].entry[0]; - for (int j = 0; j < ClusterSize; j++) - if ((tte[j].genBound8 & 0xFC) == generation8) - cnt++; - } - return cnt; + for (int i = 0; i < 1000 / ClusterSize; ++i) + for (int j = 0; j < ClusterSize; ++j) + cnt += (table[i].entry[j].genBound8 & 0xFC) == generation8; + + return cnt * 1000 / (ClusterSize * (1000 / ClusterSize)); } diff --git a/src/types.h b/src/types.h index 3b5afecf..c4c2752c 100644 --- a/src/types.h +++ b/src/types.h @@ -413,11 +413,6 @@ constexpr Rank relative_rank(Color c, Square s) { return relative_rank(c, rank_of(s)); } -inline bool opposite_colors(Square s1, Square s2) { - int s = int(s1) ^ int(s2); - return ((s >> 3) ^ s) & 1; -} - constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; }