From 902309020a8ebf97a649cacfdc2dc2881b630966 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Wed, 3 Jun 2020 11:05:58 +0100 Subject: [PATCH 01/34] join scale_factor, initiative and mg+eg reduction Merging this code into one function `winnable()`. Should allow common concepts used to adjust the eg value, either by addition or scaling, to be combined more effectively. Improve trace function. closes https://github.com/official-stockfish/Stockfish/pull/2710 No functional change. --- src/evaluate.cpp | 54 ++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index dc7134a8..12a4c7bf 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -35,7 +35,7 @@ namespace Trace { enum Tracing { NO_TRACE, TRACE }; enum Term { // The first 8 entries are reserved for PieceType - MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB + MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, WINNABLE, TOTAL, TERM_NB }; Score scores[TERM_NB][COLOR_NB]; @@ -59,7 +59,7 @@ namespace Trace { std::ostream& operator<<(std::ostream& os, Term t) { - if (t == MATERIAL || t == IMBALANCE || t == INITIATIVE || t == TOTAL) + if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL) os << " ---- ----" << " | " << " ---- ----"; else os << scores[t][WHITE] << " | " << scores[t][BLACK]; @@ -173,8 +173,7 @@ namespace { template Score threats() const; template Score passed() const; template Score space() const; - ScaleFactor scale_factor(Value eg) const; - Score initiative(Score score) const; + Value winnable(Score score) const; const Position& pos; Material::Entry* me; @@ -717,12 +716,12 @@ namespace { } - // Evaluation::initiative() computes the initiative correction value - // for the position. It is a second order bonus/malus based on the + // Evaluation::winnable() adjusts the mg and eg score components based on the // known attacking/defending status of the players. + // A single value is derived from the mg and eg values and returned. template - Score Evaluation::initiative(Score score) const { + Value Evaluation::winnable(Score score) const { int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); @@ -756,17 +755,10 @@ namespace { int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); - if (T) - Trace::add(INITIATIVE, make_score(u, v)); + mg += u; + eg += v; - return make_score(u, v); - } - - - // Evaluation::scale_factor() computes the scale factor for the winning side - - template - ScaleFactor Evaluation::scale_factor(Value eg) const { + // Compute the scale factor for the winning side Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; int sf = me->scale_factor(pos, strongSide); @@ -786,7 +778,18 @@ namespace { sf = std::min(sf, 36 + 7 * pos.count(strongSide)); } - return ScaleFactor(sf); + // Interpolate between the middlegame and (scaled by 'sf') endgame score + v = mg * int(me->game_phase()) + + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL; + v /= PHASE_MIDGAME; + + if (T) + { + Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score))); + Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL)); + } + + return Value(v); } @@ -841,14 +844,8 @@ namespace { + passed< WHITE>() - passed< BLACK>() + space< WHITE>() - space< BLACK>(); - score += initiative(score); - - // Interpolate between a middlegame and a (scaled by 'sf') endgame score - ScaleFactor sf = scale_factor(eg_value(score)); - v = mg_value(score) * int(me->game_phase()) - + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL; - - v /= PHASE_MIDGAME; + // Derive single value from mg and eg parts of score + v = winnable(score); // In case of tracing add all remaining individual evaluation terms if (T) @@ -857,7 +854,6 @@ namespace { Trace::add(IMBALANCE, me->imbalance()); Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK)); Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]); - Trace::add(TOTAL, score); } // Side to move point of view @@ -909,11 +905,11 @@ std::string Eval::trace(const Position& pos) { << " Threats | " << Term(THREAT) << " Passed | " << Term(PASSED) << " Space | " << Term(SPACE) - << " Initiative | " << Term(INITIATIVE) + << " Winnable | " << Term(WINNABLE) << " ------------+-------------+-------------+------------\n" << " Total | " << Term(TOTAL); - ss << "\nTotal evaluation: " << to_cp(v) << " (white side)\n"; + ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; return ss.str(); } From b0eb5a1ba3d094a1d2236db6f33e0d2164ec3480 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sat, 6 Jun 2020 21:25:32 -0600 Subject: [PATCH 02/34] Wrap all access to LineBB and add assert This is a non-functional code style change which provides a safe access handler for LineBB. Also includes an assert in debug mode to verify square correctness. closes https://github.com/official-stockfish/Stockfish/pull/2719 No functional change --- src/bitboard.h | 20 ++++++++++++++++---- src/evaluate.cpp | 2 +- src/movegen.cpp | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 93f838f8..704f4bb4 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -200,12 +200,24 @@ inline Bitboard adjacent_files_bb(Square s) { return shift(file_bb(s)) | shift(file_bb(s)); } - -/// between_bb() returns squares that are linearly between the given squares +/// line_bb(Square, Square) returns a Bitboard representing an entire line +/// (from board edge to board edge) that intersects the given squares. /// If the given squares are not on a same file/rank/diagonal, return 0. +/// Ex. line_bb(SQ_C4, SQ_F7) returns a bitboard with the A2-G8 diagonal. + +inline Bitboard line_bb(Square s1, Square s2) { + + assert(is_ok(s1) && is_ok(s2)); + return LineBB[s1][s2]; +} + +/// between_bb() returns a Bitboard representing squares that are linearly +/// between the given squares (excluding the given squares). +/// If the given squares are not on a same file/rank/diagonal, return 0. +/// Ex. between_bb(SQ_C4, SQ_F7) returns a bitboard with squares D5 and E6. inline Bitboard between_bb(Square s1, Square s2) { - Bitboard b = LineBB[s1][s2] & ((AllSquares << s1) ^ (AllSquares << s2)); + Bitboard b = line_bb(s1, s2) & ((AllSquares << s1) ^ (AllSquares << s2)); return b & (b - 1); //exclude lsb } @@ -249,7 +261,7 @@ inline Bitboard passed_pawn_span(Color c, Square s) { /// straight or on a diagonal line. inline bool aligned(Square s1, Square s2, Square s3) { - return LineBB[s1][s2] & s3; + return line_bb(s1, s2) & s3; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 12a4c7bf..c042c016 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -276,7 +276,7 @@ namespace { : attacks_bb(s, pos.pieces()); if (pos.blockers_for_king(Us) & s) - b &= LineBB[pos.square(Us)][s]; + b &= line_bb(pos.square(Us), s); attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b; attackedBy[Us][Pt] |= b; diff --git a/src/movegen.cpp b/src/movegen.cpp index b57f41a9..17203a95 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -332,7 +332,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // the king evasions in order to skip known illegal moves, which avoids any // useless legality checks later on. while (sliders) - sliderAttacks |= LineBB[ksq][pop_lsb(&sliders)] & ~pos.checkers(); + sliderAttacks |= line_bb(ksq, pop_lsb(&sliders)) & ~pos.checkers(); // Generate evasions for king, capture and non capture moves Bitboard b = attacks_bb(ksq) & ~pos.pieces(us) & ~sliderAttacks; From 1c65310c0e5ac639a51c3d6b9b114d48aa57bdd8 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 31 May 2020 23:31:14 -0600 Subject: [PATCH 03/34] Refactor some threads related code. This is a code style change that moves some pure thread code into the threads class. It is a bit more code, but it makes search.cpp cleaner and easier to read by hiding some thread specific functionality. STC (SMP) LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 75896 W: 12073 L: 12026 D: 51797 Ptnml(0-2): 828, 8224, 19872, 8121, 903 https://tests.stockfishchess.org/tests/view/5ed492e8f29b40b0fc95a74c closes https://github.com/official-stockfish/Stockfish/pull/2720 No functional change. --- src/search.cpp | 50 ++++++++----------------------------------------- src/thread.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++- src/thread.h | 3 +++ 3 files changed, 61 insertions(+), 43 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index efa7b9c5..e3a5a92e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -236,14 +236,8 @@ void MainThread::search() { } else { - for (Thread* th : Threads) - { - th->bestMoveChanges = 0; - if (th != this) - th->start_searching(); - } - - Thread::search(); // Let's start searching! + Threads.start_searching(); // start non-main threads + Thread::search(); // main thread start searching } // When we reach the maximum depth, we can arrive here without a raise of @@ -260,9 +254,7 @@ void MainThread::search() { Threads.stop = true; // Wait until all threads have finished - for (Thread* th : Threads) - if (th != this) - th->wait_for_search_finished(); + Threads.wait_for_search_finished(); // When playing in 'nodes as time' mode, subtract the searched nodes from // the available ones before exiting. @@ -271,37 +263,11 @@ void MainThread::search() { Thread* bestThread = this; - // Check if there are threads with a better score than main thread - if ( int(Options["MultiPV"]) == 1 - && !Limits.depth - && !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) - && rootMoves[0].pv[0] != MOVE_NONE) - { - std::map votes; - Value minScore = this->rootMoves[0].score; - - // Find minimum score - for (Thread* th: Threads) - minScore = std::min(minScore, th->rootMoves[0].score); - - // Vote according to score and depth, and select the best thread - for (Thread* th : Threads) - { - votes[th->rootMoves[0].pv[0]] += - (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); - - if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) - { - // Make sure we pick the shortest mate / TB conversion or stave off mate the longest - if (th->rootMoves[0].score > bestThread->rootMoves[0].score) - bestThread = th; - } - else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY - || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY - && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) - bestThread = th; - } - } + if (int(Options["MultiPV"]) == 1 && + !Limits.depth && + !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) && + rootMoves[0].pv[0] != MOVE_NONE) + bestThread = Threads.get_best_thread(); bestPreviousScore = bestThread->rootMoves[0].score; diff --git a/src/thread.cpp b/src/thread.cpp index c1713122..a27a60c6 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -208,7 +208,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, for (Thread* th : *this) { - th->nodes = th->tbHits = th->nmpMinPly = 0; + th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; th->rootDepth = th->completedDepth = 0; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); @@ -218,3 +218,52 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, main()->start_searching(); } + +Thread* ThreadPool::get_best_thread() const { + + Thread* bestThread = front(); + std::map votes; + Value minScore = VALUE_NONE; + + // Find minimum score of all threads + for (Thread* th: *this) + minScore = std::min(minScore, th->rootMoves[0].score); + + // Vote according to score and depth, and select the best thread + for (Thread* th : *this) + { + votes[th->rootMoves[0].pv[0]] += + (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); + + if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) + { + // Make sure we pick the shortest mate / TB conversion or stave off mate the longest + if (th->rootMoves[0].score > bestThread->rootMoves[0].score) + bestThread = th; + } + else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY + || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY + && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) + bestThread = th; + } + + return bestThread; +} + +/// Start non-main threads. + +void ThreadPool::start_searching() { + + for (Thread* th : *this) + if (th != front()) + th->start_searching(); +} + +/// Wait for non-main threads. + +void ThreadPool::wait_for_search_finished() const { + + for (Thread* th : *this) + if (th != front()) + th->wait_for_search_finished(); +} diff --git a/src/thread.h b/src/thread.h index 79be197b..a69e1d10 100644 --- a/src/thread.h +++ b/src/thread.h @@ -109,6 +109,9 @@ struct ThreadPool : public std::vector { MainThread* main() const { return static_cast(front()); } uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } + Thread* get_best_thread() const; + void start_searching(); + void wait_for_search_finished() const; std::atomic_bool stop, increaseDepth; From d0cb9b286f4d7415be002855201e75340c8adef0 Mon Sep 17 00:00:00 2001 From: NguyenPham Date: Mon, 8 Jun 2020 07:48:38 +1000 Subject: [PATCH 04/34] show coordinates when displaying board closes https://github.com/official-stockfish/Stockfish/pull/2723 No functional change --- AUTHORS | 1 + src/bitboard.cpp | 3 ++- src/position.cpp | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 36c2a47b..1cd7ff54 100644 --- a/AUTHORS +++ b/AUTHORS @@ -115,6 +115,7 @@ Nick Pelling (nickpelling) Nicklas Persson (NicklasPersson) Niklas Fiekas (niklasf) Nikolay Kostov (NikolayIT) +Nguyen Pham Ondrej Mosnテ。ト稿k (WOnder93) Oskar Werkelin Ahlin Pablo Vazquez diff --git a/src/bitboard.cpp b/src/bitboard.cpp index f650eef6..3bb3ff8f 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -56,8 +56,9 @@ const std::string Bitboards::pretty(Bitboard b) { for (File f = FILE_A; f <= FILE_H; ++f) s += b & make_square(f, r) ? "| X " : "| "; - s += "|\n+---+---+---+---+---+---+---+---+\n"; + s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n"; } + s += " a b c d e f g h\n"; return s; } diff --git a/src/position.cpp b/src/position.cpp index d2e33b30..c9db6224 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -64,10 +64,11 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { for (File f = FILE_A; f <= FILE_H; ++f) os << " | " << PieceToChar[pos.piece_on(make_square(f, r))]; - os << " |\n +---+---+---+---+---+---+---+---+\n"; + os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n"; } - os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase + os << " a b c d e f g h\n" + << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase << std::setfill('0') << std::setw(16) << pos.key() << std::setfill(' ') << std::dec << "\nCheckers: "; From b081e52239e6496070a376452ca04dcc6d1993c5 Mon Sep 17 00:00:00 2001 From: nguyenpham Date: Mon, 8 Jun 2020 08:49:27 +1000 Subject: [PATCH 05/34] Improve Readme.md about compiling Reparagraph, add an example how to compile on Unix-like systems closes https://github.com/official-stockfish/Stockfish/pull/2724 No functional change --- Readme.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Readme.md b/Readme.md index 35ff095d..2b1de86b 100644 --- a/Readme.md +++ b/Readme.md @@ -165,17 +165,23 @@ are in use, see the engine log. ## Compiling Stockfish yourself from the sources -On Unix-like systems, it should be possible to compile Stockfish -directly from the source code with the included Makefile. +Stockfish has support for 32 or 64-bit CPUs, certain hardware +instructions, big-endian machines such as Power PC, and other platforms. -Stockfish has support for 32 or 64-bit CPUs, the hardware POPCNT -instruction, big-endian machines such as Power PC, and other platforms. +On Unix-like systems, it should be easy to compile Stockfish +directly from the source code with the included Makefile in the folder +`src`. In general it is recommended to run `make help` to see a list of make +targets with corresponding descriptions. -In general it is recommended to run `make help` to see a list of make -targets with corresponding descriptions. When not using the Makefile to -compile (for instance with Microsoft MSVC) you need to manually -set/unset some switches in the compiler command line; see file *types.h* -for a quick reference. +``` + cd src + make help + make build ARCH=x86-64-modern +``` + +When not using the Makefile to compile (for instance with Microsoft MSVC) you +need to manually set/unset some switches in the compiler command line; see +file *types.h* for a quick reference. When reporting an issue or a bug, please tell us which version and compiler you used to create your executable. These informations can From 4b10578acbe099482ed40200478df4d775c01af5 Mon Sep 17 00:00:00 2001 From: Sami Kiminki Date: Fri, 5 Jun 2020 20:17:00 +0300 Subject: [PATCH 06/34] Increase the maximum hash size by a factor of 256 Conceptually group hash clusters into super clusters of 256 clusters. This scheme allows us to use hash sizes up to 32 TB (= 2^32 super clusters = 2^40 clusters). Use 48 bits of the Zobrist key to choose the cluster index. We use 8 extra bits to mitigate the quantization error for very large hashes when scaling the hash key to cluster index. The hash index computation is organized to be compatible with the existing scheme for power-of-two hash sizes up to 128 GB. Fixes https://github.com/official-stockfish/Stockfish/issues/1349 closes https://github.com/official-stockfish/Stockfish/pull/2722 Passed non-regression STC: LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 37976 W: 7336 L: 7211 D: 23429 Ptnml(0-2): 578, 4295, 9149, 4356, 610 https://tests.stockfishchess.org/tests/view/5edcbaaef29b40b0fc95abc5 No functional change. --- src/tt.cpp | 8 ++++++-- src/tt.h | 13 ++++++++++--- src/ucioption.cpp | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 0a3c54a1..92aaee00 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -65,8 +65,10 @@ void TranspositionTable::resize(size_t mbSize) { aligned_ttmem_free(mem); - clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); - table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem)); + superClusterCount = mbSize * 1024 * 1024 / (sizeof(Cluster) * ClustersPerSuperCluster); + + table = static_cast( + aligned_ttmem_alloc(superClusterCount * ClustersPerSuperCluster * sizeof(Cluster), mem)); if (!mem) { std::cerr << "Failed to allocate " << mbSize @@ -89,6 +91,8 @@ void TranspositionTable::clear() { { threads.emplace_back([this, idx]() { + const size_t clusterCount = superClusterCount * ClustersPerSuperCluster; + // Thread binding gives faster search on systems with a first-touch policy if (Options["Threads"] > 8) WinProcGroup::bindThisThread(idx); diff --git a/src/tt.h b/src/tt.h index bd723a86..76db03da 100644 --- a/src/tt.h +++ b/src/tt.h @@ -66,6 +66,7 @@ private: class TranspositionTable { static constexpr int ClusterSize = 3; + static constexpr int ClustersPerSuperCluster = 256; struct Cluster { TTEntry entry[ClusterSize]; @@ -82,15 +83,21 @@ public: void resize(size_t mbSize); void clear(); - // The 32 lowest order bits of the key are used to get the index of the cluster TTEntry* first_entry(const Key key) const { - return &table[(uint32_t(key) * uint64_t(clusterCount)) >> 32].entry[0]; + + // The index is computed from + // Idx = (K48 * SCC) / 2^40, with K48 the 48 lowest bits swizzled. + + const uint64_t firstTerm = uint32_t(key) * uint64_t(superClusterCount); + const uint64_t secondTerm = (uint16_t(key >> 32) * uint64_t(superClusterCount)) >> 16; + + return &table[(firstTerm + secondTerm) >> 24].entry[0]; } private: friend struct TTEntry; - size_t clusterCount; + size_t superClusterCount; Cluster* table; void* mem; uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 16add76e..90190b53 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -56,8 +56,8 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const void init(OptionsMap& o) { - // at most 2^32 clusters. - constexpr int MaxHashMB = Is64Bit ? 131072 : 2048; + // At most 2^32 superclusters. Supercluster = 8 kB + constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; o["Debug Log File"] << Option("", on_logger); o["Contempt"] << Option(24, -100, 100); From 3af083a7cd9be1659f1d8a39a65e33b87608f762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 10 Jun 2020 00:10:07 +0200 Subject: [PATCH 07/34] Improve the anti-shuffling policy We replace the current decrease of the complexity term in initiative when shuffling by a direct damping of the evaluation. This scheme may have two benefits over the initiative approach: a) the damping effect is more brutal for fortresses with heavy pieces on the board, because the initiative term is almost an endgame term; b) the initiative implementation had a funny side effect, almost a bug, in the rare positions where mg > 0, eg < 0 and the tampered eval returned a positive value (ie with heavy pieces still on the board): sending eg to zero via shuffling would **increase** the tampered eval instead of decreasing it, which is somewhat illogical. This patch avoids this phenomenon. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 43072 W: 8373 L: 8121 D: 26578 Ptnml(0-2): 729, 4954, 9940, 5162, 751 https://tests.stockfishchess.org/tests/view/5ee008ebf29b40b0fc95ade2 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 37376 W: 4816 L: 4543 D: 28017 Ptnml(0-2): 259, 3329, 11286, 3508, 306 https://tests.stockfishchess.org/tests/view/5ee03b06f29b40b0fc95ae0c Closes https://github.com/official-stockfish/Stockfish/pull/2727 Bench: 4757174 --- src/evaluate.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c042c016..b173cd3b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -743,7 +743,6 @@ namespace { + 24 * infiltration + 51 * !pos.non_pawn_material() - 43 * almostUnwinnable - - 2 * pos.rule50_count() -110 ; Value mg = mg_value(score); @@ -857,7 +856,12 @@ namespace { } // Side to move point of view - return (pos.side_to_move() == WHITE ? v : -v) + Tempo; + v = (pos.side_to_move() == WHITE ? v : -v) + Tempo; + + // Damp down the evaluation linearly when shuffling + v = v * (100 - pos.rule50_count()) / 100; + + return v; } } // namespace From c44c62efc24fbe6355a9c19e287b2c78e6fd6c1d Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 13 Jun 2020 05:03:59 +0300 Subject: [PATCH 08/34] Adjust history threshold for quiet moves futility pruning This patch adjusts the threshold for futility pruning of quiet moves using the continuation history array contHist[5], in the same way as it is used in movepicker. passed STC: https://tests.stockfishchess.org/tests/view/5ee3f88bca6c451633a9959f LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 55984 W: 10822 L: 10552 D: 34610 Ptnml(0-2): 952, 6435, 12941, 6719, 945 passed LTC: https://tests.stockfishchess.org/tests/view/5ee4186dca6c451633a995cf LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 41712 W: 5402 L: 5114 D: 31196 Ptnml(0-2): 293, 3766, 12469, 4016, 312 closes https://github.com/official-stockfish/Stockfish/pull/2734 Bench: 4715960 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e3a5a92e..f5887f3f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1006,7 +1006,8 @@ moves_loop: // When in check, search starts from here && ss->staticEval + 235 + 172 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] < 27400) + + (*contHist[3])[movedPiece][to_sq(move)] + + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 31400) continue; // Prune moves with negative SEE (~20 Elo) From 4d657618e956decdd51bceca77c2c5489dfcf6af Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 10 Jun 2020 13:19:21 +0200 Subject: [PATCH 09/34] Quantize eval to multiples of 16 Removes some excess precision, helps searchs. Effectively reintroduces evaluation grain, with a slightly different context. https://github.com/official-stockfish/Stockfish/commit/45dbd9cd0303d0db469670af8ec3598731a4eace passed STC LLR: 2.97 (-2.94,2.94) {-0.50,1.50} Total: 197032 W: 37938 L: 37462 D: 121632 Ptnml(0-2): 3359, 22994, 45446, 23246, 3471 https://tests.stockfishchess.org/tests/view/5ee0c228f29b40b0fc95ae53 passed LTC LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 77696 W: 9970 L: 9581 D: 58145 Ptnml(0-2): 530, 7075, 23311, 7340, 592 https://tests.stockfishchess.org/tests/view/5ee21426f29b40b0fc95af43 passed LTC SMP LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 64136 W: 7425 L: 7091 D: 49620 Ptnml(0-2): 345, 5416, 20228, 5718, 361 https://tests.stockfishchess.org/tests/view/5ee387bbf29b40b0fc95b04c closes https://github.com/official-stockfish/Stockfish/pull/2733 Bench: 4939103 --- src/evaluate.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index b173cd3b..036b93a9 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -855,6 +855,9 @@ namespace { Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]); } + // Evaluation grain + v = (v / 16) * 16; + // Side to move point of view v = (pos.side_to_move() == WHITE ? v : -v) + Tempo; From 42b7dbcb5e20ae9015122601522be8b455787a4a Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sat, 13 Jun 2020 09:54:07 +0100 Subject: [PATCH 10/34] Tuned values for search constants Tuned search constants after many search patches since the last successful tune. 1st LTC @ 60+0.6 th 1 : LLR: 2.97 (-2.94,2.94) {0.25,1.75} Total: 57656 W: 7369 L: 7036 D: 43251 Ptnml(0-2): 393, 5214, 17336, 5437, 448 https://tests.stockfishchess.org/tests/view/5ee1e074f29b40b0fc95af19 SMP LTC @ 20+0.2 th 8 : LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 83576 W: 9731 L: 9341 D: 64504 Ptnml(0-2): 464, 7062, 26369, 7406, 487 https://tests.stockfishchess.org/tests/view/5ee35a21f29b40b0fc95b008 The changes were rebased on top of a successful patch by Viz (see #2734) and two different ways of doing this were tested. The successful test modified the constants in the patch by Viz in a similar manner to the tuning run: LTC (rebased) @ 60+0.6 th 1 : LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 193384 W: 24241 L: 23521 D: 145622 Ptnml(0-2): 1309, 17497, 58472, 17993, 1421 https://tests.stockfishchess.org/tests/view/5ee43319ca6c451633a995f9 Further work: the recent patch to quantize eval #2733 affects search quit quite a bit, so doing another tune in, say, three months time might be a good idea. closes https://github.com/official-stockfish/Stockfish/pull/2735 Bench 4246971 --- src/search.cpp | 58 +++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f5887f3f..5ad650d2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -65,9 +65,9 @@ namespace { constexpr uint64_t TtHitAverageResolution = 1024; // Razor and futility margins - constexpr int RazorMargin = 531; + constexpr int RazorMargin = 516; Value futility_margin(Depth d, bool improving) { - return Value(217 * (d - improving)); + return Value(224 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -75,16 +75,16 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 511) / 1024 + (!i && r > 1007); + return (r + 529) / 1024 + (!i && r > 1050); } constexpr int futility_move_count(bool improving, Depth depth) { - return (4 + depth * depth) / (2 - improving); + return (3 + depth * depth) / (2 - improving); } // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 15 ? -8 : 19 * d * d + 155 * d - 132; + return d > 15 ? 28 : 19 * d * d + 135 * d - 136; } // Add a small random component to draw evaluations to avoid 3fold-blindness @@ -194,7 +194,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((24.8 + std::log(Threads.size())) * std::log(i)); + Reductions[i] = int((24.9 + std::log(Threads.size())) * std::log(i)); } @@ -408,7 +408,7 @@ void Thread::search() { beta = std::min(prev + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + (102 - ct / 2) * prev / (abs(prev) + 157); + int dct = ct + (104 - ct / 2) * prev / (abs(prev) + 143); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -506,13 +506,13 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (332 + 6 * (mainThread->bestPreviousScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; + double fallingEval = (293 + 6 * (mainThread->bestPreviousScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 742.0; fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.94 : 0.91; - double reduction = (1.41 + mainThread->previousTimeReduction) / (2.27 * timeReduction); + timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.93 : 0.96; + double reduction = (1.36 + mainThread->previousTimeReduction) / (2.21 * timeReduction); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) @@ -537,7 +537,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.6) + && Time.elapsed() > totalTime * 0.57) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -819,10 +819,10 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 23397 + && (ss-1)->statScore < 24714 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 32 * depth - 30 * improving + 120 * ttPv + 292 + && ss->staticEval >= beta - 29 * depth - 31 * improving + 119 * ttPv + 299 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -830,7 +830,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (854 + 68 * depth) / 258 + std::min(int(eval - beta) / 192, 3); + Depth R = (793 + 70 * depth) / 252 + std::min(int(eval - beta) / 192, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -870,10 +870,10 @@ namespace { // If we have a good enough capture and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode - && depth >= 5 + && depth > 5 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) { - Value raisedBeta = beta + 189 - 45 * improving; + Value raisedBeta = beta + 182 - 48 * improving; assert(raisedBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); int probCutCount = 0; @@ -904,7 +904,7 @@ namespace { // If the qsearch held, perform the regular search if (value >= raisedBeta) - value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode); + value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 5, !cutNode); pos.undo_move(move); @@ -1003,15 +1003,15 @@ moves_loop: // When in check, search starts from here // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 235 + 172 * lmrDepth <= alpha + && ss->staticEval + 252 + 176 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 31400) + + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 30251) continue; // Prune moves with negative SEE (~20 Elo) - if (!pos.see_ge(move, Value(-(32 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } else @@ -1027,11 +1027,11 @@ moves_loop: // When in check, search starts from here && lmrDepth < 6 && !(PvNode && abs(bestValue) < 2) && !ss->inCheck - && ss->staticEval + 270 + 384 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) + && ss->staticEval + 264 + 397 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; // See based pruning - if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) + if (!pos.see_ge(move, Value(-192) * depth)) // (~25 Elo) continue; } } @@ -1144,12 +1144,12 @@ moves_loop: // When in check, search starts from here || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || thisThread->ttHitAverage < 375 * TtHitAverageResolution * TtHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 399 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); // Decrease reduction if the ttHit running average is large - if (thisThread->ttHitAverage > 500 * TtHitAverageResolution * TtHitAverageWindow / 1024) + if (thisThread->ttHitAverage > 492 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; // Reduction if other threads are searching this position. @@ -1195,14 +1195,14 @@ moves_loop: // When in check, search starts from here - 4926; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -102 && (ss-1)->statScore < -114) + if (ss->statScore >= -99 && (ss-1)->statScore < -116) r--; - else if ((ss-1)->statScore >= -116 && ss->statScore < -154) + else if ((ss-1)->statScore >= -117 && ss->statScore < -150) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 16434; + r -= ss->statScore / 15896; } else { @@ -1474,7 +1474,7 @@ moves_loop: // When in check, search starts from here if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 154; + futilityBase = bestValue + 138; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From 995ee4b31105ad8c7976cc68c11fabfdc5108e63 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 14 Jun 2020 21:50:27 +0100 Subject: [PATCH 11/34] Retuned values after eval quantize patch. The last search tune patch was tested before the implementation of #2733 which presumably changed the search characteristics noticeably. Another tuning run was done, see https://tests.stockfishchess.org/tests/view/5ee5b434ca6c451633a9a08c and the updated values passed these tests: STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 34352 W: 6600 L: 6360 D: 21392 Ptnml(0-2): 581, 3947, 7914, 4119, 615 https://tests.stockfishchess.org/tests/view/5ee62f05ca6c451633a9a15f LTC 60+0.6 th 1 : LLR: 2.97 (-2.94,2.94) {0.25,1.75} Total: 11176 W: 1499 L: 1304 D: 8373 Ptnml(0-2): 69, 933, 3403, 1100, 83 https://tests.stockfishchess.org/tests/view/5ee6205bca6c451633a9a147 SMP LTC 20+0.2 th 8 : LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 54032 W: 6126 L: 5826 D: 42080 Ptnml(0-2): 278, 4454, 17280, 4698, 306 https://tests.stockfishchess.org/tests/view/5ee62f25ca6c451633a9a162 Closes https://github.com/official-stockfish/Stockfish/pull/2742 Bench 4957812 --- src/search.cpp | 68 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5ad650d2..cf89a892 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -65,9 +65,9 @@ namespace { constexpr uint64_t TtHitAverageResolution = 1024; // Razor and futility margins - constexpr int RazorMargin = 516; + constexpr int RazorMargin = 527; Value futility_margin(Depth d, bool improving) { - return Value(224 * (d - improving)); + return Value(227 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -75,7 +75,7 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 529) / 1024 + (!i && r > 1050); + return (r + 570) / 1024 + (!i && r > 1018); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -84,7 +84,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 15 ? 28 : 19 * d * d + 135 * d - 136; + return d > 15 ? 27 : 17 * d * d + 133 * d - 134; } // Add a small random component to draw evaluations to avoid 3fold-blindness @@ -194,7 +194,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((24.9 + std::log(Threads.size())) * std::log(i)); + Reductions[i] = int((24.8 + std::log(Threads.size())) * std::log(i)); } @@ -403,12 +403,12 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].previousScore; - delta = Value(21); + delta = Value(19); alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + (104 - ct / 2) * prev / (abs(prev) + 143); + int dct = ct + (110 - ct / 2) * prev / (abs(prev) + 140); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -506,13 +506,13 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (293 + 6 * (mainThread->bestPreviousScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 742.0; + double fallingEval = (296 + 6 * (mainThread->bestPreviousScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 725.0; fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.93 : 0.96; - double reduction = (1.36 + mainThread->previousTimeReduction) / (2.21 * timeReduction); + timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.92 : 0.95; + double reduction = (1.47 + mainThread->previousTimeReduction) / (2.22 * timeReduction); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) @@ -537,7 +537,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.57) + && Time.elapsed() > totalTime * 0.56) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -819,10 +819,10 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 24714 + && (ss-1)->statScore < 23824 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 29 * depth - 31 * improving + 119 * ttPv + 299 + && ss->staticEval >= beta - 33 * depth - 33 * improving + 112 * ttPv + 311 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -830,7 +830,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (793 + 70 * depth) / 252 + std::min(int(eval - beta) / 192, 3); + Depth R = (737 + 77 * depth) / 246 + std::min(int(eval - beta) / 192, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -870,10 +870,10 @@ namespace { // If we have a good enough capture and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode - && depth > 5 + && depth > 4 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) { - Value raisedBeta = beta + 182 - 48 * improving; + Value raisedBeta = beta + 176 - 49 * improving; assert(raisedBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); int probCutCount = 0; @@ -904,7 +904,7 @@ namespace { // If the qsearch held, perform the regular search if (value >= raisedBeta) - value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 5, !cutNode); + value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode); pos.undo_move(move); @@ -1003,15 +1003,15 @@ moves_loop: // When in check, search starts from here // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 252 + 176 * lmrDepth <= alpha + && ss->staticEval + 284 + 188 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 30251) + + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 28388) continue; // Prune moves with negative SEE (~20 Elo) - if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-(29 - std::min(lmrDepth, 17)) * lmrDepth * lmrDepth))) continue; } else @@ -1027,11 +1027,11 @@ moves_loop: // When in check, search starts from here && lmrDepth < 6 && !(PvNode && abs(bestValue) < 2) && !ss->inCheck - && ss->staticEval + 264 + 397 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) + && ss->staticEval + 267 + 391 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; // See based pruning - if (!pos.see_ge(move, Value(-192) * depth)) // (~25 Elo) + if (!pos.see_ge(move, Value(-202) * depth)) // (~25 Elo) continue; } } @@ -1144,12 +1144,12 @@ moves_loop: // When in check, search starts from here || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || thisThread->ttHitAverage < 399 * TtHitAverageResolution * TtHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 415 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); // Decrease reduction if the ttHit running average is large - if (thisThread->ttHitAverage > 492 * TtHitAverageResolution * TtHitAverageWindow / 1024) + if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; // Reduction if other threads are searching this position. @@ -1164,7 +1164,7 @@ moves_loop: // When in check, search starts from here r++; // Decrease reduction if opponent's move count is high (~5 Elo) - if ((ss-1)->moveCount > 14) + if ((ss-1)->moveCount > 13) r--; // Decrease reduction if ttMove has been singularly extended (~3 Elo) @@ -1192,17 +1192,17 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4926; + - 4826; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -99 && (ss-1)->statScore < -116) + if (ss->statScore >= -100 && (ss-1)->statScore < -112) r--; - else if ((ss-1)->statScore >= -117 && ss->statScore < -150) + else if ((ss-1)->statScore >= -125 && ss->statScore < -138) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 15896; + r -= ss->statScore / 14615; } else { @@ -1212,7 +1212,7 @@ moves_loop: // When in check, search starts from here // Unless giving check, this capture is likely bad if ( !givesCheck - && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 200 * depth <= alpha) + && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 211 * depth <= alpha) r++; } @@ -1474,7 +1474,7 @@ moves_loop: // When in check, search starts from here if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 138; + futilityBase = bestValue + 141; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -1724,8 +1724,8 @@ moves_loop: // When in check, search starts from here thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } - if (depth > 12 && ss->ply < MAX_LPH) - thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7); + if (depth > 11 && ss->ply < MAX_LPH) + thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 6); } // When playing with strength handicap, choose best move among a set of RootMoves From 1ea488d34c0b6a03fa3d89d289fe72fd1408cafd Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 14 Jun 2020 23:35:07 -0700 Subject: [PATCH 12/34] Use 128 bit multiply for TT index Remove super cluster stuff from TT and just use a 128 bit multiply. STC https://tests.stockfishchess.org/tests/view/5ee719b3aae8aec816ab7548 LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 12736 W: 2502 L: 2333 D: 7901 Ptnml(0-2): 191, 1452, 2944, 1559, 222 LTC https://tests.stockfishchess.org/tests/view/5ee732d1aae8aec816ab7556 LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 27584 W: 3431 L: 3350 D: 20803 Ptnml(0-2): 173, 2500, 8400, 2511, 208 Scheme back to being derived from https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ Also the default optimized version of the index calculation now uses fewer instructions. https://godbolt.org/z/Tktxbv Might benefit from mulx (requires -mbmi2) closes https://github.com/official-stockfish/Stockfish/pull/2744 bench: 4320954 --- src/misc.h | 13 +++++++++++++ src/search.cpp | 2 +- src/tt.cpp | 16 ++++++---------- src/tt.h | 12 ++---------- src/ucioption.cpp | 1 - 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/misc.h b/src/misc.h index 05bfc7de..373f1b77 100644 --- a/src/misc.h +++ b/src/misc.h @@ -110,6 +110,19 @@ public: { return T(rand64() & rand64() & rand64()); } }; +inline uint64_t mul_hi64(uint64_t a, uint64_t b) { +#if defined(__GNUC__) && defined(IS_64BIT) + __extension__ typedef unsigned __int128 uint128; + return ((uint128)a * (uint128)b) >> 64; +#else + uint64_t aL = (uint32_t)a, aH = a >> 32; + uint64_t bL = (uint32_t)b, bH = b >> 32; + uint64_t c1 = (aL * bL) >> 32; + uint64_t c2 = aH * bL + c1; + uint64_t c3 = aL * bH + (uint32_t)c2; + return aH * bH + (c2 >> 32) + (c3 >> 32); +#endif +} /// Under Windows it is not possible for a process to run on more than one /// logical processor group. This usually means to be limited to use max 64 diff --git a/src/search.cpp b/src/search.cpp index cf89a892..67339ed7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -662,7 +662,7 @@ namespace { // search to overwrite a previous full search TT value, so we use a different // position key in case of an excluded move. excludedMove = ss->excludedMove; - posKey = pos.key() ^ Key(excludedMove << 16); // Isn't a very good hash + posKey = pos.key() ^ (Key(excludedMove) << 48); // Isn't a very good hash tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] diff --git a/src/tt.cpp b/src/tt.cpp index 92aaee00..d0a5d4e0 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -36,17 +36,17 @@ TranspositionTable TT; // Our global transposition table void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { // Preserve any existing move for the same position - if (m || (k >> 48) != key16) + if (m || (uint16_t)k != key16) move16 = (uint16_t)m; // Overwrite less valuable entries - if ( (k >> 48) != key16 + if ((uint16_t)k != key16 || d - DEPTH_OFFSET > depth8 - 4 || b == BOUND_EXACT) { assert(d >= DEPTH_OFFSET); - key16 = (uint16_t)(k >> 48); + key16 = (uint16_t)k; value16 = (int16_t)v; eval16 = (int16_t)ev; genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); @@ -65,10 +65,8 @@ void TranspositionTable::resize(size_t mbSize) { aligned_ttmem_free(mem); - superClusterCount = mbSize * 1024 * 1024 / (sizeof(Cluster) * ClustersPerSuperCluster); - - table = static_cast( - aligned_ttmem_alloc(superClusterCount * ClustersPerSuperCluster * sizeof(Cluster), mem)); + clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); + table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem)); if (!mem) { std::cerr << "Failed to allocate " << mbSize @@ -91,8 +89,6 @@ void TranspositionTable::clear() { { threads.emplace_back([this, idx]() { - const size_t clusterCount = superClusterCount * ClustersPerSuperCluster; - // Thread binding gives faster search on systems with a first-touch policy if (Options["Threads"] > 8) WinProcGroup::bindThisThread(idx); @@ -121,7 +117,7 @@ void TranspositionTable::clear() { TTEntry* TranspositionTable::probe(const Key key, bool& found) const { TTEntry* const tte = first_entry(key); - const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster + const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster for (int i = 0; i < ClusterSize; ++i) if (!tte[i].key16 || tte[i].key16 == key16) diff --git a/src/tt.h b/src/tt.h index 76db03da..3e1d0e99 100644 --- a/src/tt.h +++ b/src/tt.h @@ -66,7 +66,6 @@ private: class TranspositionTable { static constexpr int ClusterSize = 3; - static constexpr int ClustersPerSuperCluster = 256; struct Cluster { TTEntry entry[ClusterSize]; @@ -84,20 +83,13 @@ public: void clear(); TTEntry* first_entry(const Key key) const { - - // The index is computed from - // Idx = (K48 * SCC) / 2^40, with K48 the 48 lowest bits swizzled. - - const uint64_t firstTerm = uint32_t(key) * uint64_t(superClusterCount); - const uint64_t secondTerm = (uint16_t(key >> 32) * uint64_t(superClusterCount)) >> 16; - - return &table[(firstTerm + secondTerm) >> 24].entry[0]; + return &table[mul_hi64(key, clusterCount)].entry[0]; } private: friend struct TTEntry; - size_t superClusterCount; + size_t clusterCount; Cluster* table; void* mem; uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 90190b53..7037ea57 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -56,7 +56,6 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const void init(OptionsMap& o) { - // At most 2^32 superclusters. Supercluster = 8 kB constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; o["Debug Log File"] << Option("", on_logger); From 4c72c95359e28ea3e5a4357a7679de794ebd3e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 16 Jun 2020 13:21:24 +0200 Subject: [PATCH 13/34] Small bonus to favor thorn pawns We increase a little bit the midgame value of pawns on a4, h4, a6 and h6. Original idea by Malcolm Campbell, who tried the version restricted to the pawns on the H column a couple of weeks ago and got a patch which almost passed LTC. The current pull request just adds the same idea for pawns on the A column. Possible follow-ups: maybe tweak the a5/h5 pawn values, and/or add a malus for very low king mobility in midgame? STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 33416 W: 6516 L: 6275 D: 20625 Ptnml(0-2): 575, 3847, 7659, 4016, 611 https://tests.stockfishchess.org/tests/view/5ee6c4e687586124bc2c10d4 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 134368 W: 16869 L: 16319 D: 101180 Ptnml(0-2): 908, 12083, 40708, 12521, 964 https://tests.stockfishchess.org/tests/view/5ee74e60aae8aec816ab756a closes https://github.com/official-stockfish/Stockfish/pull/2747 Bench: 5299456 --- src/psqt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psqt.cpp b/src/psqt.cpp index 7fa36ac8..abd23547 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -91,9 +91,9 @@ constexpr Score PBonus[RANK_NB][FILE_NB] = { }, { S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) }, { S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) }, - { S( -8, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S(-12, -9) }, + { S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) }, { S( 13, 9), S( 0, 4), S(-13, 3), S( 1,-12), S( 11,-12), S( -2, -6), S(-13, 13), S( 5, 8) }, - { S( -5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S(-18, 13) }, + { S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) }, { S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) } }; From bc3c215490edf24cef0ff87d74ab01eeb91ae1bc Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 17 Jun 2020 05:36:30 +0300 Subject: [PATCH 14/34] More reduction for evading pawn moves. pawn moves are irreversable unlike other evading moves; pawn is the least valuable piece in the game. So it makes a lot of sence to assume that evading pawn moves are on average not as good as other evading moves thus can be reduced more. Passed STC https://tests.stockfishchess.org/tests/view/5ee9602e563bc7aa756002dc LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 94176 W: 17993 L: 17668 D: 58515 Ptnml(0-2): 1634, 10742, 21989, 11111, 1612 Passed LTC https://tests.stockfishchess.org/tests/view/5ee97342563bc7aa75600301 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 20432 W: 2572 L: 2354 D: 15506 Ptnml(0-2): 146, 1707, 6280, 1949, 134 closes https://github.com/official-stockfish/Stockfish/pull/2749 Bench: 5073064 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 67339ed7..d96ed7da 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1186,7 +1186,7 @@ moves_loop: // When in check, search starts from here // hence break make_move(). (~2 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(reverse_move(move))) - r -= 2 + ttPv; + r -= 2 + ttPv - (type_of(movedPiece) == PAWN); ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] From 6f15e7fab277c2595633ad08fdc25bdd7e0ab166 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 21 Jun 2020 15:21:46 +0200 Subject: [PATCH 15/34] small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2695 No functional change --- src/Makefile | 2 +- src/bitbase.cpp | 16 ++++++++-------- src/bitboard.cpp | 25 ++++++++++++------------- src/bitboard.h | 2 +- src/evaluate.cpp | 11 +++++------ src/movepick.cpp | 2 +- src/movepick.h | 8 ++++---- src/pawns.cpp | 14 +++++++------- src/pawns.h | 2 +- src/psqt.cpp | 2 +- src/search.cpp | 2 +- src/syzygy/tbprobe.cpp | 6 +++--- src/timeman.cpp | 2 +- src/types.h | 17 ++++------------- 14 files changed, 50 insertions(+), 61 deletions(-) diff --git a/src/Makefile b/src/Makefile index 016aafec..41c2aff6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -336,7 +336,7 @@ ifeq ($(pext),yes) endif endif -### 3.8 Link Time Optimization, it works since gcc 4.5 but not on mingw under Windows. +### 3.8 Link Time Optimization ### This is a mix of compile and link time options because the lto link phase ### needs access to the optimization flags. ifeq ($(optimize),yes) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index be6f0d0a..7e27eb96 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -108,25 +108,25 @@ namespace { stm = Color ((idx >> 12) & 0x01); psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7))); - // Check if two pieces are on the same square or if a king can be captured + // Invalid if two pieces are on the same square or if a king can be captured if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 || ksq[WHITE] == psq || ksq[BLACK] == psq || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK]))) result = INVALID; - // Immediate win if a pawn can be promoted without getting captured + // Win if the pawn can be promoted without getting captured else if ( stm == WHITE && rank_of(psq) == RANK_7 - && ksq[stm] != psq + NORTH - && ( distance(ksq[~stm], psq + NORTH) > 1 - || (attacks_bb(ksq[stm]) & (psq + NORTH)))) + && ksq[WHITE] != psq + NORTH + && ( distance(ksq[BLACK], psq + NORTH) > 1 + || (distance(ksq[WHITE], psq + NORTH) == 1))) result = WIN; - // Immediate draw if it is a stalemate or a king captures undefended pawn + // Draw if it is stalemate or the black king can capture the pawn else if ( stm == BLACK - && ( !(attacks_bb(ksq[stm]) & ~(attacks_bb(ksq[~stm]) | pawn_attacks_bb(~stm, psq))) - || (attacks_bb(ksq[stm]) & psq & ~attacks_bb(ksq[~stm])))) + && ( !(attacks_bb(ksq[BLACK]) & ~(attacks_bb(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq))) + || (attacks_bb(ksq[BLACK]) & ~attacks_bb(ksq[WHITE]) & psq))) result = DRAW; // Position will be classified later diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 3bb3ff8f..0bf7eef9 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -40,7 +40,7 @@ namespace { Bitboard RookTable[0x19000]; // To store rook attacks Bitboard BishopTable[0x1480]; // To store bishop attacks - void init_magics(Bitboard table[], Magic magics[], Direction directions[]); + void init_magics(PieceType pt, Bitboard table[], Magic magics[]); } @@ -79,11 +79,8 @@ void Bitboards::init() { for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); - Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST }; - Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; - - init_magics(RookTable, RookMagics, RookDirections); - init_magics(BishopTable, BishopMagics, BishopDirections); + init_magics(ROOK, RookTable, RookMagics); + init_magics(BISHOP, BishopTable, BishopMagics); for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { @@ -109,15 +106,17 @@ void Bitboards::init() { namespace { - Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) { + Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { Bitboard attacks = 0; + Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; + Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST}; - for (int i = 0; i < 4; ++i) + for(Direction d : (pt == ROOK ? RookDirections : BishopDirections)) { Square s = sq; - while(safe_destination(s, directions[i]) && !(occupied & s)) - attacks |= (s += directions[i]); + while(safe_destination(s, d) && !(occupied & s)) + attacks |= (s += d); } return attacks; @@ -129,7 +128,7 @@ namespace { // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so // called "fancy" approach. - void init_magics(Bitboard table[], Magic magics[], Direction directions[]) { + void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { // Optimal PRNG seeds to pick the correct magics in the shortest time int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, @@ -149,7 +148,7 @@ namespace { // the number of 1s of the mask. Hence we deduce the size of the shift to // apply to the 64 or 32 bits word to get the index. Magic& m = magics[s]; - m.mask = sliding_attack(directions, s, 0) & ~edges; + m.mask = sliding_attack(pt, s, 0) & ~edges; m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); // Set the offset for the attacks table of the square. We have individual @@ -161,7 +160,7 @@ namespace { b = size = 0; do { occupancy[size] = b; - reference[size] = sliding_attack(directions, s, b); + reference[size] = sliding_attack(pt, s, b); if (HasPext) m.attacks[pext(b, m.mask)] = reference[size]; diff --git a/src/bitboard.h b/src/bitboard.h index 704f4bb4..0f55810c 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -253,7 +253,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) { /// the given color and on the given square is a passed pawn. inline Bitboard passed_pawn_span(Color c, Square s) { - return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s)); + return pawn_attack_span(c, s) | forward_file_bb(c, s); } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 036b93a9..3b0891a2 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -676,16 +676,15 @@ namespace { } - // Evaluation::space() computes the space evaluation for a given side. The - // space evaluation is a simple bonus based on the number of safe squares - // available for minor pieces on the central four files on ranks 2--4. Safe - // squares one, two or three squares behind a friendly pawn are counted - // twice. Finally, the space bonus is multiplied by a weight. The aim is to - // improve play on game opening. + // Evaluation::space() computes a space evaluation for a given side, aiming to improve game + // play in the opening. It is based on the number of safe squares on the 4 central files + // on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice. + // Finally, the space bonus is multiplied by a weight which decreases according to occupancy. template template Score Evaluation::space() const { + // Early exit if, for example, both queens or 6 minor pieces have been exchanged if (pos.non_pawn_material() < SpaceThreshold) return SCORE_ZERO; diff --git a/src/movepick.cpp b/src/movepick.cpp index 78102c52..5775f810 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -57,7 +57,7 @@ namespace { /// MovePicker constructor for the main search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, - const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers, int pl) + const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl) : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { diff --git a/src/movepick.h b/src/movepick.h index 33c4b086..aaff388f 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -88,9 +88,9 @@ enum StatsType { NoCaptures, Captures }; /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards typedef Stats ButterflyHistory; -/// LowPlyHistory at higher depths records successful quiet moves on plies 0 to 3 -/// and quiet moves which are/were in the PV (ttPv) -/// It get cleared with each new search and get filled during iterative deepening +/// At higher depths LowPlyHistory records successful quiet moves near the root and quiet +/// moves which are/were in the PV (ttPv) +/// It is cleared with each new search and filled during iterative deepening constexpr int MAX_LPH = 4; typedef Stats LowPlyHistory; @@ -133,7 +133,7 @@ public: const CapturePieceToHistory*, const PieceToHistory**, Move, - Move*, + const Move*, int); Move next_move(bool skipQuiets = false); diff --git a/src/pawns.cpp b/src/pawns.cpp index c1119a41..597dff2b 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -150,17 +150,17 @@ namespace { && !(theirPawns & adjacent_files_bb(s))) score -= Doubled; else - score -= Isolated - + WeakUnopposed * !opposed; + score -= Isolated + + WeakUnopposed * !opposed; } else if (backward) - score -= Backward - + WeakUnopposed * !opposed; + score -= Backward + + WeakUnopposed * !opposed; if (!support) - score -= Doubled * doubled - + WeakLever * more_than_one(lever); + score -= Doubled * doubled + + WeakLever * more_than_one(lever); } return score; @@ -196,7 +196,7 @@ Entry* probe(const Position& pos) { /// penalty for a king, looking at the king file and the two closest files. template -Score Entry::evaluate_shelter(const Position& pos, Square ksq) { +Score Entry::evaluate_shelter(const Position& pos, Square ksq) const { constexpr Color Them = ~Us; diff --git a/src/pawns.h b/src/pawns.h index a3284a0f..e6098069 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -50,7 +50,7 @@ struct Entry { Score do_king_safety(const Position& pos); template - Score evaluate_shelter(const Position& pos, Square ksq); + Score evaluate_shelter(const Position& pos, Square ksq) const; Key key; Score scores[COLOR_NB]; diff --git a/src/psqt.cpp b/src/psqt.cpp index abd23547..27c7a36f 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -106,7 +106,7 @@ Score psq[PIECE_NB][SQUARE_NB]; // tables are initialized by flipping and changing the sign of the white scores. void init() { - for (Piece pc = W_PAWN; pc <= W_KING; ++pc) + for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING}) { Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); diff --git a/src/search.cpp b/src/search.cpp index d96ed7da..563d1aab 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -670,7 +670,7 @@ namespace { ttPv = PvNode || (ttHit && tte->is_pv()); formerPv = ttPv && !PvNode; - if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !pos.captured_piece() && is_ok((ss-1)->currentMove)) + if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !priorCapture && is_ok((ss-1)->currentMove)) thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); // thisThread->ttHitAverage can be used to approximate the running average of ttHit diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 6bfd78ad..95d58945 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1200,7 +1200,7 @@ WDLScore search(Position& pos, ProbeState* result) { auto moveList = MoveList(pos); size_t totalCount = moveList.size(), moveCount = 0; - for (const Move& move : moveList) + for (const Move move : moveList) { if ( !pos.capture(move) && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN)) @@ -1362,7 +1362,7 @@ void Tablebases::init(const std::string& paths) { LeadPawnsSize[leadPawnsCnt][f] = idx; } - // Add entries in TB tables if the corresponding ".rtbw" file exsists + // Add entries in TB tables if the corresponding ".rtbw" file exists for (PieceType p1 = PAWN; p1 < KING; ++p1) { TBTables.add({KING, p1, KING}); @@ -1469,7 +1469,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { StateInfo st; int minDTZ = 0xFFFF; - for (const Move& move : MoveList(pos)) + for (const Move move : MoveList(pos)) { bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN; diff --git a/src/timeman.cpp b/src/timeman.cpp index 1f598745..d27962b7 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -79,7 +79,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { { opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0, 0.2 * limits.time[us] / double(timeLeft)); - max_scale = 4 + std::min(36, ply) / 12.0; + max_scale = std::min(7.0, 4.0 + ply / 12.0); } // x moves in y seconds (+ z increment) diff --git a/src/types.h b/src/types.h index 580c846a..969d4e65 100644 --- a/src/types.h +++ b/src/types.h @@ -40,7 +40,6 @@ #include #include -#include #include #include #include @@ -214,7 +213,6 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = { typedef int Depth; enum : int { - DEPTH_QS_CHECKS = 0, DEPTH_QS_NO_CHECKS = -1, DEPTH_QS_RECAPTURES = -5, @@ -282,11 +280,11 @@ inline Value mg_value(Score s) { } #define ENABLE_BASE_OPERATORS_ON(T) \ -constexpr T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \ -constexpr T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \ +constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ +constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ constexpr T operator-(T d) { return T(-int(d)); } \ -inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \ -inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } +inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \ +inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; } #define ENABLE_INCR_OPERATORS_ON(T) \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \ @@ -305,7 +303,6 @@ ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_INCR_OPERATORS_ON(PieceType) -ENABLE_INCR_OPERATORS_ON(Piece) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(Rank) @@ -316,12 +313,6 @@ ENABLE_BASE_OPERATORS_ON(Score) #undef ENABLE_INCR_OPERATORS_ON #undef ENABLE_BASE_OPERATORS_ON -/// Additional operators to add integers to a Value -constexpr Value operator+(Value v, int i) { return Value(int(v) + i); } -constexpr Value operator-(Value v, int i) { return Value(int(v) - i); } -inline Value& operator+=(Value& v, int i) { return v = v + i; } -inline Value& operator-=(Value& v, int i) { return v = v - i; } - /// Additional operators to add a Direction to a Square constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); } constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); } From 8a3f155b1cc1175b33ddc97d0572b5557269b0fa Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 17 Jun 2020 15:15:54 -0600 Subject: [PATCH 16/34] Make endgames consistent Changes variable names and occasionally consolidated variable declarations. Piece squares are consistently prefixed with "weak" or "strong." passed STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 29008 W: 5532 L: 5416 D: 18060 Ptnml(0-2): 355, 2983, 7723, 3077, 366 https://tests.stockfishchess.org/tests/view/5eea88d3563bc7aa75600689 closes https://github.com/official-stockfish/Stockfish/pull/2752 No functional change --- src/endgame.cpp | 384 ++++++++++++++++++++++++------------------------ 1 file changed, 194 insertions(+), 190 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 7b9c145e..d9e76348 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -28,12 +28,14 @@ namespace { // Used to drive the king towards the edge of the board // in KX vs K and KQ vs KR endgames. + // Values range from 27 (center squares) to 90 (in the corners) inline int push_to_edge(Square s) { int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s)); return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2); } // Used to drive the king towards A1H8 corners in KBN vs K endgames. + // Values range from 0 on A8H1 diagonal to 7 in A1H8 corners inline int push_to_corner(Square s) { return abs(7 - rank_of(s) - file_of(s)); } @@ -103,13 +105,13 @@ Value Endgame::operator()(const Position& pos) const { if (pos.side_to_move() == weakSide && !MoveList(pos).size()) return VALUE_DRAW; - Square winnerKSq = pos.square(strongSide); - Square loserKSq = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square weakKing = pos.square(weakSide); Value result = pos.non_pawn_material(strongSide) + pos.count(strongSide) * PawnValueEg - + push_to_edge(loserKSq) - + push_close(winnerKSq, loserKSq); + + push_to_edge(weakKing) + + push_close(strongKing, weakKing); if ( pos.count(strongSide) || pos.count(strongSide) @@ -130,16 +132,16 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square winnerKSq = pos.square(strongSide); - Square loserKSq = pos.square(weakSide); - Square bishopSq = pos.square(strongSide); + Square strongKing = pos.square(strongSide); + Square strongBishop = pos.square(strongSide); + Square weakKing = pos.square(weakSide); // 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 + 3520) - + push_close(winnerKSq, loserKSq) - + 420 * push_to_corner(opposite_colors(bishopSq, SQ_A1) ? flip_file(loserKSq) : loserKSq); + + push_close(strongKing, weakKing) + + 420 * push_to_corner(opposite_colors(strongBishop, SQ_A1) ? flip_file(weakKing) : weakKing); assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY); return strongSide == pos.side_to_move() ? result : -result; @@ -154,16 +156,16 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); // Assume strongSide is white and the pawn is on files A-D - Square wksq = normalize(pos, strongSide, pos.square(strongSide)); - Square bksq = normalize(pos, strongSide, pos.square(weakSide)); - Square psq = normalize(pos, strongSide, pos.square(strongSide)); + Square strongKing = normalize(pos, strongSide, pos.square(strongSide)); + Square strongPawn = normalize(pos, strongSide, pos.square(strongSide)); + Square weakKing = normalize(pos, strongSide, pos.square(weakSide)); Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; - if (!Bitbases::probe(wksq, psq, bksq, us)) + if (!Bitbases::probe(strongKing, strongPawn, weakKing, us)) return VALUE_DRAW; - Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); + Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(strongPawn)); return strongSide == pos.side_to_move() ? result : -result; } @@ -179,36 +181,35 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square wksq = relative_square(strongSide, pos.square(strongSide)); - Square bksq = relative_square(strongSide, pos.square(weakSide)); - Square rsq = relative_square(strongSide, pos.square(strongSide)); - Square psq = relative_square(strongSide, pos.square(weakSide)); - - Square queeningSq = make_square(file_of(psq), RANK_1); + Square strongKing = relative_square(strongSide, pos.square(strongSide)); + Square weakKing = relative_square(strongSide, pos.square(weakSide)); + Square strongRook = relative_square(strongSide, pos.square(strongSide)); + Square weakPawn = relative_square(strongSide, pos.square(weakSide)); + Square queeningSquare = make_square(file_of(weakPawn), RANK_1); Value result; // If the stronger side's king is in front of the pawn, it's a win - if (forward_file_bb(WHITE, wksq) & psq) - result = RookValueEg - distance(wksq, psq); + if (forward_file_bb(WHITE, strongKing) & weakPawn) + result = RookValueEg - distance(strongKing, weakPawn); // If the weaker side's king is too far from the pawn and the rook, // it's a win. - else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) - && distance(bksq, rsq) >= 3) - result = RookValueEg - distance(wksq, psq); + else if ( distance(weakKing, weakPawn) >= 3 + (pos.side_to_move() == weakSide) + && distance(weakKing, strongRook) >= 3) + result = RookValueEg - distance(strongKing, weakPawn); // If the pawn is far advanced and supported by the defending king, // the position is drawish - else if ( rank_of(bksq) <= RANK_3 - && distance(bksq, psq) == 1 - && rank_of(wksq) >= RANK_4 - && distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) - result = Value(80) - 8 * distance(wksq, psq); + else if ( rank_of(weakKing) <= RANK_3 + && distance(weakKing, weakPawn) == 1 + && rank_of(strongKing) >= RANK_4 + && distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide)) + result = Value(80) - 8 * distance(strongKing, weakPawn); else - result = Value(200) - 8 * ( distance(wksq, psq + SOUTH) - - distance(bksq, psq + SOUTH) - - distance(psq, queeningSq)); + result = Value(200) - 8 * ( distance(strongKing, weakPawn + SOUTH) + - distance(weakKing, weakPawn + SOUTH) + - distance(weakPawn, queeningSquare)); return strongSide == pos.side_to_move() ? result : -result; } @@ -235,9 +236,9 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, KnightValueMg, 0)); - Square bksq = pos.square(weakSide); - Square bnsq = pos.square(weakSide); - Value result = Value(push_to_edge(bksq) + push_away(bksq, bnsq)); + Square weakKing = pos.square(weakSide); + Square weakKnight = pos.square(weakSide); + Value result = Value(push_to_edge(weakKing) + push_away(weakKing, weakKnight)); return strongSide == pos.side_to_move() ? result : -result; } @@ -252,15 +253,15 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square winnerKSq = pos.square(strongSide); - Square loserKSq = pos.square(weakSide); - Square pawnSq = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square weakKing = pos.square(weakSide); + Square weakPawn = pos.square(weakSide); - Value result = Value(push_close(winnerKSq, loserKSq)); + Value result = Value(push_close(strongKing, weakKing)); - if ( relative_rank(weakSide, pawnSq) != RANK_7 - || distance(loserKSq, pawnSq) != 1 - || ((FileBBB | FileDBB | FileEBB | FileGBB) & pawnSq)) + if ( relative_rank(weakSide, weakPawn) != RANK_7 + || distance(weakKing, weakPawn) != 1 + || ((FileBBB | FileDBB | FileEBB | FileGBB) & weakPawn)) result += QueenValueEg - PawnValueEg; return strongSide == pos.side_to_move() ? result : -result; @@ -277,13 +278,13 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, weakSide, RookValueMg, 0)); - Square winnerKSq = pos.square(strongSide); - Square loserKSq = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square weakKing = pos.square(weakSide); Value result = QueenValueEg - RookValueEg - + push_to_edge(loserKSq) - + push_close(winnerKSq, loserKSq); + + push_to_edge(weakKing) + + push_close(strongKing, weakKing); return strongSide == pos.side_to_move() ? result : -result; } @@ -297,9 +298,12 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); + Square weakKing = pos.square(weakSide); + Square weakPawn = pos.square(weakSide); + Value result = PawnValueEg - + 2 * push_to_edge(pos.square(weakSide)) - - 10 * relative_rank(weakSide, pos.square(weakSide)); + + 2 * push_to_edge(weakKing) + - 10 * relative_rank(weakSide, weakPawn); return strongSide == pos.side_to_move() ? result : -result; } @@ -325,15 +329,17 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Bitboard strongPawns = pos.pieces(strongSide, PAWN); Bitboard allPawns = pos.pieces(PAWN); + Square strongBishop = pos.square(strongSide); + Square weakKing = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + // All strongSide pawns are on a single rook file? if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB)) { - Square bishopSq = pos.square(strongSide); - Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8)); - Square weakKingSq = pos.square(weakSide); + Square queeningSquare = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8)); - if ( opposite_colors(queeningSq, bishopSq) - && distance(queeningSq, weakKingSq) <= 1) + if ( opposite_colors(queeningSquare, strongBishop) + && distance(queeningSquare, weakKing) <= 1) return SCALE_FACTOR_DRAW; } @@ -343,20 +349,16 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && pos.count(weakSide) >= 1) { // Get the least advanced weakSide pawn - Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); - - Square strongKingSq = pos.square(strongSide); - Square weakKingSq = pos.square(weakSide); - Square bishopSq = pos.square(strongSide); + Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); // There's potential for a draw if our pawn is blocked on the 7th rank, // the bishop cannot attack it or they only have one pawn left - if ( relative_rank(strongSide, weakPawnSq) == RANK_7 - && (strongPawns & (weakPawnSq + pawn_push(weakSide))) - && (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongPawns))) + if ( relative_rank(strongSide, weakPawn) == RANK_7 + && (strongPawns & (weakPawn + pawn_push(weakSide))) + && (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns))) { - int strongKingDist = distance(weakPawnSq, strongKingSq); - int weakKingDist = distance(weakPawnSq, weakKingSq); + int strongKingDist = distance(weakPawn, strongKing); + int weakKingDist = distance(weakPawn, weakKing); // It's a draw if the weak king is on its back two ranks, within 2 // squares of the blocking pawn and the strong king is not @@ -364,7 +366,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w // and positions where qsearch will immediately correct the // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) - if ( relative_rank(strongSide, weakKingSq) >= RANK_7 + if ( relative_rank(strongSide, weakKing) >= RANK_7 && weakKingDist <= 2 && weakKingDist <= strongKingDist) return SCALE_FACTOR_DRAW; @@ -384,15 +386,16 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(pos.count(weakSide) == 1); assert(pos.count(weakSide) >= 1); - Square kingSq = pos.square(weakSide); - Square rsq = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square weakKing = pos.square(weakSide); + Square weakRook = pos.square(weakSide); - if ( relative_rank(weakSide, kingSq) <= RANK_2 - && relative_rank(weakSide, pos.square(strongSide)) >= RANK_4 - && relative_rank(weakSide, rsq) == RANK_3 + if ( relative_rank(weakSide, weakKing) <= RANK_2 + && relative_rank(weakSide, strongKing) >= RANK_4 + && relative_rank(weakSide, weakRook) == RANK_3 && ( pos.pieces(weakSide, PAWN) - & attacks_bb(kingSq) - & pawn_attacks_bb(strongSide, rsq))) + & attacks_bb(weakKing) + & pawn_attacks_bb(strongSide, weakRook))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -412,89 +415,89 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, weakSide, RookValueMg, 0)); // Assume strongSide is white and the pawn is on files A-D - Square wksq = normalize(pos, strongSide, pos.square(strongSide)); - Square bksq = normalize(pos, strongSide, pos.square(weakSide)); - Square wrsq = normalize(pos, strongSide, pos.square(strongSide)); - Square wpsq = normalize(pos, strongSide, pos.square(strongSide)); - Square brsq = normalize(pos, strongSide, pos.square(weakSide)); + Square strongKing = normalize(pos, strongSide, pos.square(strongSide)); + Square strongRook = normalize(pos, strongSide, pos.square(strongSide)); + Square strongPawn = normalize(pos, strongSide, pos.square(strongSide)); + Square weakKing = normalize(pos, strongSide, pos.square(weakSide)); + Square weakRook = normalize(pos, strongSide, pos.square(weakSide)); - File f = file_of(wpsq); - Rank r = rank_of(wpsq); - Square queeningSq = make_square(f, RANK_8); + File pawnFile = file_of(strongPawn); + Rank pawnRank = rank_of(strongPawn); + Square queeningSquare = make_square(pawnFile, RANK_8); int tempo = (pos.side_to_move() == strongSide); // If the pawn is not too far advanced and the defending king defends the // queening square, use the third-rank defence. - if ( r <= RANK_5 - && distance(bksq, queeningSq) <= 1 - && wksq <= SQ_H5 - && (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6))) + if ( pawnRank <= RANK_5 + && distance(weakKing, queeningSquare) <= 1 + && strongKing <= SQ_H5 + && (rank_of(weakRook) == RANK_6 || (pawnRank <= RANK_3 && rank_of(strongRook) != RANK_6))) return SCALE_FACTOR_DRAW; // The defending side saves a draw by checking from behind in case the pawn // has advanced to the 6th rank with the king behind. - if ( r == RANK_6 - && distance(bksq, queeningSq) <= 1 - && rank_of(wksq) + tempo <= RANK_6 - && (rank_of(brsq) == RANK_1 || (!tempo && distance(brsq, wpsq) >= 3))) + if ( pawnRank == RANK_6 + && distance(weakKing, queeningSquare) <= 1 + && rank_of(strongKing) + tempo <= RANK_6 + && (rank_of(weakRook) == RANK_1 || (!tempo && distance(weakRook, strongPawn) >= 3))) return SCALE_FACTOR_DRAW; - if ( r >= RANK_6 - && bksq == queeningSq - && rank_of(brsq) == RANK_1 - && (!tempo || distance(wksq, wpsq) >= 2)) + if ( pawnRank >= RANK_6 + && weakKing == queeningSquare + && rank_of(weakRook) == RANK_1 + && (!tempo || distance(strongKing, strongPawn) >= 2)) return SCALE_FACTOR_DRAW; // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 // and the black rook is behind the pawn. - if ( wpsq == SQ_A7 - && wrsq == SQ_A8 - && (bksq == SQ_H7 || bksq == SQ_G7) - && file_of(brsq) == FILE_A - && (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5)) + if ( strongPawn == SQ_A7 + && strongRook == SQ_A8 + && (weakKing == SQ_H7 || weakKing == SQ_G7) + && file_of(weakRook) == FILE_A + && (rank_of(weakRook) <= RANK_3 || file_of(strongKing) >= FILE_D || rank_of(strongKing) <= RANK_5)) return SCALE_FACTOR_DRAW; // If the defending king blocks the pawn and the attacking king is too far // away, it's a draw. - if ( r <= RANK_5 - && bksq == wpsq + NORTH - && distance(wksq, wpsq) - tempo >= 2 - && distance(wksq, brsq) - tempo >= 2) + if ( pawnRank <= RANK_5 + && weakKing == strongPawn + NORTH + && distance(strongKing, strongPawn) - tempo >= 2 + && distance(strongKing, weakRook) - tempo >= 2) return SCALE_FACTOR_DRAW; // Pawn on the 7th rank supported by the rook from behind usually wins if the // attacking king is closer to the queening square than the defending king, // and the defending king cannot gain tempi by threatening the attacking rook. - if ( r == RANK_7 - && f != FILE_A - && file_of(wrsq) == f - && wrsq != queeningSq - && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) - && (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo)) - return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq)); + if ( pawnRank == RANK_7 + && pawnFile != FILE_A + && file_of(strongRook) == pawnFile + && strongRook != queeningSquare + && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo) + && (distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo)) + return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(strongKing, queeningSquare)); // Similar to the above, but with the pawn further back - if ( f != FILE_A - && file_of(wrsq) == f - && wrsq < wpsq - && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) - && (distance(wksq, wpsq + NORTH) < distance(bksq, wpsq + NORTH) - 2 + tempo) - && ( distance(bksq, wrsq) + tempo >= 3 - || ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo - && (distance(wksq, wpsq + NORTH) < distance(bksq, wrsq) + tempo)))) + if ( pawnFile != FILE_A + && file_of(strongRook) == pawnFile + && strongRook < strongPawn + && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo) + && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn + NORTH) - 2 + tempo) + && ( distance(weakKing, strongRook) + tempo >= 3 + || ( distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo + && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn) + tempo)))) return ScaleFactor( SCALE_FACTOR_MAX - - 8 * distance(wpsq, queeningSq) - - 2 * distance(wksq, queeningSq)); + - 8 * distance(strongPawn, queeningSquare) + - 2 * distance(strongKing, queeningSquare)); // If the pawn is not far advanced and the defending king is somewhere in // the pawn's path, it's probably a draw. - if (r <= RANK_4 && bksq > wpsq) + if (pawnRank <= RANK_4 && weakKing > strongPawn) { - if (file_of(bksq) == file_of(wpsq)) + if (file_of(weakKing) == file_of(strongPawn)) return ScaleFactor(10); - if ( distance(bksq, wpsq) == 1 - && distance(wksq, bksq) > 2) - return ScaleFactor(24 - 2 * distance(wksq, bksq)); + if ( distance(weakKing, strongPawn) == 1 + && distance(strongKing, weakKing) > 2) + return ScaleFactor(24 - 2 * distance(strongKing, weakKing)); } return SCALE_FACTOR_NONE; } @@ -508,10 +511,11 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // Test for a rook pawn if (pos.pieces(PAWN) & (FileABB | FileHBB)) { - Square ksq = pos.square(weakSide); - Square bsq = pos.square(weakSide); - Square psq = pos.square(strongSide); - Rank rk = relative_rank(strongSide, psq); + Square weakKing = pos.square(weakSide); + Square weakBishop = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square strongPawn = pos.square(strongSide); + Rank pawnRank = relative_rank(strongSide, strongPawn); Direction push = pawn_push(strongSide); // If the pawn is on the 5th rank and the pawn (currently) is on @@ -519,11 +523,11 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // a fortress. Depending on the king position give a moderate // reduction or a stronger one if the defending king is near the // corner but not trapped there. - if (rk == RANK_5 && !opposite_colors(bsq, psq)) + if (pawnRank == RANK_5 && !opposite_colors(weakBishop, strongPawn)) { - int d = distance(psq + 3 * push, ksq); + int d = distance(strongPawn + 3 * push, weakKing); - if (d <= 2 && !(d == 0 && ksq == pos.square(strongSide) + 2 * push)) + if (d <= 2 && !(d == 0 && weakKing == strongKing + 2 * push)) return ScaleFactor(24); else return ScaleFactor(48); @@ -533,10 +537,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // it's drawn if the bishop attacks the square in front of the // pawn from a reasonable distance and the defending king is near // the corner - if ( rk == RANK_6 - && distance(psq + 2 * push, ksq) <= 1 - && (attacks_bb(bsq) & (psq + push)) - && distance(bsq, psq) >= 2) + if ( pawnRank == RANK_6 + && distance(strongPawn + 2 * push, weakKing) <= 1 + && (attacks_bb(weakBishop) & (strongPawn + push)) + && distance(weakBishop, strongPawn) >= 2) return ScaleFactor(8); } @@ -551,22 +555,22 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 2)); assert(verify_material(pos, weakSide, RookValueMg, 1)); - Square wpsq1 = pos.squares(strongSide)[0]; - Square wpsq2 = pos.squares(strongSide)[1]; - Square bksq = pos.square(weakSide); + Square strongPawn1 = pos.squares(strongSide)[0]; + Square strongPawn2 = pos.squares(strongSide)[1]; + Square weakKing = pos.square(weakSide); // Does the stronger side have a passed pawn? - if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) + if (pos.pawn_passed(strongSide, strongPawn1) || pos.pawn_passed(strongSide, strongPawn2)) return SCALE_FACTOR_NONE; - Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); + Rank pawnRank = std::max(relative_rank(strongSide, strongPawn1), relative_rank(strongSide, strongPawn2)); - if ( distance(bksq, wpsq1) <= 1 - && distance(bksq, wpsq2) <= 1 - && relative_rank(strongSide, bksq) > r) + if ( distance(weakKing, strongPawn1) <= 1 + && distance(weakKing, strongPawn2) <= 1 + && relative_rank(strongSide, weakKing) > pawnRank) { - assert(r > RANK_1 && r < RANK_7); - return ScaleFactor(7 * r); + assert(pawnRank > RANK_1 && pawnRank < RANK_7); + return ScaleFactor(7 * pawnRank); } return SCALE_FACTOR_NONE; } @@ -581,12 +585,12 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(pos.count(strongSide) >= 2); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square ksq = pos.square(weakSide); - Bitboard pawns = pos.pieces(strongSide, PAWN); + Square weakKing = pos.square(weakSide); + Bitboard strongPawns = pos.pieces(strongSide, PAWN); // If all pawns are ahead of the king on a single rook file, it's a draw. - if (!((pawns & ~FileABB) || (pawns & ~FileHBB)) && - !(pawns & ~passed_pawn_span(weakSide, ksq))) + if (!((strongPawns & ~FileABB) || (strongPawns & ~FileHBB)) && + !(strongPawns & ~passed_pawn_span(weakSide, weakKing))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -603,19 +607,19 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Square pawnSq = pos.square(strongSide); - Square strongBishopSq = pos.square(strongSide); - Square weakBishopSq = pos.square(weakSide); - Square weakKingSq = pos.square(weakSide); + Square strongPawn = pos.square(strongSide); + Square strongBishop = pos.square(strongSide); + Square weakBishop = pos.square(weakSide); + Square weakKing = pos.square(weakSide); // Case 1: Defending king blocks the pawn, and cannot be driven away - if ( (forward_file_bb(strongSide, pawnSq) & weakKingSq) - && ( opposite_colors(weakKingSq, strongBishopSq) - || relative_rank(strongSide, weakKingSq) <= RANK_6)) + if ( (forward_file_bb(strongSide, strongPawn) & weakKing) + && ( opposite_colors(weakKing, strongBishop) + || relative_rank(strongSide, weakKing) <= RANK_6)) return SCALE_FACTOR_DRAW; // Case 2: Opposite colored bishops - if (opposite_colors(strongBishopSq, weakBishopSq)) + if (opposite_colors(strongBishop, weakBishop)) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -629,36 +633,36 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, BishopValueMg, 2)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Square wbsq = pos.square(strongSide); - Square bbsq = pos.square(weakSide); + Square strongBishop = pos.square(strongSide); + Square weakBishop = pos.square(weakSide); - if (!opposite_colors(wbsq, bbsq)) + if (!opposite_colors(strongBishop, weakBishop)) return SCALE_FACTOR_NONE; - Square ksq = pos.square(weakSide); - Square psq1 = pos.squares(strongSide)[0]; - Square psq2 = pos.squares(strongSide)[1]; + Square weakKing = pos.square(weakSide); + Square strongPawn1 = pos.squares(strongSide)[0]; + Square strongPawn2 = pos.squares(strongSide)[1]; Square blockSq1, blockSq2; - if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) + if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2)) { - blockSq1 = psq1 + pawn_push(strongSide); - blockSq2 = make_square(file_of(psq2), rank_of(psq1)); + blockSq1 = strongPawn1 + pawn_push(strongSide); + blockSq2 = make_square(file_of(strongPawn2), rank_of(strongPawn1)); } else { - blockSq1 = psq2 + pawn_push(strongSide); - blockSq2 = make_square(file_of(psq1), rank_of(psq2)); + blockSq1 = strongPawn2 + pawn_push(strongSide); + blockSq2 = make_square(file_of(strongPawn1), rank_of(strongPawn2)); } - switch (distance(psq1, psq2)) + switch (distance(strongPawn1, strongPawn2)) { case 0: // Both pawns are on the same file. It's an easy draw if the defender firmly // controls some square in the frontmost pawn's path. - if ( file_of(ksq) == file_of(blockSq1) - && relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1) - && opposite_colors(ksq, wbsq)) + if ( file_of(weakKing) == file_of(blockSq1) + && relative_rank(strongSide, weakKing) >= relative_rank(strongSide, blockSq1) + && opposite_colors(weakKing, strongBishop)) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; @@ -667,16 +671,16 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // Pawns on adjacent files. It's a draw if the defender firmly controls the // square in front of the frontmost pawn's path, and the square diagonally // behind this square on the file of the other pawn. - if ( ksq == blockSq1 - && opposite_colors(ksq, wbsq) - && ( bbsq == blockSq2 + if ( weakKing == blockSq1 + && opposite_colors(weakKing, strongBishop) + && ( weakBishop == blockSq2 || (attacks_bb(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP)) - || distance(psq1, psq2) >= 2)) + || distance(strongPawn1, strongPawn2) >= 2)) return SCALE_FACTOR_DRAW; - else if ( ksq == blockSq2 - && opposite_colors(ksq, wbsq) - && ( bbsq == blockSq1 + else if ( weakKing == blockSq2 + && opposite_colors(weakKing, strongBishop) + && ( weakBishop == blockSq1 || (attacks_bb(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP)))) return SCALE_FACTOR_DRAW; else @@ -698,14 +702,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, weakSide, KnightValueMg, 0)); - Square pawnSq = pos.square(strongSide); - Square strongBishopSq = pos.square(strongSide); - Square weakKingSq = pos.square(weakSide); + Square strongPawn = pos.square(strongSide); + Square strongBishop = pos.square(strongSide); + Square weakKing = pos.square(weakSide); - if ( file_of(weakKingSq) == file_of(pawnSq) - && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) - && ( opposite_colors(weakKingSq, strongBishopSq) - || relative_rank(strongSide, weakKingSq) <= RANK_6)) + if ( file_of(weakKing) == file_of(strongPawn) + && relative_rank(strongSide, strongPawn) < relative_rank(strongSide, weakKing) + && ( opposite_colors(weakKing, strongBishop) + || relative_rank(strongSide, weakKing) <= RANK_6)) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -724,18 +728,18 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); // Assume strongSide is white and the pawn is on files A-D - Square wksq = normalize(pos, strongSide, pos.square(strongSide)); - Square bksq = normalize(pos, strongSide, pos.square(weakSide)); - Square psq = normalize(pos, strongSide, pos.square(strongSide)); + Square strongKing = normalize(pos, strongSide, pos.square(strongSide)); + Square weakKing = normalize(pos, strongSide, pos.square(weakSide)); + Square strongPawn = normalize(pos, strongSide, pos.square(strongSide)); Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; // If the pawn has advanced to the fifth rank or further, and is not a // rook pawn, it's too dangerous to assume that it's at least a draw. - if (rank_of(psq) >= RANK_5 && file_of(psq) != FILE_A) + if (rank_of(strongPawn) >= RANK_5 && file_of(strongPawn) != FILE_A) return SCALE_FACTOR_NONE; // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // it's probably at least a draw even with the pawn. - return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; + return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; } From e9966d9a8ec371477f49bfa0eb69fa756e078fda Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 22 Jun 2020 12:52:31 +0300 Subject: [PATCH 17/34] Introduce bonus for queen infiltration Idea is that queen feels much better when it can't be kicked away now or later by pawn moves, especially in endgame. Special thanks to Linmiao Xu for the original idea of this patch. passed STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 84008 W: 16271 L: 15958 D: 51779 Ptnml(0-2): 1476, 9688, 19420, 9887, 1533 https://tests.stockfishchess.org/tests/view/5eee7ca0447c5b640047a439 passed LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 11720 W: 1522 L: 1328 D: 8870 Ptnml(0-2): 52, 1021, 3574, 1107, 106 https://tests.stockfishchess.org/tests/view/5eefc588122d6514328d75f9 closes https://github.com/official-stockfish/Stockfish/pull/2759 Bench: 4471740 --- src/evaluate.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3b0891a2..8b4a27bc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -143,16 +143,17 @@ namespace { constexpr Score ReachableOutpost = S( 31, 22); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); + constexpr Score QueenInfiltration = S( -2, 14); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnKingRing = S( 16, 0); - constexpr Score RookOnQueenFile = S( 5, 9); - constexpr Score SliderOnQueen = S( 59, 18); + constexpr Score RookOnQueenFile = S( 6, 11); + constexpr Score SliderOnQueen = S( 60, 18); constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score TrappedRook = S( 55, 13); - constexpr Score WeakQueen = S( 51, 14); - constexpr Score WeakQueenProtection = S( 15, 0); + constexpr Score WeakQueen = S( 56, 15); + constexpr Score WeakQueenProtection = S( 14, 0); #undef S @@ -373,6 +374,10 @@ namespace { Bitboard queenPinners; if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners)) score -= WeakQueen; + + // Bonus for queen on weak square in enemy camp + if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s)) + score += QueenInfiltration; } } if (T) From 8ef6c837b7907fe6db326d6adf73fd4efeba68c9 Mon Sep 17 00:00:00 2001 From: joergoster Date: Wed, 24 Jun 2020 18:04:28 +0200 Subject: [PATCH 18/34] Fix. Bench: 4471740 --- src/misc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/misc.h b/src/misc.h index bd866842..72f621a6 100644 --- a/src/misc.h +++ b/src/misc.h @@ -142,6 +142,7 @@ inline uint64_t mul_hi64(uint64_t a, uint64_t b) { uint64_t c3 = aL * bH + (uint32_t)c2; return aH * bH + (c2 >> 32) + (c3 >> 32); #endif +} /// Under Windows it is not possible for a process to run on more than one /// logical processor group. This usually means to be limited to use max 64 From 0e932757e5421cfc9aaba33cc4b0f50e4d3378f1 Mon Sep 17 00:00:00 2001 From: joergoster Date: Wed, 24 Jun 2020 20:18:32 +0200 Subject: [PATCH 19/34] Re-enable increment operator for Piece. No functional change. --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 921cb808..c5430157 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -983,7 +983,7 @@ bool EvalList::is_valid(const Position& pos) for (Piece pc = NO_PIECE; pc < PIECE_NB; ++pc) { auto pt = type_of(pc); - if (pt == NO_PIECE || pt == 7) // 存在しない駒 + if (pt == NO_PIECE_TYPE || pt == 7) // 存在しない駒 continue; // 駒pcのBonaPieceの開始番号 From 5e119f5139fd13816ef6091285e785f44f19b202 Mon Sep 17 00:00:00 2001 From: joergoster Date: Wed, 24 Jun 2020 20:22:56 +0200 Subject: [PATCH 20/34] Finally. --- src/types.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.h b/src/types.h index 2512fc29..ad2debca 100644 --- a/src/types.h +++ b/src/types.h @@ -309,6 +309,7 @@ ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_INCR_OPERATORS_ON(PieceType) +ENABLE_INCR_OPERATORS_ON(Piece) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(Rank) From bbe98576846ad44ca531cdb6f42bf3eefa7d47c5 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 23 Jun 2020 14:55:52 +0300 Subject: [PATCH 21/34] Do less futility pruning for captures. The idea of this patch is that if capture can be described as "less valuable piece takes more valuable piece" it's not really correct to add only piece value of captured piece to static evaluation since there can be more threats in other places and opponent can't really do much but recapture our capturing piece which leaves us space for more captures thus winning more material and increasing static eval. passed STC https://tests.stockfishchess.org/tests/view/5ef0167b122d6514328d760f LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 24736 W: 4838 L: 4607 D: 15291 Ptnml(0-2): 438, 2812, 5648, 3021, 449 passed LTC https://tests.stockfishchess.org/tests/view/5ef073bc122d6514328d7693 LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 46152 W: 5865 L: 5567 D: 34720 Ptnml(0-2): 312, 4160, 13886, 4354, 364 closes https://github.com/official-stockfish/Stockfish/pull/2761 bench 4789930 --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index 563d1aab..671ac489 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1026,6 +1026,7 @@ moves_loop: // When in check, search starts from here if ( !givesCheck && lmrDepth < 6 && !(PvNode && abs(bestValue) < 2) + && PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] && !ss->inCheck && ss->staticEval + 267 + 391 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; From 527d832a6de81c455cc8818e85c309fa1443f862 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 23 Jun 2020 10:41:53 +0200 Subject: [PATCH 22/34] Support ARCH=armv8 in Makefile (#2355) Tested with bench run after compiling with - g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516 - clang version 3.8.1-24 on ThunderX CN8890. closes https://github.com/official-stockfish/Stockfish/pull/2760 fixes https://github.com/official-stockfish/Stockfish/issues/2355 No functional change. --- src/Makefile | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 41c2aff6..83e0bb14 100644 --- a/src/Makefile +++ b/src/Makefile @@ -133,6 +133,12 @@ ifeq ($(ARCH),armv7) prefetch = yes endif +ifeq ($(ARCH),armv8) + arch = armv8-a + bits = 64 + prefetch = yes +endif + ifeq ($(ARCH),ppc-32) arch = ppc endif @@ -164,7 +170,7 @@ ifeq ($(COMP),gcc) CXX=g++ CXXFLAGS += -pedantic -Wextra -Wshadow - ifeq ($(ARCH),armv7) + ifeq ($(ARCH),$(filter $(ARCH),armv7 armv8)) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -221,7 +227,7 @@ ifeq ($(COMP),clang) endif endif - ifeq ($(ARCH),armv7) + ifeq ($(ARCH),$(filter $(ARCH),armv7 armv8)) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -391,6 +397,7 @@ help: @echo "ppc-64 > PPC 64-bit" @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" + @echo "armv8 > ARMv8 64-bit" @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" @echo "" @@ -492,7 +499,8 @@ config-sanity: @test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no" @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ - test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7" + test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ + test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" From 208c53df0fc289466def7deae58f687957efb734 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 24 Jun 2020 16:23:31 +0200 Subject: [PATCH 23/34] Remove 'Minimum Thinking Time' UCI option. the option was, since at least 2014, not correctly implemented, ignoring all dynamic adjustments to optimum time in search. Instead of fixing it, remove it, no need to expose an option that will influence time management negatively. closes https://github.com/official-stockfish/Stockfish/pull/2765 No functional change. --- src/timeman.cpp | 3 +-- src/ucioption.cpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index d27962b7..a61c36d7 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -35,7 +35,6 @@ TimeManagement Time; // Our global time management object void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { - TimePoint minThinkingTime = TimePoint(Options["Minimum Thinking Time"]); TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); TimePoint slowMover = TimePoint(Options["Slow Mover"]); TimePoint npmsec = TimePoint(Options["nodestime"]); @@ -91,7 +90,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { } // Never use more than 80% of the available time for this move - optimumTime = std::max(minThinkingTime, TimePoint(opt_scale * timeLeft)); + optimumTime = TimePoint(opt_scale * timeLeft); maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime)); if (Options["Ponder"]) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 7037ea57..871edb29 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -68,7 +68,6 @@ void init(OptionsMap& o) { o["MultiPV"] << Option(1, 1, 500); o["Skill Level"] << Option(20, 0, 20); o["Move Overhead"] << Option(10, 0, 5000); - o["Minimum Thinking Time"] << Option( 0, 0, 5000); o["Slow Mover"] << Option(100, 10, 1000); o["nodestime"] << Option(0, 0, 10000); o["UCI_Chess960"] << Option(false); From 11483fe6d942a4fee6fa272f72251d6b6d6d7454 Mon Sep 17 00:00:00 2001 From: UnaiCorzo Date: Tue, 23 Jun 2020 17:56:38 +0200 Subject: [PATCH 24/34] Makefile: support lto on mingw, default to 64bits Clean and organize uppercase and spaces fixes https://github.com/official-stockfish/Stockfish/issues/2731 closes https://github.com/official-stockfish/Stockfish/pull/2763 No functional change --- AUTHORS | 1 + src/Makefile | 34 +++++++++++----------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1cd7ff54..f08d71d3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -155,6 +155,7 @@ Tom Vijlbrief (tomtor) Tomasz Sobczyk (Sopel97) Torsten Franz (torfranz, tfranzer) Tracey Emery (basepr1me) +Unai Corzo (unaiic) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) diff --git a/src/Makefile b/src/Makefile index 83e0bb14..81731e66 100644 --- a/src/Makefile +++ b/src/Makefile @@ -54,7 +54,7 @@ endif ### Section 2. High-level Configuration ### ========================================================================== # -# flag --- Comp switch --- Description +# flag --- Comp switch --- Description # ---------------------------------------------------------------------------- # # debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode @@ -77,43 +77,42 @@ endif optimize = yes debug = no sanitize = no -bits = 32 +bits = 64 prefetch = no popcnt = no sse = no pext = no ### 2.2 Architecture specific - ifeq ($(ARCH),general-32) arch = any + bits = 32 endif ifeq ($(ARCH),x86-32-old) arch = i386 + bits = 32 endif ifeq ($(ARCH),x86-32) arch = i386 + bits = 32 prefetch = yes sse = yes endif ifeq ($(ARCH),general-64) arch = any - bits = 64 endif ifeq ($(ARCH),x86-64) arch = x86_64 - bits = 64 prefetch = yes sse = yes endif ifeq ($(ARCH),x86-64-modern) arch = x86_64 - bits = 64 prefetch = yes popcnt = yes sse = yes @@ -121,7 +120,6 @@ endif ifeq ($(ARCH),x86-64-bmi2) arch = x86_64 - bits = 64 prefetch = yes popcnt = yes sse = yes @@ -131,6 +129,7 @@ endif ifeq ($(ARCH),armv7) arch = armv7 prefetch = yes + bits = 32 endif ifeq ($(ARCH),armv8) @@ -141,22 +140,20 @@ endif ifeq ($(ARCH),ppc-32) arch = ppc + bits = 32 endif ifeq ($(ARCH),ppc-64) arch = ppc64 - bits = 64 popcnt = yes prefetch = yes endif - ### ========================================================================== -### Section 3. Low-level configuration +### Section 3. Low-level Configuration ### ========================================================================== ### 3.1 Selecting compiler (default = gcc) - CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++11 $(EXTRACXXFLAGS) DEPENDFLAGS += -std=c++11 LDFLAGS += $(EXTRALDFLAGS) @@ -347,17 +344,10 @@ endif ### needs access to the optimization flags. ifeq ($(optimize),yes) ifeq ($(debug), no) - ifeq ($(comp),$(filter $(comp),gcc clang)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif - - ifeq ($(comp),mingw) - ifeq ($(KERNEL),Linux) - CXXFLAGS += -flto - LDFLAGS += $(CXXFLAGS) - endif - endif endif endif @@ -368,9 +358,8 @@ ifeq ($(OS), Android) LDFLAGS += -fPIE -pie endif - ### ========================================================================== -### Section 4. Public targets +### Section 4. Public Targets ### ========================================================================== help: @@ -468,7 +457,7 @@ default: help ### ========================================================================== -### Section 5. Private targets +### Section 5. Private Targets ### ========================================================================== all: $(EXE) .depend @@ -551,4 +540,3 @@ icc-profile-use: -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null -include .depend - From ab5cd8340f2f7f8730aa7c77476edf4a98a166e4 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 24 Jun 2020 22:19:58 +0200 Subject: [PATCH 25/34] Small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2756 No functional change --- src/bitboard.h | 29 ++++++++++++++++++----------- src/endgame.cpp | 14 +++++++------- src/evaluate.cpp | 26 +++++++++++++++----------- src/material.cpp | 19 +++++++++++-------- src/material.h | 2 +- src/misc.cpp | 21 ++++++++++++--------- src/pawns.cpp | 7 +++++++ src/position.cpp | 4 ++-- src/position.h | 1 + src/psqt.cpp | 11 ++++++----- src/search.cpp | 35 +++++++++++++++++++---------------- src/thread.cpp | 13 ++++++++++--- src/timeman.cpp | 11 ++++++----- src/tt.cpp | 3 ++- src/tt.h | 4 ++-- src/tune.cpp | 2 +- src/types.h | 6 +++--- src/ucioption.cpp | 2 +- 18 files changed, 124 insertions(+), 86 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 0f55810c..1c598108 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -110,6 +110,7 @@ inline Bitboard square_bb(Square s) { return SquareBB[s]; } + /// Overloads of bitwise operators between a Bitboard and a Square for testing /// whether a given bit is set in a bitboard, and for setting and clearing bits. @@ -200,10 +201,11 @@ inline Bitboard adjacent_files_bb(Square s) { return shift(file_bb(s)) | shift(file_bb(s)); } -/// line_bb(Square, Square) returns a Bitboard representing an entire line -/// (from board edge to board edge) that intersects the given squares. -/// If the given squares are not on a same file/rank/diagonal, return 0. -/// Ex. line_bb(SQ_C4, SQ_F7) returns a bitboard with the A2-G8 diagonal. + +/// line_bb(Square, Square) returns a bitboard representing an entire line, +/// from board edge to board edge, that intersects the given squares. If the +/// given squares are not on a same file/rank/diagonal, returns 0. For instance, +/// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal. inline Bitboard line_bb(Square s1, Square s2) { @@ -211,10 +213,11 @@ inline Bitboard line_bb(Square s1, Square s2) { return LineBB[s1][s2]; } -/// between_bb() returns a Bitboard representing squares that are linearly -/// between the given squares (excluding the given squares). -/// If the given squares are not on a same file/rank/diagonal, return 0. -/// Ex. between_bb(SQ_C4, SQ_F7) returns a bitboard with squares D5 and E6. + +/// between_bb() returns a bitboard representing squares that are linearly +/// between the given squares (excluding the given squares). If the given +/// squares are not on a same file/rank/diagonal, return 0. For instance, +/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6. inline Bitboard between_bb(Square s1, Square s2) { Bitboard b = line_bb(s1, s2) & ((AllSquares << s1) ^ (AllSquares << s2)); @@ -241,8 +244,8 @@ inline Bitboard forward_file_bb(Color c, Square s) { /// pawn_attack_span() returns a bitboard representing all the squares that can -/// be attacked by a pawn of the given color when it moves along its file, -/// starting from the given square. +/// be attacked by a pawn of the given color when it moves along its file, starting +/// from the given square. inline Bitboard pawn_attack_span(Color c, Square s) { return forward_ranks_bb(c, s) & adjacent_files_bb(s); @@ -276,7 +279,9 @@ template<> inline int distance(Square x, Square y) { return SquareDistan inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); } -/// Return the target square bitboard if we do not step off the board, empty otherwise + +/// safe_destination() returns the bitboard of target square for the given step +/// from the given square. If the step is off the board, returns empty bitboard. inline Bitboard safe_destination(Square s, int step) { @@ -284,6 +289,7 @@ inline Bitboard safe_destination(Square s, int step) return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); } + /// attacks_bb(Square) returns the pseudo attacks of the give piece type /// assuming an empty board. @@ -295,6 +301,7 @@ inline Bitboard attacks_bb(Square s) { return PseudoAttacks[Pt][s]; } + /// attacks_bb(Square, Bitboard) returns the attacks by the given piece /// assuming the board is occupied according to the passed Bitboard. /// Sliding piece attacks do not continue passed an occupied square. diff --git a/src/endgame.cpp b/src/endgame.cpp index d9e76348..be0755a8 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -268,7 +268,7 @@ Value Endgame::operator()(const Position& pos) const { } -/// KQ vs KR. This is almost identical to KX vs K: We give the attacking +/// KQ vs KR. This is almost identical to KX vs K: we give the attacking /// king a bonus for having the kings close together, and for forcing the /// defending king towards the edge. If we also take care to avoid null move for /// the defending side in the search, this is usually sufficient to win KQ vs KR. @@ -291,7 +291,7 @@ Value Endgame::operator()(const Position& pos) const { /// KNN vs KP. Very drawish, but there are some mate opportunities if we can -// press the weakSide King to a corner before the pawn advances too much. +/// press the weakSide King to a corner before the pawn advances too much. template<> Value Endgame::operator()(const Position& pos) const { @@ -352,7 +352,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); // There's potential for a draw if our pawn is blocked on the 7th rank, - // the bishop cannot attack it or they only have one pawn left + // the bishop cannot attack it or they only have one pawn left. if ( relative_rank(strongSide, weakPawn) == RANK_7 && (strongPawns & (weakPawn + pawn_push(weakSide))) && (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns))) @@ -365,7 +365,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // closer. (I think this rule only fails in practically // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w // and positions where qsearch will immediately correct the - // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) + // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w). if ( relative_rank(strongSide, weakKing) >= RANK_7 && weakKingDist <= 2 && weakKingDist <= strongKingDist) @@ -576,7 +576,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { } -/// K and two or more pawns vs K. There is just a single rule here: If all pawns +/// K and two or more pawns vs K. There is just a single rule here: if all pawns /// are on the same rook file and are blocked by the defending king, it's a draw. template<> ScaleFactor Endgame::operator()(const Position& pos) const { @@ -693,7 +693,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { } -/// KBP vs KN. There is a single rule: If the defending king is somewhere along +/// KBP vs KN. There is a single rule: if the defending king is somewhere along /// the path of the pawn, and the square of the king is not of the same color as /// the stronger side's bishop, it's a draw. template<> @@ -717,7 +717,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { /// KP vs KP. This is done by removing the weakest side's pawn and probing the -/// KP vs K bitbase: If the weakest side has a draw without the pawn, it probably +/// KP vs K bitbase: if the weakest side has a draw without the pawn, it probably /// has at least a draw with the pawn as well. The exception is when the stronger /// side's pawn is far advanced and not on a rook file; in this case it is often /// possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8b4a27bc..60ec9c72 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -127,23 +127,23 @@ namespace { }; // Assorted bonuses and penalties - constexpr Score BishopPawns = S( 3, 7); + constexpr Score BishopKingProtector = S( 6, 9); constexpr Score BishopOnKingRing = S( 24, 0); + constexpr Score BishopOutpost = S( 30, 23); + constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); - constexpr Score BishopKingProtector = S( 6, 9); constexpr Score KnightKingProtector = S( 8, 9); constexpr Score KnightOnQueen = S( 16, 11); + constexpr Score KnightOutpost = S( 56, 36); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score KnightOutpost = S( 56, 36); - constexpr Score BishopOutpost = S( 30, 23); - constexpr Score ReachableOutpost = S( 31, 22); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score QueenInfiltration = S( -2, 14); + constexpr Score ReachableOutpost = S( 31, 22); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnKingRing = S( 16, 0); constexpr Score RookOnQueenFile = S( 6, 11); @@ -152,8 +152,9 @@ namespace { constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score TrappedRook = S( 55, 13); - constexpr Score WeakQueen = S( 56, 15); constexpr Score WeakQueenProtection = S( 14, 0); + constexpr Score WeakQueen = S( 56, 15); + #undef S @@ -216,6 +217,7 @@ namespace { // Evaluation::initialize() computes king and pawn attacks, and the king ring // bitboard for a given color. This is done at the beginning of the evaluation. + template template void Evaluation::initialize() { @@ -255,6 +257,7 @@ namespace { // Evaluation::pieces() scores pieces of a given color and type + template template Score Evaluation::pieces() { @@ -377,7 +380,7 @@ namespace { // Bonus for queen on weak square in enemy camp if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s)) - score += QueenInfiltration; + score += QueenInfiltration; } } if (T) @@ -388,6 +391,7 @@ namespace { // Evaluation::king() assigns bonuses and penalties to a king of a given color + template template Score Evaluation::king() const { @@ -496,6 +500,7 @@ namespace { // Evaluation::threats() assigns bonuses according to the types of the // attacking and the attacked pieces. + template template Score Evaluation::threats() const { @@ -721,8 +726,8 @@ namespace { // Evaluation::winnable() adjusts the mg and eg score components based on the - // known attacking/defending status of the players. - // A single value is derived from the mg and eg values and returned. + // known attacking/defending status of the players. A single value is derived + // by interpolation from the mg and eg values and returned. template Value Evaluation::winnable(Score score) const { @@ -828,12 +833,11 @@ namespace { return pos.side_to_move() == WHITE ? v : -v; // Main evaluation begins here - initialize(); initialize(); // Pieces evaluated first (also populates attackedBy, attackedBy2). - // Note that the order of evaluation of the terms is left unspecified + // Note that the order of evaluation of the terms is left unspecified. score += pieces() - pieces() + pieces() - pieces() + pieces() - pieces() diff --git a/src/material.cpp b/src/material.cpp index 93699f5f..bb25d3ca 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -44,12 +44,12 @@ namespace { constexpr int QuadraticTheirs[][PIECE_TYPE_NB] = { // THEIR PIECES // pair pawn knight bishop rook queen - { 0 }, // Bishop pair - { 36, 0 }, // Pawn - { 9, 63, 0 }, // Knight OUR PIECES - { 59, 65, 42, 0 }, // Bishop - { 46, 39, 24, -24, 0 }, // Rook - { 97, 100, -42, 137, 268, 0 } // Queen + { }, // Bishop pair + { 36, }, // Pawn + { 9, 63, }, // Knight OUR PIECES + { 59, 65, 42, }, // Bishop + { 46, 39, 24, -24, }, // Rook + { 97, 100, -42, 137, 268, } // Queen }; // Endgame evaluation and scaling functions are accessed directly and not through @@ -79,8 +79,10 @@ namespace { && pos.count(~us) >= 1; } + /// imbalance() calculates the imbalance by comparing the piece count of each /// piece type for both colors. + template int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { @@ -94,9 +96,9 @@ namespace { if (!pieceCount[Us][pt1]) continue; - int v = 0; + int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1]; - for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) + for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2) v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2]; @@ -110,6 +112,7 @@ namespace { namespace Material { + /// Material::probe() looks up the current position's material configuration in /// the material hash table. It returns a pointer to the Entry if the position /// is found. Otherwise a new Entry is computed and stored there, so we don't diff --git a/src/material.h b/src/material.h index 9ab1d81c..21647f23 100644 --- a/src/material.h +++ b/src/material.h @@ -44,7 +44,7 @@ struct Entry { bool specialized_eval_exists() const { return evaluationFunction != nullptr; } Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } - // scale_factor takes a position and a color as input and returns a scale factor + // scale_factor() takes a position and a color as input and returns a scale factor // for the given color. We have to provide the position in addition to the color // because the scale factor may also be a function which should be applied to // the position. For instance, in KBP vs K endgames, the scaling function looks diff --git a/src/misc.cpp b/src/misc.cpp index c6254784..2bc05c5b 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -294,9 +294,10 @@ void prefetch(void* addr) { #endif -/// aligned_ttmem_alloc will return suitably aligned memory, and if possible use large pages. -/// The returned pointer is the aligned one, while the mem argument is the one that needs to be passed to free. -/// With c++17 some of this functionality can be simplified. +/// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages. +/// The returned pointer is the aligned one, while the mem argument is the one that needs +/// to be passed to free. With c++17 some of this functionality could be simplified. + #if defined(__linux__) && !defined(__ANDROID__) void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { @@ -336,17 +337,17 @@ static void* aligned_ttmem_alloc_large_pages(size_t allocSize) { tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, - // we still need to query GetLastError() to ensure that the privileges were actually obtained... + // we still need to query GetLastError() to ensure that the privileges were actually obtained. if (AdjustTokenPrivileges( hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) && GetLastError() == ERROR_SUCCESS) { - // round up size to full pages and allocate + // Round up size to full pages and allocate allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); mem = VirtualAlloc( NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); - // privilege no longer needed, restore previous state + // Privilege no longer needed, restore previous state AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL); } } @@ -360,7 +361,7 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { static bool firstCall = true; - // try to allocate large pages + // Try to allocate large pages mem = aligned_ttmem_alloc_large_pages(allocSize); // Suppress info strings on the first call. The first call occurs before 'uci' @@ -374,7 +375,7 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { } firstCall = false; - // fall back to regular, page aligned, allocation if necessary + // Fall back to regular, page aligned, allocation if necessary if (!mem) mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); @@ -394,7 +395,9 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { #endif -/// aligned_ttmem_free will free the previously allocated ttmem + +/// aligned_ttmem_free() will free the previously allocated ttmem + #if defined(_WIN64) void aligned_ttmem_free(void* mem) { diff --git a/src/pawns.cpp b/src/pawns.cpp index 597dff2b..d741b2ef 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -66,6 +66,12 @@ namespace { #undef S #undef V + + /// evaluate() calculates a score for the static pawn structure of the given position. + /// We cannot use the location of pieces or king in this function, as the evaluation + /// of the pawn structure will be stored in a small cache for speed reasons, and will + /// be re-used even when the pieces have moved. + template Score evaluate(const Position& pos, Pawns::Entry* e) { @@ -170,6 +176,7 @@ namespace { namespace Pawns { + /// Pawns::probe() looks up the current position's pawns configuration in /// the pawns hash table. It returns a pointer to the Entry if the position /// is found. Otherwise a new Entry is computed and stored there, so we don't diff --git a/src/position.cpp b/src/position.cpp index c9db6224..471ef01f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -105,8 +105,7 @@ Key cuckoo[8192]; Move cuckooMove[8192]; -/// Position::init() initializes at startup the various arrays used to compute -/// hash keys. +/// Position::init() initializes at startup the various arrays used to compute hash keys void Position::init() { @@ -1112,6 +1111,7 @@ bool Position::see_ge(Move m, Value threshold) const { return bool(res); } + /// Position::is_draw() tests whether the position is drawn by 50-move rule /// or by repetition. It does not detect stalemates. diff --git a/src/position.h b/src/position.h index 8f8c8f7a..8cfa3920 100644 --- a/src/position.h +++ b/src/position.h @@ -56,6 +56,7 @@ struct StateInfo { int repetition; }; + /// A list to keep track of the position states along the setup moves (from the /// start position to the position just before the search starts). Needed by /// 'draw by repetition' detection. Use a std::deque because pointers to diff --git a/src/psqt.cpp b/src/psqt.cpp index 27c7a36f..c5da9785 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -101,9 +101,10 @@ constexpr Score PBonus[RANK_NB][FILE_NB] = Score psq[PIECE_NB][SQUARE_NB]; -// init() initializes piece-square tables: the white halves of the tables are -// copied from Bonus[] adding the piece value, then the black halves of the -// tables are initialized by flipping and changing the sign of the white scores. + +// PSQT::init() initializes piece-square tables: the white halves of the tables are +// copied from Bonus[] and PBonus[], adding the piece value, then the black halves of +// the tables are initialized by flipping and changing the sign of the white scores. void init() { for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING}) @@ -113,8 +114,8 @@ void init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) { File f = File(edge_distance(file_of(s))); - psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] - : 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][flip_rank(s)] = -psq[pc][s]; } } diff --git a/src/search.cpp b/src/search.cpp index 671ac489..1e2980cb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -525,7 +525,7 @@ void Thread::search() { double totalTime = rootMoves.size() == 1 ? 0 : Time.optimum() * fallingEval * reduction * bestMoveInstability; - // Stop the search if we have exceeded the totalTime, at least 1ms search. + // Stop the search if we have exceeded the totalTime, at least 1ms search if (Time.elapsed() > totalTime) { // If we are allowed to ponder do not stop the search now but @@ -627,7 +627,7 @@ namespace { || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) - : value_draw(pos.this_thread()); + : value_draw(pos.this_thread()); // Step 3. Mate distance pruning. Even if we mate at the next move our score // would be at best mate_in(ss->ply+1), but if alpha is already bigger because @@ -767,9 +767,10 @@ namespace { // Step 6. Static evaluation of the position if (ss->inCheck) { + // Skip early pruning when in check ss->staticEval = eval = VALUE_NONE; improving = false; - goto moves_loop; // Skip early pruning when in check + goto moves_loop; } else if (ttHit) { @@ -1028,7 +1029,8 @@ moves_loop: // When in check, search starts from here && !(PvNode && abs(bestValue) < 2) && PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] && !ss->inCheck - && ss->staticEval + 267 + 391 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) + && ss->staticEval + 267 + 391 * lmrDepth + + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; // See based pruning @@ -1074,8 +1076,8 @@ moves_loop: // When in check, search starts from here else if (singularBeta >= beta) return singularBeta; - // If the eval of ttMove is greater than beta we try also if there is an other move that - // pushes it over beta, if so also produce a cutoff + // If the eval of ttMove is greater than beta we try also if there is another + // move that pushes it over beta, if so also produce a cutoff. else if (ttValue >= beta) { ss->excludedMove = move; @@ -1153,7 +1155,7 @@ moves_loop: // When in check, search starts from here if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; - // Reduction if other threads are searching this position. + // Reduction if other threads are searching this position if (th.marked()) r++; @@ -1290,7 +1292,7 @@ moves_loop: // When in check, search starts from here rm.pv.push_back(*m); // We record how often the best move has been changed in each - // iteration. This information is used for time management: When + // iteration. This information is used for time management: when // the best move changes frequently, we allocate some more time. if (moveCount > 1) ++thisThread->bestMoveChanges; @@ -1524,7 +1526,7 @@ moves_loop: // When in check, search starts from here } } - // Don't search moves with negative SEE values + // Do not search moves with negative SEE values if ( !ss->inCheck && !pos.see_ge(move)) continue; @@ -1571,7 +1573,7 @@ moves_loop: // When in check, search starts from here } } - // All legal moves have been searched. A special case: If we're in check + // All legal moves have been searched. A special case: if we're in check // and no legal moves were found, it is checkmate. if (ss->inCheck && bestValue == -VALUE_INFINITE) return mated_in(ss->ply); // Plies to mate from the root @@ -1588,7 +1590,7 @@ moves_loop: // When in check, search starts from here // value_to_tt() adjusts a mate or TB score from "plies to mate from the root" to - // "plies to mate from the current position". standard scores are unchanged. + // "plies to mate from the current position". Standard scores are unchanged. // The function is called before storing a value in the transposition table. Value value_to_tt(Value v, int ply) { @@ -1600,11 +1602,11 @@ moves_loop: // When in check, search starts from here } - // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate or TB score - // from the transposition table (which refers to the plies to mate/be mated - // from current position) to "plies to mate/be mated (TB win/loss) from the root". - // However, for mate scores, to avoid potentially false mate scores related to the 50 moves rule, - // and the graph history interaction, return an optimal TB score instead. + // value_from_tt() is the inverse of value_to_tt(): it adjusts a mate or TB score + // from the transposition table (which refers to the plies to mate/be mated from + // current position) to "plies to mate/be mated (TB win/loss) from the root". However, + // for mate scores, to avoid potentially false mate scores related to the 50 moves rule + // and the graph history interaction, we return an optimal TB score instead. Value value_from_tt(Value v, int ply, int r50c) { @@ -1764,6 +1766,7 @@ moves_loop: // When in check, search starts from here } // namespace + /// MainThread::check_time() is used to print debug info and, more importantly, /// to detect when we are out of available time and thus stop the search. diff --git a/src/thread.cpp b/src/thread.cpp index a27a60c6..a0ee2b25 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -52,6 +52,7 @@ Thread::~Thread() { stdThread.join(); } + /// Thread::bestMoveCount(Move move) return best move counter for the given root move int Thread::best_move_count(Move move) const { @@ -62,6 +63,7 @@ int Thread::best_move_count(Move move) const { return rm != rootMoves.begin() + pvLast ? rm->bestMoveCount : 0; } + /// Thread::clear() reset histories, usually before a new game void Thread::clear() { @@ -81,6 +83,7 @@ void Thread::clear() { } } + /// Thread::start_searching() wakes up the thread that will start the search void Thread::start_searching() { @@ -158,7 +161,8 @@ void ThreadPool::set(size_t requested) { } } -/// ThreadPool::clear() sets threadPool data to initial values. + +/// ThreadPool::clear() sets threadPool data to initial values void ThreadPool::clear() { @@ -170,6 +174,7 @@ void ThreadPool::clear() { main()->previousTimeReduction = 1.0; } + /// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and /// returns immediately. Main thread will wake up other threads and start the search. @@ -250,7 +255,8 @@ Thread* ThreadPool::get_best_thread() const { return bestThread; } -/// Start non-main threads. + +/// Start non-main threads void ThreadPool::start_searching() { @@ -259,7 +265,8 @@ void ThreadPool::start_searching() { th->start_searching(); } -/// Wait for non-main threads. + +/// Wait for non-main threads void ThreadPool::wait_for_search_finished() const { diff --git a/src/timeman.cpp b/src/timeman.cpp index a61c36d7..546eadd2 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -28,10 +28,11 @@ TimeManagement Time; // Our global time management object -/// init() is called at the beginning of the search and calculates the bounds -/// of time allowed for the current game ply. We currently support: -// 1) x basetime (+z increment) -// 2) x moves in y seconds (+z increment) + +/// TimeManagement::init() is called at the beginning of the search and calculates +/// the bounds of time allowed for the current game ply. We currently support: +// 1) x basetime (+ z increment) +// 2) x moves in y seconds (+ z increment) void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { @@ -60,7 +61,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { startTime = limits.startTime; - //Maximum move horizon of 50 moves + // Maximum move horizon of 50 moves int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; // Make sure timeLeft is > 0 since we may use it as a divisor diff --git a/src/tt.cpp b/src/tt.cpp index d0a5d4e0..34590903 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -30,7 +30,7 @@ TranspositionTable TT; // Our global transposition table -/// TTEntry::save populates the TTEntry with a new node's data, possibly +/// TTEntry::save() populates the TTEntry with a new node's data, possibly /// overwriting an old position. Update is not atomic and can be racy. void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { @@ -107,6 +107,7 @@ void TranspositionTable::clear() { th.join(); } + /// TranspositionTable::probe() looks up the current position in the transposition /// table. It returns true and a pointer to the TTEntry if the position is found. /// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry diff --git a/src/tt.h b/src/tt.h index 3e1d0e99..e18db8ce 100644 --- a/src/tt.h +++ b/src/tt.h @@ -60,8 +60,8 @@ private: /// A TranspositionTable is an array of Cluster, of size clusterCount. Each /// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry /// contains information on exactly one position. The size of a Cluster should -/// divide the size of a cache line for best performance, -/// as the cacheline is prefetched when possible. +/// divide the size of a cache line for best performance, as the cacheline is +/// prefetched when possible. class TranspositionTable { diff --git a/src/tune.cpp b/src/tune.cpp index 696b4cb8..c1b1c76b 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -70,7 +70,7 @@ static void make_option(const string& n, int v, const SetRange& r) { Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune); LastOption = &Options[n]; - // Print formatted parameters, ready to be copy-pasted in fishtest + // Print formatted parameters, ready to be copy-pasted in Fishtest std::cout << n << "," << v << "," << r(v).first << "," << r(v).second << "," diff --git a/src/types.h b/src/types.h index 969d4e65..0c512f5b 100644 --- a/src/types.h +++ b/src/types.h @@ -349,16 +349,16 @@ constexpr Color operator~(Color c) { return Color(c ^ BLACK); // Toggle color } -constexpr Square flip_rank(Square s) { +constexpr Square flip_rank(Square s) { // Swap A1 <-> A8 return Square(s ^ SQ_A8); } -constexpr Square flip_file(Square s) { +constexpr Square flip_file(Square s) { // Swap A1 <-> H1 return Square(s ^ SQ_H1); } constexpr Piece operator~(Piece pc) { - return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT + return Piece(pc ^ 8); // Swap color of piece B_KNIGHT <-> W_KNIGHT } constexpr CastlingRights operator&(Color c, CastlingRights cr) { diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 871edb29..c268c975 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -52,7 +52,7 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const } -/// init() initializes the UCI options to their hard-coded default values +/// UCI::init() initializes the UCI options to their hard-coded default values void init(OptionsMap& o) { From 7a3c3eacdfc6915199a0034aaa0045bec228058b Mon Sep 17 00:00:00 2001 From: FireFather Date: Wed, 24 Jun 2020 22:41:00 +0200 Subject: [PATCH 26/34] added header guards 5 include files in \eval\nnue\architectures --- src/eval/nnue/architectures/halfkp-cr-ep_256x2-32-32.h | 4 ++++ src/eval/nnue/architectures/halfkp_256x2-32-32.h | 4 ++++ src/eval/nnue/architectures/k-p-cr-ep_256x2-32-32.h | 4 ++++ src/eval/nnue/architectures/k-p-cr_256x2-32-32.h | 4 ++++ src/eval/nnue/architectures/k-p_256x2-32-32.h | 3 +++ 5 files changed, 19 insertions(+) diff --git a/src/eval/nnue/architectures/halfkp-cr-ep_256x2-32-32.h b/src/eval/nnue/architectures/halfkp-cr-ep_256x2-32-32.h index 7063f334..9f1f97c0 100644 --- a/src/eval/nnue/architectures/halfkp-cr-ep_256x2-32-32.h +++ b/src/eval/nnue/architectures/halfkp-cr-ep_256x2-32-32.h @@ -1,5 +1,8 @@ // NNUE評価関数で用いる入力特徴量とネットワーク構造の定義 +#ifndef HALFKP_CR_EP_256X2_32_32_H +#define HALFKP_CR_EP_256X2_32_32_H + #include "../features/feature_set.h" #include "../features/half_kp.h" #include "../features/castling_right.h" @@ -36,3 +39,4 @@ namespace Eval { } // namespace NNUE } // namespace Eval +#endif // HALFKP_CR_EP_256X2_32_32_H \ No newline at end of file diff --git a/src/eval/nnue/architectures/halfkp_256x2-32-32.h b/src/eval/nnue/architectures/halfkp_256x2-32-32.h index 9b25ee54..c79747c3 100644 --- a/src/eval/nnue/architectures/halfkp_256x2-32-32.h +++ b/src/eval/nnue/architectures/halfkp_256x2-32-32.h @@ -1,5 +1,8 @@ サソ// NNUE隧穂セ。髢「謨ー縺ァ逕ィ縺繧句・蜉帷音蠕エ驥上→繝阪ャ繝医Ρ繝シ繧ッ讒矩縺ョ螳夂セゥ +#ifndef HALFKP_256X2_32_32_H +#define HALFKP_256X2_32_32_H + #include "../features/feature_set.h" #include "../features/half_kp.h" @@ -33,3 +36,4 @@ using Network = Layers::OutputLayer; } // namespace NNUE } // namespace Eval +#endif // HALFKP_256X2_32_32_H diff --git a/src/eval/nnue/architectures/k-p-cr-ep_256x2-32-32.h b/src/eval/nnue/architectures/k-p-cr-ep_256x2-32-32.h index 17871169..dc761866 100644 --- a/src/eval/nnue/architectures/k-p-cr-ep_256x2-32-32.h +++ b/src/eval/nnue/architectures/k-p-cr-ep_256x2-32-32.h @@ -1,5 +1,8 @@ // NNUE評価関数で用いる入力特徴量とネットワーク構造の定義 +#ifndef K_P_CR_EP_256X2_32_32_H +#define K_P_CR_EP_256X2_32_32_H + #include "../features/feature_set.h" #include "../features/k.h" #include "../features/p.h" @@ -36,3 +39,4 @@ namespace Eval { } // namespace NNUE } // namespace Eval +#endif // K_P_CR_EP_256X2_32_32_H \ No newline at end of file diff --git a/src/eval/nnue/architectures/k-p-cr_256x2-32-32.h b/src/eval/nnue/architectures/k-p-cr_256x2-32-32.h index 9ce7ecf1..331cb4f2 100644 --- a/src/eval/nnue/architectures/k-p-cr_256x2-32-32.h +++ b/src/eval/nnue/architectures/k-p-cr_256x2-32-32.h @@ -1,5 +1,8 @@ // NNUE評価関数で用いる入力特徴量とネットワーク構造の定義 +#ifndef K_P_CR_256X2_32_32_H +#define K_P_CR_256X2_32_32_H + #include "../features/feature_set.h" #include "../features/k.h" #include "../features/p.h" @@ -35,3 +38,4 @@ namespace Eval { } // namespace NNUE } // namespace Eval +#endif // K_P_CR_256X2_32_32_H \ No newline at end of file diff --git a/src/eval/nnue/architectures/k-p_256x2-32-32.h b/src/eval/nnue/architectures/k-p_256x2-32-32.h index b77aeaa6..2576ddfa 100644 --- a/src/eval/nnue/architectures/k-p_256x2-32-32.h +++ b/src/eval/nnue/architectures/k-p_256x2-32-32.h @@ -1,4 +1,6 @@ サソ// NNUE隧穂セ。髢「謨ー縺ァ逕ィ縺繧句・蜉帷音蠕エ驥上→繝阪ャ繝医Ρ繝シ繧ッ讒矩縺ョ螳夂セゥ +#ifndef K_P_256X2_32_32_H +#define K_P_256X2_32_32_H #include "../features/feature_set.h" #include "../features/k.h" @@ -33,3 +35,4 @@ using Network = Layers::OutputLayer; } // namespace NNUE } // namespace Eval +#endif // K_P_256X2_32_32_H \ No newline at end of file From 8c8a30233c3742dbd60d3998c9e91ca4c783a147 Mon Sep 17 00:00:00 2001 From: FireFather Date: Thu, 25 Jun 2020 04:38:39 +0200 Subject: [PATCH 27/34] Update evaluate_nnue.cpp --- src/eval/nnue/evaluate_nnue.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/eval/nnue/evaluate_nnue.cpp b/src/eval/nnue/evaluate_nnue.cpp index ce478783..46b3b5f9 100644 --- a/src/eval/nnue/evaluate_nnue.cpp +++ b/src/eval/nnue/evaluate_nnue.cpp @@ -250,10 +250,14 @@ void load_eval() { if (!result) { // 隱ュ縺ソ霎シ縺ソ繧ィ繝ゥ繝シ縺ョ縺ィ縺咲オゆコ縺励※縺上l縺ェ縺縺ィ蝗ー繧九 - std::cout << "Error! : failed to read " << NNUE::kFileName << std::endl; - my_exit(); + std::cout << "Error! " << NNUE::kFileName << " not found or wrong format" << std::endl; + //my_exit(); } + else + std::cout << "info string NNUE " << NNUE::kFileName << " found & loaded" << std::endl; } + else + std::cout << "info string NNUE " << NNUE::kFileName << " not loaded" << std::endl; } // 蛻晄悄蛹 From a5fb69008cd30901f9ac3d2e74684f38c3b51014 Mon Sep 17 00:00:00 2001 From: joergoster Date: Thu, 25 Jun 2020 15:43:33 +0200 Subject: [PATCH 28/34] Bugfix. No legal move is either mate or stalemate. --- src/learn/learner.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index 526c027c..221a561e 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -504,16 +504,24 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id) } if (pos.is_draw(ply)) { - // Do not write if draw. - break; +#if defined (LEARN_GENSFEN_USE_DRAW_RESULT) + flush_psv(0); +#endif + // Do not write if draw. + break; } // 蜈ィ鬧偵&繧後※隧ー繧薙〒縺縺溘j縺励↑縺縺具シ - if (MoveList(pos).size() == 0) + if (MoveList(pos).size() == 0) // Can be mate or stalemate { // (縺薙ョ螻髱「縺ョ荳縺、蜑阪ョ螻髱「縺セ縺ァ縺ッ譖ク縺榊コ縺) // Write the positions other than this position if checkmated. - flush_psv(-1); + if (pos.checkers()) // Mate + flush_psv(-1); +#if defined (LEARN_GENSFEN_USE_DRAW_RESULT) + else // Stalemate + flush_psv(0); +#endif break; } @@ -578,7 +586,7 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id) if (pos.is_draw(0)) { #if defined (LEARN_GENSFEN_USE_DRAW_RESULT) // 蠑輔″蛻縺代r譖ク縺榊コ縺吶→縺 - flush_psv(is_win); + flush_psv(0); #endif break; } From 2af46deede06386ea483d500cece4deb4ed1a58f Mon Sep 17 00:00:00 2001 From: joergoster Date: Thu, 25 Jun 2020 15:52:19 +0200 Subject: [PATCH 29/34] Fix include. --- src/types.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.h b/src/types.h index 6fe6d154..a4a9f315 100644 --- a/src/types.h +++ b/src/types.h @@ -40,6 +40,7 @@ #include #include +#include #include #include #include From 0761d9504e32b15cde4aa5c36b9d440fb84b8e89 Mon Sep 17 00:00:00 2001 From: rqs Date: Sat, 27 Jun 2020 13:06:05 +0900 Subject: [PATCH 30/34] add convert_bin and option for draw positions --- src/learn/learn.h | 6 ++-- src/learn/learner.cpp | 73 +++++++++++++++++++++++++++++-------------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/learn/learn.h b/src/learn/learn.h index 58a017bd..8e3172d3 100644 --- a/src/learn/learn.h +++ b/src/learn/learn.h @@ -165,8 +165,8 @@ typedef float LearnFloatType; // 蠑輔″蛻縺代↓閾ウ縺」縺溘→縺阪√◎繧後r謨吝クォ螻髱「縺ィ縺励※譖ク縺榊コ縺 // 縺薙l繧偵☆繧九⊇縺縺瑚憶縺縺九←縺縺九ッ蠕ョ螯吶 // #define LEARN_GENSFEN_USE_DRAW_RESULT - - +extern bool use_draw_in_training; +extern bool use_hash_in_training; // ====================== // configure // ====================== @@ -234,4 +234,4 @@ namespace Learner #endif -#endif // ifndef _LEARN_H_ \ No newline at end of file +#endif // ifndef _LEARN_H_ diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index 221a561e..09af98d3 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -84,6 +84,10 @@ #include #endif +bool use_draw_in_training=false; +bool use_draw_in_validation=false; +bool use_hash_in_training=true; + using namespace std; //// 縺薙l縺ッ謗「邏「驛ィ縺ァ螳夂セゥ縺輔l縺ヲ縺繧九b縺ョ縺ィ縺吶k縲 @@ -1248,11 +1252,8 @@ struct SfenReader { if (eval_limit < abs(p.score)) continue; -#if !defined (LEARN_GENSFEN_USE_DRAW_RESULT) - if (p.game_result == 0) + if (!use_draw_in_validation && p.game_result == 0) continue; -#endif - sfen_for_mse.push_back(p); } else { break; @@ -1934,10 +1935,10 @@ void LearnerThink::thread_worker(size_t thread_id) if (eval_limit < abs(ps.score)) goto RetryRead; -#if !defined (LEARN_GENSFEN_USE_DRAW_RESULT) - if (ps.game_result == 0) + + if (!use_draw_in_training && ps.game_result == 0) goto RetryRead; -#endif + // 蠎冗乢螻髱「縺ォ髢「縺吶k隱ュ縺ソ鬟帙ー縺 if (ps.gamePly < prng.rand(reduction_gameply)) @@ -1961,13 +1962,13 @@ void LearnerThink::thread_worker(size_t thread_id) { auto key = pos.key(); // rmse縺ョ險育ョ礼畑縺ォ菴ソ縺」縺ヲ縺繧句ア髱「縺ェ繧蛾勁螟悶☆繧九 - if (sr.is_for_rmse(key)) + if (sr.is_for_rmse(key) && use_hash_in_training) goto RetryRead; // 逶エ霑代〒逕ィ縺縺溷ア髱「繧る勁螟悶☆繧九 auto hash_index = size_t(key & (sr.READ_SFEN_HASH_SIZE - 1)); auto key2 = sr.hash[hash_index]; - if (key == key2) + if (key == key2 && use_hash_in_training) goto RetryRead; sr.hash[hash_index] = key; // 莉雁屓縺ョkey縺ォ蜈・繧梧崛縺医※縺翫¥縲 } @@ -2416,30 +2417,36 @@ void shuffle_files_on_memory(const vector& filenames,const string output std::cout << "..shuffle_on_memory done." << std::endl; } -void convert_bin(const vector& filenames , const string& output_file_name) +void convert_bin(const vector& filenames, const string& output_file_name, const int ply_minimum, const int ply_maximum, const int interpolate_eval) { std::fstream fs; + uint64_t data_size=0; + uint64_t filtered_size = 0; auto th = Threads.main(); auto &tpos = th->rootPos; // plain蠖「蠑上ョ髮大キセ繧偵d縺ュ縺繧臥視逕ィ縺ョpackedsfenvalue縺ォ螟画鋤縺吶k fs.open(output_file_name, ios::app | ios::binary); - + StateListPtr states; for (auto filename : filenames) { std::cout << "convert " << filename << " ... "; std::string line; ifstream ifs; ifs.open(filename); PackedSfenValue p; + data_size = 0; + filtered_size = 0; p.gamePly = 1; // apery蠖「蠑上〒縺ッ蜷ォ縺セ繧後↑縺縲ゆク蠢懷晄悄蛹悶☆繧九∋縺 + bool ignore_flag = false; + while (std::getline(ifs, line)) { std::stringstream ss(line); std::string token; std::string value; ss >> token; - if (token == "sfen") { - StateInfo si; - tpos.set(line.substr(5), false, &si, Threads.main()); - tpos.sfen_pack(p.sfen); + if (token == "fen") { + states = StateListPtr(new std::deque(1)); // Drop old and create a new one + tpos.set(line.substr(4), false, &states->back(), Threads.main()); + tpos.sfen_pack(p.sfen); } else if (token == "move") { ss >> value; @@ -2451,23 +2458,37 @@ void convert_bin(const vector& filenames , const string& output_file_nam else if (token == "ply") { int temp; ss >> temp; + if(temp < ply_minimum || temp > ply_maximum){ + ignore_flag = true; + } p.gamePly = uint16_t(temp); // 豁、蜃ヲ縺ョ繧ュ繝」繧ケ繝医>繧峨↑縺シ + if (interpolate_eval != 0){ + p.score = min(3000, interpolate_eval * temp); + } } else if (token == "result") { int temp; ss >> temp; p.game_result = int8_t(temp); // 豁、蜃ヲ縺ョ繧ュ繝」繧ケ繝医>繧峨↑縺シ + if (interpolate_eval){ + p.score = p.score * p.game_result; + } } else if (token == "e") { + if(!ignore_flag){ fs.write((char*)&p, sizeof(PackedSfenValue)); + data_size+=1; // debug - /* - std::cout<> eta3; else if (option == "eta1_epoch") is >> eta1_epoch; else if (option == "eta2_epoch") is >> eta2_epoch; - + else if (option == "use_draw_in_training") is >> use_draw_in_training; + else if (option == "use_draw_in_validation") is >> use_draw_in_validation; + else if (option == "use_hash_in_training") is >> use_hash_in_training; // 蜑イ蠑慕紫 else if (option == "discount_rate") is >> discount_rate; @@ -2672,7 +2698,7 @@ void learn(Position&, istringstream& is) else if (option == "eval_limit") is >> eval_limit; else if (option == "save_only_once") save_only_once = true; else if (option == "no_shuffle") no_shuffle = true; - + #if defined(EVAL_NNUE) else if (option == "nn_batch_size") is >> nn_batch_size; else if (option == "newbob_decay") is >> newbob_decay; @@ -2687,6 +2713,7 @@ void learn(Position&, istringstream& is) // 髮大キセ縺ョconvert髢「騾」 else if (option == "convert_plain") use_convert_plain = true; else if (option == "convert_bin") use_convert_bin = true; + else if (option == "interpolate_eval") is >> interpolate_eval; // 縺輔b縺ェ縺上ー縲√◎繧後ッ繝輔ぃ繧、繝ォ蜷阪〒縺ゅk縲 else filenames.push_back(option); @@ -2796,7 +2823,7 @@ void learn(Position&, istringstream& is) { is_ready(true); cout << "convert_bin.." << endl; - convert_bin(filenames,output_file_name); + convert_bin(filenames,output_file_name, ply_minimum, ply_maximum, interpolate_eval); return; } From 4c926b8eb4ccdd129bdf83b354cb092d2206b4a0 Mon Sep 17 00:00:00 2001 From: rqs Date: Sat, 27 Jun 2020 13:08:12 +0900 Subject: [PATCH 31/34] add pgn_to_plain --- script/README.md | 52 ++++++++++++++++++++++++++++++++ script/pgn_to_plain.py | 68 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 script/README.md create mode 100644 script/pgn_to_plain.py diff --git a/script/README.md b/script/README.md new file mode 100644 index 00000000..feb57ca2 --- /dev/null +++ b/script/README.md @@ -0,0 +1,52 @@ +# `pgn_to_plain` +This script converts pgn files into text file to apply `learn convert_bin` command. You need to import [python-chess](https://pypi.org/project/python-chess/) to use this script. + + + pip install python-chess + + +# Example of Qhapaq's finetune using `pgn_to_plain` + +## Download data +You can download data from [here](http://rebel13.nl/index.html) + +## Convert pgn files + +**Important : convert text will be superheavy (approx 200 byte / position)** + + python pgn_to_plain.py --pgn "pgn/*.pgn" --start_ply 1 --output converted_pgn.txt + + +`--pgn` option supports wildcard. When you use pgn files with elo >= 3300, You will get 1.7 GB text file. + + +## Convert into training data + + +### Example build command + + make nnue-learn ARCH=x86-64 + +See `src/Makefile` for detail. + + +### Convert + + ./stockfish + learn convert_bin converted_pgn.txt output_file_name pgn_bin.bin + learn shuffle pgn_bin.bin + +You also need to prepare validation data for training like following. + + python pgn_to_plain.py --pgn "pgn/ccrl-40-15-3400.pgn" --start_ply 1 --output ccrl-40-15-3400.txt + ./stockfish + learn convert_bin ccrl-40-15-3400.txt ccrl-40-15-3400_plain.bin + + +### Learn + + ./stockfish + setoption name Threads value 8 + learn shuffled_sfen.bin newbob_decay 0.5 validation_set_file_name ccrl-40-15-3400_plain.bin nn_batch_size 50000 batchsize 1000000 eval_save_interval 8000000 eta 0.05 lambda 0.0 eval_limit 3000 mirror_percentage 0 use_draw_in_training 1 + + diff --git a/script/pgn_to_plain.py b/script/pgn_to_plain.py new file mode 100644 index 00000000..61aa9917 --- /dev/null +++ b/script/pgn_to_plain.py @@ -0,0 +1,68 @@ +import chess.pgn +import argparse +import glob +from typing import List + +# todo close in c++ tools using pgn-extract +# https://www.cs.kent.ac.uk/people/staff/djb/pgn-extract/help.html#-w + +def parse_result(result_str:str, board:chess.Board) -> int: + if result_str == "1/2-1/2": + return 0 + if result_str == "0-1": + if board.turn == chess.WHITE: + return -1 + else: + return 1 + elif result_str == "1-0": + if board.turn == chess.WHITE: + return 1 + else: + return 0 + else: + print("illeagal result", result_str) + raise ValueError + +def game_sanity_check(game: chess.pgn.Game) -> bool: + if not game.headers["Result"] in ["1/2-1/2", "0-1", "1-0"]: + print("invalid result", game.headers["Result"]) + return False + return True + +def parse_game(game: chess.pgn.Game, writer, start_play: int=1)->None: + board: chess.Board = game.board() + if not game_sanity_check(game): + return + result: str = game.headers["Result"] + for ply, move in enumerate(game.mainline_moves()): + if ply >= start_play: + writer.write("fen " + board.fen() + "\n") + writer.write("move " + str(move) + "\n") + writer.write("score 0\n") + writer.write("ply " + str(ply)+"\n") + writer.write("result " + str(parse_result(result, board)) +"\n") + writer.write("e\n") + + board.push(move) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--pgn", type=str, required=True) + parser.add_argument("--start_ply", type=int, default=1) + parser.add_argument("--output", type=str, default="plain.txt") + args = parser.parse_args() + + pgn_files: List[str] = glob.glob(args.pgn) + f = open(args.output, 'w') + for pgn_file in pgn_files: + print("parse", pgn_file) + pgn_loader = open(pgn_file) + while True: + game = chess.pgn.read_game(pgn_loader) + if game is None: + break + parse_game(game, f, args.start_ply) + f.close() + +if __name__=="__main__": + main() From aa2dc962f516d0ae8905c534eaf3c05427457bc0 Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 27 Jun 2020 14:00:12 +0900 Subject: [PATCH 32/34] Added use_draw_in_training_data_generation option to write out draw games to the training data. --- src/learn/learn.h | 4 ++-- src/learn/learner.cpp | 43 ++++++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/learn/learn.h b/src/learn/learn.h index 8e3172d3..246e5cc9 100644 --- a/src/learn/learn.h +++ b/src/learn/learn.h @@ -165,8 +165,8 @@ typedef float LearnFloatType; // 蠑輔″蛻縺代↓閾ウ縺」縺溘→縺阪√◎繧後r謨吝クォ螻髱「縺ィ縺励※譖ク縺榊コ縺 // 縺薙l繧偵☆繧九⊇縺縺瑚憶縺縺九←縺縺九ッ蠕ョ螯吶 // #define LEARN_GENSFEN_USE_DRAW_RESULT -extern bool use_draw_in_training; -extern bool use_hash_in_training; + + // ====================== // configure // ====================== diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index 09af98d3..98a310c4 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -84,10 +84,6 @@ #include #endif -bool use_draw_in_training=false; -bool use_draw_in_validation=false; -bool use_hash_in_training=true; - using namespace std; //// 縺薙l縺ッ謗「邏「驛ィ縺ァ螳夂セゥ縺輔l縺ヲ縺繧九b縺ョ縺ィ縺吶k縲 @@ -115,6 +111,11 @@ namespace Learner // 螻髱「縺ョ驟榊 : PSVector 縺ッ packed sfen vector 縺ョ逡・縲 typedef std::vector PSVector; +bool use_draw_in_training_data_generation = false; +bool use_draw_in_training = false; +bool use_draw_in_validation = false; +bool use_hash_in_training = true; + // ----------------------------------- // 螻髱「縺ョ繝輔ぃ繧、繝ォ縺ク縺ョ譖ク縺榊コ縺 // ----------------------------------- @@ -499,19 +500,19 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id) // 髟キ謇区焚縺ォ驕斐@縺溘ョ縺 if (ply >= MAX_PLY2) { -#if defined (LEARN_GENSFEN_USE_DRAW_RESULT) - // 蜍晄風 = 蠑輔″蛻縺代→縺励※譖ク縺榊コ縺吶 - // 縺薙≧縺励◆縺サ縺縺瑚ェ蛻縺悟・邇峨@縺溘→縺阪↓縲∫嶌謇九ョ蜈・邇峨r險ア縺励↓縺上>(縺九b) - flush_psv(0); -#endif + if (use_draw_in_training_data_generation) { + // 蜍晄風 = 蠑輔″蛻縺代→縺励※譖ク縺榊コ縺吶 + // 縺薙≧縺励◆縺サ縺縺瑚ェ蛻縺悟・邇峨@縺溘→縺阪↓縲∫嶌謇九ョ蜈・邇峨r險ア縺励↓縺上>(縺九b) + flush_psv(0); + } break; } if (pos.is_draw(ply)) { -#if defined (LEARN_GENSFEN_USE_DRAW_RESULT) - flush_psv(0); -#endif - // Do not write if draw. + if (use_draw_in_training_data_generation) { + // Write if draw. + flush_psv(0); + } break; } @@ -522,10 +523,9 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id) // Write the positions other than this position if checkmated. if (pos.checkers()) // Mate flush_psv(-1); -#if defined (LEARN_GENSFEN_USE_DRAW_RESULT) - else // Stalemate - flush_psv(0); -#endif + else if (use_draw_in_training_data_generation) { + flush_psv(0); // Stalemate + } break; } @@ -588,10 +588,10 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id) // 蜷蜊譌・謇九↓蠢懊§縺溷ヲ逅縲 if (pos.is_draw(0)) { -#if defined (LEARN_GENSFEN_USE_DRAW_RESULT) - // 蠑輔″蛻縺代r譖ク縺榊コ縺吶→縺 - flush_psv(0); -#endif + if (use_draw_in_training_data_generation) { + // Write if draw. + flush_psv(0); + } break; } @@ -2660,6 +2660,7 @@ void learn(Position&, istringstream& is) else if (option == "eta3") is >> eta3; else if (option == "eta1_epoch") is >> eta1_epoch; else if (option == "eta2_epoch") is >> eta2_epoch; + else if (option == "use_draw_in_training_data_generation") is >> use_draw_in_training_data_generation; else if (option == "use_draw_in_training") is >> use_draw_in_training; else if (option == "use_draw_in_validation") is >> use_draw_in_validation; else if (option == "use_hash_in_training") is >> use_hash_in_training; From e229015127df8264fc23cdc594e906aebb429096 Mon Sep 17 00:00:00 2001 From: tttak Date: Sat, 27 Jun 2020 15:24:20 +0900 Subject: [PATCH 33/34] =?UTF-8?q?learn=20convert=5Fbin=5Ffrom=5Fpgn-extrac?= =?UTF-8?q?t=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit http://rebel13.nl/download/data.html Download Selected Lichess games pgn-extract --fencomments -Wlalg --nochecks --nomovenumbers --noresults -w500000 -N -V -o comp-2019-06.txt comp-2019-06.pgn stockfish.exe setoption name SkipLoadingEval value true isready learn convert_bin_from_pgn-extract output_file_name fens_comp-2019-06.bin comp-2019-06.txt --- src/learn/learner.cpp | 202 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 1 deletion(-) diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index 98a310c4..0567af76 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "learn.h" #include "multi_think.h" @@ -2494,7 +2495,196 @@ void convert_bin(const vector& filenames, const string& output_file_name std::cout << "all done" << std::endl; fs.close(); } - + +static inline void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); +} + +static inline void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return !std::isspace(ch); + }).base(), s.end()); +} + +static inline void trim(std::string &s) { + ltrim(s); + rtrim(s); +} + +int parse_game_result_from_pgn_extract(std::string result) { + // White Win + if (result == "\"1-0\"") { + return 1; + } + // Black Win + else if (result == "\"0-1\"") { + return -1; + } + // Draw + else { + return 0; + } +} + +// 0.25 --> 25 +// #-4 --> -mate_in(4) +// #3 --> mate_in(3) +Value parse_score_from_pgn_extract(std::string eval) { + if (eval.substr(0, 1) == "#") { + if (eval.substr(1, 1) == "-") { + return -mate_in(stoi(eval.substr(2, eval.length() - 2))); + } + else { + return mate_in(stoi(eval.substr(1, eval.length() - 1))); + } + } + else { + return Value(stod(eval) * 100.0f); + } +} + +// pgn-extract蠖「蠑上ョ謨吝クォ繧偵d縺ュ縺繧臥視逕ィ縺ョPackedSfenValue縺ォ螟画鋤縺吶k +void convert_bin_from_pgn_extract(const vector& filenames, const string& output_file_name) +{ + auto th = Threads.main(); + auto &pos = th->rootPos; + + std::fstream ofs; + ofs.open(output_file_name, ios::out | ios::binary); + + int game_count = 0; + int fen_count = 0; + + for (auto filename : filenames) { + std::cout << now_string() << " convert " << filename << std::endl; + ifstream ifs; + ifs.open(filename); + + int game_result = 0; + + std::string line; + while (std::getline(ifs, line)) { + + if (line.empty()) { + continue; + } + + else if (line.substr(0, 1) == "[") { + std::regex pattern_result(R"(\[Result (.+?)\])"); + std::smatch match; + + // example: [Result "1-0"] + if (std::regex_search(line, match, pattern_result)) { + game_result = parse_game_result_from_pgn_extract(match.str(1)); + //std::cout << "game_result=" << game_result << std::endl; + + game_count++; + if (game_count % 10000 == 0) { + std::cout << now_string() << " game_count=" << game_count << ", fen_count=" << fen_count << std::endl; + } + } + + continue; + } + + else { + int gamePly = 0; + + PackedSfenValue psv; + memset((char*)&psv, 0, sizeof(PackedSfenValue)); + + auto itr = line.cbegin(); + + while (true) { + gamePly++; + + std::regex pattern_bracket(R"(\{(.+?)\})"); + std::regex pattern_eval(R"(\[\%eval (.+?)\])"); + std::regex pattern_move(R"((.+?)\{)"); + std::smatch match; + + // example: { [%eval 0.25] [%clk 0:10:00] } + if (!std::regex_search(itr, line.cend(), match, pattern_bracket)) { + break; + } + + itr += match.position(0) + match.length(0); + std::string str_eval_clk = match.str(1); + trim(str_eval_clk); + //std::cout << "str_eval_clk="<< str_eval_clk << std::endl; + + // example: [%eval 0.25] + // example: [%eval #-4] + // example: [%eval #3] + if (!std::regex_search(str_eval_clk, match, pattern_eval)) { + continue; + } + else { + std::string str_eval = match.str(1); + trim(str_eval); + psv.score = parse_score_from_pgn_extract(str_eval); + //std::cout << "psv.score=" << psv.score << std::endl; + } + + // example: { rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq d3 0 1 } + if (!std::regex_search(itr, line.cend(), match, pattern_bracket)) { + break; + } + + itr += match.position(0) + match.length(0); + std::string str_fen = match.str(1); + trim(str_fen); + //std::cout << "str_fen=" << str_fen << std::endl; + + StateInfo si; + pos.set(str_fen, false, &si, th); + pos.sfen_pack(psv.sfen); + + // example: d7d5 { + if (!std::regex_search(itr, line.cend(), match, pattern_move)) { + break; + } + + itr += match.position(0) + match.length(0) - 1; + std::string str_move = match.str(1); + trim(str_move); + //std::cout << "str_move=" << str_move << std::endl; + psv.move = UCI::to_move(pos, str_move); + + // + psv.gamePly = gamePly; + psv.game_result = game_result; + + if (pos.side_to_move() == BLACK) { + psv.score *= -1; + psv.game_result *= -1; + } + + //std::cout << "write: " + // << "score=" << psv.score + // << ", move=" << psv.move + // << ", gamePly=" << psv.gamePly + // << ", game_result=" << (int)psv.game_result + // << std::endl; + + ofs.write((char*)&psv, sizeof(PackedSfenValue)); + memset((char*)&psv, 0, sizeof(PackedSfenValue)); + + fen_count++; + } + + game_result = 0; + } + } + } + + std::cout << now_string() << " game_count=" << game_count << ", fen_count=" << fen_count << std::endl; + std::cout << now_string() << " all done" << std::endl; + ofs.close(); +} + //void convert_plain(const vector& filenames , const string& output_file_name) //{ // Position tpos; @@ -2581,6 +2771,8 @@ void learn(Position&, istringstream& is) int ply_minimum = 0; int ply_maximum = 114514; bool interpolate_eval = 0; + // pgn-extract蠖「蠑上ョ謨吝クォ繧偵d縺ュ縺繧臥視縺ョbin縺ォ螟画鋤縺吶k + bool use_convert_bin_from_pgn_extract = false; // 縺昴l繧峨ョ縺ィ縺阪↓譖ク縺榊コ縺吶ヵ繧。繧、繝ォ蜷(繝繝輔か繝ォ繝医〒縺ッ"shuffled_sfen.bin") string output_file_name = "shuffled_sfen.bin"; @@ -2715,6 +2907,7 @@ void learn(Position&, istringstream& is) else if (option == "convert_plain") use_convert_plain = true; else if (option == "convert_bin") use_convert_bin = true; else if (option == "interpolate_eval") is >> interpolate_eval; + else if (option == "convert_bin_from_pgn-extract") use_convert_bin_from_pgn_extract = true; // 縺輔b縺ェ縺上ー縲√◎繧後ッ繝輔ぃ繧、繝ォ蜷阪〒縺ゅk縲 else filenames.push_back(option); @@ -2828,6 +3021,13 @@ void learn(Position&, istringstream& is) return; } + if (use_convert_bin_from_pgn_extract) + { + is_ready(true); + cout << "convert_bin_from_pgn-extract.." << endl; + convert_bin_from_pgn_extract(filenames, output_file_name); + return; + } cout << "loop : " << loop << endl; cout << "eval_limit : " << eval_limit << endl; From 13eb5400204fb2fea68ef0ca5875a668eabac339 Mon Sep 17 00:00:00 2001 From: nodchip Date: Sat, 27 Jun 2020 22:19:22 +0900 Subject: [PATCH 34/34] Changed the formula to calculate winning ratio to 1/(1+10^(-Eval/4)). --- src/learn/learner.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp index 98a310c4..f59f458f 100644 --- a/src/learn/learner.cpp +++ b/src/learn/learner.cpp @@ -1027,9 +1027,24 @@ double sigmoid(double x) // 隧穂セ。蛟、繧貞享邇[0,1]縺ォ螟画鋤縺吶k髢「謨ー double winning_percentage(double value) { - // 縺薙ョ600.0縺ィ縺縺螳壽焚縺ッ縲}onanza螳壽焚縲(ponanza縺後◎縺縺励※縺繧九i縺励>縺ィ縺縺諢丞袖縺ァ) - // 繧イ繝シ繝縺ョ騾イ陦悟コヲ縺ォ蜷医o縺帙◆繧ゅョ縺ォ縺励◆縺サ縺縺後>縺縺九b遏・繧後↑縺縺代←繧ゅ√◎縺ョ蜉ケ譫懊ョ縺サ縺ゥ縺ッ荳肴弱 - return sigmoid(value / 600.0); + // In Maxima, + // load("C:/maxima-5.44.0/cform.lisp"); + // PawnValueEg = 206; + // cform(1.0 / (1.0 + 10.0 ^ (-value / PawnValueEg / 4.0))); + constexpr double PawnValue = PawnValueEg; + return 1.0 * pow(pow(10.0, -0.25 * pow(PawnValue, -1) * value) + 1.0, -1); +} + +double delta_winning_percentage(double value) +{ + // In Maxima, + // load("C:/maxima-5.44.0/cform.lisp"); + // PawnValueEg = 206; + // cform(diff(1.0/(1.0+10.0^(-value/PawnValue/4.0)),value)); + constexpr double PawnValue = PawnValueEg; + return + 0.5756462732485115 * pow(PawnValue, -1) * pow(10.0, -0.25 * pow(PawnValue, -1) * value) * + pow(pow(10.0, -0.25 * pow(PawnValue, -1) * value) + 1.0, -2); } // 譎ョ騾壹ョ繧キ繧ー繝「繧、繝蛾未謨ー縺ョ蟆朱未謨ー縲 @@ -1127,8 +1142,9 @@ double calc_grad(Value deep, Value shallow , const PackedSfenValue& psv) // elmo(WCSC27)譁ケ蠑 // 螳滄圀縺ョ繧イ繝シ繝縺ョ蜍晄風縺ァ陬懈ュ」縺吶k縲 - const double eval_winrate = winning_percentage(shallow); - const double teacher_winrate = winning_percentage(deep); + const double q = winning_percentage(shallow); + const double p = winning_percentage(deep); + const double dq = delta_winning_percentage(shallow); // 譛溷セ蜍晉紫繧貞享縺」縺ヲ縺繧後ー1縲∬イ縺代※縺繧後ー 0縲∝シ輔″蛻縺代↑繧0.5縺ィ縺励※陬懈ュ」鬆縺ィ縺励※逕ィ縺繧九 // game_result = 1,0,-1縺ェ縺ョ縺ァ1雜ウ縺励※2縺ァ蜑イ繧九 @@ -1139,7 +1155,9 @@ double calc_grad(Value deep, Value shallow , const PackedSfenValue& psv) // 螳滄圀縺ョ蜍晉紫繧定」懈ュ」鬆縺ィ縺励※菴ソ縺」縺ヲ縺繧九 // 縺薙l縺憩lmo(WCSC27)縺ョ繧「繧、繝繧「縺ァ縲∫樟莉」縺ョ繧ェ繝シ繝代シ繝縲 - const double grad = (1 - lambda) * (eval_winrate - t) + lambda * (eval_winrate - teacher_winrate); + const double pp = (q - p) * dq / q / (1.0 - q); + const double tt = (q - t) * dq / q / (1.0 - q); + const double grad = lambda * pp + (1.0 - lambda) * tt; return grad; }