stockfish-11.orig/0000755000175000017500000000000013610452365012360 5ustar pdmpdmstockfish-11.orig/.travis.yml0000644000175000017500000000466313610452365014502 0ustar pdmpdmlanguage: cpp dist: xenial matrix: include: - os: linux compiler: gcc addons: apt: sources: ['ubuntu-toolchain-r-test'] packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl'] env: - COMPILER=g++-8 - COMP=gcc - os: linux compiler: clang addons: apt: sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0'] packages: ['clang-6.0', 'llvm-6.0-dev', 'g++-multilib', 'valgrind', 'expect', 'curl'] env: - COMPILER=clang++-6.0 - COMP=clang - LDFLAGS=-fuse-ld=lld - os: osx compiler: gcc env: - COMPILER=g++ - COMP=gcc - os: osx compiler: clang env: - COMPILER=clang++ V='Apple LLVM 9.4.1' # Apple LLVM version 9.1.0 (clang-902.0.39.2) - COMP=clang branches: only: - master before_script: - cd src script: # Obtain bench reference from git log - git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig - export benchref=$(cat git_sig) - echo "Reference bench:" $benchref # # Verify bench number against various builds - export CXXFLAGS=-Werror - make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref # # Check perft and reproducible search - ../tests/perft.sh - ../tests/reprosearch.sh # # Valgrind # - export CXXFLAGS="-O1 -fno-inline" - if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64 debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi - if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi # # Sanitizer # # Use g++-8 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi stockfish-11.orig/tests/0000755000175000017500000000000013610452365013522 5ustar pdmpdmstockfish-11.orig/tests/signature.sh0000755000175000017500000000137613610452365016071 0ustar pdmpdm#!/bin/bash # obtain and optionally verify Bench / signature # if no reference is given, the output is deliberately limited to just the signature error() { echo "running bench for signature failed on line $1" exit 1 } trap 'error ${LINENO}' ERR # obtain signature=`./stockfish bench 2>&1 | grep "Nodes searched : " | awk '{print $4}'` if [ $# -gt 0 ]; then # compare to given reference if [ "$1" != "$signature" ]; then if [ -z "$signature" ]; then echo "No signature obtained from bench. Code crashed or assert triggered ?" else echo "signature mismatch: reference $1 obtained: $signature ." fi exit 1 else echo "signature OK: $signature" fi else # just report signature echo $signature fi stockfish-11.orig/tests/reprosearch.sh0000755000175000017500000000237513610452365016405 0ustar pdmpdm#!/bin/bash # verify reproducible search error() { echo "reprosearch testing failed on line $1" exit 1 } trap 'error ${LINENO}' ERR echo "reprosearch testing started" # repeat two short games, separated by ucinewgame. # with go nodes $nodes they should result in exactly # the same node count for each iteration. cat << EOF > repeat.exp set timeout 10 spawn ./stockfish lassign \$argv nodes send "uci\n" expect "uciok" send "ucinewgame\n" send "position startpos\n" send "go nodes \$nodes\n" expect "bestmove" send "position startpos moves e2e4 e7e6\n" send "go nodes \$nodes\n" expect "bestmove" send "ucinewgame\n" send "position startpos\n" send "go nodes \$nodes\n" expect "bestmove" send "position startpos moves e2e4 e7e6\n" send "go nodes \$nodes\n" expect "bestmove" send "quit\n" expect eof EOF # to increase the likelyhood of finding a non-reproducible case, # the allowed number of nodes are varied systematically for i in `seq 1 20` do nodes=$((100*3**i/2**i)) echo "reprosearch testing with $nodes nodes" # each line should appear exactly an even number of times expect repeat.exp $nodes 2>&1 | grep -o "nodes [0-9]*" | sort | uniq -c | awk '{if ($1%2!=0) exit(1)}' done rm repeat.exp echo "reprosearch testing OK" stockfish-11.orig/tests/perft.sh0000755000175000017500000000205413610452365015202 0ustar pdmpdm#!/bin/bash # verify perft numbers (positions from www.chessprogramming.org/Perft_Results) error() { echo "perft testing failed on line $1" exit 1 } trap 'error ${LINENO}' ERR echo "perft testing started" cat << EOF > perft.exp set timeout 10 lassign \$argv pos depth result spawn ./stockfish send "position \$pos\\ngo perft \$depth\\n" expect "Nodes searched? \$result" {} timeout {exit 1} send "quit\\n" expect eof EOF expect perft.exp startpos 5 4865609 > /dev/null expect perft.exp "fen r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" 5 193690690 > /dev/null expect perft.exp "fen 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -" 6 11030083 > /dev/null expect perft.exp "fen r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" 5 15833292 > /dev/null expect perft.exp "fen rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8" 5 89941194 > /dev/null expect perft.exp "fen r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10" 5 164075551 > /dev/null rm perft.exp echo "perft testing OK" stockfish-11.orig/tests/instrumented.sh0000755000175000017500000000610613610452365016605 0ustar pdmpdm#!/bin/bash # check for errors under valgrind or sanitizers. error() { echo "instrumented testing failed on line $1" exit 1 } trap 'error ${LINENO}' ERR # define suitable post and prefixes for testing options case $1 in --valgrind) echo "valgrind testing started" prefix='' exeprefix='valgrind --error-exitcode=42' postfix='1>/dev/null' threads="1" ;; --valgrind-thread) echo "valgrind-thread testing started" prefix='' exeprefix='valgrind --error-exitcode=42' postfix='1>/dev/null' threads="2" ;; --sanitizer-undefined) echo "sanitizer-undefined testing started" prefix='!' exeprefix='' postfix='2>&1 | grep -A50 "runtime error:"' threads="1" ;; --sanitizer-thread) echo "sanitizer-thread testing started" prefix='!' exeprefix='' postfix='2>&1 | grep -A50 "WARNING: ThreadSanitizer:"' threads="2" cat << EOF > tsan.supp race:TTEntry::move race:TTEntry::depth race:TTEntry::bound race:TTEntry::save race:TTEntry::value race:TTEntry::eval race:TTEntry::is_pv race:TranspositionTable::probe race:TranspositionTable::hashfull EOF export TSAN_OPTIONS="suppressions=./tsan.supp" ;; *) echo "unknown testing started" prefix='' exeprefix='' postfix='' threads="1" ;; esac # simple command line testing for args in "eval" \ "go nodes 1000" \ "go depth 10" \ "go movetime 1000" \ "go wtime 8000 btime 8000 winc 500 binc 500" \ "bench 128 $threads 10 default depth" do echo "$prefix $exeprefix ./stockfish $args $postfix" eval "$prefix $exeprefix ./stockfish $args $postfix" done # more general testing, following an uci protocol exchange cat << EOF > game.exp set timeout 10 spawn $exeprefix ./stockfish send "uci\n" expect "uciok" send "setoption name Threads value $threads\n" send "ucinewgame\n" send "position startpos\n" send "go nodes 1000\n" expect "bestmove" send "position startpos moves e2e4 e7e6\n" send "go nodes 1000\n" expect "bestmove" send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n" send "go depth 30\n" expect "bestmove" send "quit\n" expect eof # return error code of the spawned program, useful for valgrind lassign [wait] pid spawnid os_error_flag value exit \$value EOF #download TB as needed if [ ! -d ../tests/syzygy ]; then curl -sL https://api.github.com/repos/niklasf/python-chess/tarball/9b9aa13f9f36d08aadfabff872882f4ab1494e95 | tar -xzf - mv niklasf-python-chess-9b9aa13 ../tests/syzygy fi cat << EOF > syzygy.exp set timeout 240 spawn $exeprefix ./stockfish send "uci\n" send "setoption name SyzygyPath value ../tests/syzygy/\n" expect "info string Found 35 tablebases" {} timeout {exit 1} send "bench 128 1 10 default depth\n" send "quit\n" expect eof # return error code of the spawned program, useful for valgrind lassign [wait] pid spawnid os_error_flag value exit \$value EOF for exp in game.exp syzygy.exp do echo "$prefix expect $exp $postfix" eval "$prefix expect $exp $postfix" rm $exp done rm -f tsan.supp echo "instrumented testing OK" stockfish-11.orig/src/0000755000175000017500000000000013610452365013147 5ustar pdmpdmstockfish-11.orig/src/material.cpp0000644000175000017500000002000313610452365015444 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include // For std::memset #include "material.h" #include "thread.h" using namespace std; namespace { // Polynomial material imbalance parameters constexpr int QuadraticOurs[][PIECE_TYPE_NB] = { // OUR PIECES // pair pawn knight bishop rook queen {1438 }, // Bishop pair { 40, 38 }, // Pawn { 32, 255, -62 }, // Knight OUR PIECES { 0, 104, 4, 0 }, // Bishop { -26, -2, 47, 105, -208 }, // Rook {-189, 24, 117, 133, -134, -6 } // Queen }; 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 }; // Endgame evaluation and scaling functions are accessed directly and not through // the function maps because they correspond to more than one material hash key. Endgame EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKBPsK[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKQKRPs[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKPsK[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKPKP[] = { Endgame(WHITE), Endgame(BLACK) }; // Helper used to detect a given material distribution bool is_KXK(const Position& pos, Color us) { return !more_than_one(pos.pieces(~us)) && pos.non_pawn_material(us) >= RookValueMg; } bool is_KBPsK(const Position& pos, Color us) { return pos.non_pawn_material(us) == BishopValueMg && pos.count(us) >= 1; } bool is_KQKRPs(const Position& pos, Color us) { return !pos.count(us) && pos.non_pawn_material(us) == QueenValueMg && pos.count(~us) == 1 && 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]) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); int bonus = 0; // Second-degree polynomial material imbalance, by Tord Romstad for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) { if (!pieceCount[Us][pt1]) continue; int v = 0; for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2]; bonus += pieceCount[Us][pt1] * v; } return bonus; } } // 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 /// have to recompute all when the same material configuration occurs again. Entry* probe(const Position& pos) { Key key = pos.material_key(); Entry* e = pos.this_thread()->materialTable[key]; if (e->key == key) return e; std::memset(e, 0, sizeof(Entry)); e->key = key; e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; Value npm_w = pos.non_pawn_material(WHITE); Value npm_b = pos.non_pawn_material(BLACK); Value npm = clamp(npm_w + npm_b, EndgameLimit, MidgameLimit); // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME] e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); // Let's look if we have a specialized evaluation function for this particular // material configuration. Firstly we look for a fixed configuration one, then // for a generic one if the previous search failed. if ((e->evaluationFunction = Endgames::probe(key)) != nullptr) return e; for (Color c : { WHITE, BLACK }) if (is_KXK(pos, c)) { e->evaluationFunction = &EvaluateKXK[c]; return e; } // OK, we didn't find any special evaluation function for the current material // configuration. Is there a suitable specialized scaling function? const auto* sf = Endgames::probe(key); if (sf) { e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned return e; } // We didn't find any specialized scaling function, so fall back on generic // ones that refer to more than one material distribution. Note that in this // case we don't return after setting the function. for (Color c : { WHITE, BLACK }) { if (is_KBPsK(pos, c)) e->scalingFunction[c] = &ScaleKBPsK[c]; else if (is_KQKRPs(pos, c)) e->scalingFunction[c] = &ScaleKQKRPs[c]; } if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board { if (!pos.count(BLACK)) { assert(pos.count(WHITE) >= 2); e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; } else if (!pos.count(WHITE)) { assert(pos.count(BLACK) >= 2); e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; } else if (pos.count(WHITE) == 1 && pos.count(BLACK) == 1) { // This is a special case because we set scaling functions // for both colors instead of only one. e->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; e->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; } } // Zero or just one pawn makes it difficult to win, even with a small material // advantage. This catches some trivial draws like KK, KBK and KNK and gives a // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN). if (!pos.count(WHITE) && npm_w - npm_b <= BishopValueMg) e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : npm_b <= BishopValueMg ? 4 : 14); if (!pos.count(BLACK) && npm_b - npm_w <= BishopValueMg) e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : npm_w <= BishopValueMg ? 4 : 14); // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // for the bishop pair "extended piece", which allows us to be more flexible // in defining bishop pair bonuses. const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = { { pos.count(WHITE) > 1, pos.count(WHITE), pos.count(WHITE), pos.count(WHITE) , pos.count(WHITE), pos.count(WHITE) }, { pos.count(BLACK) > 1, pos.count(BLACK), pos.count(BLACK), pos.count(BLACK) , pos.count(BLACK), pos.count(BLACK) } }; e->value = int16_t((imbalance(pieceCount) - imbalance(pieceCount)) / 16); return e; } } // namespace Material stockfish-11.orig/src/tt.cpp0000644000175000017500000001264313610452365014310 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include // For std::memset #include #include #include "bitboard.h" #include "misc.h" #include "thread.h" #include "tt.h" #include "uci.h" TranspositionTable TT; // Our global transposition table /// 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) { // Preserve any existing move for the same position if (m || (k >> 48) != key16) move16 = (uint16_t)m; // Overwrite less valuable entries if ( (k >> 48) != key16 || d - DEPTH_OFFSET > depth8 - 4 || b == BOUND_EXACT) { assert(d >= DEPTH_OFFSET); key16 = (uint16_t)(k >> 48); value16 = (int16_t)v; eval16 = (int16_t)ev; genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); depth8 = (uint8_t)(d - DEPTH_OFFSET); } } /// TranspositionTable::resize() sets the size of the transposition table, /// measured in megabytes. Transposition table consists of a power of 2 number /// of clusters and each cluster consists of ClusterSize number of TTEntry. void TranspositionTable::resize(size_t mbSize) { Threads.main()->wait_for_search_finished(); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); free(mem); mem = malloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1); if (!mem) { std::cerr << "Failed to allocate " << mbSize << "MB for transposition table." << std::endl; exit(EXIT_FAILURE); } table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1)); clear(); } /// TranspositionTable::clear() initializes the entire transposition table to zero, // in a multi-threaded way. void TranspositionTable::clear() { std::vector threads; for (size_t idx = 0; idx < Options["Threads"]; ++idx) { threads.emplace_back([this, idx]() { // Thread binding gives faster search on systems with a first-touch policy if (Options["Threads"] > 8) WinProcGroup::bindThisThread(idx); // Each thread will zero its part of the hash table const size_t stride = clusterCount / Options["Threads"], start = stride * idx, len = idx != Options["Threads"] - 1 ? stride : clusterCount - start; std::memset(&table[start], 0, len * sizeof(Cluster)); }); } for (std::thread& th: threads) 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 /// to be replaced later. The replace value of an entry is calculated as its depth /// minus 8 times its relative age. TTEntry t1 is considered more valuable than /// TTEntry t2 if its replace value is greater than that of t2. 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 for (int i = 0; i < ClusterSize; ++i) if (!tte[i].key16 || tte[i].key16 == key16) { tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh return found = (bool)tte[i].key16, &tte[i]; } // Find an entry to be replaced according to the replacement strategy TTEntry* replace = tte; for (int i = 1; i < ClusterSize; ++i) // Due to our packed storage format for generation and its cyclic // nature we add 263 (256 is the modulus plus 7 to keep the unrelated // lowest three bits from affecting the result) to calculate the entry // age correctly even after generation8 overflows into the next cycle. if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8) > tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8)) replace = &tte[i]; return found = false, replace; } /// TranspositionTable::hashfull() returns an approximation of the hashtable /// occupation during a search. The hash is x permill full, as per UCI protocol. int TranspositionTable::hashfull() const { int cnt = 0; for (int i = 0; i < 1000 / ClusterSize; ++i) for (int j = 0; j < ClusterSize; ++j) cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8; return cnt * 1000 / (ClusterSize * (1000 / ClusterSize)); } stockfish-11.orig/src/endgame.h0000644000175000017500000000702113610452365014720 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED #include #include #include #include #include #include "position.h" #include "types.h" /// EndgameCode lists all supported endgame functions by corresponding codes enum EndgameCode { EVALUATION_FUNCTIONS, KNNK, // KNN vs K KNNKP, // KNN vs KP KXK, // Generic "mate lone king" eval KBNK, // KBN vs K KPK, // KP vs K KRKP, // KR vs KP KRKB, // KR vs KB KRKN, // KR vs KN KQKP, // KQ vs KP KQKR, // KQ vs KR SCALING_FUNCTIONS, KBPsK, // KB and pawns vs K KQKRPs, // KQ vs KR and pawns KRPKR, // KRP vs KR KRPKB, // KRP vs KB KRPPKRP, // KRPP vs KRP KPsK, // K and pawns vs K KBPKB, // KBP vs KB KBPPKB, // KBPP vs KB KBPKN, // KBP vs KN KNPK, // KNP vs K KNPKB, // KNP vs KB KPKP // KP vs KP }; /// Endgame functions can be of two types depending on whether they return a /// Value or a ScaleFactor. template using eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type; /// Base and derived functors for endgame evaluation and scaling functions template struct EndgameBase { explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {} virtual ~EndgameBase() = default; virtual T operator()(const Position&) const = 0; const Color strongSide, weakSide; }; template> struct Endgame : public EndgameBase { explicit Endgame(Color c) : EndgameBase(c) {} T operator()(const Position&) const override; }; /// The Endgames namespace handles the pointers to endgame evaluation and scaling /// base objects in two std::map. We use polymorphism to invoke the actual /// endgame function by calling its virtual operator(). namespace Endgames { template using Ptr = std::unique_ptr>; template using Map = std::unordered_map>; extern std::pair, Map> maps; void init(); template Map& map() { return std::get::value>(maps); } template> void add(const std::string& code) { StateInfo st; map()[Position().set(code, WHITE, &st).material_key()] = Ptr(new Endgame(WHITE)); map()[Position().set(code, BLACK, &st).material_key()] = Ptr(new Endgame(BLACK)); } template const EndgameBase* probe(Key key) { auto it = map().find(key); return it != map().end() ? it->second.get() : nullptr; } } #endif // #ifndef ENDGAME_H_INCLUDED stockfish-11.orig/src/movepick.h0000644000175000017500000001352013610452365015136 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED #include #include #include #include "movegen.h" #include "position.h" #include "types.h" /// StatsEntry stores the stat table value. It is usually a number but could /// be a move or even a nested history. We use a class instead of naked value /// to directly call history update operator<<() on the entry so to use stats /// tables at caller sites as simple multi-dim arrays. template class StatsEntry { T entry; public: void operator=(const T& v) { entry = v; } T* operator&() { return &entry; } T* operator->() { return &entry; } operator const T&() const { return entry; } void operator<<(int bonus) { assert(abs(bonus) <= D); // Ensure range is [-D, D] static_assert(D <= std::numeric_limits::max(), "D overflows T"); entry += bonus - entry * abs(bonus) / D; assert(abs(entry) <= D); } }; /// Stats is a generic N-dimensional array used to store various statistics. /// The first template parameter T is the base type of the array, the second /// template parameter D limits the range of updates in [-D, D] when we update /// values with the << operator, while the last parameters (Size and Sizes) /// encode the dimensions of the array. template struct Stats : public std::array, Size> { typedef Stats stats; void fill(const T& v) { // For standard-layout 'this' points to first struct member assert(std::is_standard_layout::value); typedef StatsEntry entry; entry* p = reinterpret_cast(this); std::fill(p, p + sizeof(*this) / sizeof(entry), v); } }; template struct Stats : public std::array, Size> {}; /// In stats table, D=0 means that the template parameter is not used enum StatsParams { NOT_USED = 0 }; enum StatsType { NoCaptures, Captures }; /// ButterflyHistory records how often quiet moves have been successful or /// unsuccessful during the current search, and is used for reduction and move /// ordering decisions. It uses 2 tables (one for each color) indexed by /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards typedef Stats ButterflyHistory; /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous /// move, see www.chessprogramming.org/Countermove_Heuristic typedef Stats CounterMoveHistory; /// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] typedef Stats CapturePieceToHistory; /// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] typedef Stats PieceToHistory; /// ContinuationHistory is the combined history of a given pair of moves, usually /// the current one given a previous one. The nested history table is based on /// PieceToHistory instead of ButterflyBoards. typedef Stats ContinuationHistory; /// MovePicker class is used to pick one pseudo legal move at a time from the /// current position. The most important method is next_move(), which returns a /// new pseudo legal move each time it is called, until there are no moves left, /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha /// beta algorithm, MovePicker attempts to return the moves which are most likely /// to get a cut-off first. class MovePicker { enum PickType { Next, Best }; public: MovePicker(const MovePicker&) = delete; MovePicker& operator=(const MovePicker&) = delete; MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Square); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Move, Move*); Move next_move(bool skipQuiets = false); private: template Move select(Pred); template void score(); ExtMove* begin() { return cur; } ExtMove* end() { return endMoves; } const Position& pos; const ButterflyHistory* mainHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; Move ttMove; ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; int stage; Square recaptureSquare; Value threshold; Depth depth; ExtMove moves[MAX_MOVES]; }; #endif // #ifndef MOVEPICK_H_INCLUDED stockfish-11.orig/src/timeman.h0000644000175000017500000000331513610452365014754 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED #include "misc.h" #include "search.h" #include "thread.h" /// The TimeManagement class computes the optimal time to think depending on /// the maximum available time, the game move number and other parameters. class TimeManagement { public: void init(Search::LimitsType& limits, Color us, int ply); TimePoint optimum() const { return optimumTime; } TimePoint maximum() const { return maximumTime; } TimePoint elapsed() const { return Search::Limits.npmsec ? TimePoint(Threads.nodes_searched()) : now() - startTime; } int64_t availableNodes; // When in 'nodes as time' mode private: TimePoint startTime; TimePoint optimumTime; TimePoint maximumTime; }; extern TimeManagement Time; #endif // #ifndef TIMEMAN_H_INCLUDED stockfish-11.orig/src/syzygy/0000755000175000017500000000000013610452365014525 5ustar pdmpdmstockfish-11.orig/src/syzygy/tbprobe.cpp0000644000175000017500000016672413610452365016706 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (c) 2013 Ronald de Man Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include // For std::memset and std::memcpy #include #include #include #include #include #include #include #include "../bitboard.h" #include "../movegen.h" #include "../position.h" #include "../search.h" #include "../types.h" #include "../uci.h" #include "tbprobe.h" #ifndef _WIN32 #include #include #include #include #else #define WIN32_LEAN_AND_MEAN #ifndef NOMINMAX # define NOMINMAX // Disable macros min() and max() #endif #include #endif using namespace Tablebases; int Tablebases::MaxCardinality; namespace { constexpr int TBPIECES = 7; // Max number of supported pieces enum { BigEndian, LittleEndian }; enum TBType { KEY, WDL, DTZ }; // Used as template parameter // Each table has a set of flags: all of them refer to DTZ tables, the last one to WDL tables enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, Wide = 16, SingleValue = 128 }; inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); } inline Square operator^=(Square& s, int i) { return s = Square(int(s) ^ i); } inline Square operator^(Square s, int i) { return Square(int(s) ^ i); } const std::string PieceToChar = " PNBRQK pnbrqk"; int MapPawns[SQUARE_NB]; int MapB1H1H7[SQUARE_NB]; int MapA1D1D4[SQUARE_NB]; int MapKK[10][SQUARE_NB]; // [MapA1D1D4][SQUARE_NB] int Binomial[6][SQUARE_NB]; // [k][n] k elements from a set of n elements int LeadPawnIdx[6][SQUARE_NB]; // [leadPawnsCnt][SQUARE_NB] int LeadPawnsSize[6][4]; // [leadPawnsCnt][FILE_A..FILE_D] // Comparison function to sort leading pawns in ascending MapPawns[] order bool pawns_comp(Square i, Square j) { return MapPawns[i] < MapPawns[j]; } int off_A1H8(Square sq) { return int(rank_of(sq)) - file_of(sq); } constexpr Value WDL_to_value[] = { -VALUE_MATE + MAX_PLY + 1, VALUE_DRAW - 2, VALUE_DRAW, VALUE_DRAW + 2, VALUE_MATE - MAX_PLY - 1 }; template inline void swap_endian(T& x) { static_assert(std::is_unsigned::value, "Argument of swap_endian not unsigned"); uint8_t tmp, *c = (uint8_t*)&x; for (int i = 0; i < Half; ++i) tmp = c[i], c[i] = c[End - i], c[End - i] = tmp; } template<> inline void swap_endian(uint8_t&) {} template T number(void* addr) { static const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; static const bool IsLittleEndian = (Le.c[0] == 4); T v; if ((uintptr_t)addr & (alignof(T) - 1)) // Unaligned pointer (very rare) std::memcpy(&v, addr, sizeof(T)); else v = *((T*)addr); if (LE != IsLittleEndian) swap_endian(v); return v; } // DTZ tables don't store valid scores for moves that reset the rule50 counter // like captures and pawn moves but we can easily recover the correct dtz of the // previous move if we know the position's WDL score. int dtz_before_zeroing(WDLScore wdl) { return wdl == WDLWin ? 1 : wdl == WDLCursedWin ? 101 : wdl == WDLBlessedLoss ? -101 : wdl == WDLLoss ? -1 : 0; } // Return the sign of a number (-1, 0, 1) template int sign_of(T val) { return (T(0) < val) - (val < T(0)); } // Numbers in little endian used by sparseIndex[] to point into blockLength[] struct SparseEntry { char block[4]; // Number of block char offset[2]; // Offset within the block }; static_assert(sizeof(SparseEntry) == 6, "SparseEntry must be 6 bytes"); typedef uint16_t Sym; // Huffman symbol struct LR { enum Side { Left, Right }; uint8_t lr[3]; // The first 12 bits is the left-hand symbol, the second 12 // bits is the right-hand symbol. If symbol has length 1, // then the left-hand symbol is the stored value. template Sym get() { return S == Left ? ((lr[1] & 0xF) << 8) | lr[0] : S == Right ? (lr[2] << 4) | (lr[1] >> 4) : (assert(false), Sym(-1)); } }; static_assert(sizeof(LR) == 3, "LR tree entry must be 3 bytes"); // Tablebases data layout is structured as following: // // TBFile: memory maps/unmaps the physical .rtbw and .rtbz files // TBTable: one object for each file with corresponding indexing information // TBTables: has ownership of TBTable objects, keeping a list and a hash // class TBFile memory maps/unmaps the single .rtbw and .rtbz files. Files are // memory mapped for best performance. Files are mapped at first access: at init // time only existence of the file is checked. class TBFile : public std::ifstream { std::string fname; public: // Look for and open the file among the Paths directories where the .rtbw // and .rtbz files can be found. Multiple directories are separated by ";" // on Windows and by ":" on Unix-based operating systems. // // Example: // C:\tb\wdl345;C:\tb\wdl6;D:\tb\dtz345;D:\tb\dtz6 static std::string Paths; TBFile(const std::string& f) { #ifndef _WIN32 constexpr char SepChar = ':'; #else constexpr char SepChar = ';'; #endif std::stringstream ss(Paths); std::string path; while (std::getline(ss, path, SepChar)) { fname = path + "/" + f; std::ifstream::open(fname); if (is_open()) return; } } // Memory map the file and check it. File should be already open and will be // closed after mapping. uint8_t* map(void** baseAddress, uint64_t* mapping, TBType type) { assert(is_open()); close(); // Need to re-open to get native file descriptor #ifndef _WIN32 struct stat statbuf; int fd = ::open(fname.c_str(), O_RDONLY); if (fd == -1) return *baseAddress = nullptr, nullptr; fstat(fd, &statbuf); if (statbuf.st_size % 64 != 16) { std::cerr << "Corrupt tablebase file " << fname << std::endl; exit(EXIT_FAILURE); } *mapping = statbuf.st_size; *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); madvise(*baseAddress, statbuf.st_size, MADV_RANDOM); ::close(fd); if (*baseAddress == MAP_FAILED) { std::cerr << "Could not mmap() " << fname << std::endl; exit(EXIT_FAILURE); } #else // Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored. HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr); if (fd == INVALID_HANDLE_VALUE) return *baseAddress = nullptr, nullptr; DWORD size_high; DWORD size_low = GetFileSize(fd, &size_high); if (size_low % 64 != 16) { std::cerr << "Corrupt tablebase file " << fname << std::endl; exit(EXIT_FAILURE); } HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr); CloseHandle(fd); if (!mmap) { std::cerr << "CreateFileMapping() failed" << std::endl; exit(EXIT_FAILURE); } *mapping = (uint64_t)mmap; *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0); if (!*baseAddress) { std::cerr << "MapViewOfFile() failed, name = " << fname << ", error = " << GetLastError() << std::endl; exit(EXIT_FAILURE); } #endif uint8_t* data = (uint8_t*)*baseAddress; constexpr uint8_t Magics[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 }, { 0x71, 0xE8, 0x23, 0x5D } }; if (memcmp(data, Magics[type == WDL], 4)) { std::cerr << "Corrupted table in file " << fname << std::endl; unmap(*baseAddress, *mapping); return *baseAddress = nullptr, nullptr; } return data + 4; // Skip Magics's header } static void unmap(void* baseAddress, uint64_t mapping) { #ifndef _WIN32 munmap(baseAddress, mapping); #else UnmapViewOfFile(baseAddress); CloseHandle((HANDLE)mapping); #endif } }; std::string TBFile::Paths; // struct PairsData contains low level indexing information to access TB data. // There are 8, 4 or 2 PairsData records for each TBTable, according to type of // table and if positions have pawns or not. It is populated at first access. struct PairsData { uint8_t flags; // Table flags, see enum TBFlag uint8_t maxSymLen; // Maximum length in bits of the Huffman symbols uint8_t minSymLen; // Minimum length in bits of the Huffman symbols uint32_t blocksNum; // Number of blocks in the TB file size_t sizeofBlock; // Block size in bytes size_t span; // About every span values there is a SparseIndex[] entry Sym* lowestSym; // lowestSym[l] is the symbol of length l with the lowest value LR* btree; // btree[sym] stores the left and right symbols that expand sym uint16_t* blockLength; // Number of stored positions (minus one) for each block: 1..65536 uint32_t blockLengthSize; // Size of blockLength[] table: padded so it's bigger than blocksNum SparseEntry* sparseIndex; // Partial indices into blockLength[] size_t sparseIndexSize; // Size of SparseIndex[] table uint8_t* data; // Start of Huffman compressed data std::vector base64; // base64[l - min_sym_len] is the 64bit-padded lowest symbol of length l std::vector symlen; // Number of values (-1) represented by a given Huffman symbol: 1..256 Piece pieces[TBPIECES]; // Position pieces: the order of pieces defines the groups uint64_t groupIdx[TBPIECES+1]; // Start index used for the encoding of the group's pieces int groupLen[TBPIECES+1]; // Number of pieces in a given group: KRKN -> (3, 1) uint16_t map_idx[4]; // WDLWin, WDLLoss, WDLCursedWin, WDLBlessedLoss (used in DTZ) }; // struct TBTable contains indexing information to access the corresponding TBFile. // There are 2 types of TBTable, corresponding to a WDL or a DTZ file. TBTable // is populated at init time but the nested PairsData records are populated at // first access, when the corresponding file is memory mapped. template struct TBTable { typedef typename std::conditional::type Ret; static constexpr int Sides = Type == WDL ? 2 : 1; std::atomic_bool ready; void* baseAddress; uint8_t* map; uint64_t mapping; Key key; Key key2; int pieceCount; bool hasPawns; bool hasUniquePieces; uint8_t pawnCount[2]; // [Lead color / other color] PairsData items[Sides][4]; // [wtm / btm][FILE_A..FILE_D or 0] PairsData* get(int stm, int f) { return &items[stm % Sides][hasPawns ? f : 0]; } TBTable() : ready(false), baseAddress(nullptr) {} explicit TBTable(const std::string& code); explicit TBTable(const TBTable& wdl); ~TBTable() { if (baseAddress) TBFile::unmap(baseAddress, mapping); } }; template<> TBTable::TBTable(const std::string& code) : TBTable() { StateInfo st; Position pos; key = pos.set(code, WHITE, &st).material_key(); pieceCount = pos.count(); hasPawns = pos.pieces(PAWN); hasUniquePieces = false; for (Color c : { WHITE, BLACK }) for (PieceType pt = PAWN; pt < KING; ++pt) if (popcount(pos.pieces(c, pt)) == 1) hasUniquePieces = true; // Set the leading color. In case both sides have pawns the leading color // is the side with less pawns because this leads to better compression. bool c = !pos.count(BLACK) || ( pos.count(WHITE) && pos.count(BLACK) >= pos.count(WHITE)); pawnCount[0] = pos.count(c ? WHITE : BLACK); pawnCount[1] = pos.count(c ? BLACK : WHITE); key2 = pos.set(code, BLACK, &st).material_key(); } template<> TBTable::TBTable(const TBTable& wdl) : TBTable() { // Use the corresponding WDL table to avoid recalculating all from scratch key = wdl.key; key2 = wdl.key2; pieceCount = wdl.pieceCount; hasPawns = wdl.hasPawns; hasUniquePieces = wdl.hasUniquePieces; pawnCount[0] = wdl.pawnCount[0]; pawnCount[1] = wdl.pawnCount[1]; } // class TBTables creates and keeps ownership of the TBTable objects, one for // each TB file found. It supports a fast, hash based, table lookup. Populated // at init time, accessed at probe time. class TBTables { typedef std::tuple*, TBTable*> Entry; static constexpr int Size = 1 << 12; // 4K table, indexed by key's 12 lsb static constexpr int Overflow = 1; // Number of elements allowed to map to the last bucket Entry hashTable[Size + Overflow]; std::deque> wdlTable; std::deque> dtzTable; void insert(Key key, TBTable* wdl, TBTable* dtz) { uint32_t homeBucket = (uint32_t)key & (Size - 1); Entry entry = std::make_tuple(key, wdl, dtz); // Ensure last element is empty to avoid overflow when looking up for (uint32_t bucket = homeBucket; bucket < Size + Overflow - 1; ++bucket) { Key otherKey = std::get(hashTable[bucket]); if (otherKey == key || !std::get(hashTable[bucket])) { hashTable[bucket] = entry; return; } // Robin Hood hashing: If we've probed for longer than this element, // insert here and search for a new spot for the other element instead. uint32_t otherHomeBucket = (uint32_t)otherKey & (Size - 1); if (otherHomeBucket > homeBucket) { swap(entry, hashTable[bucket]); key = otherKey; homeBucket = otherHomeBucket; } } std::cerr << "TB hash table size too low!" << std::endl; exit(EXIT_FAILURE); } public: template TBTable* get(Key key) { for (const Entry* entry = &hashTable[(uint32_t)key & (Size - 1)]; ; ++entry) { if (std::get(*entry) == key || !std::get(*entry)) return std::get(*entry); } } void clear() { memset(hashTable, 0, sizeof(hashTable)); wdlTable.clear(); dtzTable.clear(); } size_t size() const { return wdlTable.size(); } void add(const std::vector& pieces); }; TBTables TBTables; // If the corresponding file exists two new objects TBTable and TBTable // are created and added to the lists and hash table. Called at init time. void TBTables::add(const std::vector& pieces) { std::string code; for (PieceType pt : pieces) code += PieceToChar[pt]; TBFile file(code.insert(code.find('K', 1), "v") + ".rtbw"); // KRK -> KRvK if (!file.is_open()) // Only WDL file is checked return; file.close(); MaxCardinality = std::max((int)pieces.size(), MaxCardinality); wdlTable.emplace_back(code); dtzTable.emplace_back(wdlTable.back()); // Insert into the hash keys for both colors: KRvK with KR white and black insert(wdlTable.back().key , &wdlTable.back(), &dtzTable.back()); insert(wdlTable.back().key2, &wdlTable.back(), &dtzTable.back()); } // TB tables are compressed with canonical Huffman code. The compressed data is divided into // blocks of size d->sizeofBlock, and each block stores a variable number of symbols. // Each symbol represents either a WDL or a (remapped) DTZ value, or a pair of other symbols // (recursively). If you keep expanding the symbols in a block, you end up with up to 65536 // WDL or DTZ values. Each symbol represents up to 256 values and will correspond after // Huffman coding to at least 1 bit. So a block of 32 bytes corresponds to at most // 32 x 8 x 256 = 65536 values. This maximum is only reached for tables that consist mostly // of draws or mostly of wins, but such tables are actually quite common. In principle, the // blocks in WDL tables are 64 bytes long (and will be aligned on cache lines). But for // mostly-draw or mostly-win tables this can leave many 64-byte blocks only half-filled, so // in such cases blocks are 32 bytes long. The blocks of DTZ tables are up to 1024 bytes long. // The generator picks the size that leads to the smallest table. The "book" of symbols and // Huffman codes is the same for all blocks in the table. A non-symmetric pawnless TB file // will have one table for wtm and one for btm, a TB file with pawns will have tables per // file a,b,c,d also in this case one set for wtm and one for btm. int decompress_pairs(PairsData* d, uint64_t idx) { // Special case where all table positions store the same value if (d->flags & TBFlag::SingleValue) return d->minSymLen; // First we need to locate the right block that stores the value at index "idx". // Because each block n stores blockLength[n] + 1 values, the index i of the block // that contains the value at position idx is: // // for (i = -1, sum = 0; sum <= idx; i++) // sum += blockLength[i + 1] + 1; // // This can be slow, so we use SparseIndex[] populated with a set of SparseEntry that // point to known indices into blockLength[]. Namely SparseIndex[k] is a SparseEntry // that stores the blockLength[] index and the offset within that block of the value // with index I(k), where: // // I(k) = k * d->span + d->span / 2 (1) // First step is to get the 'k' of the I(k) nearest to our idx, using definition (1) uint32_t k = idx / d->span; // Then we read the corresponding SparseIndex[] entry uint32_t block = number(&d->sparseIndex[k].block); int offset = number(&d->sparseIndex[k].offset); // Now compute the difference idx - I(k). From definition of k we know that // // idx = k * d->span + idx % d->span (2) // // So from (1) and (2) we can compute idx - I(K): int diff = idx % d->span - d->span / 2; // Sum the above to offset to find the offset corresponding to our idx offset += diff; // Move to previous/next block, until we reach the correct block that contains idx, // that is when 0 <= offset <= d->blockLength[block] while (offset < 0) offset += d->blockLength[--block] + 1; while (offset > d->blockLength[block]) offset -= d->blockLength[block++] + 1; // Finally, we find the start address of our block of canonical Huffman symbols uint32_t* ptr = (uint32_t*)(d->data + ((uint64_t)block * d->sizeofBlock)); // Read the first 64 bits in our block, this is a (truncated) sequence of // unknown number of symbols of unknown length but we know the first one // is at the beginning of this 64 bits sequence. uint64_t buf64 = number(ptr); ptr += 2; int buf64Size = 64; Sym sym; while (true) { int len = 0; // This is the symbol length - d->min_sym_len // Now get the symbol length. For any symbol s64 of length l right-padded // to 64 bits we know that d->base64[l-1] >= s64 >= d->base64[l] so we // can find the symbol length iterating through base64[]. while (buf64 < d->base64[len]) ++len; // All the symbols of a given length are consecutive integers (numerical // sequence property), so we can compute the offset of our symbol of // length len, stored at the beginning of buf64. sym = (buf64 - d->base64[len]) >> (64 - len - d->minSymLen); // Now add the value of the lowest symbol of length len to get our symbol sym += number(&d->lowestSym[len]); // If our offset is within the number of values represented by symbol sym // we are done... if (offset < d->symlen[sym] + 1) break; // ...otherwise update the offset and continue to iterate offset -= d->symlen[sym] + 1; len += d->minSymLen; // Get the real length buf64 <<= len; // Consume the just processed symbol buf64Size -= len; if (buf64Size <= 32) { // Refill the buffer buf64Size += 32; buf64 |= (uint64_t)number(ptr++) << (64 - buf64Size); } } // Ok, now we have our symbol that expands into d->symlen[sym] + 1 symbols. // We binary-search for our value recursively expanding into the left and // right child symbols until we reach a leaf node where symlen[sym] + 1 == 1 // that will store the value we need. while (d->symlen[sym]) { Sym left = d->btree[sym].get(); // If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and // expands in a pair (d->symlen[left] = 23, d->symlen[right] = 11), then // we know that, for instance the ten-th value (offset = 10) will be on // the left side because in Recursive Pairing child symbols are adjacent. if (offset < d->symlen[left] + 1) sym = left; else { offset -= d->symlen[left] + 1; sym = d->btree[sym].get(); } } return d->btree[sym].get(); } bool check_dtz_stm(TBTable*, int, File) { return true; } bool check_dtz_stm(TBTable* entry, int stm, File f) { auto flags = entry->get(stm, f)->flags; return (flags & TBFlag::STM) == stm || ((entry->key == entry->key2) && !entry->hasPawns); } // DTZ scores are sorted by frequency of occurrence and then assigned the // values 0, 1, 2, ... in order of decreasing frequency. This is done for each // of the four WDLScore values. The mapping information necessary to reconstruct // the original values is stored in the TB file and read during map[] init. WDLScore map_score(TBTable*, File, int value, WDLScore) { return WDLScore(value - 2); } int map_score(TBTable* entry, File f, int value, WDLScore wdl) { constexpr int WDLMap[] = { 1, 3, 0, 2, 0 }; auto flags = entry->get(0, f)->flags; uint8_t* map = entry->map; uint16_t* idx = entry->get(0, f)->map_idx; if (flags & TBFlag::Mapped) { if (flags & TBFlag::Wide) value = ((uint16_t *)map)[idx[WDLMap[wdl + 2]] + value]; else value = map[idx[WDLMap[wdl + 2]] + value]; } // DTZ tables store distance to zero in number of moves or plies. We // want to return plies, so we have convert to plies when needed. if ( (wdl == WDLWin && !(flags & TBFlag::WinPlies)) || (wdl == WDLLoss && !(flags & TBFlag::LossPlies)) || wdl == WDLCursedWin || wdl == WDLBlessedLoss) value *= 2; return value + 1; } // Compute a unique index out of a position and use it to probe the TB file. To // encode k pieces of same type and color, first sort the pieces by square in // ascending order s1 <= s2 <= ... <= sk then compute the unique index as: // // idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk] // template Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result) { Square squares[TBPIECES]; Piece pieces[TBPIECES]; uint64_t idx; int next = 0, size = 0, leadPawnsCnt = 0; PairsData* d; Bitboard b, leadPawns = 0; File tbFile = FILE_A; // A given TB entry like KRK has associated two material keys: KRvk and Kvkr. // If both sides have the same pieces keys are equal. In this case TB tables // only store the 'white to move' case, so if the position to lookup has black // to move, we need to switch the color and flip the squares before to lookup. bool symmetricBlackToMove = (entry->key == entry->key2 && pos.side_to_move()); // TB files are calculated for white as stronger side. For instance we have // KRvK, not KvKR. A position where stronger side is white will have its // material key == entry->key, otherwise we have to switch the color and // flip the squares before to lookup. bool blackStronger = (pos.material_key() != entry->key); int flipColor = (symmetricBlackToMove || blackStronger) * 8; int flipSquares = (symmetricBlackToMove || blackStronger) * 56; int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move(); // For pawns, TB files store 4 separate tables according if leading pawn is on // file a, b, c or d after reordering. The leading pawn is the one with maximum // MapPawns[] value, that is the one most toward the edges and with lowest rank. if (entry->hasPawns) { // In all the 4 tables, pawns are at the beginning of the piece sequence and // their color is the reference one. So we just pick the first one. Piece pc = Piece(entry->get(0, 0)->pieces[0] ^ flipColor); assert(type_of(pc) == PAWN); leadPawns = b = pos.pieces(color_of(pc), PAWN); do squares[size++] = pop_lsb(&b) ^ flipSquares; while (b); leadPawnsCnt = size; std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp)); tbFile = map_to_queenside(file_of(squares[0])); } // DTZ tables are one-sided, i.e. they store positions only for white to // move or only for black to move, so check for side to move to be stm, // early exit otherwise. if (!check_dtz_stm(entry, stm, tbFile)) return *result = CHANGE_STM, Ret(); // Now we are ready to get all the position pieces (but the lead pawns) and // directly map them to the correct color and square. b = pos.pieces() ^ leadPawns; do { Square s = pop_lsb(&b); squares[size] = s ^ flipSquares; pieces[size++] = Piece(pos.piece_on(s) ^ flipColor); } while (b); assert(size >= 2); d = entry->get(stm, tbFile); // Then we reorder the pieces to have the same sequence as the one stored // in pieces[i]: the sequence that ensures the best compression. for (int i = leadPawnsCnt; i < size - 1; ++i) for (int j = i + 1; j < size; ++j) if (d->pieces[i] == pieces[j]) { std::swap(pieces[i], pieces[j]); std::swap(squares[i], squares[j]); break; } // Now we map again the squares so that the square of the lead piece is in // the triangle A1-D1-D4. if (file_of(squares[0]) > FILE_D) for (int i = 0; i < size; ++i) squares[i] ^= 7; // Horizontal flip: SQ_H1 -> SQ_A1 // Encode leading pawns starting with the one with minimum MapPawns[] and // proceeding in ascending order. if (entry->hasPawns) { idx = LeadPawnIdx[leadPawnsCnt][squares[0]]; std::sort(squares + 1, squares + leadPawnsCnt, pawns_comp); for (int i = 1; i < leadPawnsCnt; ++i) idx += Binomial[i][MapPawns[squares[i]]]; goto encode_remaining; // With pawns we have finished special treatments } // In positions withouth pawns, we further flip the squares to ensure leading // piece is below RANK_5. if (rank_of(squares[0]) > RANK_4) for (int i = 0; i < size; ++i) squares[i] ^= SQ_A8; // Vertical flip: SQ_A8 -> SQ_A1 // Look for the first piece of the leading group not on the A1-D4 diagonal // and ensure it is mapped below the diagonal. for (int i = 0; i < d->groupLen[0]; ++i) { if (!off_A1H8(squares[i])) continue; if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C3 for (int j = i; j < size; ++j) squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63); break; } // Encode the leading group. // // Suppose we have KRvK. Let's say the pieces are on square numbers wK, wR // and bK (each 0...63). The simplest way to map this position to an index // is like this: // // index = wK * 64 * 64 + wR * 64 + bK; // // But this way the TB is going to have 64*64*64 = 262144 positions, with // lots of positions being equivalent (because they are mirrors of each // other) and lots of positions being invalid (two pieces on one square, // adjacent kings, etc.). // Usually the first step is to take the wK and bK together. There are just // 462 ways legal and not-mirrored ways to place the wK and bK on the board. // Once we have placed the wK and bK, there are 62 squares left for the wR // Mapping its square from 0..63 to available squares 0..61 can be done like: // // wR -= (wR > wK) + (wR > bK); // // In words: if wR "comes later" than wK, we deduct 1, and the same if wR // "comes later" than bK. In case of two same pieces like KRRvK we want to // place the two Rs "together". If we have 62 squares left, we can place two // Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be // swapped and still get the same position.) // // In case we have at least 3 unique pieces (inlcuded kings) we encode them // together. if (entry->hasUniquePieces) { int adjust1 = squares[1] > squares[0]; int adjust2 = (squares[2] > squares[0]) + (squares[2] > squares[1]); // First piece is below a1-h8 diagonal. MapA1D1D4[] maps the b1-d1-d3 // triangle to 0...5. There are 63 squares for second piece and and 62 // (mapped to 0...61) for the third. if (off_A1H8(squares[0])) idx = ( MapA1D1D4[squares[0]] * 63 + (squares[1] - adjust1)) * 62 + squares[2] - adjust2; // First piece is on a1-h8 diagonal, second below: map this occurence to // 6 to differentiate from the above case, rank_of() maps a1-d4 diagonal // to 0...3 and finally MapB1H1H7[] maps the b1-h1-h7 triangle to 0..27. else if (off_A1H8(squares[1])) idx = ( 6 * 63 + rank_of(squares[0]) * 28 + MapB1H1H7[squares[1]]) * 62 + squares[2] - adjust2; // First two pieces are on a1-h8 diagonal, third below else if (off_A1H8(squares[2])) idx = 6 * 63 * 62 + 4 * 28 * 62 + rank_of(squares[0]) * 7 * 28 + (rank_of(squares[1]) - adjust1) * 28 + MapB1H1H7[squares[2]]; // All 3 pieces on the diagonal a1-h8 else idx = 6 * 63 * 62 + 4 * 28 * 62 + 4 * 7 * 28 + rank_of(squares[0]) * 7 * 6 + (rank_of(squares[1]) - adjust1) * 6 + (rank_of(squares[2]) - adjust2); } else // We don't have at least 3 unique pieces, like in KRRvKBB, just map // the kings. idx = MapKK[MapA1D1D4[squares[0]]][squares[1]]; encode_remaining: idx *= d->groupIdx[0]; Square* groupSq = squares + d->groupLen[0]; // Encode remainig pawns then pieces according to square, in ascending order bool remainingPawns = entry->hasPawns && entry->pawnCount[1]; while (d->groupLen[++next]) { std::sort(groupSq, groupSq + d->groupLen[next]); uint64_t n = 0; // Map down a square if "comes later" than a square in the previous // groups (similar to what done earlier for leading group pieces). for (int i = 0; i < d->groupLen[next]; ++i) { auto f = [&](Square s) { return groupSq[i] > s; }; auto adjust = std::count_if(squares, groupSq, f); n += Binomial[i + 1][groupSq[i] - adjust - 8 * remainingPawns]; } remainingPawns = false; idx += n * d->groupIdx[next]; groupSq += d->groupLen[next]; } // Now that we have the index, decompress the pair and get the score return map_score(entry, tbFile, decompress_pairs(d, idx), wdl); } // Group together pieces that will be encoded together. The general rule is that // a group contains pieces of same type and color. The exception is the leading // group that, in case of positions withouth pawns, can be formed by 3 different // pieces (default) or by the king pair when there is not a unique piece apart // from the kings. When there are pawns, pawns are always first in pieces[]. // // As example KRKN -> KRK + N, KNNK -> KK + NN, KPPKP -> P + PP + K + K // // The actual grouping depends on the TB generator and can be inferred from the // sequence of pieces in piece[] array. template void set_groups(T& e, PairsData* d, int order[], File f) { int n = 0, firstLen = e.hasPawns ? 0 : e.hasUniquePieces ? 3 : 2; d->groupLen[n] = 1; // Number of pieces per group is stored in groupLen[], for instance in KRKN // the encoder will default on '111', so groupLen[] will be (3, 1). for (int i = 1; i < e.pieceCount; ++i) if (--firstLen > 0 || d->pieces[i] == d->pieces[i - 1]) d->groupLen[n]++; else d->groupLen[++n] = 1; d->groupLen[++n] = 0; // Zero-terminated // The sequence in pieces[] defines the groups, but not the order in which // they are encoded. If the pieces in a group g can be combined on the board // in N(g) different ways, then the position encoding will be of the form: // // g1 * N(g2) * N(g3) + g2 * N(g3) + g3 // // This ensures unique encoding for the whole position. The order of the // groups is a per-table parameter and could not follow the canonical leading // pawns/pieces -> remainig pawns -> remaining pieces. In particular the // first group is at order[0] position and the remaining pawns, when present, // are at order[1] position. bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides int next = pp ? 2 : 1; int freeSquares = 64 - d->groupLen[0] - (pp ? d->groupLen[1] : 0); uint64_t idx = 1; for (int k = 0; next < n || k == order[0] || k == order[1]; ++k) if (k == order[0]) // Leading pawns or pieces { d->groupIdx[0] = idx; idx *= e.hasPawns ? LeadPawnsSize[d->groupLen[0]][f] : e.hasUniquePieces ? 31332 : 462; } else if (k == order[1]) // Remaining pawns { d->groupIdx[1] = idx; idx *= Binomial[d->groupLen[1]][48 - d->groupLen[0]]; } else // Remainig pieces { d->groupIdx[next] = idx; idx *= Binomial[d->groupLen[next]][freeSquares]; freeSquares -= d->groupLen[next++]; } d->groupIdx[n] = idx; } // In Recursive Pairing each symbol represents a pair of childern symbols. So // read d->btree[] symbols data and expand each one in his left and right child // symbol until reaching the leafs that represent the symbol value. uint8_t set_symlen(PairsData* d, Sym s, std::vector& visited) { visited[s] = true; // We can set it now because tree is acyclic Sym sr = d->btree[s].get(); if (sr == 0xFFF) return 0; Sym sl = d->btree[s].get(); if (!visited[sl]) d->symlen[sl] = set_symlen(d, sl, visited); if (!visited[sr]) d->symlen[sr] = set_symlen(d, sr, visited); return d->symlen[sl] + d->symlen[sr] + 1; } uint8_t* set_sizes(PairsData* d, uint8_t* data) { d->flags = *data++; if (d->flags & TBFlag::SingleValue) { d->blocksNum = d->blockLengthSize = 0; d->span = d->sparseIndexSize = 0; // Broken MSVC zero-init d->minSymLen = *data++; // Here we store the single value return data; } // groupLen[] is a zero-terminated list of group lengths, the last groupIdx[] // element stores the biggest index that is the tb size. uint64_t tbSize = d->groupIdx[std::find(d->groupLen, d->groupLen + 7, 0) - d->groupLen]; d->sizeofBlock = 1ULL << *data++; d->span = 1ULL << *data++; d->sparseIndexSize = (tbSize + d->span - 1) / d->span; // Round up auto padding = number(data++); d->blocksNum = number(data); data += sizeof(uint32_t); d->blockLengthSize = d->blocksNum + padding; // Padded to ensure SparseIndex[] // does not point out of range. d->maxSymLen = *data++; d->minSymLen = *data++; d->lowestSym = (Sym*)data; d->base64.resize(d->maxSymLen - d->minSymLen + 1); // The canonical code is ordered such that longer symbols (in terms of // the number of bits of their Huffman code) have lower numeric value, // so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian). // Starting from this we compute a base64[] table indexed by symbol length // and containing 64 bit values so that d->base64[i] >= d->base64[i+1]. // See http://www.eecs.harvard.edu/~michaelm/E210/huffman.pdf for (int i = d->base64.size() - 2; i >= 0; --i) { d->base64[i] = (d->base64[i + 1] + number(&d->lowestSym[i]) - number(&d->lowestSym[i + 1])) / 2; assert(d->base64[i] * 2 >= d->base64[i+1]); } // Now left-shift by an amount so that d->base64[i] gets shifted 1 bit more // than d->base64[i+1] and given the above assert condition, we ensure that // d->base64[i] >= d->base64[i+1]. Moreover for any symbol s64 of length i // and right-padded to 64 bits holds d->base64[i-1] >= s64 >= d->base64[i]. for (size_t i = 0; i < d->base64.size(); ++i) d->base64[i] <<= 64 - i - d->minSymLen; // Right-padding to 64 bits data += d->base64.size() * sizeof(Sym); d->symlen.resize(number(data)); data += sizeof(uint16_t); d->btree = (LR*)data; // The compression scheme used is "Recursive Pairing", that replaces the most // frequent adjacent pair of symbols in the source message by a new symbol, // reevaluating the frequencies of all of the symbol pairs with respect to // the extended alphabet, and then repeating the process. // See http://www.larsson.dogma.net/dcc99.pdf std::vector visited(d->symlen.size()); for (Sym sym = 0; sym < d->symlen.size(); ++sym) if (!visited[sym]) d->symlen[sym] = set_symlen(d, sym, visited); return data + d->symlen.size() * sizeof(LR) + (d->symlen.size() & 1); } uint8_t* set_dtz_map(TBTable&, uint8_t* data, File) { return data; } uint8_t* set_dtz_map(TBTable& e, uint8_t* data, File maxFile) { e.map = data; for (File f = FILE_A; f <= maxFile; ++f) { auto flags = e.get(0, f)->flags; if (flags & TBFlag::Mapped) { if (flags & TBFlag::Wide) { data += (uintptr_t)data & 1; // Word alignment, we may have a mixed table for (int i = 0; i < 4; ++i) { // Sequence like 3,x,x,x,1,x,0,2,x,x e.get(0, f)->map_idx[i] = (uint16_t)((uint16_t *)data - (uint16_t *)e.map + 1); data += 2 * number(data) + 2; } } else { for (int i = 0; i < 4; ++i) { e.get(0, f)->map_idx[i] = (uint16_t)(data - e.map + 1); data += *data + 1; } } } } return data += (uintptr_t)data & 1; // Word alignment } // Populate entry's PairsData records with data from the just memory mapped file. // Called at first access. template void set(T& e, uint8_t* data) { PairsData* d; enum { Split = 1, HasPawns = 2 }; assert(e.hasPawns == bool(*data & HasPawns)); assert((e.key != e.key2) == bool(*data & Split)); data++; // First byte stores flags const int sides = T::Sides == 2 && (e.key != e.key2) ? 2 : 1; const File maxFile = e.hasPawns ? FILE_D : FILE_A; bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides assert(!pp || e.pawnCount[0]); for (File f = FILE_A; f <= maxFile; ++f) { for (int i = 0; i < sides; i++) *e.get(i, f) = PairsData(); int order[][2] = { { *data & 0xF, pp ? *(data + 1) & 0xF : 0xF }, { *data >> 4, pp ? *(data + 1) >> 4 : 0xF } }; data += 1 + pp; for (int k = 0; k < e.pieceCount; ++k, ++data) for (int i = 0; i < sides; i++) e.get(i, f)->pieces[k] = Piece(i ? *data >> 4 : *data & 0xF); for (int i = 0; i < sides; ++i) set_groups(e, e.get(i, f), order[i], f); } data += (uintptr_t)data & 1; // Word alignment for (File f = FILE_A; f <= maxFile; ++f) for (int i = 0; i < sides; i++) data = set_sizes(e.get(i, f), data); data = set_dtz_map(e, data, maxFile); for (File f = FILE_A; f <= maxFile; ++f) for (int i = 0; i < sides; i++) { (d = e.get(i, f))->sparseIndex = (SparseEntry*)data; data += d->sparseIndexSize * sizeof(SparseEntry); } for (File f = FILE_A; f <= maxFile; ++f) for (int i = 0; i < sides; i++) { (d = e.get(i, f))->blockLength = (uint16_t*)data; data += d->blockLengthSize * sizeof(uint16_t); } for (File f = FILE_A; f <= maxFile; ++f) for (int i = 0; i < sides; i++) { data = (uint8_t*)(((uintptr_t)data + 0x3F) & ~0x3F); // 64 byte alignment (d = e.get(i, f))->data = data; data += d->blocksNum * d->sizeofBlock; } } // If the TB file corresponding to the given position is already memory mapped // then return its base address, otherwise try to memory map and init it. Called // at every probe, memory map and init only at first access. Function is thread // safe and can be called concurrently. template void* mapped(TBTable& e, const Position& pos) { static std::mutex mutex; // Use 'acquire' to avoid a thread reading 'ready' == true while // another is still working. (compiler reordering may cause this). if (e.ready.load(std::memory_order_acquire)) return e.baseAddress; // Could be nullptr if file does not exist std::unique_lock lk(mutex); if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock return e.baseAddress; // Pieces strings in decreasing order for each color, like ("KPP","KR") std::string fname, w, b; for (PieceType pt = KING; pt >= PAWN; --pt) { w += std::string(popcount(pos.pieces(WHITE, pt)), PieceToChar[pt]); b += std::string(popcount(pos.pieces(BLACK, pt)), PieceToChar[pt]); } fname = (e.key == pos.material_key() ? w + 'v' + b : b + 'v' + w) + (Type == WDL ? ".rtbw" : ".rtbz"); uint8_t* data = TBFile(fname).map(&e.baseAddress, &e.mapping, Type); if (data) set(e, data); e.ready.store(true, std::memory_order_release); return e.baseAddress; } template::Ret> Ret probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw) { if (pos.count() == 2) // KvK return Ret(WDLDraw); TBTable* entry = TBTables.get(pos.material_key()); if (!entry || !mapped(*entry, pos)) return *result = FAIL, Ret(); return do_probe_table(pos, entry, wdl, result); } // For a position where the side to move has a winning capture it is not necessary // to store a winning value so the generator treats such positions as "don't cares" // and tries to assign to it a value that improves the compression ratio. Similarly, // if the side to move has a drawing capture, then the position is at least drawn. // If the position is won, then the TB needs to store a win value. But if the // position is drawn, the TB may store a loss value if that is better for compression. // All of this means that during probing, the engine must look at captures and probe // their results and must probe the position itself. The "best" result of these // probes is the correct result for the position. // DTZ tables do not store values when a following move is a zeroing winning move // (winning capture or winning pawn move). Also DTZ store wrong values for positions // where the best move is an ep-move (even if losing). So in all these cases set // the state to ZEROING_BEST_MOVE. template WDLScore search(Position& pos, ProbeState* result) { WDLScore value, bestValue = WDLLoss; StateInfo st; auto moveList = MoveList(pos); size_t totalCount = moveList.size(), moveCount = 0; for (const Move& move : moveList) { if ( !pos.capture(move) && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN)) continue; moveCount++; pos.do_move(move, st); value = -search(pos, result); pos.undo_move(move); if (*result == FAIL) return WDLDraw; if (value > bestValue) { bestValue = value; if (value >= WDLWin) { *result = ZEROING_BEST_MOVE; // Winning DTZ-zeroing move return value; } } } // In case we have already searched all the legal moves we don't have to probe // the TB because the stored score could be wrong. For instance TB tables // do not contain information on position with ep rights, so in this case // the result of probe_wdl_table is wrong. Also in case of only capture // moves, for instance here 4K3/4q3/6p1/2k5/6p1/8/8/8 w - - 0 7, we have to // return with ZEROING_BEST_MOVE set. bool noMoreMoves = (moveCount && moveCount == totalCount); if (noMoreMoves) value = bestValue; else { value = probe_table(pos, result); if (*result == FAIL) return WDLDraw; } // DTZ stores a "don't care" value if bestValue is a win if (bestValue >= value) return *result = ( bestValue > WDLDraw || noMoreMoves ? ZEROING_BEST_MOVE : OK), bestValue; return *result = OK, value; } } // namespace /// Tablebases::init() is called at startup and after every change to /// "SyzygyPath" UCI option to (re)create the various tables. It is not thread /// safe, nor it needs to be. void Tablebases::init(const std::string& paths) { TBTables.clear(); MaxCardinality = 0; TBFile::Paths = paths; if (paths.empty() || paths == "") return; // MapB1H1H7[] encodes a square below a1-h8 diagonal to 0..27 int code = 0; for (Square s = SQ_A1; s <= SQ_H8; ++s) if (off_A1H8(s) < 0) MapB1H1H7[s] = code++; // MapA1D1D4[] encodes a square in the a1-d1-d4 triangle to 0..9 std::vector diagonal; code = 0; for (Square s = SQ_A1; s <= SQ_D4; ++s) if (off_A1H8(s) < 0 && file_of(s) <= FILE_D) MapA1D1D4[s] = code++; else if (!off_A1H8(s) && file_of(s) <= FILE_D) diagonal.push_back(s); // Diagonal squares are encoded as last ones for (auto s : diagonal) MapA1D1D4[s] = code++; // MapKK[] encodes all the 461 possible legal positions of two kings where // the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4 // diagonal, the other one shall not to be above the a1-h8 diagonal. std::vector> bothOnDiagonal; code = 0; for (int idx = 0; idx < 10; idx++) for (Square s1 = SQ_A1; s1 <= SQ_D4; ++s1) if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0 { for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) if ((PseudoAttacks[KING][s1] | s1) & s2) continue; // Illegal position else if (!off_A1H8(s1) && off_A1H8(s2) > 0) continue; // First on diagonal, second above else if (!off_A1H8(s1) && !off_A1H8(s2)) bothOnDiagonal.emplace_back(idx, s2); else MapKK[idx][s2] = code++; } // Legal positions with both kings on diagonal are encoded as last ones for (auto p : bothOnDiagonal) MapKK[p.first][p.second] = code++; // Binomial[] stores the Binomial Coefficents using Pascal rule. There // are Binomial[k][n] ways to choose k elements from a set of n elements. Binomial[0][0] = 1; for (int n = 1; n < 64; n++) // Squares for (int k = 0; k < 6 && k <= n; ++k) // Pieces Binomial[k][n] = (k > 0 ? Binomial[k - 1][n - 1] : 0) + (k < n ? Binomial[k ][n - 1] : 0); // MapPawns[s] encodes squares a2-h7 to 0..47. This is the number of possible // available squares when the leading one is in 's'. Moreover the pawn with // highest MapPawns[] is the leading pawn, the one nearest the edge and, // among pawns with same file, the one with lowest rank. int availableSquares = 47; // Available squares when lead pawn is in a2 // Init the tables for the encoding of leading pawns group: with 7-men TB we // can have up to 5 leading pawns (KPPPPPK). for (int leadPawnsCnt = 1; leadPawnsCnt <= 5; ++leadPawnsCnt) for (File f = FILE_A; f <= FILE_D; ++f) { // Restart the index at every file because TB table is splitted // by file, so we can reuse the same index for different files. int idx = 0; // Sum all possible combinations for a given file, starting with // the leading pawn on rank 2 and increasing the rank. for (Rank r = RANK_2; r <= RANK_7; ++r) { Square sq = make_square(f, r); // Compute MapPawns[] at first pass. // If sq is the leading pawn square, any other pawn cannot be // below or more toward the edge of sq. There are 47 available // squares when sq = a2 and reduced by 2 for any rank increase // due to mirroring: sq == a3 -> no a2, h2, so MapPawns[a3] = 45 if (leadPawnsCnt == 1) { MapPawns[sq] = availableSquares--; MapPawns[sq ^ 7] = availableSquares--; // Horizontal flip } LeadPawnIdx[leadPawnsCnt][sq] = idx; idx += Binomial[leadPawnsCnt - 1][MapPawns[sq]]; } // After a file is traversed, store the cumulated per-file index LeadPawnsSize[leadPawnsCnt][f] = idx; } // Add entries in TB tables if the corresponding ".rtbw" file exsists for (PieceType p1 = PAWN; p1 < KING; ++p1) { TBTables.add({KING, p1, KING}); for (PieceType p2 = PAWN; p2 <= p1; ++p2) { TBTables.add({KING, p1, p2, KING}); TBTables.add({KING, p1, KING, p2}); for (PieceType p3 = PAWN; p3 < KING; ++p3) TBTables.add({KING, p1, p2, KING, p3}); for (PieceType p3 = PAWN; p3 <= p2; ++p3) { TBTables.add({KING, p1, p2, p3, KING}); for (PieceType p4 = PAWN; p4 <= p3; ++p4) { TBTables.add({KING, p1, p2, p3, p4, KING}); for (PieceType p5 = PAWN; p5 <= p4; ++p5) TBTables.add({KING, p1, p2, p3, p4, p5, KING}); for (PieceType p5 = PAWN; p5 < KING; ++p5) TBTables.add({KING, p1, p2, p3, p4, KING, p5}); } for (PieceType p4 = PAWN; p4 < KING; ++p4) { TBTables.add({KING, p1, p2, p3, KING, p4}); for (PieceType p5 = PAWN; p5 <= p4; ++p5) TBTables.add({KING, p1, p2, p3, KING, p4, p5}); } } for (PieceType p3 = PAWN; p3 <= p1; ++p3) for (PieceType p4 = PAWN; p4 <= (p1 == p3 ? p2 : p3); ++p4) TBTables.add({KING, p1, p2, KING, p3, p4}); } } sync_cout << "info string Found " << TBTables.size() << " tablebases" << sync_endl; } // Probe the WDL table for a particular position. // If *result != FAIL, the probe was successful. // The return value is from the point of view of the side to move: // -2 : loss // -1 : loss, but draw under 50-move rule // 0 : draw // 1 : win, but draw under 50-move rule // 2 : win WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) { *result = OK; return search(pos, result); } // Probe the DTZ table for a particular position. // If *result != FAIL, the probe was successful. // The return value is from the point of view of the side to move: // n < -100 : loss, but draw under 50-move rule // -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0) // -1 : loss, the side to move is mated // 0 : draw // 1 < n <= 100 : win in n ply (assuming 50-move counter == 0) // 100 < n : win, but draw under 50-move rule // // The return value n can be off by 1: a return value -n can mean a loss // in n+1 ply and a return value +n can mean a win in n+1 ply. This // cannot happen for tables with positions exactly on the "edge" of // the 50-move rule. // // This implies that if dtz > 0 is returned, the position is certainly // a win if dtz + 50-move-counter <= 99. Care must be taken that the engine // picks moves that preserve dtz + 50-move-counter <= 99. // // If n = 100 immediately after a capture or pawn move, then the position // is also certainly a win, and during the whole phase until the next // capture or pawn move, the inequality to be preserved is // dtz + 50-movecounter <= 100. // // In short, if a move is available resulting in dtz + 50-move-counter <= 99, // then do not accept moves leading to dtz + 50-move-counter == 100. int Tablebases::probe_dtz(Position& pos, ProbeState* result) { *result = OK; WDLScore wdl = search(pos, result); if (*result == FAIL || wdl == WDLDraw) // DTZ tables don't store draws return 0; // DTZ stores a 'don't care' value in this case, or even a plain wrong // one as in case the best move is a losing ep, so it cannot be probed. if (*result == ZEROING_BEST_MOVE) return dtz_before_zeroing(wdl); int dtz = probe_table(pos, result, wdl); if (*result == FAIL) return 0; if (*result != CHANGE_STM) return (dtz + 100 * (wdl == WDLBlessedLoss || wdl == WDLCursedWin)) * sign_of(wdl); // DTZ stores results for the other side, so we need to do a 1-ply search and // find the winning move that minimizes DTZ. StateInfo st; int minDTZ = 0xFFFF; for (const Move& move : MoveList(pos)) { bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN; pos.do_move(move, st); // For zeroing moves we want the dtz of the move _before_ doing it, // otherwise we will get the dtz of the next move sequence. Search the // position after the move to get the score sign (because even in a // winning position we could make a losing capture or going for a draw). dtz = zeroing ? -dtz_before_zeroing(search(pos, result)) : -probe_dtz(pos, result); // If the move mates, force minDTZ to 1 if (dtz == 1 && pos.checkers() && MoveList(pos).size() == 0) minDTZ = 1; // Convert result from 1-ply search. Zeroing moves are already accounted // by dtz_before_zeroing() that returns the DTZ of the previous move. if (!zeroing) dtz += sign_of(dtz); // Skip the draws and if we are winning only pick positive dtz if (dtz < minDTZ && sign_of(dtz) == sign_of(wdl)) minDTZ = dtz; pos.undo_move(move); if (*result == FAIL) return 0; } // When there are no legal moves, the position is mate: we return -1 return minDTZ == 0xFFFF ? -1 : minDTZ; } // Use the DTZ tables to rank root moves. // // A return value false indicates that not all probes were successful. bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { ProbeState result; StateInfo st; // Obtain 50-move counter for the root position int cnt50 = pos.rule50_count(); // Check whether a position was repeated since the last zeroing move. bool rep = pos.has_repeated(); int dtz, bound = Options["Syzygy50MoveRule"] ? 900 : 1; // Probe and rank each move for (auto& m : rootMoves) { pos.do_move(m.pv[0], st); // Calculate dtz for the current move counting from the root position if (pos.rule50_count() == 0) { // In case of a zeroing move, dtz is one of -101/-1/0/1/101 WDLScore wdl = -probe_wdl(pos, &result); dtz = dtz_before_zeroing(wdl); } else { // Otherwise, take dtz for the new position and correct by 1 ply dtz = -probe_dtz(pos, &result); dtz = dtz > 0 ? dtz + 1 : dtz < 0 ? dtz - 1 : dtz; } // Make sure that a mating move is assigned a dtz value of 1 if ( pos.checkers() && dtz == 2 && MoveList(pos).size() == 0) dtz = 1; pos.undo_move(m.pv[0]); if (result == FAIL) return false; // Better moves are ranked higher. Certain wins are ranked equally. // Losing moves are ranked equally unless a 50-move draw is in sight. int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? 1000 : 1000 - (dtz + cnt50)) : dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -1000 : -1000 + (-dtz + cnt50)) : 0; m.tbRank = r; // Determine the score to be displayed for this move. Assign at least // 1 cp to cursed wins and let it grow to 49 cp as the positions gets // closer to a real win. m.tbScore = r >= bound ? VALUE_MATE - MAX_PLY - 1 : r > 0 ? Value((std::max( 3, r - 800) * int(PawnValueEg)) / 200) : r == 0 ? VALUE_DRAW : r > -bound ? Value((std::min(-3, r + 800) * int(PawnValueEg)) / 200) : -VALUE_MATE + MAX_PLY + 1; } return true; } // Use the WDL tables to rank root moves. // This is a fallback for the case that some or all DTZ tables are missing. // // A return value false indicates that not all probes were successful. bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { static const int WDL_to_rank[] = { -1000, -899, 0, 899, 1000 }; ProbeState result; StateInfo st; bool rule50 = Options["Syzygy50MoveRule"]; // Probe and rank each move for (auto& m : rootMoves) { pos.do_move(m.pv[0], st); WDLScore wdl = -probe_wdl(pos, &result); pos.undo_move(m.pv[0]); if (result == FAIL) return false; m.tbRank = WDL_to_rank[wdl + 2]; if (!rule50) wdl = wdl > WDLDraw ? WDLWin : wdl < WDLDraw ? WDLLoss : WDLDraw; m.tbScore = WDL_to_value[wdl + 2]; } return true; } stockfish-11.orig/src/syzygy/tbprobe.h0000644000175000017500000000470113610452365016335 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (c) 2013 Ronald de Man Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TBPROBE_H #define TBPROBE_H #include #include "../search.h" namespace Tablebases { enum WDLScore { WDLLoss = -2, // Loss WDLBlessedLoss = -1, // Loss, but draw under 50-move rule WDLDraw = 0, // Draw WDLCursedWin = 1, // Win, but draw under 50-move rule WDLWin = 2, // Win WDLScoreNone = -1000 }; // Possible states after a probing operation enum ProbeState { FAIL = 0, // Probe failed (missing file table) OK = 1, // Probe succesful CHANGE_STM = -1, // DTZ should check the other side ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move) }; extern int MaxCardinality; void init(const std::string& paths); WDLScore probe_wdl(Position& pos, ProbeState* result); int probe_dtz(Position& pos, ProbeState* result); bool root_probe(Position& pos, Search::RootMoves& rootMoves); bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves); void rank_root_moves(Position& pos, Search::RootMoves& rootMoves); inline std::ostream& operator<<(std::ostream& os, const WDLScore v) { os << (v == WDLLoss ? "Loss" : v == WDLBlessedLoss ? "Blessed loss" : v == WDLDraw ? "Draw" : v == WDLCursedWin ? "Cursed win" : v == WDLWin ? "Win" : "None"); return os; } inline std::ostream& operator<<(std::ostream& os, const ProbeState v) { os << (v == FAIL ? "Failed" : v == OK ? "Success" : v == CHANGE_STM ? "Probed opponent side" : v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None"); return os; } } #endif stockfish-11.orig/src/bitbase.cpp0000644000175000017500000001424413610452365015271 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "bitboard.h" #include "types.h" namespace { // There are 24 possible pawn squares: files A to D and ranks from 2 to 7. // Positions with the pawn on files E to H will be mirrored before probing. constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608 // Each uint32_t stores results of 32 positions, one per bit uint32_t KPKBitbase[MAX_INDEX / 32]; // A KPK bitbase index is an integer in [0, IndexMax] range // // Information is mapped in a way that minimizes the number of iterations: // // bit 0- 5: white king square (from SQ_A1 to SQ_H8) // bit 6-11: black king square (from SQ_A1 to SQ_H8) // bit 12: side to move (WHITE or BLACK) // bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2) unsigned index(Color us, Square bksq, Square wksq, Square psq) { return int(wksq) | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); } enum Result { INVALID = 0, UNKNOWN = 1, DRAW = 2, WIN = 4 }; Result& operator|=(Result& r, Result v) { return r = Result(r | v); } struct KPKPosition { KPKPosition() = default; explicit KPKPosition(unsigned idx); operator Result() const { return result; } Result classify(const std::vector& db) { return us == WHITE ? classify(db) : classify(db); } template Result classify(const std::vector& db); Color us; Square ksq[COLOR_NB], psq; Result result; }; } // namespace bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) { assert(file_of(wpsq) <= FILE_D); unsigned idx = index(us, bksq, wksq, wpsq); return KPKBitbase[idx / 32] & (1 << (idx & 0x1F)); } void Bitbases::init() { std::vector db(MAX_INDEX); unsigned idx, repeat = 1; // Initialize db with known win / draw positions for (idx = 0; idx < MAX_INDEX; ++idx) db[idx] = KPKPosition(idx); // Iterate through the positions until none of the unknown positions can be // changed to either wins or draws (15 cycles needed). while (repeat) for (repeat = idx = 0; idx < MAX_INDEX; ++idx) repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN); // Map 32 results into one KPKBitbase[] entry for (idx = 0; idx < MAX_INDEX; ++idx) if (db[idx] == WIN) KPKBitbase[idx / 32] |= 1 << (idx & 0x1F); } namespace { KPKPosition::KPKPosition(unsigned idx) { ksq[WHITE] = Square((idx >> 0) & 0x3F); ksq[BLACK] = Square((idx >> 6) & 0x3F); us = 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 if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 || ksq[WHITE] == psq || ksq[BLACK] == psq || (us == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK]))) result = INVALID; // Immediate win if a pawn can be promoted without getting captured else if ( us == WHITE && rank_of(psq) == RANK_7 && ksq[us] != psq + NORTH && ( distance(ksq[~us], psq + NORTH) > 1 || (PseudoAttacks[KING][ksq[us]] & (psq + NORTH)))) result = WIN; // Immediate draw if it is a stalemate or a king captures undefended pawn else if ( us == BLACK && ( !(PseudoAttacks[KING][ksq[us]] & ~(PseudoAttacks[KING][ksq[~us]] | PawnAttacks[~us][psq])) || (PseudoAttacks[KING][ksq[us]] & psq & ~PseudoAttacks[KING][ksq[~us]]))) result = DRAW; // Position will be classified later else result = UNKNOWN; } template Result KPKPosition::classify(const std::vector& db) { // White to move: If one move leads to a position classified as WIN, the result // of the current position is WIN. If all moves lead to positions classified // as DRAW, the current position is classified as DRAW, otherwise the current // position is classified as UNKNOWN. // // Black to move: If one move leads to a position classified as DRAW, the result // of the current position is DRAW. If all moves lead to positions classified // as WIN, the position is classified as WIN, otherwise the current position is // classified as UNKNOWN. constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Result Good = (Us == WHITE ? WIN : DRAW); constexpr Result Bad = (Us == WHITE ? DRAW : WIN); Result r = INVALID; Bitboard b = PseudoAttacks[KING][ksq[Us]]; while (b) r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)] : db[index(Them, pop_lsb(&b), ksq[Them] , psq)]; if (Us == WHITE) { if (rank_of(psq) < RANK_7) // Single push r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH)]; if ( rank_of(psq) == RANK_2 // Double push && psq + NORTH != ksq[Us] && psq + NORTH != ksq[Them]) r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH + NORTH)]; } return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad; } } // namespace stockfish-11.orig/src/pawns.h0000644000175000017500000000447613610452365014463 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PAWNS_H_INCLUDED #define PAWNS_H_INCLUDED #include "misc.h" #include "position.h" #include "types.h" namespace Pawns { /// Pawns::Entry contains various information about a pawn structure. A lookup /// to the pawn hash table (performed by calling the probe function) returns a /// pointer to an Entry object. struct Entry { Score pawn_score(Color c) const { return scores[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); } template Score king_safety(const Position& pos) { return kingSquares[Us] == pos.square(Us) && castlingRights[Us] == pos.castling_rights(Us) ? kingSafety[Us] : (kingSafety[Us] = do_king_safety(pos)); } template Score do_king_safety(const Position& pos); template Score evaluate_shelter(const Position& pos, Square ksq); Key key; Score scores[COLOR_NB]; Bitboard passedPawns[COLOR_NB]; Bitboard pawnAttacks[COLOR_NB]; Bitboard pawnAttacksSpan[COLOR_NB]; Square kingSquares[COLOR_NB]; Score kingSafety[COLOR_NB]; int castlingRights[COLOR_NB]; }; typedef HashTable Table; Entry* probe(const Position& pos); } // namespace Pawns #endif // #ifndef PAWNS_H_INCLUDED stockfish-11.orig/src/thread.cpp0000644000175000017500000001504413610452365015126 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include // For std::count #include "movegen.h" #include "search.h" #include "thread.h" #include "uci.h" #include "syzygy/tbprobe.h" #include "tt.h" ThreadPool Threads; // Global object /// Thread constructor launches the thread and waits until it goes to sleep /// in idle_loop(). Note that 'searching' and 'exit' should be already set. Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { wait_for_search_finished(); } /// Thread destructor wakes up the thread in idle_loop() and waits /// for its termination. Thread should be already waiting. Thread::~Thread() { assert(!searching); exit = true; start_searching(); stdThread.join(); } /// Thread::bestMoveCount(Move move) return best move counter for the given root move int Thread::best_move_count(Move move) { auto rm = std::find(rootMoves.begin() + pvIdx, rootMoves.begin() + pvLast, move); return rm != rootMoves.begin() + pvLast ? rm->bestMoveCount : 0; } /// Thread::clear() reset histories, usually before a new game void Thread::clear() { counterMoves.fill(MOVE_NONE); mainHistory.fill(0); captureHistory.fill(0); for (bool inCheck : { false, true }) for (StatsType c : { NoCaptures, Captures }) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) h->fill(0); for (bool inCheck : { false, true }) for (StatsType c : { NoCaptures, Captures }) continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); } /// Thread::start_searching() wakes up the thread that will start the search void Thread::start_searching() { std::lock_guard lk(mutex); searching = true; cv.notify_one(); // Wake up the thread in idle_loop() } /// Thread::wait_for_search_finished() blocks on the condition variable /// until the thread has finished searching. void Thread::wait_for_search_finished() { std::unique_lock lk(mutex); cv.wait(lk, [&]{ return !searching; }); } /// Thread::idle_loop() is where the thread is parked, blocked on the /// condition variable, when it has no work to do. void Thread::idle_loop() { // If OS already scheduled us on a different group than 0 then don't overwrite // the choice, eventually we are one of many one-threaded processes running on // some Windows NUMA hardware, for instance in fishtest. To make it simple, // just check if running threads are below a threshold, in this case all this // NUMA machinery is not needed. if (Options["Threads"] > 8) WinProcGroup::bindThisThread(idx); while (true) { std::unique_lock lk(mutex); searching = false; cv.notify_one(); // Wake up anyone waiting for search finished cv.wait(lk, [&]{ return searching; }); if (exit) return; lk.unlock(); search(); } } /// ThreadPool::set() creates/destroys threads to match the requested number. /// Created and launched threads will immediately go to sleep in idle_loop. /// Upon resizing, threads are recreated to allow for binding if necessary. void ThreadPool::set(size_t requested) { if (size() > 0) { // destroy any existing thread(s) main()->wait_for_search_finished(); while (size() > 0) delete back(), pop_back(); } if (requested > 0) { // create new thread(s) push_back(new MainThread(0)); while (size() < requested) push_back(new Thread(size())); clear(); // Reallocate the hash with the new threadpool size TT.resize(Options["Hash"]); // Init thread number dependent search params. Search::init(); } } /// ThreadPool::clear() sets threadPool data to initial values. void ThreadPool::clear() { for (Thread* th : *this) th->clear(); main()->callsCnt = 0; main()->previousScore = VALUE_INFINITE; 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. void ThreadPool::start_thinking(Position& pos, StateListPtr& states, const Search::LimitsType& limits, bool ponderMode) { main()->wait_for_search_finished(); main()->stopOnPonderhit = stop = false; increaseDepth = true; main()->ponder = ponderMode; Search::Limits = limits; Search::RootMoves rootMoves; for (const auto& m : MoveList(pos)) if ( limits.searchmoves.empty() || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) rootMoves.emplace_back(m); if (!rootMoves.empty()) Tablebases::rank_root_moves(pos, rootMoves); // After ownership transfer 'states' becomes empty, so if we stop the search // and call 'go' again without setting a new position states.get() == NULL. assert(states.get() || setupStates.get()); if (states.get()) setupStates = std::move(states); // Ownership transfer, states is now empty // We use Position::set() to set root position across threads. But there are // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot // be deduced from a fen string, so set() clears them and to not lose the info // we need to backup and later restore setupStates->back(). Note that setupStates // is shared by threads but is accessed in read-only mode. StateInfo tmp = setupStates->back(); for (Thread* th : *this) { th->nodes = th->tbHits = th->nmpMinPly = 0; th->rootDepth = th->completedDepth = 0; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); } setupStates->back() = tmp; main()->start_searching(); } stockfish-11.orig/src/benchmark.cpp0000644000175000017500000001406613610452365015614 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "position.h" using namespace std; namespace { const vector Defaults = { "setoption name UCI_Chess960 value false", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4", "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13", "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16", "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17", "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11", "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1", "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1", "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3", "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1", "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1", "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1", "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1", "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1", "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1", "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1", "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1", "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1", "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1", "5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90", "4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21", "r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16", "3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40", // 5-man positions "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate "8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate "8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw // 6-man positions "8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate "8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw // 7-man positions "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw // Mate and stalemate positions "6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1", "r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1", "8/8/8/8/8/6k1/6p1/6K1 w - -", "7k/7P/6K1/8/3B4/8/8/8 b - -", // Chess 960 "setoption name UCI_Chess960 value true", "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w KQkq - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", "setoption name UCI_Chess960 value false" }; } // namespace /// setup_bench() builds a list of UCI commands to be run by bench. There /// are five parameters: TT size in MB, number of search threads that /// should be used, the limit value spent for each position, a file name /// where to look for positions in FEN format and the type of the limit: /// depth, perft, nodes and movetime (in millisecs). /// /// bench -> search default positions up to depth 13 /// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB) /// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec /// bench 64 1 100000 default nodes -> search default positions for 100K nodes each /// bench 16 1 5 default perft -> run a perft 5 on default positions vector setup_bench(const Position& current, istream& is) { vector fens, list; string go, token; // Assign default values to missing arguments string ttSize = (is >> token) ? token : "16"; string threads = (is >> token) ? token : "1"; string limit = (is >> token) ? token : "13"; string fenFile = (is >> token) ? token : "default"; string limitType = (is >> token) ? token : "depth"; go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; if (fenFile == "default") fens = Defaults; else if (fenFile == "current") fens.push_back(current.fen()); else { string fen; ifstream file(fenFile); if (!file.is_open()) { cerr << "Unable to open file " << fenFile << endl; exit(EXIT_FAILURE); } while (getline(file, fen)) if (!fen.empty()) fens.push_back(fen); file.close(); } list.emplace_back("setoption name Threads value " + threads); list.emplace_back("setoption name Hash value " + ttSize); list.emplace_back("ucinewgame"); for (const string& fen : fens) if (fen.find("setoption") != string::npos) list.emplace_back(fen); else { list.emplace_back("position fen " + fen); list.emplace_back(go); } return list; } stockfish-11.orig/src/evaluate.h0000644000175000017500000000231113610452365015123 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef EVALUATE_H_INCLUDED #define EVALUATE_H_INCLUDED #include #include "types.h" class Position; namespace Eval { constexpr Value Tempo = Value(28); // Must be visible to search std::string trace(const Position& pos); Value evaluate(const Position& pos); } #endif // #ifndef EVALUATE_H_INCLUDED stockfish-11.orig/src/thread.h0000644000175000017500000000703213610452365014571 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef THREAD_H_INCLUDED #define THREAD_H_INCLUDED #include #include #include #include #include #include "material.h" #include "movepick.h" #include "pawns.h" #include "position.h" #include "search.h" #include "thread_win32_osx.h" /// Thread class keeps together all the thread-related stuff. We use /// per-thread pawn and material hash tables so that once we get a /// pointer to an entry its life time is unlimited and we don't have /// to care about someone changing the entry under our feet. class Thread { std::mutex mutex; std::condition_variable cv; size_t idx; bool exit = false, searching = true; // Set before starting std::thread NativeThread stdThread; public: explicit Thread(size_t); virtual ~Thread(); virtual void search(); void clear(); void idle_loop(); void start_searching(); void wait_for_search_finished(); int best_move_count(Move move); Pawns::Table pawnsTable; Material::Table materialTable; size_t pvIdx, pvLast; uint64_t ttHitAverage; int selDepth, nmpMinPly; Color nmpColor; std::atomic nodes, tbHits, bestMoveChanges; Position rootPos; Search::RootMoves rootMoves; Depth rootDepth, completedDepth; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; Score contempt; }; /// MainThread is a derived class specific for main thread struct MainThread : public Thread { using Thread::Thread; void search() override; void check_time(); double previousTimeReduction; Value previousScore; Value iterValue[4]; int callsCnt; bool stopOnPonderhit; std::atomic_bool ponder; }; /// ThreadPool struct handles all the threads-related stuff like init, starting, /// parking and, most importantly, launching a thread. All the access to threads /// is done through this class. struct ThreadPool : public std::vector { void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false); void clear(); void set(size_t); 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); } std::atomic_bool stop, increaseDepth; private: StateListPtr setupStates; uint64_t accumulate(std::atomic Thread::* member) const { uint64_t sum = 0; for (Thread* th : *this) sum += (th->*member).load(std::memory_order_relaxed); return sum; } }; extern ThreadPool Threads; #endif // #ifndef THREAD_H_INCLUDED stockfish-11.orig/src/uci.cpp0000644000175000017500000002453313610452365014442 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "evaluate.h" #include "movegen.h" #include "position.h" #include "search.h" #include "thread.h" #include "timeman.h" #include "tt.h" #include "uci.h" #include "syzygy/tbprobe.h" using namespace std; extern vector setup_bench(const Position&, istream&); namespace { // FEN string of the initial position, normal chess const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; // position() is called when engine receives the "position" UCI command. // The function sets up the position described in the given FEN string ("fen") // or the starting position ("startpos") and then makes the moves given in the // following move list ("moves"). void position(Position& pos, istringstream& is, StateListPtr& states) { Move m; string token, fen; is >> token; if (token == "startpos") { fen = StartFEN; is >> token; // Consume "moves" token if any } else if (token == "fen") while (is >> token && token != "moves") fen += token + " "; else return; states = StateListPtr(new std::deque(1)); // Drop old and create a new one pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main()); // Parse move list (if any) while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) { states->emplace_back(); pos.do_move(m, states->back()); } } // setoption() is called when engine receives the "setoption" UCI command. The // function updates the UCI option ("name") to the given value ("value"). void setoption(istringstream& is) { string token, name, value; is >> token; // Consume "name" token // Read option name (can contain spaces) while (is >> token && token != "value") name += (name.empty() ? "" : " ") + token; // Read option value (can contain spaces) while (is >> token) value += (value.empty() ? "" : " ") + token; if (Options.count(name)) Options[name] = value; else sync_cout << "No such option: " << name << sync_endl; } // go() is called when engine receives the "go" UCI command. The function sets // the thinking time and other parameters from the input string, then starts // the search. void go(Position& pos, istringstream& is, StateListPtr& states) { Search::LimitsType limits; string token; bool ponderMode = false; limits.startTime = now(); // As early as possible! while (is >> token) if (token == "searchmoves") while (is >> token) limits.searchmoves.push_back(UCI::to_move(pos, token)); else if (token == "wtime") is >> limits.time[WHITE]; else if (token == "btime") is >> limits.time[BLACK]; else if (token == "winc") is >> limits.inc[WHITE]; else if (token == "binc") is >> limits.inc[BLACK]; else if (token == "movestogo") is >> limits.movestogo; else if (token == "depth") is >> limits.depth; else if (token == "nodes") is >> limits.nodes; else if (token == "movetime") is >> limits.movetime; else if (token == "mate") is >> limits.mate; else if (token == "perft") is >> limits.perft; else if (token == "infinite") limits.infinite = 1; else if (token == "ponder") ponderMode = true; Threads.start_thinking(pos, states, limits, ponderMode); } // bench() is called when engine receives the "bench" command. Firstly // a list of UCI commands is setup according to bench parameters, then // it is run one by one printing a summary at the end. void bench(Position& pos, istream& args, StateListPtr& states) { string token; uint64_t num, nodes = 0, cnt = 1; vector list = setup_bench(pos, args); num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; }); TimePoint elapsed = now(); for (const auto& cmd : list) { istringstream is(cmd); is >> skipws >> token; if (token == "go" || token == "eval") { cerr << "\nPosition: " << cnt++ << '/' << num << endl; if (token == "go") { go(pos, is, states); Threads.main()->wait_for_search_finished(); nodes += Threads.nodes_searched(); } else sync_cout << "\n" << Eval::trace(pos) << sync_endl; } else if (token == "setoption") setoption(is); else if (token == "position") position(pos, is, states); else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while } elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' dbg_print(); // Just before exiting cerr << "\n===========================" << "\nTotal time (ms) : " << elapsed << "\nNodes searched : " << nodes << "\nNodes/second : " << 1000 * nodes / elapsed << endl; } } // namespace /// UCI::loop() waits for a command from stdin, parses it and calls the appropriate /// function. Also intercepts EOF from stdin to ensure gracefully exiting if the /// GUI dies unexpectedly. When called with some command line arguments, e.g. to /// run 'bench', once the command is executed the function returns immediately. /// In addition to the UCI ones, also some additional debug commands are supported. void UCI::loop(int argc, char* argv[]) { Position pos; string token, cmd; StateListPtr states(new std::deque(1)); pos.set(StartFEN, false, &states->back(), Threads.main()); for (int i = 1; i < argc; ++i) cmd += std::string(argv[i]) + " "; do { if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF cmd = "quit"; istringstream is(cmd); token.clear(); // Avoid a stale if getline() returns empty or blank line is >> skipws >> token; if ( token == "quit" || token == "stop") Threads.stop = true; // The GUI sends 'ponderhit' to tell us the user has played the expected move. // So 'ponderhit' will be sent if we were told to ponder on the same move the // user has played. We should continue searching but switch from pondering to // normal search. else if (token == "ponderhit") Threads.main()->ponder = false; // Switch to normal search else if (token == "uci") sync_cout << "id name " << engine_info(true) << "\n" << Options << "\nuciok" << sync_endl; else if (token == "setoption") setoption(is); else if (token == "go") go(pos, is, states); else if (token == "position") position(pos, is, states); else if (token == "ucinewgame") Search::clear(); else if (token == "isready") sync_cout << "readyok" << sync_endl; // Additional custom non-UCI commands, mainly for debugging. // Do not use these commands during a search! else if (token == "flip") pos.flip(); else if (token == "bench") bench(pos, is, states); else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; else if (token == "compiler") sync_cout << compiler_info() << sync_endl; else sync_cout << "Unknown command: " << cmd << sync_endl; } while (token != "quit" && argc == 1); // Command line args are one-shot } /// UCI::value() converts a Value to a string suitable for use with the UCI /// protocol specification: /// /// cp The score from the engine's point of view in centipawns. /// mate Mate in y moves, not plies. If the engine is getting mated /// use negative values for y. string UCI::value(Value v) { assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); stringstream ss; if (abs(v) < VALUE_MATE - MAX_PLY) ss << "cp " << v * 100 / PawnValueEg; else ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; return ss.str(); } /// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) std::string UCI::square(Square s) { return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) }; } /// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). /// The only special case is castling, where we print in the e1g1 notation in /// normal chess mode, and in e1h1 notation in chess960 mode. Internally all /// castling moves are always encoded as 'king captures rook'. string UCI::move(Move m, bool chess960) { Square from = from_sq(m); Square to = to_sq(m); if (m == MOVE_NONE) return "(none)"; if (m == MOVE_NULL) return "0000"; if (type_of(m) == CASTLING && !chess960) to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); string move = UCI::square(from) + UCI::square(to); if (type_of(m) == PROMOTION) move += " pnbrqk"[promotion_type(m)]; return move; } /// UCI::to_move() converts a string representing a move in coordinate notation /// (g1f3, a7a8q) to the corresponding legal Move, if any. Move UCI::to_move(const Position& pos, string& str) { if (str.length() == 5) // Junior could send promotion piece in uppercase str[4] = char(tolower(str[4])); for (const auto& m : MoveList(pos)) if (str == UCI::move(m, pos.is_chess960())) return m; return MOVE_NONE; } stockfish-11.orig/src/psqt.cpp0000644000175000017500000001242613610452365014647 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "types.h" Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; namespace PSQT { #define S(mg, eg) make_score(mg, eg) // Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece // type on a given square a (middlegame, endgame) score pair is assigned. Table // is defined for files A..D and white side: it is symmetric for black side and // second half of the files. constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { }, { }, { // Knight { S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) }, { S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) }, { S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) }, { S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) }, { S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) }, { S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) }, { S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) }, { S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) } }, { // Bishop { S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) }, { S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) }, { S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) }, { S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) }, { S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) }, { S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) }, { S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) }, { S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) } }, { // Rook { S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) }, { S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) }, { S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) }, { S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) }, { S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) }, { S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) }, { S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) }, { S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) } }, { // Queen { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, { S(-3,-55), S( 5,-31), S( 8,-22), S(12, -4) }, { S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) }, { S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) }, { S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) }, { S(-4,-38), S(10,-18), S( 6,-12), S( 8, 1) }, { S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) }, { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) } }, { // King { S(271, 1), S(327, 45), S(271, 85), S(198, 76) }, { S(278, 53), S(303,100), S(234,133), S(179,135) }, { S(195, 88), S(258,130), S(169,169), S(120,175) }, { S(164,103), S(190,156), S(138,172), S( 98,172) }, { S(154, 96), S(179,166), S(105,199), S( 70,199) }, { S(123, 92), S(145,172), S( 81,184), S( 31,191) }, { S( 88, 47), S(120,121), S( 65,116), S( 33,131) }, { S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) } } }; constexpr Score PBonus[RANK_NB][FILE_NB] = { // Pawn (asymmetric distribution) { }, { 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( 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( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) } }; #undef S 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. void init() { for (Piece pc = W_PAWN; pc <= W_KING; ++pc) { PieceValue[MG][~pc] = PieceValue[MG][pc]; PieceValue[EG][~pc] = PieceValue[EG][pc]; Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); for (Square s = SQ_A1; s <= SQ_H8; ++s) { File f = map_to_queenside(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] = -psq[pc][s]; } } } } // namespace PSQT stockfish-11.orig/src/misc.cpp0000644000175000017500000002574113610452365014617 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef _WIN32 #if _WIN32_WINNT < 0x0601 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0601 // Force to include needed API prototypes #endif #ifndef NOMINMAX #define NOMINMAX #endif #include // The needed Windows API for processor groups could be missed from old Windows // versions, so instead of calling them directly (forcing the linker to resolve // the calls at compile time), try to load them at runtime. To do this we need // first to define the corresponding function pointers. extern "C" { typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY); typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); } #endif #include #include #include #include #include #include "misc.h" #include "thread.h" using namespace std; namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. const string Version = "11"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We /// can toggle the logging of std::cout and std:cin at runtime whilst preserving /// usual I/O functionality, all without changing a single line of code! /// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {} int sync() override { return logBuf->pubsync(), buf->pubsync(); } int overflow(int c) override { return log(buf->sputc((char)c), "<< "); } int underflow() override { return buf->sgetc(); } int uflow() override { return log(buf->sbumpc(), ">> "); } streambuf *buf, *logBuf; int log(int c, const char* prefix) { static int last = '\n'; // Single log file if (last == '\n') logBuf->sputn(prefix, 3); return last = logBuf->sputc((char)c); } }; class Logger { Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {} ~Logger() { start(""); } ofstream file; Tie in, out; public: static void start(const std::string& fname) { static Logger l; if (!fname.empty() && !l.file.is_open()) { l.file.open(fname, ifstream::out); if (!l.file.is_open()) { cerr << "Unable to open debug log file " << fname << endl; exit(EXIT_FAILURE); } cin.rdbuf(&l.in); cout.rdbuf(&l.out); } else if (fname.empty() && l.file.is_open()) { cout.rdbuf(l.out.buf); cin.rdbuf(l.in.buf); l.file.close(); } } }; } // namespace /// engine_info() returns the full name of the current Stockfish version. This /// will be either "Stockfish DD-MM-YY" (where DD-MM-YY is the date when /// the program was compiled) or "Stockfish ", depending on whether /// Version is empty. const string engine_info(bool to_uci) { const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); string month, day, year; stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008" ss << "Stockfish " << Version << setfill('0'); if (Version.empty()) { date >> month >> day >> year; ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); } ss << (Is64Bit ? " 64" : "") << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : "")) << (to_uci ? "\nid author ": " by ") << "T. Romstad, M. Costalba, J. Kiiski, G. Linscott"; return ss.str(); } /// compiler_info() returns a string trying to describe the compiler we use const std::string compiler_info() { #define STRINGIFY2(x) #x #define STRINGIFY(x) STRINGIFY2(x) #define VER_STRING(major, minor, patch) STRINGIFY(major) "." STRINGIFY(minor) "." STRINGIFY(patch) /// Predefined macros hell: /// /// __GNUC__ Compiler is gcc, Clang or Intel on Linux /// __INTEL_COMPILER Compiler is Intel /// _MSC_VER Compiler is MSVC or Intel on Windows /// _WIN32 Building on Windows (any) /// _WIN64 Building on Windows 64 bit std::string compiler = "\nCompiled by "; #ifdef __clang__ compiler += "clang++ "; compiler += VER_STRING(__clang_major__, __clang_minor__, __clang_patchlevel__); #elif __INTEL_COMPILER compiler += "Intel compiler "; compiler += "(version "; compiler += STRINGIFY(__INTEL_COMPILER) " update " STRINGIFY(__INTEL_COMPILER_UPDATE); compiler += ")"; #elif _MSC_VER compiler += "MSVC "; compiler += "(version "; compiler += STRINGIFY(_MSC_FULL_VER) "." STRINGIFY(_MSC_BUILD); compiler += ")"; #elif __GNUC__ compiler += "g++ (GNUC) "; compiler += VER_STRING(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); #else compiler += "Unknown compiler "; compiler += "(unknown version)"; #endif #if defined(__APPLE__) compiler += " on Apple"; #elif defined(__CYGWIN__) compiler += " on Cygwin"; #elif defined(__MINGW64__) compiler += " on MinGW64"; #elif defined(__MINGW32__) compiler += " on MinGW32"; #elif defined(__ANDROID__) compiler += " on Android"; #elif defined(__linux__) compiler += " on Linux"; #elif defined(_WIN64) compiler += " on Microsoft Windows 64-bit"; #elif defined(_WIN32) compiler += " on Microsoft Windows 32-bit"; #else compiler += " on unknown system"; #endif compiler += "\n __VERSION__ macro expands to: "; #ifdef __VERSION__ compiler += __VERSION__; #else compiler += "(undefined macro)"; #endif compiler += "\n"; return compiler; } /// Debug functions used mainly to collect run-time statistics static std::atomic hits[2], means[2]; void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); } void dbg_mean_of(int v) { ++means[0]; means[1] += v; } void dbg_print() { if (hits[0]) cerr << "Total " << hits[0] << " Hits " << hits[1] << " hit rate (%) " << 100 * hits[1] / hits[0] << endl; if (means[0]) cerr << "Total " << means[0] << " Mean " << (double)means[1] / means[0] << endl; } /// Used to serialize access to std::cout to avoid multiple threads writing at /// the same time. std::ostream& operator<<(std::ostream& os, SyncCout sc) { static std::mutex m; if (sc == IO_LOCK) m.lock(); if (sc == IO_UNLOCK) m.unlock(); return os; } /// Trampoline helper to avoid moving Logger to misc.h void start_logger(const std::string& fname) { Logger::start(fname); } /// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking /// function that doesn't stall the CPU waiting for data to be loaded from memory, /// which can be quite slow. #ifdef NO_PREFETCH void prefetch(void*) {} #else void prefetch(void* addr) { # if defined(__INTEL_COMPILER) // This hack prevents prefetches from being optimized away by // Intel compiler. Both MSVC and gcc seem not be affected by this. __asm__ (""); # endif # if defined(__INTEL_COMPILER) || defined(_MSC_VER) _mm_prefetch((char*)addr, _MM_HINT_T0); # else __builtin_prefetch(addr); # endif } #endif namespace WinProcGroup { #ifndef _WIN32 void bindThisThread(size_t) {} #else /// best_group() retrieves logical processor information using Windows specific /// API and returns the best group id for the thread with index idx. Original /// code from Texel by Peter Österlund. int best_group(size_t idx) { int threads = 0; int nodes = 0; int cores = 0; DWORD returnLength = 0; DWORD byteOffset = 0; // Early exit if the needed API is not available at runtime HMODULE k32 = GetModuleHandle("Kernel32.dll"); auto fun1 = (fun1_t)(void(*)())GetProcAddress(k32, "GetLogicalProcessorInformationEx"); if (!fun1) return -1; // First call to get returnLength. We expect it to fail due to null buffer if (fun1(RelationAll, nullptr, &returnLength)) return -1; // Once we know returnLength, allocate the buffer SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength); // Second call, now we expect to succeed if (!fun1(RelationAll, buffer, &returnLength)) { free(buffer); return -1; } while (byteOffset < returnLength) { if (ptr->Relationship == RelationNumaNode) nodes++; else if (ptr->Relationship == RelationProcessorCore) { cores++; threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; } assert(ptr->Size); byteOffset += ptr->Size; ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size); } free(buffer); std::vector groups; // Run as many threads as possible on the same node until core limit is // reached, then move on filling the next node. for (int n = 0; n < nodes; n++) for (int i = 0; i < cores / nodes; i++) groups.push_back(n); // In case a core has more than one logical processor (we assume 2) and we // have still threads to allocate, then spread them evenly across available // nodes. for (int t = 0; t < threads - cores; t++) groups.push_back(t % nodes); // If we still have more threads than the total number of logical processors // then return -1 and let the OS to decide what to do. return idx < groups.size() ? groups[idx] : -1; } /// bindThisThread() set the group affinity of the current thread void bindThisThread(size_t idx) { // Use only local variables to be thread-safe int group = best_group(idx); if (group == -1) return; // Early exit if the needed API are not available at runtime HMODULE k32 = GetModuleHandle("Kernel32.dll"); auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity"); if (!fun2 || !fun3) return; GROUP_AFFINITY affinity; if (fun2(group, &affinity)) fun3(GetCurrentThread(), &affinity, nullptr); } #endif } // namespace WinProcGroup stockfish-11.orig/src/movegen.h0000644000175000017500000000416213610452365014763 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED #include #include "types.h" class Position; enum GenType { CAPTURES, QUIETS, QUIET_CHECKS, EVASIONS, NON_EVASIONS, LEGAL }; struct ExtMove { Move move; int value; operator Move() const { return move; } void operator=(Move m) { move = m; } // Inhibit unwanted implicit conversions to Move // with an ambiguity that yields to a compile error. operator float() const = delete; }; inline bool operator<(const ExtMove& f, const ExtMove& s) { return f.value < s.value; } template ExtMove* generate(const Position& pos, ExtMove* moveList); /// The MoveList struct is a simple wrapper around generate(). It sometimes comes /// in handy to use this class instead of the low level generate() function. template struct MoveList { explicit MoveList(const Position& pos) : last(generate(pos, moveList)) {} const ExtMove* begin() const { return moveList; } const ExtMove* end() const { return last; } size_t size() const { return last - moveList; } bool contains(Move move) const { return std::find(begin(), end(), move) != end(); } private: ExtMove moveList[MAX_MOVES], *last; }; #endif // #ifndef MOVEGEN_H_INCLUDED stockfish-11.orig/src/uci.h0000644000175000017500000000461713610452365014110 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef UCI_H_INCLUDED #define UCI_H_INCLUDED #include #include #include "types.h" class Position; namespace UCI { class Option; /// Custom comparator because UCI options should be case insensitive struct CaseInsensitiveLess { bool operator() (const std::string&, const std::string&) const; }; /// Our options container is actually a std::map typedef std::map OptionsMap; /// Option class implements an option as defined by UCI protocol class Option { typedef void (*OnChange)(const Option&); public: Option(OnChange = nullptr); Option(bool v, OnChange = nullptr); Option(const char* v, OnChange = nullptr); Option(double v, int minv, int maxv, OnChange = nullptr); Option(const char* v, const char* cur, OnChange = nullptr); Option& operator=(const std::string&); void operator<<(const Option&); operator double() const; operator std::string() const; bool operator==(const char*) const; private: friend std::ostream& operator<<(std::ostream&, const OptionsMap&); std::string defaultValue, currentValue, type; int min, max; size_t idx; OnChange on_change; }; void init(OptionsMap&); void loop(int argc, char* argv[]); std::string value(Value v); std::string square(Square s); std::string move(Move m, bool chess960); std::string pv(const Position& pos, Depth depth, Value alpha, Value beta); Move to_move(const Position& pos, std::string& str); } // namespace UCI extern UCI::OptionsMap Options; #endif // #ifndef UCI_H_INCLUDED stockfish-11.orig/src/pawns.cpp0000644000175000017500000002133313610452365015005 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "bitboard.h" #include "pawns.h" #include "position.h" #include "thread.h" namespace { #define V Value #define S(mg, eg) make_score(mg, eg) // Pawn penalties constexpr Score Backward = S( 9, 24); constexpr Score BlockedStorm = S(82, 82); constexpr Score Doubled = S(11, 56); constexpr Score Isolated = S( 5, 15); constexpr Score WeakLever = S( 0, 56); constexpr Score WeakUnopposed = S(13, 27); // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; // Strength of pawn shelter for our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = { { V( -6), V( 81), V( 93), V( 58), V( 39), V( 18), V( 25) }, { V(-43), V( 61), V( 35), V(-49), V(-29), V(-11), V( -63) }, { V(-10), V( 75), V( 23), V( -2), V( 32), V( 3), V( -45) }, { V(-39), V(-13), V(-29), V(-52), V(-48), V(-67), V(-166) } }; // Danger of enemy pawns moving toward our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn // is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn // on edge, likely blocked by our king. constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = { { V( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) }, { V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) }, { V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) }, { V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) } }; #undef S #undef V template Score evaluate(const Position& pos, Pawns::Entry* e) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Up = pawn_push(Us); Bitboard neighbours, stoppers, support, phalanx, opposed; Bitboard lever, leverPush, blocked; Square s; bool backward, passed, doubled; Score score = SCORE_ZERO; const Square* pl = pos.squares(Us); Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard doubleAttackThem = pawn_double_attacks_bb(theirPawns); e->passedPawns[Us] = 0; e->kingSquares[Us] = SQ_NONE; e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb(ourPawns); // Loop through all pawns of the current color and score each pawn while ((s = *pl++) != SQ_NONE) { assert(pos.piece_on(s) == make_piece(Us, PAWN)); Rank r = relative_rank(Us, s); // Flag the pawn opposed = theirPawns & forward_file_bb(Us, s); blocked = theirPawns & (s + Up); stoppers = theirPawns & passed_pawn_span(Us, s); lever = theirPawns & PawnAttacks[Us][s]; leverPush = theirPawns & PawnAttacks[Us][s + Up]; doubled = ourPawns & (s - Up); neighbours = ourPawns & adjacent_files_bb(s); phalanx = neighbours & rank_bb(s); support = neighbours & rank_bb(s - Up); // A pawn is backward when it is behind all pawns of the same color on // the adjacent files and cannot safely advance. backward = !(neighbours & forward_ranks_bb(Them, s + Up)) && (leverPush | blocked); // Compute additional span if pawn is not backward nor blocked if (!backward && !blocked) e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); // A pawn is passed if one of the three following conditions is true: // (a) there is no stoppers except some levers // (b) the only stoppers are the leverPush, but we outnumber them // (c) there is only one front stopper which can be levered. passed = !(stoppers ^ lever) || ( !(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush)) || ( stoppers == blocked && r >= RANK_5 && (shift(support) & ~(theirPawns | doubleAttackThem))); // Passed pawns will be properly scored later in evaluation when we have // full attack info. if (passed) e->passedPawns[Us] |= s; // Score this pawn if (support | phalanx) { int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) + 21 * popcount(support); score += make_score(v, v * (r - 2) / 4); } else if (!neighbours) score -= Isolated + WeakUnopposed * !opposed; else if (backward) score -= Backward + WeakUnopposed * !opposed; if (!support) score -= Doubled * doubled + WeakLever * more_than_one(lever); } return score; } } // 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 /// have to recompute all when the same pawns configuration occurs again. Entry* probe(const Position& pos) { Key key = pos.pawn_key(); Entry* e = pos.this_thread()->pawnsTable[key]; if (e->key == key) return e; e->key = key; e->scores[WHITE] = evaluate(pos, e); e->scores[BLACK] = evaluate(pos, e); return e; } /// Entry::evaluate_shelter() calculates the shelter bonus and the storm /// penalty for a king, looking at the king file and the two closest files. template Score Entry::evaluate_shelter(const Position& pos, Square ksq) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); Bitboard ourPawns = b & pos.pieces(Us); Bitboard theirPawns = b & pos.pieces(Them); Score bonus = make_score(5, 5); File center = clamp(file_of(ksq), FILE_B, FILE_G); for (File f = File(center - 1); f <= File(center + 1); ++f) { b = ourPawns & file_bb(f); int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; b = theirPawns & file_bb(f); int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; File d = map_to_queenside(f); bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) bonus -= BlockedStorm * int(theirRank == RANK_3); else bonus -= make_score(UnblockedStorm[d][theirRank], 0); } return bonus; } /// Entry::do_king_safety() calculates a bonus for king safety. It is called only /// when king square changes, which is about 20% of total king_safety() calls. template Score Entry::do_king_safety(const Position& pos) { Square ksq = pos.square(Us); kingSquares[Us] = ksq; castlingRights[Us] = pos.castling_rights(Us); auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); }; Score shelter = evaluate_shelter(pos, ksq); // If we can castle use the bonus after castling if it is bigger if (pos.can_castle(Us & KING_SIDE)) shelter = std::max(shelter, evaluate_shelter(pos, relative_square(Us, SQ_G1)), compare); if (pos.can_castle(Us & QUEEN_SIDE)) shelter = std::max(shelter, evaluate_shelter(pos, relative_square(Us, SQ_C1)), compare); // In endgame we like to bring our king near our closest pawn Bitboard pawns = pos.pieces(Us, PAWN); int minPawnDist = pawns ? 8 : 0; if (pawns & PseudoAttacks[KING][ksq]) minPawnDist = 1; else while (pawns) minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); return shelter - make_score(0, 16 * minPawnDist); } // Explicit template instantiation template Score Entry::do_king_safety(const Position& pos); template Score Entry::do_king_safety(const Position& pos); } // namespace Pawns stockfish-11.orig/src/thread_win32_osx.h0000644000175000017500000000436313610452365016510 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef THREAD_WIN32_OSX_H_INCLUDED #define THREAD_WIN32_OSX_H_INCLUDED #include /// On OSX threads other than the main thread are created with a reduced stack /// size of 512KB by default, this is too low for deep searches, which require /// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE. /// The implementation calls pthread_create() with the stack size parameter /// equal to the linux 8MB default, on platforms that support it. #if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) #include static const size_t TH_STACK_SIZE = 8 * 1024 * 1024; template > void* start_routine(void* ptr) { P* p = reinterpret_cast(ptr); (p->first->*(p->second))(); // Call member function pointer delete p; return NULL; } class NativeThread { pthread_t thread; public: template> explicit NativeThread(void(T::*fun)(), T* obj) { pthread_attr_t attr_storage, *attr = &attr_storage; pthread_attr_init(attr); pthread_attr_setstacksize(attr, TH_STACK_SIZE); pthread_create(&thread, attr, start_routine, new P(obj, fun)); } void join() { pthread_join(thread, NULL); } }; #else // Default case: use STL classes typedef std::thread NativeThread; #endif #endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED stockfish-11.orig/src/timeman.cpp0000644000175000017500000001227713610452365015316 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "search.h" #include "timeman.h" #include "uci.h" TimeManagement Time; // Our global time management object namespace { enum TimeType { OptimumTime, MaxTime }; constexpr int MoveHorizon = 50; // Plan time management at most this many moves ahead constexpr double MaxRatio = 7.3; // When in trouble, we can step over reserved time with this ratio constexpr double StealRatio = 0.34; // However we must not steal time from remaining moves over this ratio // move_importance() is a skew-logistic function based on naive statistical // analysis of "how many games are still undecided after n half-moves". Game // is considered "undecided" as long as neither side has >275cp advantage. // Data was extracted from the CCRL game database with some simple filtering criteria. double move_importance(int ply) { constexpr double XScale = 6.85; constexpr double XShift = 64.5; constexpr double Skew = 0.171; return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero } template TimePoint remaining(TimePoint myTime, int movesToGo, int ply, TimePoint slowMover) { constexpr double TMaxRatio = (T == OptimumTime ? 1.0 : MaxRatio); constexpr double TStealRatio = (T == OptimumTime ? 0.0 : StealRatio); double moveImportance = (move_importance(ply) * slowMover) / 100.0; double otherMovesImportance = 0.0; for (int i = 1; i < movesToGo; ++i) otherMovesImportance += move_importance(ply + 2 * i); double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance); double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance); return TimePoint(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast } } // namespace /// init() is called at the beginning of the search and calculates the allowed /// thinking time out of the time control and current game ply. We support four /// different kinds of time controls, passed in 'limits': /// /// inc == 0 && movestogo == 0 means: x basetime [sudden death!] /// inc == 0 && movestogo != 0 means: x moves in y minutes /// inc > 0 && movestogo == 0 means: x basetime + z increment /// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { TimePoint minThinkingTime = Options["Minimum Thinking Time"]; TimePoint moveOverhead = Options["Move Overhead"]; TimePoint slowMover = Options["Slow Mover"]; TimePoint npmsec = Options["nodestime"]; TimePoint hypMyTime; // If we have to play in 'nodes as time' mode, then convert from time // to nodes, and use resulting values in time management formulas. // WARNING: to avoid time losses, the given npmsec (nodes per millisecond) // must be much lower than the real engine speed. if (npmsec) { if (!availableNodes) // Only once at game start availableNodes = npmsec * limits.time[us]; // Time is in msec // Convert from milliseconds to nodes limits.time[us] = TimePoint(availableNodes); limits.inc[us] *= npmsec; limits.npmsec = npmsec; } startTime = limits.startTime; optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime); const int maxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon; // We calculate optimum time usage for different hypothetical "moves to go" values // and choose the minimum of calculated search time values. Usually the greatest // hypMTG gives the minimum values. for (int hypMTG = 1; hypMTG <= maxMTG; ++hypMTG) { // Calculate thinking time for hypothetical "moves to go"-value hypMyTime = limits.time[us] + limits.inc[us] * (hypMTG - 1) - moveOverhead * (2 + std::min(hypMTG, 40)); hypMyTime = std::max(hypMyTime, TimePoint(0)); TimePoint t1 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); TimePoint t2 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); optimumTime = std::min(t1, optimumTime); maximumTime = std::min(t2, maximumTime); } if (Options["Ponder"]) optimumTime += optimumTime / 4; } stockfish-11.orig/src/main.cpp0000644000175000017500000000267513610452365014611 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "bitboard.h" #include "position.h" #include "search.h" #include "thread.h" #include "tt.h" #include "uci.h" #include "endgame.h" #include "syzygy/tbprobe.h" namespace PSQT { void init(); } int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; UCI::init(Options); PSQT::init(); Bitboards::init(); Position::init(); Bitbases::init(); Endgames::init(); Threads.set(Options["Threads"]); Search::clear(); // After threads are up UCI::loop(argc, argv); Threads.set(0); return 0; } stockfish-11.orig/src/search.h0000644000175000017500000000625313610452365014573 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED #include #include "misc.h" #include "movepick.h" #include "types.h" class Position; namespace Search { /// Threshold used for countermoves based pruning constexpr int CounterMovePruneThreshold = 0; /// Stack struct keeps track of the information we need to remember from nodes /// shallower and deeper in the tree during the search. Each search thread has /// its own array of Stack objects, indexed by the current ply. struct Stack { Move* pv; PieceToHistory* continuationHistory; int ply; Move currentMove; Move excludedMove; Move killers[2]; Value staticEval; int statScore; int moveCount; }; /// RootMove struct is used for moves at the root of the tree. For each root move /// we store a score and a PV (really a refutation in the case of moves which /// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. struct RootMove { explicit RootMove(Move m) : pv(1, m) {} bool extract_ponder_from_tt(Position& pos); bool operator==(const Move& m) const { return pv[0] == m; } bool operator<(const RootMove& m) const { // Sort in descending order return m.score != score ? m.score < score : m.previousScore < previousScore; } Value score = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE; int selDepth = 0; int tbRank = 0; int bestMoveCount = 0; Value tbScore; std::vector pv; }; typedef std::vector RootMoves; /// LimitsType struct stores information sent by GUI about available time to /// search the current move, maximum depth/time, or if we are in analysis mode. struct LimitsType { LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); movestogo = depth = mate = perft = infinite = 0; nodes = 0; } bool use_time_management() const { return !(mate | movetime | depth | nodes | perft | infinite); } std::vector searchmoves; TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; int movestogo, depth, mate, perft, infinite; int64_t nodes; }; extern LimitsType Limits; void init(); void clear(); } // namespace Search #endif // #ifndef SEARCH_H_INCLUDED stockfish-11.orig/src/position.h0000644000175000017500000003236513610452365015175 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef POSITION_H_INCLUDED #define POSITION_H_INCLUDED #include #include #include // For std::unique_ptr #include #include "bitboard.h" #include "types.h" /// StateInfo struct stores information needed to restore a Position object to /// its previous state when we retract a move. Whenever a move is made on the /// board (by calling Position::do_move), a StateInfo object must be passed. struct StateInfo { // Copied when making a move Key pawnKey; Key materialKey; Value nonPawnMaterial[COLOR_NB]; int castlingRights; int rule50; int pliesFromNull; Square epSquare; // Not copied when making a move (will be recomputed anyhow) Key key; Bitboard checkersBB; Piece capturedPiece; StateInfo* previous; Bitboard blockersForKing[COLOR_NB]; Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; 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 /// elements are not invalidated upon list resizing. typedef std::unique_ptr> StateListPtr; /// Position class stores information regarding the board representation as /// pieces, side to move, hash keys, castling info, etc. Important methods are /// do_move() and undo_move(), used by the search to update node info when /// traversing the search tree. class Thread; class Position { public: static void init(); Position() = default; Position(const Position&) = delete; Position& operator=(const Position&) = delete; // FEN string input/output Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); Position& set(const std::string& code, Color c, StateInfo* si); const std::string fen() const; // Position representation Bitboard pieces() const; Bitboard pieces(PieceType pt) const; Bitboard pieces(PieceType pt1, PieceType pt2) const; Bitboard pieces(Color c) const; Bitboard pieces(Color c, PieceType pt) const; Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const; Piece piece_on(Square s) const; Square ep_square() const; bool empty(Square s) const; template int count(Color c) const; template int count() const; template const Square* squares(Color c) const; template Square square(Color c) const; bool is_on_semiopen_file(Color c, Square s) const; // Castling int castling_rights(Color c) const; bool can_castle(CastlingRights cr) const; bool castling_impeded(CastlingRights cr) const; Square castling_rook_square(CastlingRights cr) const; // Checking Bitboard checkers() const; Bitboard blockers_for_king(Color c) const; Bitboard check_squares(PieceType pt) const; bool is_discovery_check_on_king(Color c, Move m) const; // Attacks to/from a given square Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s, Bitboard occupied) const; Bitboard attacks_from(PieceType pt, Square s) const; template Bitboard attacks_from(Square s) const; template Bitboard attacks_from(Square s, Color c) const; Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; // Properties of moves bool legal(Move m) const; bool pseudo_legal(const Move m) const; bool capture(Move m) const; bool capture_or_promotion(Move m) const; bool gives_check(Move m) const; bool advanced_pawn_push(Move m) const; Piece moved_piece(Move m) const; Piece captured_piece() const; // Piece specific bool pawn_passed(Color c, Square s) const; bool opposite_bishops() const; int pawns_on_same_color_squares(Color c, Square s) const; // Doing and undoing moves void do_move(Move m, StateInfo& newSt); void do_move(Move m, StateInfo& newSt, bool givesCheck); void undo_move(Move m); void do_null_move(StateInfo& newSt); void undo_null_move(); // Static Exchange Evaluation bool see_ge(Move m, Value threshold = VALUE_ZERO) const; // Accessing hash keys Key key() const; Key key_after(Move m) const; Key material_key() const; Key pawn_key() const; // Other properties of the position Color side_to_move() const; int game_ply() const; bool is_chess960() const; Thread* this_thread() const; bool is_draw(int ply) const; bool has_game_cycle(int ply) const; bool has_repeated() const; int rule50_count() const; Score psq_score() const; Value non_pawn_material(Color c) const; Value non_pawn_material() const; // Position consistency check, for debugging bool pos_is_ok() const; void flip(); private: // Initialization helpers (used while setting up a position) void set_castling_right(Color c, Square rfrom); void set_state(StateInfo* si) const; void set_check_info(StateInfo* si) const; // Other helpers void put_piece(Piece pc, Square s); void remove_piece(Piece pc, Square s); void move_piece(Piece pc, Square from, Square to); template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); // Data members Piece board[SQUARE_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; Bitboard byColorBB[COLOR_NB]; int pieceCount[PIECE_NB]; Square pieceList[PIECE_NB][16]; int index[SQUARE_NB]; int castlingRightsMask[SQUARE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB]; int gamePly; Color sideToMove; Score psq; Thread* thisThread; StateInfo* st; bool chess960; }; namespace PSQT { extern Score psq[PIECE_NB][SQUARE_NB]; } extern std::ostream& operator<<(std::ostream& os, const Position& pos); inline Color Position::side_to_move() const { return sideToMove; } inline bool Position::empty(Square s) const { return board[s] == NO_PIECE; } inline Piece Position::piece_on(Square s) const { return board[s]; } inline Piece Position::moved_piece(Move m) const { return board[from_sq(m)]; } inline Bitboard Position::pieces() const { return byTypeBB[ALL_PIECES]; } inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; } inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { return byTypeBB[pt1] | byTypeBB[pt2]; } inline Bitboard Position::pieces(Color c) const { return byColorBB[c]; } inline Bitboard Position::pieces(Color c, PieceType pt) const { return byColorBB[c] & byTypeBB[pt]; } inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]); } template inline int Position::count(Color c) const { return pieceCount[make_piece(c, Pt)]; } template inline int Position::count() const { return pieceCount[make_piece(WHITE, Pt)] + pieceCount[make_piece(BLACK, Pt)]; } template inline const Square* Position::squares(Color c) const { return pieceList[make_piece(c, Pt)]; } template inline Square Position::square(Color c) const { assert(pieceCount[make_piece(c, Pt)] == 1); return pieceList[make_piece(c, Pt)][0]; } inline Square Position::ep_square() const { return st->epSquare; } inline bool Position::is_on_semiopen_file(Color c, Square s) const { return !(pieces(c, PAWN) & file_bb(s)); } inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; } inline int Position::castling_rights(Color c) const { return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING); } inline bool Position::castling_impeded(CastlingRights cr) const { assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); return byTypeBB[ALL_PIECES] & castlingPath[cr]; } inline Square Position::castling_rook_square(CastlingRights cr) const { assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); return castlingRookSquare[cr]; } template inline Bitboard Position::attacks_from(Square s) const { static_assert(Pt != PAWN, "Pawn attacks need color"); return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, byTypeBB[ALL_PIECES]) : Pt == QUEEN ? attacks_from(s) | attacks_from(s) : PseudoAttacks[Pt][s]; } template<> inline Bitboard Position::attacks_from(Square s, Color c) const { return PawnAttacks[c][s]; } inline Bitboard Position::attacks_from(PieceType pt, Square s) const { return attacks_bb(pt, s, byTypeBB[ALL_PIECES]); } inline Bitboard Position::attackers_to(Square s) const { return attackers_to(s, byTypeBB[ALL_PIECES]); } inline Bitboard Position::checkers() const { return st->checkersBB; } inline Bitboard Position::blockers_for_king(Color c) const { return st->blockersForKing[c]; } inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; } inline bool Position::is_discovery_check_on_king(Color c, Move m) const { return st->blockersForKing[c] & from_sq(m); } inline bool Position::pawn_passed(Color c, Square s) const { return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); } inline bool Position::advanced_pawn_push(Move m) const { return type_of(moved_piece(m)) == PAWN && relative_rank(sideToMove, to_sq(m)) > RANK_5; } inline int Position::pawns_on_same_color_squares(Color c, Square s) const { return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares)); } inline Key Position::key() const { return st->key; } inline Key Position::pawn_key() const { return st->pawnKey; } inline Key Position::material_key() const { return st->materialKey; } inline Score Position::psq_score() const { return psq; } inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; } inline Value Position::non_pawn_material() const { return st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK]; } inline int Position::game_ply() const { return gamePly; } inline int Position::rule50_count() const { return st->rule50; } inline bool Position::opposite_bishops() const { return pieceCount[W_BISHOP] == 1 && pieceCount[B_BISHOP] == 1 && opposite_colors(square(WHITE), square(BLACK)); } inline bool Position::is_chess960() const { return chess960; } inline bool Position::capture_or_promotion(Move m) const { assert(is_ok(m)); return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m)); } inline bool Position::capture(Move m) const { assert(is_ok(m)); // Castling is encoded as "king captures rook" return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT; } inline Piece Position::captured_piece() const { return st->capturedPiece; } inline Thread* Position::this_thread() const { return thisThread; } inline void Position::put_piece(Piece pc, Square s) { board[s] = pc; byTypeBB[ALL_PIECES] |= s; byTypeBB[type_of(pc)] |= s; byColorBB[color_of(pc)] |= s; index[s] = pieceCount[pc]++; pieceList[pc][index[s]] = s; pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; psq += PSQT::psq[pc][s]; } inline void Position::remove_piece(Piece pc, Square s) { // WARNING: This is not a reversible operation. If we remove a piece in // do_move() and then replace it in undo_move() we will put it at the end of // the list and not in its original place, it means index[] and pieceList[] // are not invariant to a do_move() + undo_move() sequence. byTypeBB[ALL_PIECES] ^= s; byTypeBB[type_of(pc)] ^= s; byColorBB[color_of(pc)] ^= s; /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ Square lastSquare = pieceList[pc][--pieceCount[pc]]; index[lastSquare] = index[s]; pieceList[pc][index[lastSquare]] = lastSquare; pieceList[pc][pieceCount[pc]] = SQ_NONE; pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; psq -= PSQT::psq[pc][s]; } inline void Position::move_piece(Piece pc, Square from, Square to) { // index[from] is not updated and becomes stale. This works as long as index[] // is accessed just by known occupied squares. Bitboard fromTo = from | to; byTypeBB[ALL_PIECES] ^= fromTo; byTypeBB[type_of(pc)] ^= fromTo; byColorBB[color_of(pc)] ^= fromTo; board[from] = NO_PIECE; board[to] = pc; index[to] = index[from]; pieceList[pc][index[to]] = to; psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; } inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); } #endif // #ifndef POSITION_H_INCLUDED stockfish-11.orig/src/search.cpp0000644000175000017500000020547413610452365015134 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include // For std::memset #include #include #include "evaluate.h" #include "misc.h" #include "movegen.h" #include "movepick.h" #include "position.h" #include "search.h" #include "thread.h" #include "timeman.h" #include "tt.h" #include "uci.h" #include "syzygy/tbprobe.h" namespace Search { LimitsType Limits; } namespace Tablebases { int Cardinality; bool RootInTB; bool UseRule50; Depth ProbeDepth; } namespace TB = Tablebases; using std::string; using Eval::evaluate; using namespace Search; namespace { // Different node types, used as a template parameter enum NodeType { NonPV, PV }; constexpr uint64_t ttHitAverageWindow = 4096; constexpr uint64_t ttHitAverageResolution = 1024; // Razor and futility margins constexpr int RazorMargin = 531; Value futility_margin(Depth d, bool improving) { return Value(217 * (d - improving)); } // Reductions lookup table, initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; return (r + 511) / 1024 + (!i && r > 1007); } constexpr int futility_move_count(bool improving, Depth depth) { return (5 + depth * depth) * (1 + improving) / 2 - 1; } // History and stats update bonus, based on depth int stat_bonus(Depth d) { return d > 15 ? -8 : 19 * d * d + 155 * d - 132; } // Add a small random component to draw evaluations to avoid 3fold-blindness Value value_draw(Thread* thisThread) { return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); } // Skill structure is used to implement strength limit struct Skill { explicit Skill(int l) : level(l) {} bool enabled() const { return level < 20; } bool time_to_pick(Depth depth) const { return depth == 1 + level; } Move pick_best(size_t multiPV); int level; Move best = MOVE_NONE; }; // Breadcrumbs are used to mark nodes as being searched by a given thread struct Breadcrumb { std::atomic thread; std::atomic key; }; std::array breadcrumbs; // ThreadHolding structure keeps track of which thread left breadcrumbs at the given // node for potential reductions. A free node will be marked upon entering the moves // loop by the constructor, and unmarked upon leaving that loop by the destructor. struct ThreadHolding { explicit ThreadHolding(Thread* thisThread, Key posKey, int ply) { location = ply < 8 ? &breadcrumbs[posKey & (breadcrumbs.size() - 1)] : nullptr; otherThread = false; owning = false; if (location) { // See if another already marked this location, if not, mark it ourselves Thread* tmp = (*location).thread.load(std::memory_order_relaxed); if (tmp == nullptr) { (*location).thread.store(thisThread, std::memory_order_relaxed); (*location).key.store(posKey, std::memory_order_relaxed); owning = true; } else if ( tmp != thisThread && (*location).key.load(std::memory_order_relaxed) == posKey) otherThread = true; } } ~ThreadHolding() { if (owning) // Free the marked location (*location).thread.store(nullptr, std::memory_order_relaxed); } bool marked() { return otherThread; } private: Breadcrumb* location; bool otherThread, owning; }; template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth); // perft() is our utility to verify move generation. All the leaf nodes up // to the given depth are generated and counted, and the sum is returned. template uint64_t perft(Position& pos, Depth depth) { StateInfo st; uint64_t cnt, nodes = 0; const bool leaf = (depth == 2); for (const auto& m : MoveList(pos)) { if (Root && depth <= 1) cnt = 1, nodes++; else { pos.do_move(m, st); cnt = leaf ? MoveList(pos).size() : perft(pos, depth - 1); nodes += cnt; pos.undo_move(m); } if (Root) sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl; } return nodes; } } // namespace /// Search::init() is called at startup to initialize various lookup tables void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) Reductions[i] = int((24.8 + std::log(Threads.size()) / 2) * std::log(i)); } /// Search::clear() resets search state to its initial value void Search::clear() { Threads.main()->wait_for_search_finished(); Time.availableNodes = 0; TT.clear(); Threads.clear(); Tablebases::init(Options["SyzygyPath"]); // Free mapped files } /// MainThread::search() is started when the program receives the UCI 'go' /// command. It searches from the root position and outputs the "bestmove". void MainThread::search() { if (Limits.perft) { nodes = perft(rootPos, Limits.perft); sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl; return; } Color us = rootPos.side_to_move(); Time.init(Limits, us, rootPos.game_ply()); TT.new_search(); if (rootMoves.empty()) { rootMoves.emplace_back(MOVE_NONE); sync_cout << "info depth 0 score " << UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) << sync_endl; } else { for (Thread* th : Threads) { th->bestMoveChanges = 0; if (th != this) th->start_searching(); } Thread::search(); // Let's start searching! } // When we reach the maximum depth, we can arrive here without a raise of // Threads.stop. However, if we are pondering or in an infinite search, // the UCI protocol states that we shouldn't print the best move before the // GUI sends a "stop" or "ponderhit" command. We therefore simply wait here // until the GUI sends one of those commands. while (!Threads.stop && (ponder || Limits.infinite)) {} // Busy wait for a stop or a ponder reset // Stop the threads if not already stopped (also raise the stop if // "ponderhit" just reset Threads.ponder). Threads.stop = true; // Wait until all threads have finished for (Thread* th : Threads) if (th != this) th->wait_for_search_finished(); // When playing in 'nodes as time' mode, subtract the searched nodes from // the available ones before exiting. if (Limits.npmsec) Time.availableNodes += Limits.inc[us] - Threads.nodes_searched(); Thread* bestThread = this; // Check if there are threads with a better score than main thread if ( Options["MultiPV"] == 1 && !Limits.depth && !(Skill(Options["Skill Level"]).enabled() || Options["UCI_LimitStrength"]) && rootMoves[0].pv[0] != MOVE_NONE) { std::map votes; Value minScore = this->rootMoves[0].score; // Find out 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 (bestThread->rootMoves[0].score >= VALUE_MATE_IN_MAX_PLY) { // Make sure we pick the shortest mate if (th->rootMoves[0].score > bestThread->rootMoves[0].score) bestThread = th; } else if ( th->rootMoves[0].score >= VALUE_MATE_IN_MAX_PLY || votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]) bestThread = th; } } previousScore = bestThread->rootMoves[0].score; // Send again PV info if we have a new best thread if (bestThread != this) sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE) << sync_endl; sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos)) std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); std::cout << sync_endl; } /// Thread::search() is the main iterative deepening loop. It calls search() /// repeatedly with increasing depth until the allocated thinking time has been /// consumed, the user stops the search, or the maximum search depth is reached. void Thread::search() { // To allow access to (ss-7) up to (ss+2), the stack must be oversized. // The former is needed to allow update_continuation_histories(ss-1, ...), // which accesses its argument at ss-6, also near the root. // The latter is needed for statScores and killer initialization. Stack stack[MAX_PLY+10], *ss = stack+7; Move pv[MAX_PLY+1]; Value bestValue, alpha, beta, delta; Move lastBestMove = MOVE_NONE; Depth lastBestMoveDepth = 0; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1, totBestMoveChanges = 0; Color us = rootPos.side_to_move(); int iterIdx = 0; std::memset(ss-7, 0, 10 * sizeof(Stack)); for (int i = 7; i > 0; i--) (ss-i)->continuationHistory = &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel ss->pv = pv; bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; if (mainThread) { if (mainThread->previousScore == VALUE_INFINITE) for (int i=0; i<4; ++i) mainThread->iterValue[i] = VALUE_ZERO; else for (int i=0; i<4; ++i) mainThread->iterValue[i] = mainThread->previousScore; } size_t multiPV = Options["MultiPV"]; // Pick integer skill levels, but non-deterministically round up or down // such that the average integer skill corresponds to the input floating point one. // UCI_Elo is converted to a suitable fractional skill level, using anchoring // to CCRL Elo (goldfish 1.13 = 2000) and a fit through Ordo derived Elo // for match (TC 60+0.6) results spanning a wide range of k values. PRNG rng(now()); double floatLevel = Options["UCI_LimitStrength"] ? clamp(std::pow((Options["UCI_Elo"] - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0) : double(Options["Skill Level"]); int intLevel = int(floatLevel) + ((floatLevel - int(floatLevel)) * 1024 > rng.rand() % 1024 ? 1 : 0); Skill skill(intLevel); // When playing with strength handicap enable MultiPV search that we will // use behind the scenes to retrieve a set of possible moves. if (skill.enabled()) multiPV = std::max(multiPV, (size_t)4); multiPV = std::min(multiPV, rootMoves.size()); ttHitAverage = ttHitAverageWindow * ttHitAverageResolution / 2; int ct = int(Options["Contempt"]) * PawnValueEg / 100; // From centipawns // In analysis mode, adjust contempt in accordance with user preference if (Limits.infinite || Options["UCI_AnalyseMode"]) ct = Options["Analysis Contempt"] == "Off" ? 0 : Options["Analysis Contempt"] == "Both" ? ct : Options["Analysis Contempt"] == "White" && us == BLACK ? -ct : Options["Analysis Contempt"] == "Black" && us == WHITE ? -ct : ct; // Evaluation score is from the white point of view contempt = (us == WHITE ? make_score(ct, ct / 2) : -make_score(ct, ct / 2)); int searchAgainCounter = 0; // Iterative deepening loop until requested to stop or the target depth is reached while ( ++rootDepth < MAX_PLY && !Threads.stop && !(Limits.depth && mainThread && rootDepth > Limits.depth)) { // Age out PV variability metric if (mainThread) totBestMoveChanges /= 2; // Save the last iteration's scores before first PV line is searched and // all the move scores except the (new) PV are set to -VALUE_INFINITE. for (RootMove& rm : rootMoves) rm.previousScore = rm.score; size_t pvFirst = 0; pvLast = 0; if (!Threads.increaseDepth) searchAgainCounter++; // MultiPV loop. We perform a full root search for each PV line for (pvIdx = 0; pvIdx < multiPV && !Threads.stop; ++pvIdx) { if (pvIdx == pvLast) { pvFirst = pvLast; for (pvLast++; pvLast < rootMoves.size(); pvLast++) if (rootMoves[pvLast].tbRank != rootMoves[pvFirst].tbRank) break; } // Reset UCI info selDepth for each depth and each PV line selDepth = 0; // Reset aspiration window starting size if (rootDepth >= 4) { Value previousScore = rootMoves[pvIdx].previousScore; delta = Value(21 + abs(previousScore) / 256); alpha = std::max(previousScore - delta,-VALUE_INFINITE); beta = std::min(previousScore + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) int dct = ct + (102 - ct / 2) * previousScore / (abs(previousScore) + 157); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); } // Start with a small aspiration window and, in the case of a fail // high/low, re-search with a bigger window until we don't fail // high/low anymore. int failedHighCnt = 0; while (true) { Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); bestValue = ::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the // first and eventually the new best one are set to -VALUE_INFINITE // and we want to keep the same order for all the moves except the // new PV that goes to the front. Note that in case of MultiPV // search the already searched PV lines are preserved. std::stable_sort(rootMoves.begin() + pvIdx, rootMoves.begin() + pvLast); // If search has been stopped, we break immediately. Sorting is // safe because RootMoves is still valid, although it refers to // the previous iteration. if (Threads.stop) break; // When failing high/low give some update (without cluttering // the UI) before a re-search. if ( mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) && Time.elapsed() > 3000) sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl; // In case of failing low/high increase aspiration window and // re-search, otherwise exit the loop. if (bestValue <= alpha) { beta = (alpha + beta) / 2; alpha = std::max(bestValue - delta, -VALUE_INFINITE); failedHighCnt = 0; if (mainThread) mainThread->stopOnPonderhit = false; } else if (bestValue >= beta) { beta = std::min(bestValue + delta, VALUE_INFINITE); ++failedHighCnt; } else { ++rootMoves[pvIdx].bestMoveCount; break; } delta += delta / 4 + 5; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } // Sort the PV lines searched so far and update the GUI std::stable_sort(rootMoves.begin() + pvFirst, rootMoves.begin() + pvIdx + 1); if ( mainThread && (Threads.stop || pvIdx + 1 == multiPV || Time.elapsed() > 3000)) sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl; } if (!Threads.stop) completedDepth = rootDepth; if (rootMoves[0].pv[0] != lastBestMove) { lastBestMove = rootMoves[0].pv[0]; lastBestMoveDepth = rootDepth; } // Have we found a "mate in x"? if ( Limits.mate && bestValue >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - bestValue <= 2 * Limits.mate) Threads.stop = true; if (!mainThread) continue; // If skill level is enabled and time is up, pick a sub-optimal best move if (skill.enabled() && skill.time_to_pick(rootDepth)) skill.pick_best(multiPV); // Do we have time for the next iteration? Can we stop searching now? if ( Limits.use_time_management() && !Threads.stop && !mainThread->stopOnPonderhit) { double fallingEval = (332 + 6 * (mainThread->previousScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; fallingEval = 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); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) { totBestMoveChanges += th->bestMoveChanges; th->bestMoveChanges = 0; } double bestMoveInstability = 1 + totBestMoveChanges / Threads.size(); // Stop the search if we have only one legal move, or if available time elapsed if ( rootMoves.size() == 1 || Time.elapsed() > Time.optimum() * fallingEval * reduction * bestMoveInstability) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". if (mainThread->ponder) mainThread->stopOnPonderhit = true; else Threads.stop = true; } else if ( Threads.increaseDepth && !mainThread->ponder && Time.elapsed() > Time.optimum() * fallingEval * reduction * bestMoveInstability * 0.6) Threads.increaseDepth = false; else Threads.increaseDepth = true; } mainThread->iterValue[iterIdx] = bestValue; iterIdx = (iterIdx + 1) & 3; } if (!mainThread) return; mainThread->previousTimeReduction = timeReduction; // If skill level is enabled, swap best PV line with the sub-optimal one if (skill.enabled()) std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(), skill.best ? skill.best : skill.pick_best(multiPV))); } namespace { // search<>() is the main search function for both PV and non-PV nodes template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { constexpr bool PvNode = NT == PV; const bool rootNode = PvNode && ss->ply == 0; // Check if we have an upcoming move which draws by repetition, or // if the opponent had an alternative move earlier to this position. if ( pos.rule50_count() >= 3 && alpha < VALUE_DRAW && !rootNode && pos.has_game_cycle(ss->ply)) { alpha = value_draw(pos.this_thread()); if (alpha >= beta) return alpha; } // Dive into quiescence search when the depth reaches zero if (depth <= 0) return qsearch(pos, ss, alpha, beta); assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); assert(0 < depth && depth < MAX_PLY); assert(!(PvNode && cutNode)); Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64]; StateInfo st; TTEntry* tte; Key posKey; Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; bool ttHit, ttPv, inCheck, givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularLMR; Piece movedPiece; int moveCount, captureCount, quietCount; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); moveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; // Check for the available remaining time if (thisThread == Threads.main()) static_cast(thisThread)->check_time(); // Used to send selDepth info to GUI (selDepth counts from 1, ply from 0) if (PvNode && thisThread->selDepth < ss->ply + 1) thisThread->selDepth = ss->ply + 1; if (!rootNode) { // Step 2. Check for aborted search and immediate draw if ( Threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !inCheck) ? evaluate(pos) : 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 // a shorter mate was found upward in the tree then there is no need to search // because we will never beat the current alpha. Same logic but with reversed // signs applies also in the opposite condition of being mated instead of giving // mate. In this case return a fail-high score. alpha = std::max(mated_in(ss->ply), alpha); beta = std::min(mate_in(ss->ply+1), beta); if (alpha >= beta) return alpha; } assert(0 <= ss->ply && ss->ply < MAX_PLY); (ss+1)->ply = ss->ply + 1; (ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; Square prevSq = to_sq((ss-1)->currentMove); // Initialize statScore to zero for the grandchildren of the current position. // So statScore is shared between all grandchildren and only the first grandchild // starts with statScore = 0. Later grandchildren start with the last calculated // statScore of the previous grandchild. This influences the reduction rules in // LMR which are based on the statScore of parent position. if (rootNode) (ss+4)->statScore = 0; else (ss+2)->statScore = 0; // Step 4. Transposition table lookup. We don't want the score of a partial // 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 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] : ttHit ? tte->move() : MOVE_NONE; ttPv = PvNode || (ttHit && tte->is_pv()); // thisThread->ttHitAverage can be used to approximate the running average of ttHit thisThread->ttHitAverage = (ttHitAverageWindow - 1) * thisThread->ttHitAverage / ttHitAverageWindow + ttHitAverageResolution * ttHit; // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ttHit && tte->depth() >= depth && ttValue != VALUE_NONE // Possible in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) { // If ttMove is quiet, update move sorting heuristics on TT hit if (ttMove) { if (ttValue >= beta) { if (!pos.capture_or_promotion(ttMove)) update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !priorCapture) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); } // Penalty for a quiet ttMove that fails low else if (!pos.capture_or_promotion(ttMove)) { int penalty = -stat_bonus(depth); thisThread->mainHistory[us][from_to(ttMove)] << penalty; update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty); } } if (pos.rule50_count() < 90) return ttValue; } // Step 5. Tablebases probe if (!rootNode && TB::Cardinality) { int piecesCount = pos.count(); if ( piecesCount <= TB::Cardinality && (piecesCount < TB::Cardinality || depth >= TB::ProbeDepth) && pos.rule50_count() == 0 && !pos.can_castle(ANY_CASTLING)) { TB::ProbeState err; TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err); // Force check of time on the next occasion if (thisThread == Threads.main()) static_cast(thisThread)->callsCnt = 0; if (err != TB::ProbeState::FAIL) { thisThread->tbHits.fetch_add(1, std::memory_order_relaxed); int drawScore = TB::UseRule50 ? 1 : 0; value = wdl < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply + 1 : wdl > drawScore ? VALUE_MATE - MAX_PLY - ss->ply - 1 : VALUE_DRAW + 2 * wdl * drawScore; Bound b = wdl < -drawScore ? BOUND_UPPER : wdl > drawScore ? BOUND_LOWER : BOUND_EXACT; if ( b == BOUND_EXACT || (b == BOUND_LOWER ? value >= beta : value <= alpha)) { tte->save(posKey, value_to_tt(value, ss->ply), ttPv, b, std::min(MAX_PLY - 1, depth + 6), MOVE_NONE, VALUE_NONE); return value; } if (PvNode) { if (b == BOUND_LOWER) bestValue = value, alpha = std::max(alpha, bestValue); else maxValue = value; } } } } // Step 6. Static evaluation of the position if (inCheck) { ss->staticEval = eval = VALUE_NONE; improving = false; goto moves_loop; // Skip early pruning when in check } else if (ttHit) { // Never assume anything about values stored in TT ss->staticEval = eval = tte->eval(); if (eval == VALUE_NONE) ss->staticEval = eval = evaluate(pos); if (eval == VALUE_DRAW) eval = value_draw(thisThread); // Can ttValue be used as a better position evaluation? if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) eval = ttValue; } else { if ((ss-1)->currentMove != MOVE_NULL) { int bonus = -(ss-1)->statScore / 512; ss->staticEval = eval = evaluate(pos) + bonus; } else ss->staticEval = eval = -(ss-1)->staticEval + 2 * Eval::Tempo; tte->save(posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } // Step 7. Razoring (~1 Elo) if ( !rootNode // The required rootNode PV handling is not available in qsearch && depth < 2 && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); improving = (ss-2)->staticEval == VALUE_NONE ? (ss->staticEval >= (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE) : ss->staticEval >= (ss-2)->staticEval; // Step 8. Futility pruning: child node (~50 Elo) if ( !PvNode && depth < 6 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL && (ss-1)->statScore < 23397 && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 32 * depth + 292 - improving * 30 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) { 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); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; pos.do_null_move(st); Value nullValue = -search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode); pos.undo_null_move(); if (nullValue >= beta) { // Do not return unproven mate scores if (nullValue >= VALUE_MATE_IN_MAX_PLY) nullValue = beta; if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 13)) return nullValue; assert(!thisThread->nmpMinPly); // Recursive verification is not allowed // Do verification search at high depths, with null move pruning disabled // for us, until ply exceeds nmpMinPly. thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / 4; thisThread->nmpColor = us; Value v = search(pos, ss, beta-1, beta, depth-R, false); thisThread->nmpMinPly = 0; if (v >= beta) return nullValue; } } // Step 10. ProbCut (~10 Elo) // 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 && abs(beta) < VALUE_MATE_IN_MAX_PLY) { Value raisedBeta = std::min(beta + 189 - 45 * improving, VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory); int probCutCount = 0; while ( (move = mp.next_move()) != MOVE_NONE && probCutCount < 2 + 2 * cutNode) if (move != excludedMove && pos.legal(move)) { assert(pos.capture_or_promotion(move)); assert(depth >= 5); captureOrPromotion = true; probCutCount++; ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[inCheck] [captureOrPromotion] [pos.moved_piece(move)] [to_sq(move)]; pos.do_move(move, st); // Perform a preliminary qsearch to verify that the move holds value = -qsearch(pos, ss+1, -raisedBeta, -raisedBeta+1); // If the qsearch held, perform the regular search if (value >= raisedBeta) value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode); pos.undo_move(move); if (value >= raisedBeta) return value; } } // Step 11. Internal iterative deepening (~1 Elo) if (depth >= 7 && !ttMove) { search(pos, ss, alpha, beta, depth - 7, cutNode); tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; } moves_loop: // When in check, search starts from here const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr , (ss-4)->continuationHistory, nullptr , (ss-6)->continuationHistory }; Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, countermove, ss->killers); value = bestValue; singularLMR = moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { assert(is_ok(move)); if (move == excludedMove) continue; // At root obey the "searchmoves" option and skip moves not listed in Root // Move List. As a consequence any illegal move is also skipped. In MultiPV // mode we also skip PV moves which have been already searched and those // of lower "TB rank" if we are in a TB root position. if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->pvIdx, thisThread->rootMoves.begin() + thisThread->pvLast, move)) continue; ss->moveCount = ++moveCount; if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) sync_cout << "info depth " << depth << " currmove " << UCI::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl; if (PvNode) (ss+1)->pv = nullptr; extension = 0; captureOrPromotion = pos.capture_or_promotion(move); movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); // Calculate new depth for this move newDepth = depth - 1; // Step 13. Pruning at shallow depth (~200 Elo) if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_MATED_IN_MAX_PLY) { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold moveCountPruning = moveCount >= futility_move_count(improving, depth); if ( !captureOrPromotion && !givesCheck) { // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); // Countermoves based pruning (~20 Elo) if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) continue; // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 6 && !inCheck && ss->staticEval + 235 + 172 * lmrDepth <= alpha && thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] < 25000) continue; // Prune moves with negative SEE (~20 Elo) if (!pos.see_ge(move, Value(-(32 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } else if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) continue; } // Step 14. Extensions (~75 Elo) // Singular extension search (~70 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), // then that move is singular and should be extended. To verify this we do // a reduced search on all the other moves but the ttMove and if the // result is lower than ttValue minus a margin then we will extend the ttMove. if ( depth >= 6 && move == ttMove && !rootNode && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ && abs(ttValue) < VALUE_KNOWN_WIN && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3 && pos.legal(move)) { Value singularBeta = ttValue - 2 * depth; Depth halfDepth = depth / 2; ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, halfDepth, cutNode); ss->excludedMove = MOVE_NONE; if (value < singularBeta) { extension = 1; singularLMR = true; } // Multi-cut pruning // Our ttMove is assumed to fail high, and now we failed high also on a reduced // search without the ttMove. So we assume this expected Cut-node is not singular, // that multiple moves fail high, and we can prune the whole subtree by returning // a soft bound. else if (singularBeta >= beta) return singularBeta; } // Check extension (~2 Elo) else if ( givesCheck && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; // Passed pawn extension else if ( move == ss->killers[0] && pos.advanced_pawn_push(move) && pos.pawn_passed(us, to_sq(move))) extension = 1; // Last captures extension else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg && pos.non_pawn_material() <= 2 * RookValueMg) extension = 1; // Castling extension if (type_of(move) == CASTLING) extension = 1; // Add extension to new depth newDepth += extension; // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); // Check for legality just before making the move if (!rootNode && !pos.legal(move)) { ss->moveCount = --moveCount; continue; } // Update the current move (this must be done after singular extension search) ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[inCheck] [captureOrPromotion] [movedPiece] [to_sq(move)]; // Step 15. Make the move pos.do_move(move, st, givesCheck); // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 && moveCount > 1 + rootNode + (rootNode && bestValue < alpha) && (!rootNode || thisThread->best_move_count(move) == 0) && ( !captureOrPromotion || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode || thisThread->ttHitAverage < 375 * 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) r--; // Reduction if other threads are searching this position. if (th.marked()) r++; // Decrease reduction if position is or has been on the PV (~10 Elo) if (ttPv) r -= 2; // Decrease reduction if opponent's move count is high (~5 Elo) if ((ss-1)->moveCount > 14) r--; // Decrease reduction if ttMove has been singularly extended (~3 Elo) if (singularLMR) r -= 2; if (!captureOrPromotion) { // Increase reduction if ttMove is a capture (~5 Elo) if (ttCapture) r++; // Increase reduction for cut nodes (~10 Elo) if (cutNode) r += 2; // Decrease reduction for moves that escape a capture. Filter out // castling moves, because they are coded as "king captures rook" and // hence break make_move(). (~2 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(reverse_move(move))) r -= 2; ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - 4926; // Reset statScore to zero if negative and most stats shows >= 0 if ( ss->statScore < 0 && (*contHist[0])[movedPiece][to_sq(move)] >= 0 && (*contHist[1])[movedPiece][to_sq(move)] >= 0 && thisThread->mainHistory[us][from_to(move)] >= 0) ss->statScore = 0; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) if (ss->statScore >= -102 && (ss-1)->statScore < -114) r--; else if ((ss-1)->statScore >= -116 && ss->statScore < -154) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r -= ss->statScore / 16384; } // Increase reduction for captures/promotions if late move and at low depth else if (depth < 8 && moveCount > 2) r++; Depth d = clamp(newDepth - r, 1, newDepth); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); doFullDepthSearch = (value > alpha && d != newDepth), didLMR = true; } else doFullDepthSearch = !PvNode || moveCount > 1, didLMR = false; // Step 17. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); if (didLMR && !captureOrPromotion) { int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); if (move == ss->killers[0]) bonus += bonus / 4; update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } } // For PV nodes only, do a full PV search on the first move or after a fail // high (in the latter case search only if value < beta), otherwise let the // parent node fail low with value <= alpha and try another move. if (PvNode && (moveCount == 1 || (value > alpha && (rootNode || value < beta)))) { (ss+1)->pv = pv; (ss+1)->pv[0] = MOVE_NONE; value = -search(pos, ss+1, -beta, -alpha, newDepth, false); } // Step 18. Undo move pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); // Step 19. Check for a new best move // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. if (Threads.stop.load(std::memory_order_relaxed)) return VALUE_ZERO; if (rootNode) { RootMove& rm = *std::find(thisThread->rootMoves.begin(), thisThread->rootMoves.end(), move); // PV move or new best move? if (moveCount == 1 || value > alpha) { rm.score = value; rm.selDepth = thisThread->selDepth; rm.pv.resize(1); assert((ss+1)->pv); for (Move* m = (ss+1)->pv; *m != MOVE_NONE; ++m) 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 // the best move changes frequently, we allocate some more time. if (moveCount > 1) ++thisThread->bestMoveChanges; } else // All other moves but the PV are set to the lowest value: this // is not a problem when sorting because the sort is stable and the // move position in the list is preserved - just the PV is pushed up. rm.score = -VALUE_INFINITE; } if (value > bestValue) { bestValue = value; if (value > alpha) { bestMove = move; if (PvNode && !rootNode) // Update pv even in fail-high case update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha! Always alpha < beta alpha = value; else { assert(value >= beta); // Fail high ss->statScore = 0; break; } } } if (move != bestMove) { if (captureOrPromotion && captureCount < 32) capturesSearched[captureCount++] = move; else if (!captureOrPromotion && quietCount < 64) quietsSearched[quietCount++] = move; } } // The following condition would detect a stop only after move loop has been // completed. But in this case bestValue is valid because we have fully // searched our subtree, and we can anyhow save the result in TT. /* if (Threads.stop) return VALUE_DRAW; */ // Step 20. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be a mate or a stalemate. If we are in a singular extension search then // return a fail low score. assert(moveCount || !inCheck || excludedMove || !MoveList(pos).size()); if (!moveCount) bestValue = excludedMove ? alpha : inCheck ? mated_in(ss->ply) : VALUE_DRAW; else if (bestMove) update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, quietsSearched, quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth)); if (PvNode) bestValue = std::min(bestValue, maxValue); if (!excludedMove) tte->save(posKey, value_to_tt(bestValue, ss->ply), ttPv, bestValue >= beta ? BOUND_LOWER : PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, depth, bestMove, ss->staticEval); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); return bestValue; } // qsearch() is the quiescence search function, which is called by the main search // function with zero depth, or recursively with further decreasing depth per call. template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { constexpr bool PvNode = NT == PV; assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); assert(depth <= 0); Move pv[MAX_PLY+1]; StateInfo st; TTEntry* tte; Key posKey; Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; bool ttHit, pvHit, inCheck, givesCheck, captureOrPromotion, evasionPrunable; int moveCount; if (PvNode) { oldAlpha = alpha; // To flag BOUND_EXACT when eval above alpha and no available moves (ss+1)->pv = pv; ss->pv[0] = MOVE_NONE; } Thread* thisThread = pos.this_thread(); (ss+1)->ply = ss->ply + 1; bestMove = MOVE_NONE; inCheck = pos.checkers(); moveCount = 0; // Check for an immediate draw or maximum ply reached if ( pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !inCheck) ? evaluate(pos) : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); // Decide whether or not to include checks: this fixes also the type of // TT entry depth that we are going to use. Note that in qsearch we use // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. ttDepth = inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; // Transposition table lookup posKey = pos.key(); tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; pvHit = ttHit && tte->is_pv(); if ( !PvNode && ttHit && tte->depth() >= ttDepth && ttValue != VALUE_NONE // Only in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) return ttValue; // Evaluate the position statically if (inCheck) { ss->staticEval = VALUE_NONE; bestValue = futilityBase = -VALUE_INFINITE; } else { if (ttHit) { // Never assume anything about values stored in TT if ((ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos); // Can ttValue be used as a better position evaluation? if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))) bestValue = ttValue; } else ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Eval::Tempo; // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { if (!ttHit) tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, BOUND_LOWER, DEPTH_NONE, MOVE_NONE, ss->staticEval); return bestValue; } if (PvNode && bestValue > alpha) alpha = bestValue; futilityBase = bestValue + 154; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr , (ss-4)->continuationHistory, nullptr , (ss-6)->continuationHistory }; // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // be generated. MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, to_sq((ss-1)->currentMove)); // Loop through the moves until no moves remain or a beta cutoff occurs while ((move = mp.next_move()) != MOVE_NONE) { assert(is_ok(move)); givesCheck = pos.gives_check(move); captureOrPromotion = pos.capture_or_promotion(move); moveCount++; // Futility pruning if ( !inCheck && !givesCheck && futilityBase > -VALUE_KNOWN_WIN && !pos.advanced_pawn_push(move)) { assert(type_of(move) != ENPASSANT); // Due to !pos.advanced_pawn_push futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))]; if (futilityValue <= alpha) { bestValue = std::max(bestValue, futilityValue); continue; } if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) { bestValue = std::max(bestValue, futilityBase); continue; } } // Detect non-capture evasions that are candidates to be pruned evasionPrunable = inCheck && (depth != 0 || moveCount > 2) && bestValue > VALUE_MATED_IN_MAX_PLY && !pos.capture(move); // Don't search moves with negative SEE values if ( (!inCheck || evasionPrunable) && !pos.see_ge(move)) continue; // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); // Check for legality just before making the move if (!pos.legal(move)) { moveCount--; continue; } ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[inCheck] [captureOrPromotion] [pos.moved_piece(move)] [to_sq(move)]; // Make and search the move pos.do_move(move, st, givesCheck); value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); // Check for a new best move if (value > bestValue) { bestValue = value; if (value > alpha) { bestMove = move; if (PvNode) // Update pv even in fail-high case update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha here! alpha = value; else break; // Fail high } } } // All legal moves have been searched. A special case: If we're in check // and no legal moves were found, it is checkmate. if (inCheck && bestValue == -VALUE_INFINITE) return mated_in(ss->ply); // Plies to mate from the root tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, bestValue >= beta ? BOUND_LOWER : PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, ttDepth, bestMove, ss->staticEval); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); return bestValue; } // value_to_tt() adjusts a mate score from "plies to mate from the root" to // "plies to mate from the current position". Non-mate scores are unchanged. // The function is called before storing a value in the transposition table. Value value_to_tt(Value v, int ply) { assert(v != VALUE_NONE); return v >= VALUE_MATE_IN_MAX_PLY ? v + ply : v <= VALUE_MATED_IN_MAX_PLY ? v - ply : v; } // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score // from the transposition table (which refers to the plies to mate/be mated // from current position) to "plies to mate/be mated from the root". Value value_from_tt(Value v, int ply, int r50c) { return v == VALUE_NONE ? VALUE_NONE : v >= VALUE_MATE_IN_MAX_PLY ? VALUE_MATE - v > 99 - r50c ? VALUE_MATE_IN_MAX_PLY : v - ply : v <= VALUE_MATED_IN_MAX_PLY ? VALUE_MATE + v > 99 - r50c ? VALUE_MATED_IN_MAX_PLY : v + ply : v; } // update_pv() adds current move and appends child pv[] void update_pv(Move* pv, Move move, Move* childPv) { for (*pv++ = move; childPv && *childPv != MOVE_NONE; ) *pv++ = *childPv++; *pv = MOVE_NONE; } // update_all_stats() updates stats at the end of search() when a bestMove is found void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) { int bonus1, bonus2; Color us = pos.side_to_move(); Thread* thisThread = pos.this_thread(); CapturePieceToHistory& captureHistory = thisThread->captureHistory; Piece moved_piece = pos.moved_piece(bestMove); PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); bonus1 = stat_bonus(depth + 1); bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus : stat_bonus(depth); // smaller bonus if (!pos.capture_or_promotion(bestMove)) { update_quiet_stats(pos, ss, bestMove, bonus2); // Decrease all the non-best quiet moves for (int i = 0; i < quietCount; ++i) { thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2; update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bonus2); } } else captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1; // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0])) && !pos.captured_piece()) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1); // Decrease all the non-best capture moves for (int i = 0; i < captureCount; ++i) { moved_piece = pos.moved_piece(capturesSearched[i]); captured = type_of(pos.piece_on(to_sq(capturesSearched[i]))); captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -bonus1; } } // update_continuation_histories() updates histories of the move pairs formed // by moves at ply -1, -2, and -4 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { for (int i : {1, 2, 4, 6}) if (is_ok((ss-i)->currentMove)) (*(ss-i)->continuationHistory)[pc][to] << bonus; } // update_quiet_stats() updates move sorting heuristics void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { if (ss->killers[0] != move) { ss->killers[1] = ss->killers[0]; ss->killers[0] = move; } Color us = pos.side_to_move(); Thread* thisThread = pos.this_thread(); thisThread->mainHistory[us][from_to(move)] << bonus; update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); if (type_of(pos.moved_piece(move)) != PAWN) thisThread->mainHistory[us][from_to(reverse_move(move))] << -bonus; if (is_ok((ss-1)->currentMove)) { Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } } // When playing with strength handicap, choose best move among a set of RootMoves // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. Move Skill::pick_best(size_t multiPV) { const RootMoves& rootMoves = Threads.main()->rootMoves; static PRNG rng(now()); // PRNG sequence should be non-deterministic // RootMoves are already sorted by score in descending order Value topScore = rootMoves[0].score; int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValueMg); int weakness = 120 - 2 * level; int maxScore = -VALUE_INFINITE; // Choose best move. For each move score we add two terms, both dependent on // weakness. One is deterministic and bigger for weaker levels, and one is // random. Then we choose the move with the resulting highest score. for (size_t i = 0; i < multiPV; ++i) { // This is our magic formula int push = ( weakness * int(topScore - rootMoves[i].score) + delta * (rng.rand() % weakness)) / 128; if (rootMoves[i].score + push >= maxScore) { maxScore = rootMoves[i].score + push; best = rootMoves[i].pv[0]; } } return best; } } // 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. void MainThread::check_time() { if (--callsCnt > 0) return; // When using nodes, ensure checking rate is not lower than 0.1% of nodes callsCnt = Limits.nodes ? std::min(1024, int(Limits.nodes / 1024)) : 1024; static TimePoint lastInfoTime = now(); TimePoint elapsed = Time.elapsed(); TimePoint tick = Limits.startTime + elapsed; if (tick - lastInfoTime >= 1000) { lastInfoTime = tick; dbg_print(); } // We should not stop pondering until told so by the GUI if (ponder) return; if ( (Limits.use_time_management() && (elapsed > Time.maximum() - 10 || stopOnPonderhit)) || (Limits.movetime && elapsed >= Limits.movetime) || (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes)) Threads.stop = true; } /// UCI::pv() formats PV information according to the UCI protocol. UCI requires /// that all (if any) unsearched PV lines are sent using a previous search score. string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { std::stringstream ss; TimePoint elapsed = Time.elapsed() + 1; const RootMoves& rootMoves = pos.this_thread()->rootMoves; size_t pvIdx = pos.this_thread()->pvIdx; size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size()); uint64_t nodesSearched = Threads.nodes_searched(); uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0); for (size_t i = 0; i < multiPV; ++i) { bool updated = rootMoves[i].score != -VALUE_INFINITE; if (depth == 1 && !updated) continue; Depth d = updated ? depth : depth - 1; Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore; bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY; v = tb ? rootMoves[i].tbScore : v; if (ss.rdbuf()->in_avail()) // Not at first line ss << "\n"; ss << "info" << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 << " score " << UCI::value(v); if (!tb && i == pvIdx) ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); ss << " nodes " << nodesSearched << " nps " << nodesSearched * 1000 / elapsed; if (elapsed > 1000) // Earlier makes little sense ss << " hashfull " << TT.hashfull(); ss << " tbhits " << tbHits << " time " << elapsed << " pv"; for (Move m : rootMoves[i].pv) ss << " " << UCI::move(m, pos.is_chess960()); } return ss.str(); } /// RootMove::extract_ponder_from_tt() is called in case we have no ponder move /// before exiting the search, for instance, in case we stop the search during a /// fail high at root. We try hard to have a ponder move to return to the GUI, /// otherwise in case of 'ponder on' we have nothing to think on. bool RootMove::extract_ponder_from_tt(Position& pos) { StateInfo st; bool ttHit; assert(pv.size() == 1); if (pv[0] == MOVE_NONE) return false; pos.do_move(pv[0], st); TTEntry* tte = TT.probe(pos.key(), ttHit); if (ttHit) { Move m = tte->move(); // Local copy to be SMP safe if (MoveList(pos).contains(m)) pv.push_back(m); } pos.undo_move(pv[0]); return pv.size() > 1; } void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { RootInTB = false; UseRule50 = bool(Options["Syzygy50MoveRule"]); ProbeDepth = int(Options["SyzygyProbeDepth"]); Cardinality = int(Options["SyzygyProbeLimit"]); bool dtz_available = true; // Tables with fewer pieces than SyzygyProbeLimit are searched with // ProbeDepth == DEPTH_ZERO if (Cardinality > MaxCardinality) { Cardinality = MaxCardinality; ProbeDepth = 0; } if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) { // Rank moves using DTZ tables RootInTB = root_probe(pos, rootMoves); if (!RootInTB) { // DTZ tables are missing; try to rank moves using WDL tables dtz_available = false; RootInTB = root_probe_wdl(pos, rootMoves); } } if (RootInTB) { // Sort moves according to TB rank std::sort(rootMoves.begin(), rootMoves.end(), [](const RootMove &a, const RootMove &b) { return a.tbRank > b.tbRank; } ); // Probe during search only if DTZ is not available and we are winning if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW) Cardinality = 0; } else { // Clean up if root_probe() and root_probe_wdl() have failed for (auto& m : rootMoves) m.tbRank = 0; } } stockfish-11.orig/src/evaluate.cpp0000644000175000017500000010146013610452365015463 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include // For std::memset #include #include #include "bitboard.h" #include "evaluate.h" #include "material.h" #include "pawns.h" #include "thread.h" 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 }; Score scores[TERM_NB][COLOR_NB]; double to_cp(Value v) { return double(v) / PawnValueEg; } void add(int idx, Color c, Score s) { scores[idx][c] = s; } void add(int idx, Score w, Score b = SCORE_ZERO) { scores[idx][WHITE] = w; scores[idx][BLACK] = b; } std::ostream& operator<<(std::ostream& os, Score s) { os << std::setw(5) << to_cp(mg_value(s)) << " " << std::setw(5) << to_cp(eg_value(s)); return os; } std::ostream& operator<<(std::ostream& os, Term t) { if (t == MATERIAL || t == IMBALANCE || t == INITIATIVE || t == TOTAL) os << " ---- ----" << " | " << " ---- ----"; else os << scores[t][WHITE] << " | " << scores[t][BLACK]; os << " | " << scores[t][WHITE] - scores[t][BLACK] << "\n"; return os; } } using namespace Trace; namespace { // Threshold for lazy and space evaluation constexpr Value LazyThreshold = Value(1400); constexpr Value SpaceThreshold = Value(12222); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; // Penalties for enemy's safe checks constexpr int QueenSafeCheck = 780; constexpr int RookSafeCheck = 1080; constexpr int BishopSafeCheck = 635; constexpr int KnightSafeCheck = 790; #define S(mg, eg) make_score(mg, eg) // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game, // indexed by piece type and number of attacked squares in the mobility area. constexpr Score MobilityBonus[][32] = { { S(-62,-81), S(-53,-56), S(-12,-30), S( -4,-14), S( 3, 8), S( 13, 15), // Knights S( 22, 23), S( 28, 27), S( 33, 33) }, { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishops S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86), S( 91, 88), S( 98, 97) }, { S(-58,-76), S(-27,-18), S(-15, 28), S(-10, 55), S( -5, 69), S( -2, 82), // Rooks S( 9,112), S( 16,118), S( 30,132), S( 29,142), S( 32,155), S( 38,165), S( 46,166), S( 48,169), S( 58,171) }, { S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queens S( 28, 61), S( 41, 73), S( 43, 79), S( 48, 92), S( 56, 94), S( 60,104), S( 60,113), S( 66,120), S( 67,123), S( 70,126), S( 71,133), S( 73,136), S( 79,140), S( 88,143), S( 88,148), S( 99,166), S(102,170), S(102,175), S(106,184), S(109,191), S(113,206), S(116,212) } }; // RookOnFile[semiopen/open] contains bonuses for each rook when there is // no (friendly) pawn on the rook file. constexpr Score RookOnFile[] = { S(21, 4), S(47, 25) }; // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to // which piece type attacks which one. Attacks on lesser pieces which are // pawn-defended are not considered. constexpr Score ThreatByMinor[PIECE_TYPE_NB] = { S(0, 0), S(6, 32), S(59, 41), S(79, 56), S(90, 119), S(79, 161) }; constexpr Score ThreatByRook[PIECE_TYPE_NB] = { S(0, 0), S(3, 44), S(38, 71), S(38, 61), S(0, 38), S(51, 38) }; // PassedRank[Rank] contains a bonus according to the rank of a passed pawn constexpr Score PassedRank[RANK_NB] = { S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260) }; // Assorted bonuses and penalties constexpr Score BishopPawns = S( 3, 7); constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); constexpr Score KingProtector = S( 7, 8); constexpr Score KnightOnQueen = S( 16, 12); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score Outpost = S( 30, 21); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score ReachableOutpost = S( 32, 10); constexpr Score RookOnQueenFile = S( 7, 6); constexpr Score SliderOnQueen = S( 59, 18); constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score TrappedRook = S( 52, 10); constexpr Score WeakQueen = S( 49, 15); #undef S // Evaluation class computes and stores attacks tables and other working data template class Evaluation { public: Evaluation() = delete; explicit Evaluation(const Position& p) : pos(p) {} Evaluation& operator=(const Evaluation&) = delete; Value value(); private: template void initialize(); template Score pieces(); template Score king() const; template Score threats() const; template Score passed() const; template Score space() const; ScaleFactor scale_factor(Value eg) const; Score initiative(Score score) const; const Position& pos; Material::Entry* me; Pawns::Entry* pe; Bitboard mobilityArea[COLOR_NB]; Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO }; // attackedBy[color][piece type] is a bitboard representing all squares // attacked by a given color and piece type. Special "piece types" which // is also calculated is ALL_PIECES. Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB]; // attackedBy2[color] are the squares attacked by at least 2 units of a given // color, including x-rays. But diagonal x-rays through pawns are not computed. Bitboard attackedBy2[COLOR_NB]; // kingRing[color] are the squares adjacent to the king plus some other // very near squares, depending on king position. Bitboard kingRing[COLOR_NB]; // kingAttackersCount[color] is the number of pieces of the given color // which attack a square in the kingRing of the enemy king. int kingAttackersCount[COLOR_NB]; // kingAttackersWeight[color] is the sum of the "weights" of the pieces of // the given color which attack a square in the kingRing of the enemy king. // The weights of the individual piece types are given by the elements in // the KingAttackWeights array. int kingAttackersWeight[COLOR_NB]; // kingAttacksCount[color] is the number of attacks by the given color to // squares directly adjacent to the enemy king. Pieces which attack more // than one square are counted multiple times. For instance, if there is // a white knight on g5 and black's king is on g8, this white knight adds 2 // to kingAttacksCount[WHITE]. int kingAttacksCount[COLOR_NB]; }; // 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() { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Up = pawn_push(Us); constexpr Direction Down = -Up; constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB); const Square ksq = pos.square(Us); Bitboard dblAttackByPawn = pawn_double_attacks_bb(pos.pieces(Us, PAWN)); // Find our pawns that are blocked or on the first two ranks Bitboard b = pos.pieces(Us, PAWN) & (shift(pos.pieces()) | LowRanks); // Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king // or controlled by enemy pawns are excluded from the mobility area. mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them)); // Initialize attackedBy[] for king and pawns attackedBy[Us][KING] = pos.attacks_from(ksq); attackedBy[Us][PAWN] = pe->pawn_attacks(Us); attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); // Init our king safety tables Square s = make_square(clamp(file_of(ksq), FILE_B, FILE_G), clamp(rank_of(ksq), RANK_2, RANK_7)); kingRing[Us] = PseudoAttacks[KING][s] | s; kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; // Remove from kingRing[] the squares defended by two pawns kingRing[Us] &= ~dblAttackByPawn; } // Evaluation::pieces() scores pieces of a given color and type template template Score Evaluation::pieces() { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Down = -pawn_push(Us); constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB : Rank5BB | Rank4BB | Rank3BB); const Square* pl = pos.squares(Us); Bitboard b, bb; Score score = SCORE_ZERO; attackedBy[Us][Pt] = 0; for (Square s = *pl; s != SQ_NONE; s = *++pl) { // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN)) : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK)) : pos.attacks_from(s); if (pos.blockers_for_king(Us) & s) b &= LineBB[pos.square(Us)][s]; attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b; attackedBy[Us][Pt] |= b; attackedBy[Us][ALL_PIECES] |= b; if (b & kingRing[Them]) { kingAttackersCount[Us]++; kingAttackersWeight[Us] += KingAttackWeights[Pt]; kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]); } int mob = popcount(b & mobilityArea[Us]); mobility[Us] += MobilityBonus[Pt - 2][mob]; if (Pt == BISHOP || Pt == KNIGHT) { // Bonus if piece is on an outpost square or can reach one bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); if (bb & s) score += Outpost * (Pt == KNIGHT ? 2 : 1); else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) score += ReachableOutpost; // Knight and Bishop bonus for being right behind a pawn if (shift(pos.pieces(PAWN)) & s) score += MinorBehindPawn; // Penalty if the piece is far from the king score -= KingProtector * distance(s, pos.square(Us)); if (Pt == BISHOP) { // Penalty according to number of pawns on the same color square as the // bishop, bigger when the center files are blocked with pawns. Bitboard blocked = pos.pieces(Us, PAWN) & shift(pos.pieces()); score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s) * (1 + popcount(blocked & CenterFiles)); // Bonus for bishop on a long diagonal which can "see" both center squares if (more_than_one(attacks_bb(s, pos.pieces(PAWN)) & Center)) score += LongDiagonalBishop; // An important Chess960 pattern: a cornered bishop blocked by a friendly // pawn diagonally in front of it is a very serious problem, especially // when that pawn is also blocked. if ( pos.is_chess960() && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) { Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); if (pos.piece_on(s + d) == make_piece(Us, PAWN)) score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2 : CorneredBishop; } } } if (Pt == ROOK) { // Bonus for rook on the same file as a queen if (file_bb(s) & pos.pieces(QUEEN)) score += RookOnQueenFile; // Bonus for rook on an open or semi-open file if (pos.is_on_semiopen_file(Us, s)) score += RookOnFile[pos.is_on_semiopen_file(Them, s)]; // Penalty when trapped by the king, even more if the king cannot castle else if (mob <= 3) { File kf = file_of(pos.square(Us)); if ((kf < FILE_E) == (file_of(s) < kf)) score -= TrappedRook * (1 + !pos.castling_rights(Us)); } } if (Pt == QUEEN) { // Penalty if any relative pin or discovered attack against the queen Bitboard queenPinners; if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners)) score -= WeakQueen; } } if (T) Trace::add(Pt, Us, score); return score; } // Evaluation::king() assigns bonuses and penalties to a king of a given color template template Score Evaluation::king() const { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0; Bitboard rookChecks, queenChecks, bishopChecks, knightChecks; int kingDanger = 0; const Square ksq = pos.square(Us); // Init the score with king shelter and enemy pawns storm Score score = pe->king_safety(pos); // Attacked squares defended at most once by our queen or king weak = attackedBy[Them][ALL_PIECES] & ~attackedBy2[Us] & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]); // Analyse the safe enemy's checks which are possible on next move safe = ~pos.pieces(Them); safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]); b1 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); b2 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); // Enemy rooks checks rookChecks = b1 & safe & attackedBy[Them][ROOK]; if (rookChecks) kingDanger += RookSafeCheck; else unsafeChecks |= b1 & attackedBy[Them][ROOK]; // Enemy queen safe checks: we count them only if they are from squares from // which we can't give a rook check, because rook checks are more valuable. queenChecks = (b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN] & ~rookChecks; if (queenChecks) kingDanger += QueenSafeCheck; // Enemy bishops checks: we count them only if they are from squares from // which we can't give a queen check, because queen checks are more valuable. bishopChecks = b2 & attackedBy[Them][BISHOP] & safe & ~queenChecks; if (bishopChecks) kingDanger += BishopSafeCheck; else unsafeChecks |= b2 & attackedBy[Them][BISHOP]; // Enemy knights checks knightChecks = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; if (knightChecks & safe) kingDanger += KnightSafeCheck; else unsafeChecks |= knightChecks; // Find the squares that opponent attacks in our king flank, the squares // which they attack twice in that flank, and the squares that we defend. b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; b2 = b1 & attackedBy2[Them]; b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; int kingFlankAttack = popcount(b1) + popcount(b2); int kingFlankDefense = popcount(b3); kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] + 185 * popcount(kingRing[Us] & weak) + 148 * popcount(unsafeChecks) + 98 * popcount(pos.blockers_for_king(Us)) + 69 * kingAttacksCount[Them] + 3 * kingFlankAttack * kingFlankAttack / 8 + mg_value(mobility[Them] - mobility[Us]) - 873 * !pos.count(Them) - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) - 6 * mg_value(score) / 8 - 4 * kingFlankDefense + 37; // Transform the kingDanger units into a Score, and subtract it from the evaluation if (kingDanger > 100) score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16); // Penalty when our king is on a pawnless flank if (!(pos.pieces(PAWN) & KingFlank[file_of(ksq)])) score -= PawnlessFlank; // Penalty if king flank is under attack, potentially moving toward the king score -= FlankAttacks * kingFlankAttack; if (T) Trace::add(KING, Us, score); return score; } // Evaluation::threats() assigns bonuses according to the types of the // attacking and the attacked pieces. template template Score Evaluation::threats() const { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Up = pawn_push(Us); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe; Score score = SCORE_ZERO; // Non-pawn enemies nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(PAWN); // Squares strongly protected by the enemy, either because they defend the // square with a pawn, or because they defend the square twice and we don't. stronglyProtected = attackedBy[Them][PAWN] | (attackedBy2[Them] & ~attackedBy2[Us]); // Non-pawn enemies, strongly protected defended = nonPawnEnemies & stronglyProtected; // Enemies not strongly protected and under our attack weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; // Bonus according to the kind of attacking pieces if (defended | weak) { b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]); while (b) score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))]; b = weak & attackedBy[Us][ROOK]; while (b) score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))]; if (weak & attackedBy[Us][KING]) score += ThreatByKing; b = ~attackedBy[Them][ALL_PIECES] | (nonPawnEnemies & attackedBy2[Us]); score += Hanging * popcount(weak & b); } // Bonus for restricting their piece moves b = attackedBy[Them][ALL_PIECES] & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; score += RestrictedPiece * popcount(b); // Protected or unattacked squares safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES]; // Bonus for attacking enemy pieces with our relatively safe pawns b = pos.pieces(Us, PAWN) & safe; b = pawn_attacks_bb(b) & nonPawnEnemies; score += ThreatBySafePawn * popcount(b); // Find squares where our pawns can push on the next move b = shift(pos.pieces(Us, PAWN)) & ~pos.pieces(); b |= shift(b & TRank3BB) & ~pos.pieces(); // Keep only the squares which are relatively safe b &= ~attackedBy[Them][PAWN] & safe; // Bonus for safe pawn threats on the next move b = pawn_attacks_bb(b) & nonPawnEnemies; score += ThreatByPawnPush * popcount(b); // Bonus for threats on the next moves against enemy queen if (pos.count(Them) == 1) { Square s = pos.square(Them); safe = mobilityArea[Us] & ~stronglyProtected; b = attackedBy[Us][KNIGHT] & pos.attacks_from(s); score += KnightOnQueen * popcount(b & safe); b = (attackedBy[Us][BISHOP] & pos.attacks_from(s)) | (attackedBy[Us][ROOK ] & pos.attacks_from(s)); score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]); } if (T) Trace::add(THREAT, Us, score); return score; } // Evaluation::passed() evaluates the passed pawns and candidate passed // pawns of the given color. template template Score Evaluation::passed() const { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Up = pawn_push(Us); auto king_proximity = [&](Color c, Square s) { return std::min(distance(pos.square(c), s), 5); }; Bitboard b, bb, squaresToQueen, unsafeSquares; Score score = SCORE_ZERO; b = pe->passed_pawns(Us); while (b) { Square s = pop_lsb(&b); assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up))); int r = relative_rank(Us, s); Score bonus = PassedRank[r]; if (r > RANK_3) { int w = 5 * r - 13; Square blockSq = s + Up; // Adjust bonus based on the king's proximity bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4 - king_proximity(Us, blockSq) * 2) * w); // If blockSq is not the queening square then consider also a second push if (r != RANK_7) bonus -= make_score(0, king_proximity(Us, blockSq + Up) * w); // If the pawn is free to advance, then increase the bonus if (pos.empty(blockSq)) { squaresToQueen = forward_file_bb(Us, s); unsafeSquares = passed_pawn_span(Us, s); bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN); if (!(pos.pieces(Them) & bb)) unsafeSquares &= attackedBy[Them][ALL_PIECES]; // If there are no enemy attacks on passed pawn span, assign a big bonus. // Otherwise assign a smaller bonus if the path to queen is not attacked // and even smaller bonus if it is attacked but block square is not. int k = !unsafeSquares ? 35 : !(unsafeSquares & squaresToQueen) ? 20 : !(unsafeSquares & blockSq) ? 9 : 0 ; // Assign a larger bonus if the block square is defended if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq)) k += 5; bonus += make_score(k * w, k * w); } } // r > RANK_3 // Scale down bonus for candidate passers which need more than one // pawn push to become passed, or have a pawn in front of them. if ( !pos.pawn_passed(Us, s + Up) || (pos.pieces(PAWN) & (s + Up))) bonus = bonus / 2; score += bonus - PassedFile * map_to_queenside(file_of(s)); } if (T) Trace::add(PASSED, Us, score); return score; } // 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. template template Score Evaluation::space() const { if (pos.non_pawn_material() < SpaceThreshold) return SCORE_ZERO; constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Down = -pawn_push(Us); constexpr Bitboard SpaceMask = Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB) : CenterFiles & (Rank7BB | Rank6BB | Rank5BB); // Find the available squares for our pieces inside the area defined by SpaceMask Bitboard safe = SpaceMask & ~pos.pieces(Us, PAWN) & ~attackedBy[Them][PAWN]; // Find all squares which are at most three squares behind some friendly pawn Bitboard behind = pos.pieces(Us, PAWN); behind |= shift(behind); behind |= shift(behind); int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]); int weight = pos.count(Us) - 1; Score score = make_score(bonus * weight * weight / 16, 0); if (T) Trace::add(SPACE, Us, score); return score; } // Evaluation::initiative() computes the initiative correction value // for the position. It is a second order bonus/malus based on the // known attacking/defending status of the players. template Score Evaluation::initiative(Score score) const { Value mg = mg_value(score); Value eg = eg_value(score); int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); bool infiltration = rank_of(pos.square(WHITE)) > RANK_4 || rank_of(pos.square(BLACK)) < RANK_5; bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) && (pos.pieces(PAWN) & KingSide); bool almostUnwinnable = !pe->passed_count() && outflanking < 0 && !pawnsOnBothFlanks; // Compute the initiative bonus for the attacking side int complexity = 9 * pe->passed_count() + 11 * pos.count() + 9 * outflanking + 12 * infiltration + 21 * pawnsOnBothFlanks + 51 * !pos.non_pawn_material() - 43 * almostUnwinnable - 100 ; // Now apply the bonus: note that we find the attacking side by extracting the // sign of the midgame or endgame values, and that we carefully cap the bonus // so that the midgame and endgame scores do not change sign after the bonus. int u = ((mg > 0) - (mg < 0)) * std::max(std::min(complexity + 50, 0), -abs(mg)); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); if (T) Trace::add(INITIATIVE, make_score(u, 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 { Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; int sf = me->scale_factor(pos, strongSide); // If scale is not already specific, scale down the endgame via general heuristics if (sf == SCALE_FACTOR_NORMAL) { if ( pos.opposite_bishops() && pos.non_pawn_material() == 2 * BishopValueMg) sf = 22 ; else sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count(strongSide)); sf = std::max(0, sf - (pos.rule50_count() - 12) / 4); } return ScaleFactor(sf); } // Evaluation::value() is the main function of the class. It computes the various // parts of the evaluation and returns the value of the position from the point // of view of the side to move. template Value Evaluation::value() { assert(!pos.checkers()); // Probe the material hash table me = Material::probe(pos); // If we have a specialized evaluation function for the current material // configuration, call it and return. if (me->specialized_eval_exists()) return me->evaluate(pos); // Initialize score by reading the incrementally updated scores included in // the position object (material + piece square tables) and the material // imbalance. Score is computed internally from the white point of view. Score score = pos.psq_score() + me->imbalance() + pos.this_thread()->contempt; // Probe the pawn hash table pe = Pawns::probe(pos); score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK); // Early exit if score is high Value v = (mg_value(score) + eg_value(score)) / 2; if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64) return pos.side_to_move() == WHITE ? v : -v; // Main evaluation begins here initialize(); initialize(); // Pieces should be evaluated first (populate attack tables) score += pieces() - pieces() + pieces() - pieces() + pieces() - pieces() + pieces() - pieces(); score += mobility[WHITE] - mobility[BLACK]; score += king< WHITE>() - king< BLACK>() + threats() - threats() + 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; // In case of tracing add all remaining individual evaluation terms if (T) { Trace::add(MATERIAL, pos.psq_score()); 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); } return (pos.side_to_move() == WHITE ? v : -v) // Side to move point of view + Eval::Tempo; } } // namespace /// evaluate() is the evaluator for the outer world. It returns a static /// evaluation of the position from the point of view of the side to move. Value Eval::evaluate(const Position& pos) { return Evaluation(pos).value(); } /// trace() is like evaluate(), but instead of returning a value, it returns /// a string (suitable for outputting to stdout) that contains the detailed /// descriptions and values of each evaluation term. Useful for debugging. std::string Eval::trace(const Position& pos) { if (pos.checkers()) return "Total evaluation: none (in check)"; std::memset(scores, 0, sizeof(scores)); pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt Value v = Evaluation(pos).value(); v = pos.side_to_move() == WHITE ? v : -v; // Trace scores are from white's point of view std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) << " Term | White | Black | Total \n" << " | MG EG | MG EG | MG EG \n" << " ------------+-------------+-------------+------------\n" << " Material | " << Term(MATERIAL) << " Imbalance | " << Term(IMBALANCE) << " Pawns | " << Term(PAWN) << " Knights | " << Term(KNIGHT) << " Bishops | " << Term(BISHOP) << " Rooks | " << Term(ROOK) << " Queens | " << Term(QUEEN) << " Mobility | " << Term(MOBILITY) << " King safety | " << Term(KING) << " Threats | " << Term(THREAT) << " Passed | " << Term(PASSED) << " Space | " << Term(SPACE) << " Initiative | " << Term(INITIATIVE) << " ------------+-------------+-------------+------------\n" << " Total | " << Term(TOTAL); ss << "\nTotal evaluation: " << to_cp(v) << " (white side)\n"; return ss.str(); } stockfish-11.orig/src/ucioption.cpp0000644000175000017500000001421613610452365015670 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "misc.h" #include "search.h" #include "thread.h" #include "tt.h" #include "uci.h" #include "syzygy/tbprobe.h" using std::string; UCI::OptionsMap Options; // Global object namespace UCI { /// 'On change' actions, triggered by an option's value change void on_clear_hash(const Option&) { Search::clear(); } void on_hash_size(const Option& o) { TT.resize(o); } void on_logger(const Option& o) { start_logger(o); } void on_threads(const Option& o) { Threads.set(o); } void on_tb_path(const Option& o) { Tablebases::init(o); } /// Our case insensitive less() function as required by UCI protocol bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), [](char c1, char c2) { return tolower(c1) < tolower(c2); }); } /// init() initializes the UCI options to their hard-coded default values void init(OptionsMap& o) { // at most 2^32 clusters. constexpr int MaxHashMB = Is64Bit ? 131072 : 2048; o["Debug Log File"] << Option("", on_logger); o["Contempt"] << Option(24, -100, 100); o["Analysis Contempt"] << Option("Both var Off var White var Black var Both", "Both"); o["Threads"] << Option(1, 1, 512, on_threads); o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); o["Clear Hash"] << Option(on_clear_hash); o["Ponder"] << Option(false); o["MultiPV"] << Option(1, 1, 500); o["Skill Level"] << Option(20, 0, 20); o["Move Overhead"] << Option(30, 0, 5000); o["Minimum Thinking Time"] << Option(20, 0, 5000); o["Slow Mover"] << Option(84, 10, 1000); o["nodestime"] << Option(0, 0, 10000); o["UCI_Chess960"] << Option(false); o["UCI_AnalyseMode"] << Option(false); o["UCI_LimitStrength"] << Option(false); o["UCI_Elo"] << Option(1350, 1350, 2850); o["SyzygyPath"] << Option("", on_tb_path); o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); } /// operator<<() is used to print all the options default values in chronological /// insertion order (the idx field) and in the format defined by the UCI protocol. std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { for (size_t idx = 0; idx < om.size(); ++idx) for (const auto& it : om) if (it.second.idx == idx) { const Option& o = it.second; os << "\noption name " << it.first << " type " << o.type; if (o.type == "string" || o.type == "check" || o.type == "combo") os << " default " << o.defaultValue; if (o.type == "spin") os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max " << o.max; break; } return os; } /// Option class constructors and conversion operators Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f) { defaultValue = currentValue = v; } Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f) { defaultValue = currentValue = (v ? "true" : "false"); } Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f) {} Option::Option(double v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f) { defaultValue = currentValue = std::to_string(v); } Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), on_change(f) { defaultValue = v; currentValue = cur; } Option::operator double() const { assert(type == "check" || type == "spin"); return (type == "spin" ? stof(currentValue) : currentValue == "true"); } Option::operator std::string() const { assert(type == "string"); return currentValue; } bool Option::operator==(const char* s) const { assert(type == "combo"); return !CaseInsensitiveLess()(currentValue, s) && !CaseInsensitiveLess()(s, currentValue); } /// operator<<() inits options and assigns idx in the correct printing order void Option::operator<<(const Option& o) { static size_t insert_order = 0; *this = o; idx = insert_order++; } /// operator=() updates currentValue and triggers on_change() action. It's up to /// the GUI to check for option's limits, but we could receive the new value /// from the user by console window, so let's check the bounds anyway. Option& Option::operator=(const string& v) { assert(!type.empty()); if ( (type != "button" && v.empty()) || (type == "check" && v != "true" && v != "false") || (type == "spin" && (stof(v) < min || stof(v) > max))) return *this; if (type == "combo") { OptionsMap comboMap; // To have case insensitive compare string token; std::istringstream ss(defaultValue); while (ss >> token) comboMap[token] << Option(); if (!comboMap.count(v) || v == "var") return *this; } if (type != "button") currentValue = v; if (on_change) on_change(*this); return *this; } } // namespace UCI stockfish-11.orig/src/types.h0000644000175000017500000003175613610452365014500 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TYPES_H_INCLUDED #define TYPES_H_INCLUDED /// When compiling with provided Makefile (e.g. for Linux and OSX), configuration /// is done automatically. To get started type 'make help'. /// /// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches /// need to be set manually: /// /// -DNDEBUG | Disable debugging mode. Always use this for release. /// /// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to /// | run on some very old machines. /// /// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works /// | only in 64-bit mode and requires hardware with popcnt support. /// /// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works /// | only in 64-bit mode and requires hardware with pext support. #include #include #include #include #include #include #if defined(_MSC_VER) // Disable some silly and noisy warning from MSVC compiler #pragma warning(disable: 4127) // Conditional expression is constant #pragma warning(disable: 4146) // Unary minus operator applied to unsigned type #pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false' #endif /// Predefined macros hell: /// /// __GNUC__ Compiler is gcc, Clang or Intel on Linux /// __INTEL_COMPILER Compiler is Intel /// _MSC_VER Compiler is MSVC or Intel on Windows /// _WIN32 Building on Windows (any) /// _WIN64 Building on Windows 64 bit #if defined(_WIN64) && defined(_MSC_VER) // No Makefile used # include // Microsoft header for _BitScanForward64() # define IS_64BIT #endif #if defined(USE_POPCNT) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) # include // Intel and Microsoft header for _mm_popcnt_u64() #endif #if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) # include // Intel and Microsoft header for _mm_prefetch() #endif #if defined(USE_PEXT) # include // Header for _pext_u64() intrinsic # define pext(b, m) _pext_u64(b, m) #else # define pext(b, m) 0 #endif #ifdef USE_POPCNT constexpr bool HasPopCnt = true; #else constexpr bool HasPopCnt = false; #endif #ifdef USE_PEXT constexpr bool HasPext = true; #else constexpr bool HasPext = false; #endif #ifdef IS_64BIT constexpr bool Is64Bit = true; #else constexpr bool Is64Bit = false; #endif typedef uint64_t Key; typedef uint64_t Bitboard; constexpr int MAX_MOVES = 256; constexpr int MAX_PLY = 246; /// A move needs 16 bits to be stored /// /// bit 0- 5: destination square (from 0 to 63) /// bit 6-11: origin square (from 0 to 63) /// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2) /// bit 14-15: special move flag: promotion (1), en passant (2), castling (3) /// NOTE: EN-PASSANT bit is set only when a pawn can be captured /// /// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in /// any normal move destination square is always different from origin square /// while MOVE_NONE and MOVE_NULL have the same origin and destination square. enum Move : int { MOVE_NONE, MOVE_NULL = 65 }; enum MoveType { NORMAL, PROMOTION = 1 << 14, ENPASSANT = 2 << 14, CASTLING = 3 << 14 }; enum Color { WHITE, BLACK, COLOR_NB = 2 }; enum CastlingRights { NO_CASTLING, WHITE_OO, WHITE_OOO = WHITE_OO << 1, BLACK_OO = WHITE_OO << 2, BLACK_OOO = WHITE_OO << 3, KING_SIDE = WHITE_OO | BLACK_OO, QUEEN_SIDE = WHITE_OOO | BLACK_OOO, WHITE_CASTLING = WHITE_OO | WHITE_OOO, BLACK_CASTLING = BLACK_OO | BLACK_OOO, ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING, CASTLING_RIGHT_NB = 16 }; enum Phase { PHASE_ENDGAME, PHASE_MIDGAME = 128, MG = 0, EG = 1, PHASE_NB = 2 }; enum ScaleFactor { SCALE_FACTOR_DRAW = 0, SCALE_FACTOR_NORMAL = 64, SCALE_FACTOR_MAX = 128, SCALE_FACTOR_NONE = 255 }; enum Bound { BOUND_NONE, BOUND_UPPER, BOUND_LOWER, BOUND_EXACT = BOUND_UPPER | BOUND_LOWER }; enum Value : int { VALUE_ZERO = 0, VALUE_DRAW = 0, VALUE_KNOWN_WIN = 10000, VALUE_MATE = 32000, VALUE_INFINITE = 32001, VALUE_NONE = 32002, VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, PawnValueMg = 128, PawnValueEg = 213, KnightValueMg = 781, KnightValueEg = 854, BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, QueenValueMg = 2538, QueenValueEg = 2682, MidgameLimit = 15258, EndgameLimit = 3915 }; enum PieceType { NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, ALL_PIECES = 0, PIECE_TYPE_NB = 8 }; enum Piece { NO_PIECE, W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, PIECE_NB = 16 }; extern Value PieceValue[PHASE_NB][PIECE_NB]; typedef int Depth; enum : int { DEPTH_QS_CHECKS = 0, DEPTH_QS_NO_CHECKS = -1, DEPTH_QS_RECAPTURES = -5, DEPTH_NONE = -6, DEPTH_OFFSET = DEPTH_NONE, }; enum Square : int { SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3, SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4, SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5, SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6, SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7, SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, SQ_NONE, SQUARE_NB = 64 }; enum Direction : int { NORTH = 8, EAST = 1, SOUTH = -NORTH, WEST = -EAST, NORTH_EAST = NORTH + EAST, SOUTH_EAST = SOUTH + EAST, SOUTH_WEST = SOUTH + WEST, NORTH_WEST = NORTH + WEST }; enum File : int { FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB }; enum Rank : int { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB }; /// Score enum stores a middlegame and an endgame value in a single integer (enum). /// The least significant 16 bits are used to store the middlegame value and the /// upper 16 bits are used to store the endgame value. We have to take care to /// avoid left-shifting a signed int to avoid undefined behavior. enum Score : int { SCORE_ZERO }; constexpr Score make_score(int mg, int eg) { return Score((int)((unsigned int)eg << 16) + mg); } /// Extracting the signed lower and upper 16 bits is not so trivial because /// according to the standard a simple cast to short is implementation defined /// and so is a right shift of a signed integer. inline Value eg_value(Score s) { union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) }; return Value(eg.s); } inline Value mg_value(Score s) { union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) }; return Value(mg.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 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; } #define ENABLE_INCR_OPERATORS_ON(T) \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \ inline T& operator--(T& d) { return d = T(int(d) - 1); } #define ENABLE_FULL_OPERATORS_ON(T) \ ENABLE_BASE_OPERATORS_ON(T) \ constexpr T operator*(int i, T d) { return T(i * int(d)); } \ constexpr T operator*(T d, int i) { return T(int(d) * i); } \ constexpr T operator/(T d, int i) { return T(int(d) / i); } \ constexpr int operator/(T d1, T d2) { return int(d1) / int(d2); } \ inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } 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) ENABLE_BASE_OPERATORS_ON(Score) #undef ENABLE_FULL_OPERATORS_ON #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)); } inline Square& operator+=(Square& s, Direction d) { return s = s + d; } inline Square& operator-=(Square& s, Direction d) { return s = s - d; } /// Only declared but not defined. We don't want to multiply two scores due to /// a very high risk of overflow. So user should explicitly convert to integer. Score operator*(Score, Score) = delete; /// Division of a Score must be handled separately for each term inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); } /// Multiplication of a Score by an integer. We check for overflow in debug mode. inline Score operator*(Score s, int i) { Score result = Score(int(s) * i); assert(eg_value(result) == (i * eg_value(s))); assert(mg_value(result) == (i * mg_value(s))); assert((i == 0) || (result / i) == s); return result; } /// Multiplication of a Score by a boolean inline Score operator*(Score s, bool b) { return Score(int(s) * int(b)); } constexpr Color operator~(Color c) { return Color(c ^ BLACK); // Toggle color } constexpr Square operator~(Square s) { return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 } constexpr Piece operator~(Piece pc) { return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT } inline File map_to_queenside(File f) { return std::min(f, File(FILE_H - f)); // Map files ABCDEFGH to files ABCDDCBA } constexpr CastlingRights operator&(Color c, CastlingRights cr) { return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); } constexpr Value mate_in(int ply) { return VALUE_MATE - ply; } constexpr Value mated_in(int ply) { return -VALUE_MATE + ply; } constexpr Square make_square(File f, Rank r) { return Square((r << 3) + f); } constexpr Piece make_piece(Color c, PieceType pt) { return Piece((c << 3) + pt); } constexpr PieceType type_of(Piece pc) { return PieceType(pc & 7); } inline Color color_of(Piece pc) { assert(pc != NO_PIECE); return Color(pc >> 3); } constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } constexpr File file_of(Square s) { return File(s & 7); } constexpr Rank rank_of(Square s) { return Rank(s >> 3); } constexpr Square relative_square(Color c, Square s) { return Square(s ^ (c * 56)); } constexpr Rank relative_rank(Color c, Rank r) { return Rank(r ^ (c * 7)); } constexpr Rank relative_rank(Color c, Square s) { return relative_rank(c, rank_of(s)); } constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; } constexpr Square from_sq(Move m) { return Square((m >> 6) & 0x3F); } constexpr Square to_sq(Move m) { return Square(m & 0x3F); } constexpr int from_to(Move m) { return m & 0xFFF; } constexpr MoveType type_of(Move m) { return MoveType(m & (3 << 14)); } constexpr PieceType promotion_type(Move m) { return PieceType(((m >> 12) & 3) + KNIGHT); } constexpr Move make_move(Square from, Square to) { return Move((from << 6) + to); } constexpr Move reverse_move(Move m) { return make_move(to_sq(m), from_sq(m)); } template constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); } constexpr bool is_ok(Move m) { return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE } #endif // #ifndef TYPES_H_INCLUDED stockfish-11.orig/src/Makefile0000644000175000017500000003326113610452365014614 0ustar pdmpdm# Stockfish, a UCI chess playing engine derived from Glaurung 2.1 # Copyright (C) 2004-2008 Tord Romstad (Glaurung author) # Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad # Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad # # Stockfish is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Stockfish is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ### ========================================================================== ### Section 1. General Configuration ### ========================================================================== ### Executable name ifeq ($(COMP),mingw) EXE = stockfish.exe else EXE = stockfish endif ### Installation dir definitions PREFIX = /usr/local BINDIR = $(PREFIX)/bin ### Built-in benchmark for pgo-builds PGOBENCH = ./$(EXE) bench ### Object files OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \ material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \ search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o ### Establish the operating system name KERNEL = $(shell uname -s) ifeq ($(KERNEL),Linux) OS = $(shell uname -o) endif ### ========================================================================== ### Section 2. High-level Configuration ### ========================================================================== # # flag --- Comp switch --- Description # ---------------------------------------------------------------------------- # # debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode # sanitize = undefined/thread/no (-fsanitize ) # --- ( undefined ) --- enable undefined behavior checks # --- ( thread ) --- enable threading error checks # optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations # arch = (name) --- (-arch) --- Target architecture # bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system # prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction # popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction # sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions # pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction # # Note that Makefile is space sensitive, so when adding new architectures # or modifying existing flags, you have to make sure there are no extra spaces # at the end of the line for flag values. ### 2.1. General and architecture defaults optimize = yes debug = no sanitize = no bits = 32 prefetch = no popcnt = no sse = no pext = no ### 2.2 Architecture specific ifeq ($(ARCH),general-32) arch = any endif ifeq ($(ARCH),x86-32-old) arch = i386 endif ifeq ($(ARCH),x86-32) arch = i386 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 endif ifeq ($(ARCH),x86-64-bmi2) arch = x86_64 bits = 64 prefetch = yes popcnt = yes sse = yes pext = yes endif ifeq ($(ARCH),armv7) arch = armv7 prefetch = yes endif ifeq ($(ARCH),ppc-32) arch = ppc endif ifeq ($(ARCH),ppc-64) arch = ppc64 bits = 64 popcnt = yes prefetch = yes endif ### ========================================================================== ### 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) ifeq ($(COMP),) COMP=gcc endif ifeq ($(COMP),gcc) comp=gcc CXX=g++ CXXFLAGS += -pedantic -Wextra -Wshadow ifeq ($(ARCH),armv7) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif else CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif ifneq ($(KERNEL),Darwin) LDFLAGS += -Wl,--no-as-needed endif endif ifeq ($(COMP),mingw) comp=mingw ifeq ($(KERNEL),Linux) ifeq ($(bits),64) ifeq ($(shell which x86_64-w64-mingw32-c++-posix),) CXX=x86_64-w64-mingw32-c++ else CXX=x86_64-w64-mingw32-c++-posix endif else ifeq ($(shell which i686-w64-mingw32-c++-posix),) CXX=i686-w64-mingw32-c++ else CXX=i686-w64-mingw32-c++-posix endif endif else CXX=g++ endif CXXFLAGS += -Wextra -Wshadow LDFLAGS += -static endif ifeq ($(COMP),icc) comp=icc CXX=icpc CXXFLAGS += -diag-disable 1476,10120 -Wcheck -Wabi -Wdeprecated -strict-ansi endif ifeq ($(COMP),clang) comp=clang CXX=clang++ CXXFLAGS += -pedantic -Wextra -Wshadow ifneq ($(KERNEL),Darwin) ifneq ($(KERNEL),OpenBSD) LDFLAGS += -latomic endif endif ifeq ($(ARCH),armv7) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif else CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif endif ifeq ($(comp),icc) profile_make = icc-profile-make profile_use = icc-profile-use else ifeq ($(comp),clang) profile_make = clang-profile-make profile_use = clang-profile-use else profile_make = gcc-profile-make profile_use = gcc-profile-use endif endif ifeq ($(KERNEL),Darwin) CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.9 LDFLAGS += -arch $(arch) -mmacosx-version-min=10.9 endif ### Travis CI script uses COMPILER to overwrite CXX ifdef COMPILER COMPCXX=$(COMPILER) endif ### Allow overwriting CXX from command line ifdef COMPCXX CXX=$(COMPCXX) endif ### On mingw use Windows threads, otherwise POSIX ifneq ($(comp),mingw) # On Android Bionic's C library comes with its own pthread implementation bundled in ifneq ($(OS),Android) # Haiku has pthreads in its libroot, so only link it in on other platforms ifneq ($(KERNEL),Haiku) LDFLAGS += -lpthread endif endif endif ### 3.2.1 Debugging ifeq ($(debug),no) CXXFLAGS += -DNDEBUG else CXXFLAGS += -g endif ### 3.2.2 Debugging with undefined behavior sanitizers ifneq ($(sanitize),no) CXXFLAGS += -g3 -fsanitize=$(sanitize) -fuse-ld=gold LDFLAGS += -fsanitize=$(sanitize) -fuse-ld=gold endif ### 3.3 Optimization ifeq ($(optimize),yes) CXXFLAGS += -O3 ifeq ($(comp),gcc) ifeq ($(OS), Android) CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp endif endif ifeq ($(comp),$(filter $(comp),gcc clang icc)) ifeq ($(KERNEL),Darwin) CXXFLAGS += -mdynamic-no-pic endif endif endif ### 3.4 Bits ifeq ($(bits),64) CXXFLAGS += -DIS_64BIT endif ### 3.5 prefetch ifeq ($(prefetch),yes) ifeq ($(sse),yes) CXXFLAGS += -msse DEPENDFLAGS += -msse endif else CXXFLAGS += -DNO_PREFETCH endif ### 3.6 popcnt ifeq ($(popcnt),yes) ifeq ($(arch),ppc64) CXXFLAGS += -DUSE_POPCNT else ifeq ($(comp),icc) CXXFLAGS += -msse3 -DUSE_POPCNT else CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT endif endif ### 3.7 pext ifeq ($(pext),yes) CXXFLAGS += -DUSE_PEXT ifeq ($(comp),$(filter $(comp),gcc clang mingw)) CXXFLAGS += -msse4 -mbmi2 endif endif ### 3.8 Link Time Optimization, it works since gcc 4.5 but not on mingw under Windows. ### This is a mix of compile and link time options because the lto link phase ### needs access to the optimization flags. ifeq ($(optimize),yes) ifeq ($(debug), no) ifeq ($(comp),$(filter $(comp),gcc clang)) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif ifeq ($(comp),mingw) ifeq ($(KERNEL),Linux) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif endif endif endif ### 3.9 Android 5 can only run position independent executables. Note that this ### breaks Android 4.0 and earlier. ifeq ($(OS), Android) CXXFLAGS += -fPIE LDFLAGS += -fPIE -pie endif ### ========================================================================== ### Section 4. Public targets ### ========================================================================== help: @echo "" @echo "To compile stockfish, type: " @echo "" @echo "make target ARCH=arch [COMP=compiler] [COMPCXX=cxx]" @echo "" @echo "Supported targets:" @echo "" @echo "build > Standard build" @echo "profile-build > PGO build" @echo "strip > Strip executable" @echo "install > Install executable" @echo "clean > Clean up" @echo "" @echo "Supported archs:" @echo "" @echo "x86-64-bmi2 > x86 64-bit with pext support (also enables SSE4)" @echo "x86-64-modern > x86 64-bit with popcnt support (also enables SSE3)" @echo "x86-64 > x86 64-bit generic" @echo "x86-32 > x86 32-bit (also enables SSE)" @echo "x86-32-old > x86 32-bit fall back for old hardware" @echo "ppc-64 > PPC 64-bit" @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" @echo "" @echo "Supported compilers:" @echo "" @echo "gcc > Gnu compiler (default)" @echo "mingw > Gnu compiler with MinGW under Windows" @echo "clang > LLVM Clang compiler" @echo "icc > Intel compiler" @echo "" @echo "Simple examples. If you don't know what to do, you likely want to run: " @echo "" @echo "make build ARCH=x86-64 (This is for 64-bit systems)" @echo "make build ARCH=x86-32 (This is for 32-bit systems)" @echo "" @echo "Advanced examples, for experienced users: " @echo "" @echo "make build ARCH=x86-64 COMP=clang" @echo "make profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8" @echo "" .PHONY: help build profile-build strip install clean objclean profileclean help \ config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ clang-profile-use clang-profile-make build: config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all profile-build: config-sanity objclean profileclean @echo "" @echo "Step 1/4. Building instrumented executable ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) @echo "" @echo "Step 2/4. Running benchmark for pgo-build ..." $(PGOBENCH) > /dev/null @echo "" @echo "Step 3/4. Building optimized executable ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use) @echo "" @echo "Step 4/4. Deleting profile data ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean strip: strip $(EXE) install: -mkdir -p -m 755 $(BINDIR) -cp $(EXE) $(BINDIR) -strip $(BINDIR)/$(EXE) #clean all clean: objclean profileclean @rm -f .depend *~ core # clean binaries and objects objclean: @rm -f $(EXE) *.o ./syzygy/*.o # clean auxiliary profiling files profileclean: @rm -rf profdir @rm -f bench.txt *.gcda ./syzygy/*.gcda *.gcno ./syzygy/*.gcno @rm -f stockfish.profdata *.profraw default: help ### ========================================================================== ### Section 5. Private targets ### ========================================================================== all: $(EXE) .depend config-sanity: @echo "" @echo "Config:" @echo "debug: '$(debug)'" @echo "sanitize: '$(sanitize)'" @echo "optimize: '$(optimize)'" @echo "arch: '$(arch)'" @echo "bits: '$(bits)'" @echo "kernel: '$(KERNEL)'" @echo "os: '$(OS)'" @echo "prefetch: '$(prefetch)'" @echo "popcnt: '$(popcnt)'" @echo "sse: '$(sse)'" @echo "pext: '$(pext)'" @echo "" @echo "Flags:" @echo "CXX: $(CXX)" @echo "CXXFLAGS: $(CXXFLAGS)" @echo "LDFLAGS: $(LDFLAGS)" @echo "" @echo "Testing config sanity. If this fails, try 'make help' ..." @echo "" @test "$(debug)" = "yes" || test "$(debug)" = "no" @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 "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" @test "$(sse)" = "yes" || test "$(sse)" = "no" @test "$(pext)" = "yes" || test "$(pext)" = "no" @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" $(EXE): $(OBJS) $(CXX) -o $@ $(OBJS) $(LDFLAGS) clang-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-instr-generate ' \ EXTRALDFLAGS=' -fprofile-instr-generate' \ all clang-profile-use: llvm-profdata merge -output=stockfish.profdata *.profraw $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \ EXTRALDFLAGS='-fprofile-use ' \ all gcc-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-generate' \ EXTRALDFLAGS='-lgcov' \ all gcc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-use -fno-peel-loops -fno-tracer' \ EXTRALDFLAGS='-lgcov' \ all icc-profile-make: @mkdir -p profdir $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-prof-gen=srcpos -prof_dir ./profdir' \ all icc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \ all .depend: -@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null -include .depend stockfish-11.orig/src/movepick.cpp0000644000175000017500000002144613610452365015477 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "movepick.h" namespace { enum Stages { MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, REFUTATION, QUIET_INIT, QUIET, BAD_CAPTURE, EVASION_TT, EVASION_INIT, EVASION, PROBCUT_TT, PROBCUT_INIT, PROBCUT, QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK }; // partial_insertion_sort() sorts moves in descending order up to and including // a given limit. The order of moves smaller than the limit is left unspecified. void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p) if (p->value >= limit) { ExtMove tmp = *p, *q; *p = *++sortedEnd; for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q) *q = *(q - 1); *q = tmp; } } } // namespace /// Constructors of the MovePicker class. As arguments we pass information /// to help it to return the (presumably) good moves first, to decide which /// moves to return (in the quiescence search, for instance, we only want to /// search captures, promotions, and some checks) and how important good move /// ordering is at the current node. /// MovePicker constructor for the main search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers) : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) { assert(d > 0); stage = pos.checkers() ? EVASION_TT : MAIN_TT; ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; stage += (ttMove == MOVE_NONE); } /// MovePicker constructor for quiescence search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), recaptureSquare(rs), depth(d) { assert(d <= 0); stage = pos.checkers() ? EVASION_TT : QSEARCH_TT; ttMove = ttm && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; stage += (ttMove == MOVE_NONE); } /// MovePicker constructor for ProbCut: we generate captures with SEE greater /// than or equal to the given threshold. MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) : pos(p), captureHistory(cph), threshold(th) { assert(!pos.checkers()); stage = PROBCUT_TT; ttMove = ttm && pos.capture(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE; stage += (ttMove == MOVE_NONE); } /// MovePicker::score() assigns a numerical value to each move in a list, used /// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring /// captures with a good history. Quiets moves are ordered using the histories. template void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); for (auto& m : *this) if (Type == CAPTURES) m.value = int(PieceValue[MG][pos.piece_on(to_sq(m))]) * 6 + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; else if (Type == QUIETS) m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]; else // Type == EVASIONS { if (pos.capture(m)) m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))); else m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] - (1 << 28); } } /// MovePicker::select() returns the next move satisfying a predicate function. /// It never returns the TT move. template Move MovePicker::select(Pred filter) { while (cur < endMoves) { if (T == Best) std::swap(*cur, *std::max_element(cur, endMoves)); if (*cur != ttMove && filter()) return *cur++; cur++; } return MOVE_NONE; } /// MovePicker::next_move() is the most important method of the MovePicker class. It /// returns a new pseudo legal move every time it is called until there are no more /// moves left, picking the move with the highest score from a list of generated moves. Move MovePicker::next_move(bool skipQuiets) { top: switch (stage) { case MAIN_TT: case EVASION_TT: case QSEARCH_TT: case PROBCUT_TT: ++stage; return ttMove; case CAPTURE_INIT: case PROBCUT_INIT: case QCAPTURE_INIT: cur = endBadCaptures = moves; endMoves = generate(pos, cur); score(); ++stage; goto top; case GOOD_CAPTURE: if (select([&](){ return pos.see_ge(*cur, Value(-55 * cur->value / 1024)) ? // Move losing capture to endBadCaptures to be tried later true : (*endBadCaptures++ = *cur, false); })) return *(cur - 1); // Prepare the pointers to loop over the refutations array cur = std::begin(refutations); endMoves = std::end(refutations); // If the countermove is the same as a killer, skip it if ( refutations[0].move == refutations[2].move || refutations[1].move == refutations[2].move) --endMoves; ++stage; /* fallthrough */ case REFUTATION: if (select([&](){ return *cur != MOVE_NONE && !pos.capture(*cur) && pos.pseudo_legal(*cur); })) return *(cur - 1); ++stage; /* fallthrough */ case QUIET_INIT: if (!skipQuiets) { cur = endBadCaptures; endMoves = generate(pos, cur); score(); partial_insertion_sort(cur, endMoves, -3000 * depth); } ++stage; /* fallthrough */ case QUIET: if ( !skipQuiets && select([&](){return *cur != refutations[0].move && *cur != refutations[1].move && *cur != refutations[2].move;})) return *(cur - 1); // Prepare the pointers to loop over the bad captures cur = moves; endMoves = endBadCaptures; ++stage; /* fallthrough */ case BAD_CAPTURE: return select([](){ return true; }); case EVASION_INIT: cur = moves; endMoves = generate(pos, cur); score(); ++stage; /* fallthrough */ case EVASION: return select([](){ return true; }); case PROBCUT: return select([&](){ return pos.see_ge(*cur, threshold); }); case QCAPTURE: if (select([&](){ return depth > DEPTH_QS_RECAPTURES || to_sq(*cur) == recaptureSquare; })) return *(cur - 1); // If we did not find any move and we do not try checks, we have finished if (depth != DEPTH_QS_CHECKS) return MOVE_NONE; ++stage; /* fallthrough */ case QCHECK_INIT: cur = moves; endMoves = generate(pos, cur); ++stage; /* fallthrough */ case QCHECK: return select([](){ return true; }); } assert(false); return MOVE_NONE; // Silence warning } stockfish-11.orig/src/movegen.cpp0000644000175000017500000003046613610452365015324 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "movegen.h" #include "position.h" namespace { template ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) *moveList++ = make(to - D, to, QUEEN); if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { *moveList++ = make(to - D, to, ROOK); *moveList++ = make(to - D, to, BISHOP); *moveList++ = make(to - D, to, KNIGHT); } // Knight promotion is the only promotion that can give a direct check // that's not already included in the queen promotion. if (Type == QUIET_CHECKS && (PseudoAttacks[KNIGHT][to] & ksq)) *moveList++ = make(to - D, to, KNIGHT); else (void)ksq; // Silence a warning under MSVC return moveList; } template ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { // Compute some compile time parameters relative to the white side constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); constexpr Direction Up = pawn_push(Us); constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); const Square ksq = pos.square(Them); Bitboard emptySquares; Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target: Type == CAPTURES ? target : pos.pieces(Them)); // Single and double pawn pushes, no promotions if (Type != CAPTURES) { emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); Bitboard b1 = shift(pawnsNotOn7) & emptySquares; Bitboard b2 = shift(b1 & TRank3BB) & emptySquares; if (Type == EVASIONS) // Consider only blocking squares { b1 &= target; b2 &= target; } if (Type == QUIET_CHECKS) { b1 &= pos.attacks_from(ksq, Them); b2 &= pos.attacks_from(ksq, Them); // Add pawn pushes which give discovered check. This is possible only // if the pawn is not on the same file as the enemy king, because we // don't generate captures. Note that a possible discovery check // promotion has been already generated amongst the captures. Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7; if (dcCandidateQuiets) { Bitboard dc1 = shift(dcCandidateQuiets) & emptySquares & ~file_bb(ksq); Bitboard dc2 = shift(dc1 & TRank3BB) & emptySquares; b1 |= dc1; b2 |= dc2; } } while (b1) { Square to = pop_lsb(&b1); *moveList++ = make_move(to - Up, to); } while (b2) { Square to = pop_lsb(&b2); *moveList++ = make_move(to - Up - Up, to); } } // Promotions and underpromotions if (pawnsOn7) { if (Type == CAPTURES) emptySquares = ~pos.pieces(); if (Type == EVASIONS) emptySquares &= target; Bitboard b1 = shift(pawnsOn7) & enemies; Bitboard b2 = shift(pawnsOn7) & enemies; Bitboard b3 = shift(pawnsOn7) & emptySquares; while (b1) moveList = make_promotions(moveList, pop_lsb(&b1), ksq); while (b2) moveList = make_promotions(moveList, pop_lsb(&b2), ksq); while (b3) moveList = make_promotions(moveList, pop_lsb(&b3), ksq); } // Standard and en-passant captures if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { Bitboard b1 = shift(pawnsNotOn7) & enemies; Bitboard b2 = shift(pawnsNotOn7) & enemies; while (b1) { Square to = pop_lsb(&b1); *moveList++ = make_move(to - UpRight, to); } while (b2) { Square to = pop_lsb(&b2); *moveList++ = make_move(to - UpLeft, to); } if (pos.ep_square() != SQ_NONE) { assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6)); // An en passant capture can be an evasion only if the checking piece // is the double pushed pawn and so is in the target. Otherwise this // is a discovery check and we are forced to do otherwise. if (Type == EVASIONS && !(target & (pos.ep_square() - Up))) return moveList; b1 = pawnsNotOn7 & pos.attacks_from(pos.ep_square(), Them); assert(b1); while (b1) *moveList++ = make(pop_lsb(&b1), pos.ep_square()); } } return moveList; } template ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); const Square* pl = pos.squares(us); for (Square from = *pl; from != SQ_NONE; from = *++pl) { if (Checks) { if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) && !(PseudoAttacks[Pt][from] & target & pos.check_squares(Pt))) continue; if (pos.blockers_for_king(~us) & from) continue; } Bitboard b = pos.attacks_from(from) & target; if (Checks) b &= pos.check_squares(Pt); while (b) *moveList++ = make_move(from, pop_lsb(&b)); } return moveList; } template ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { constexpr CastlingRights OO = Us & KING_SIDE; constexpr CastlingRights OOO = Us & QUEEN_SIDE; constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations moveList = generate_pawn_moves(pos, moveList, target); moveList = generate_moves(pos, moveList, Us, target); moveList = generate_moves(pos, moveList, Us, target); moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target); moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target); if (Type != QUIET_CHECKS && Type != EVASIONS) { Square ksq = pos.square(Us); Bitboard b = pos.attacks_from(ksq) & target; while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO))) { if (!pos.castling_impeded(OO) && pos.can_castle(OO)) *moveList++ = make(ksq, pos.castling_rook_square(OO)); if (!pos.castling_impeded(OOO) && pos.can_castle(OOO)) *moveList++ = make(ksq, pos.castling_rook_square(OOO)); } } return moveList; } } // namespace /// Generates all pseudo-legal captures and queen promotions /// Generates all pseudo-legal non-captures and underpromotions /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. template ExtMove* generate(const Position& pos, ExtMove* moveList) { static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()"); assert(!pos.checkers()); Color us = pos.side_to_move(); Bitboard target = Type == CAPTURES ? pos.pieces(~us) : Type == QUIETS ? ~pos.pieces() : Type == NON_EVASIONS ? ~pos.pieces(us) : 0; return us == WHITE ? generate_all(pos, moveList, target) : generate_all(pos, moveList, target); } // Explicit template instantiations template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); /// generate generates all pseudo-legal non-captures and knight /// underpromotions that give check. Returns a pointer to the end of the move list. template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { assert(!pos.checkers()); Color us = pos.side_to_move(); Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us); while (dc) { Square from = pop_lsb(&dc); PieceType pt = type_of(pos.piece_on(from)); if (pt == PAWN) continue; // Will be generated together with direct checks Bitboard b = pos.attacks_from(pt, from) & ~pos.pieces(); if (pt == KING) b &= ~PseudoAttacks[QUEEN][pos.square(~us)]; while (b) *moveList++ = make_move(from, pop_lsb(&b)); } return us == WHITE ? generate_all(pos, moveList, ~pos.pieces()) : generate_all(pos, moveList, ~pos.pieces()); } /// generate generates all pseudo-legal check evasions when the side /// to move is in check. Returns a pointer to the end of the move list. template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { assert(pos.checkers()); Color us = pos.side_to_move(); Square ksq = pos.square(us); Bitboard sliderAttacks = 0; Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN); // Find all the squares attacked by slider checkers. We will remove them from // the king evasions in order to skip known illegal moves, which avoids any // useless legality checks later on. while (sliders) { Square checksq = pop_lsb(&sliders); sliderAttacks |= LineBB[checksq][ksq] ^ checksq; } // Generate evasions for king, capture and non capture moves Bitboard b = pos.attacks_from(ksq) & ~pos.pieces(us) & ~sliderAttacks; while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); if (more_than_one(pos.checkers())) return moveList; // Double check, only a king move can save the day // Generate blocking evasions or captures of the checking piece Square checksq = lsb(pos.checkers()); Bitboard target = between_bb(checksq, ksq) | checksq; return us == WHITE ? generate_all(pos, moveList, target) : generate_all(pos, moveList, target); } /// generate generates all the legal moves in the given position template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { Color us = pos.side_to_move(); Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us); Square ksq = pos.square(us); ExtMove* cur = moveList; moveList = pos.checkers() ? generate(pos, moveList) : generate(pos, moveList); while (cur != moveList) if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT) && !pos.legal(*cur)) *cur = (--moveList)->move; else ++cur; return moveList; } stockfish-11.orig/src/material.h0000644000175000017500000000565013610452365015124 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MATERIAL_H_INCLUDED #define MATERIAL_H_INCLUDED #include "endgame.h" #include "misc.h" #include "position.h" #include "types.h" namespace Material { /// Material::Entry contains various information about a material configuration. /// It contains a material imbalance evaluation, a function pointer to a special /// endgame evaluation function (which in most cases is NULL, meaning that the /// standard evaluation function will be used), and scale factors. /// /// The scale factors are used to scale the evaluation score up or down. For /// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4, /// which will result in scores of absolute value less than one pawn. struct Entry { Score imbalance() const { return make_score(value, value); } Phase game_phase() const { return gamePhase; } 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 // 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 // for rook pawns and wrong-colored bishops. ScaleFactor scale_factor(const Position& pos, Color c) const { ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos) : SCALE_FACTOR_NONE; return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]); } Key key; const EndgameBase* evaluationFunction; const EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each // side (e.g. KPKP, KBPsK) int16_t value; uint8_t factor[COLOR_NB]; Phase gamePhase; }; typedef HashTable Table; Entry* probe(const Position& pos); } // namespace Material #endif // #ifndef MATERIAL_H_INCLUDED stockfish-11.orig/src/bitboard.h0000644000175000017500000002654113610452365015116 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED #include #include "types.h" namespace Bitbases { void init(); bool probe(Square wksq, Square wpsq, Square bksq, Color us); } namespace Bitboards { void init(); const std::string pretty(Bitboard b); } constexpr Bitboard AllSquares = ~Bitboard(0); constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; constexpr Bitboard FileABB = 0x0101010101010101ULL; constexpr Bitboard FileBBB = FileABB << 1; constexpr Bitboard FileCBB = FileABB << 2; constexpr Bitboard FileDBB = FileABB << 3; constexpr Bitboard FileEBB = FileABB << 4; constexpr Bitboard FileFBB = FileABB << 5; constexpr Bitboard FileGBB = FileABB << 6; constexpr Bitboard FileHBB = FileABB << 7; constexpr Bitboard Rank1BB = 0xFF; constexpr Bitboard Rank2BB = Rank1BB << (8 * 1); constexpr Bitboard Rank3BB = Rank1BB << (8 * 2); constexpr Bitboard Rank4BB = Rank1BB << (8 * 3); constexpr Bitboard Rank5BB = Rank1BB << (8 * 4); constexpr Bitboard Rank6BB = Rank1BB << (8 * 5); constexpr Bitboard Rank7BB = Rank1BB << (8 * 6); constexpr Bitboard Rank8BB = Rank1BB << (8 * 7); constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB; constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB; constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB); constexpr Bitboard KingFlank[FILE_NB] = { QueenSide ^ FileDBB, QueenSide, QueenSide, CenterFiles, CenterFiles, KingSide, KingSide, KingSide ^ FileEBB }; extern uint8_t PopCnt16[1 << 16]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern Bitboard SquareBB[SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; /// Magic holds all magic bitboards relevant data for a single square struct Magic { Bitboard mask; Bitboard magic; Bitboard* attacks; unsigned shift; // Compute the attack's index using the 'magic bitboards' approach unsigned index(Bitboard occupied) const { if (HasPext) return unsigned(pext(occupied, mask)); if (Is64Bit) return unsigned(((occupied & mask) * magic) >> shift); unsigned lo = unsigned(occupied) & unsigned(mask); unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32); return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift; } }; extern Magic RookMagics[SQUARE_NB]; extern Magic BishopMagics[SQUARE_NB]; inline Bitboard square_bb(Square s) { assert(s >= SQ_A1 && s <= SQ_H8); 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. inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); } inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); } inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); } inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); } inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); } inline Bitboard operator&(Square s, Bitboard b) { return b & s; } inline Bitboard operator|(Square s, Bitboard b) { return b | s; } inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; } inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | square_bb(s2); } constexpr bool more_than_one(Bitboard b) { return b & (b - 1); } inline bool opposite_colors(Square s1, Square s2) { return bool(DarkSquares & s1) != bool(DarkSquares & s2); } /// rank_bb() and file_bb() return a bitboard representing all the squares on /// the given file or rank. inline Bitboard rank_bb(Rank r) { return Rank1BB << (8 * r); } inline Bitboard rank_bb(Square s) { return rank_bb(rank_of(s)); } inline Bitboard file_bb(File f) { return FileABB << f; } inline Bitboard file_bb(Square s) { return file_bb(file_of(s)); } /// shift() moves a bitboard one step along direction D template constexpr Bitboard shift(Bitboard b) { return D == NORTH ? b << 8 : D == SOUTH ? b >> 8 : D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16 : D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1 : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9 : 0; } /// pawn_attacks_bb() returns the squares attacked by pawns of the given color /// from the squares in the given bitboard. template constexpr Bitboard pawn_attacks_bb(Bitboard b) { return C == WHITE ? shift(b) | shift(b) : shift(b) | shift(b); } /// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the /// given color from the squares in the given bitboard. template constexpr Bitboard pawn_double_attacks_bb(Bitboard b) { return C == WHITE ? shift(b) & shift(b) : shift(b) & shift(b); } /// adjacent_files_bb() returns a bitboard representing all the squares on the /// adjacent files of the given one. 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 /// If the given squares are not on a same file/rank/diagonal, return 0. inline Bitboard between_bb(Square s1, Square s2) { return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2))) ^(AllSquares << (s2 + !(s1 < s2)))); } /// forward_ranks_bb() returns a bitboard representing the squares on the ranks /// in front of the given one, from the point of view of the given color. For instance, /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. inline Bitboard forward_ranks_bb(Color c, Square s) { return c == WHITE ? ~Rank1BB << 8 * (rank_of(s) - RANK_1) : ~Rank8BB >> 8 * (RANK_8 - rank_of(s)); } /// forward_file_bb() returns a bitboard representing all the squares along the /// line in front of the given one, from the point of view of the given color. inline Bitboard forward_file_bb(Color c, Square s) { return forward_ranks_bb(c, s) & file_bb(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. inline Bitboard pawn_attack_span(Color c, Square s) { return forward_ranks_bb(c, s) & adjacent_files_bb(s); } /// passed_pawn_span() returns a bitboard which can be used to test if a pawn of /// 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)); } /// aligned() returns true if the squares s1, s2 and s3 are aligned either on a /// straight or on a diagonal line. inline bool aligned(Square s1, Square s2, Square s3) { return LineBB[s1][s2] & s3; } /// distance() functions return the distance between x and y, defined as the /// number of steps for a king in x to reach y. template inline int distance(Square x, Square y); template<> inline int distance(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); } template<> inline int distance(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); } template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } template constexpr const T& clamp(const T& v, const T& lo, const T& hi) { return v < lo ? lo : v > hi ? hi : v; } /// attacks_bb() returns a bitboard representing all the squares attacked by a /// piece of type Pt (bishop or rook) placed on 's'. template inline Bitboard attacks_bb(Square s, Bitboard occupied) { const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s]; return m.attacks[m.index(occupied)]; } inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) { assert(pt != PAWN); switch (pt) { case BISHOP: return attacks_bb(s, occupied); case ROOK : return attacks_bb< ROOK>(s, occupied); case QUEEN : return attacks_bb(s, occupied) | attacks_bb(s, occupied); default : return PseudoAttacks[pt][s]; } } /// popcount() counts the number of non-zero bits in a bitboard inline int popcount(Bitboard b) { #ifndef USE_POPCNT union { Bitboard bb; uint16_t u[4]; } v = { b }; return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]]; #elif defined(_MSC_VER) || defined(__INTEL_COMPILER) return (int)_mm_popcnt_u64(b); #else // Assumed gcc or compatible compiler return __builtin_popcountll(b); #endif } /// lsb() and msb() return the least/most significant bit in a non-zero bitboard #if defined(__GNUC__) // GCC, Clang, ICC inline Square lsb(Bitboard b) { assert(b); return Square(__builtin_ctzll(b)); } inline Square msb(Bitboard b) { assert(b); return Square(63 ^ __builtin_clzll(b)); } #elif defined(_MSC_VER) // MSVC #ifdef _WIN64 // MSVC, WIN64 inline Square lsb(Bitboard b) { assert(b); unsigned long idx; _BitScanForward64(&idx, b); return (Square) idx; } inline Square msb(Bitboard b) { assert(b); unsigned long idx; _BitScanReverse64(&idx, b); return (Square) idx; } #else // MSVC, WIN32 inline Square lsb(Bitboard b) { assert(b); unsigned long idx; if (b & 0xffffffff) { _BitScanForward(&idx, int32_t(b)); return Square(idx); } else { _BitScanForward(&idx, int32_t(b >> 32)); return Square(idx + 32); } } inline Square msb(Bitboard b) { assert(b); unsigned long idx; if (b >> 32) { _BitScanReverse(&idx, int32_t(b >> 32)); return Square(idx + 32); } else { _BitScanReverse(&idx, int32_t(b)); return Square(idx); } } #endif #else // Compiler is neither GCC nor MSVC compatible #error "Compiler not supported." #endif /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard inline Square pop_lsb(Bitboard* b) { const Square s = lsb(*b); *b &= *b - 1; return s; } /// frontmost_sq() returns the most advanced square for the given color inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } #endif // #ifndef BITBOARD_H_INCLUDED stockfish-11.orig/src/tt.h0000644000175000017500000000631213610452365013751 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TT_H_INCLUDED #define TT_H_INCLUDED #include "misc.h" #include "types.h" /// TTEntry struct is the 10 bytes transposition table entry, defined as below: /// /// key 16 bit /// move 16 bit /// value 16 bit /// eval value 16 bit /// generation 5 bit /// pv node 1 bit /// bound type 2 bit /// depth 8 bit struct TTEntry { Move move() const { return (Move )move16; } Value value() const { return (Value)value16; } Value eval() const { return (Value)eval16; } Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; } bool is_pv() const { return (bool)(genBound8 & 0x4); } Bound bound() const { return (Bound)(genBound8 & 0x3); } void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev); private: friend class TranspositionTable; uint16_t key16; uint16_t move16; int16_t value16; int16_t eval16; uint8_t genBound8; uint8_t depth8; }; /// A TranspositionTable consists of a power of 2 number of clusters and each /// cluster consists of ClusterSize number of TTEntry. Each non-empty entry /// contains information of exactly one position. The size of a cluster should /// divide the size of a cache line size, to ensure that clusters never cross /// cache lines. This ensures best cache performance, as the cacheline is /// prefetched, as soon as possible. class TranspositionTable { static constexpr int CacheLineSize = 64; static constexpr int ClusterSize = 3; struct Cluster { TTEntry entry[ClusterSize]; char padding[2]; // Align to a divisor of the cache line size }; static_assert(CacheLineSize % sizeof(Cluster) == 0, "Cluster size incorrect"); public: ~TranspositionTable() { free(mem); } void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound TTEntry* probe(const Key key, bool& found) const; int hashfull() const; 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]; } private: friend struct TTEntry; size_t clusterCount; Cluster* table; void* mem; uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 }; extern TranspositionTable TT; #endif // #ifndef TT_H_INCLUDED stockfish-11.orig/src/position.cpp0000644000175000017500000011765613610452365015537 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include // For offsetof() #include // For std::memset, std::memcmp #include #include #include "bitboard.h" #include "misc.h" #include "movegen.h" #include "position.h" #include "thread.h" #include "tt.h" #include "uci.h" #include "syzygy/tbprobe.h" using std::string; namespace Zobrist { Key psq[PIECE_NB][SQUARE_NB]; Key enpassant[FILE_NB]; Key castling[CASTLING_RIGHT_NB]; Key side, noPawns; } namespace { const string PieceToChar(" PNBRQK pnbrqk"); constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; } // namespace /// operator<<(Position) returns an ASCII representation of the position std::ostream& operator<<(std::ostream& os, const Position& pos) { os << "\n +---+---+---+---+---+---+---+---+\n"; for (Rank r = RANK_8; r >= RANK_1; --r) { for (File f = FILE_A; f <= FILE_H; ++f) os << " | " << PieceToChar[pos.piece_on(make_square(f, r))]; os << " |\n +---+---+---+---+---+---+---+---+\n"; } os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase << std::setfill('0') << std::setw(16) << pos.key() << std::setfill(' ') << std::dec << "\nCheckers: "; for (Bitboard b = pos.checkers(); b; ) os << UCI::square(pop_lsb(&b)) << " "; if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) { StateInfo st; Position p; p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread()); Tablebases::ProbeState s1, s2; Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1); int dtz = Tablebases::probe_dtz(p, &s2); os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")" << "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")"; } return os; } // Marcel van Kervinck's cuckoo algorithm for fast detection of "upcoming repetition" // situations. Description of the algorithm in the following paper: // https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf // First and second hash functions for indexing the cuckoo tables inline int H1(Key h) { return h & 0x1fff; } inline int H2(Key h) { return (h >> 16) & 0x1fff; } // Cuckoo tables with Zobrist hashes of valid reversible moves, and the moves themselves Key cuckoo[8192]; Move cuckooMove[8192]; /// Position::init() initializes at startup the various arrays used to compute /// hash keys. void Position::init() { PRNG rng(1070372); for (Piece pc : Pieces) for (Square s = SQ_A1; s <= SQ_H8; ++s) Zobrist::psq[pc][s] = rng.rand(); for (File f = FILE_A; f <= FILE_H; ++f) Zobrist::enpassant[f] = rng.rand(); for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) { Zobrist::castling[cr] = 0; Bitboard b = cr; while (b) { Key k = Zobrist::castling[1ULL << pop_lsb(&b)]; Zobrist::castling[cr] ^= k ? k : rng.rand(); } } Zobrist::side = rng.rand(); Zobrist::noPawns = rng.rand(); // Prepare the cuckoo tables std::memset(cuckoo, 0, sizeof(cuckoo)); std::memset(cuckooMove, 0, sizeof(cuckooMove)); int count = 0; for (Piece pc : Pieces) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) if (PseudoAttacks[type_of(pc)][s1] & s2) { Move move = make_move(s1, s2); Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side; int i = H1(key); while (true) { std::swap(cuckoo[i], key); std::swap(cuckooMove[i], move); if (move == MOVE_NONE) // Arrived at empty slot? break; i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot } count++; } assert(count == 3668); } /// Position::set() initializes the position object with the given FEN string. /// This function is not very robust - make sure that input FENs are correct, /// this is assumed to be the responsibility of the GUI. Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) { /* A FEN string defines a particular position using only the ASCII character set. A FEN string contains six fields separated by a space. The fields are: 1) Piece placement (from white's perspective). Each rank is described, starting with rank 8 and ending with rank 1. Within each rank, the contents of each square are described from file A through file H. Following the Standard Algebraic Notation (SAN), each piece is identified by a single letter taken from the standard English names. White pieces are designated using upper-case letters ("PNBRQK") whilst Black uses lowercase ("pnbrqk"). Blank squares are noted using digits 1 through 8 (the number of blank squares), and "/" separates ranks. 2) Active color. "w" means white moves next, "b" means black. 3) Castling availability. If neither side can castle, this is "-". Otherwise, this has one or more letters: "K" (White can castle kingside), "Q" (White can castle queenside), "k" (Black can castle kingside), and/or "q" (Black can castle queenside). 4) En passant target square (in algebraic notation). If there's no en passant target square, this is "-". If a pawn has just made a 2-square move, this is the position "behind" the pawn. This is recorded only if there is a pawn in position to make an en passant capture, and if there really is a pawn that might have advanced two squares. 5) Halfmove clock. This is the number of halfmoves since the last pawn advance or capture. This is used to determine if a draw can be claimed under the fifty-move rule. 6) Fullmove number. The number of the full move. It starts at 1, and is incremented after Black's move. */ unsigned char col, row, token; size_t idx; Square sq = SQ_A8; std::istringstream ss(fenStr); std::memset(this, 0, sizeof(Position)); std::memset(si, 0, sizeof(StateInfo)); std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); st = si; ss >> std::noskipws; // 1. Piece placement while ((ss >> token) && !isspace(token)) { if (isdigit(token)) sq += (token - '0') * EAST; // Advance the given number of files else if (token == '/') sq += 2 * SOUTH; else if ((idx = PieceToChar.find(token)) != string::npos) { put_piece(Piece(idx), sq); ++sq; } } // 2. Active color ss >> token; sideToMove = (token == 'w' ? WHITE : BLACK); ss >> token; // 3. Castling availability. Compatible with 3 standards: Normal FEN standard, // Shredder-FEN that uses the letters of the columns on which the rooks began // the game instead of KQkq and also X-FEN standard that, in case of Chess960, // if an inner rook is associated with the castling right, the castling tag is // replaced by the file letter of the involved rook, as for the Shredder-FEN. while ((ss >> token) && !isspace(token)) { Square rsq; Color c = islower(token) ? BLACK : WHITE; Piece rook = make_piece(c, ROOK); token = char(toupper(token)); if (token == 'K') for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {} else if (token == 'Q') for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {} else if (token >= 'A' && token <= 'H') rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); else continue; set_castling_right(c, rsq); } // 4. En passant square. Ignore if no pawn capture is possible if ( ((ss >> col) && (col >= 'a' && col <= 'h')) && ((ss >> row) && (row == '3' || row == '6'))) { st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)) || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))) st->epSquare = SQ_NONE; } else st->epSquare = SQ_NONE; // 5-6. Halfmove clock and fullmove number ss >> std::skipws >> st->rule50 >> gamePly; // Convert from fullmove starting from 1 to gamePly starting from 0, // handle also common incorrect FEN with fullmove = 0. gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK); chess960 = isChess960; thisThread = th; set_state(st); assert(pos_is_ok()); return *this; } /// Position::set_castling_right() is a helper function used to set castling /// rights given the corresponding color and the rook starting square. void Position::set_castling_right(Color c, Square rfrom) { Square kfrom = square(c); CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE); st->castlingRights |= cr; castlingRightsMask[kfrom] |= cr; castlingRightsMask[rfrom] |= cr; castlingRookSquare[cr] = rfrom; Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1); Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1); castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto) & ~(square_bb(kfrom) | rfrom); } /// Position::set_check_info() sets king attacks to detect if a move gives check void Position::set_check_info(StateInfo* si) const { si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), si->pinners[BLACK]); si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), si->pinners[WHITE]); Square ksq = square(~sideToMove); si->checkSquares[PAWN] = attacks_from(ksq, ~sideToMove); si->checkSquares[KNIGHT] = attacks_from(ksq); si->checkSquares[BISHOP] = attacks_from(ksq); si->checkSquares[ROOK] = attacks_from(ksq); si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK]; si->checkSquares[KING] = 0; } /// Position::set_state() computes the hash keys of the position, and other /// data that once computed is updated incrementally as moves are made. /// The function is only used when a new position is set up, and to verify /// the correctness of the StateInfo data when running in debug mode. void Position::set_state(StateInfo* si) const { si->key = si->materialKey = 0; si->pawnKey = Zobrist::noPawns; si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; si->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); set_check_info(si); for (Bitboard b = pieces(); b; ) { Square s = pop_lsb(&b); Piece pc = piece_on(s); si->key ^= Zobrist::psq[pc][s]; if (type_of(pc) == PAWN) si->pawnKey ^= Zobrist::psq[pc][s]; else if (type_of(pc) != KING) si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc]; } if (si->epSquare != SQ_NONE) si->key ^= Zobrist::enpassant[file_of(si->epSquare)]; if (sideToMove == BLACK) si->key ^= Zobrist::side; si->key ^= Zobrist::castling[si->castlingRights]; for (Piece pc : Pieces) for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) si->materialKey ^= Zobrist::psq[pc][cnt]; } /// Position::set() is an overload to initialize the position object with /// the given endgame code string like "KBPKN". It is mainly a helper to /// get the material key out of an endgame code. Position& Position::set(const string& code, Color c, StateInfo* si) { assert(code.length() > 0 && code.length() < 8); assert(code[0] == 'K'); string sides[] = { code.substr(code.find('K', 1)), // Weak code.substr(0, code.find('K', 1)) }; // Strong std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/" + sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10"; return set(fenStr, false, si, nullptr); } /// Position::fen() returns a FEN representation of the position. In case of /// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. const string Position::fen() const { int emptyCnt; std::ostringstream ss; for (Rank r = RANK_8; r >= RANK_1; --r) { for (File f = FILE_A; f <= FILE_H; ++f) { for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f) ++emptyCnt; if (emptyCnt) ss << emptyCnt; if (f <= FILE_H) ss << PieceToChar[piece_on(make_square(f, r))]; } if (r > RANK_1) ss << '/'; } ss << (sideToMove == WHITE ? " w " : " b "); if (can_castle(WHITE_OO)) ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO ))) : 'K'); if (can_castle(WHITE_OOO)) ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q'); if (can_castle(BLACK_OO)) ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO ))) : 'k'); if (can_castle(BLACK_OOO)) ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q'); if (!can_castle(ANY_CASTLING)) ss << '-'; ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; return ss.str(); } /// Position::slider_blockers() returns a bitboard of all the pieces (both colors) /// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a /// slider if removing that piece from the board would result in a position where /// square 's' is attacked. For example, a king-attack blocking piece can be either /// a pinned or a discovered check piece, according if its color is the opposite /// or the same of the color of the slider. Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const { Bitboard blockers = 0; pinners = 0; // Snipers are sliders that attack 's' when a piece and other snipers are removed Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK)) | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; Bitboard occupancy = pieces() ^ snipers; while (snipers) { Square sniperSq = pop_lsb(&snipers); Bitboard b = between_bb(s, sniperSq) & occupancy; if (b && !more_than_one(b)) { blockers |= b; if (b & pieces(color_of(piece_on(s)))) pinners |= sniperSq; } } return blockers; } /// Position::attackers_to() computes a bitboard of all pieces which attack a /// given square. Slider attacks use the occupied bitboard to indicate occupancy. Bitboard Position::attackers_to(Square s, Bitboard occupied) const { return (attacks_from(s, BLACK) & pieces(WHITE, PAWN)) | (attacks_from(s, WHITE) & pieces(BLACK, PAWN)) | (attacks_from(s) & pieces(KNIGHT)) | (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN)) | (attacks_bb(s, occupied) & pieces(BISHOP, QUEEN)) | (attacks_from(s) & pieces(KING)); } /// Position::legal() tests whether a pseudo-legal move is legal bool Position::legal(Move m) const { assert(is_ok(m)); Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); // En passant captures are a tricky special case. Because they are rather // uncommon, we do it simply by testing whether the king is attacked after // the move is made. if (type_of(m) == ENPASSANT) { Square ksq = square(us); Square capsq = to - pawn_push(us); Bitboard occupied = (pieces() ^ from ^ capsq) | to; assert(to == ep_square()); assert(moved_piece(m) == make_piece(us, PAWN)); assert(piece_on(capsq) == make_piece(~us, PAWN)); assert(piece_on(to) == NO_PIECE); return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK)) && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); } // Castling moves generation does not check if the castling path is clear of // enemy attacks, it is delayed at a later time: now! if (type_of(m) == CASTLING) { // After castling, the rook and king final positions are the same in // Chess960 as they would be in standard chess. to = relative_square(us, to > from ? SQ_G1 : SQ_C1); Direction step = to > from ? WEST : EAST; for (Square s = to; s != from; s += step) if (attackers_to(s) & pieces(~us)) return false; // In case of Chess960, verify that when moving the castling rook we do // not discover some hidden checker. // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. return !chess960 || !(attacks_bb(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN)); } // If the moving piece is a king, check whether the destination square is // attacked by the opponent. if (type_of(piece_on(from)) == KING) return !(attackers_to(to) & pieces(~us)); // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. return !(blockers_for_king(us) & from) || aligned(from, to, square(us)); } /// Position::pseudo_legal() takes a random move and tests whether the move is /// pseudo legal. It is used to validate moves from TT that can be corrupted /// due to SMP concurrent access or hash position key aliasing. bool Position::pseudo_legal(const Move m) const { Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); Piece pc = moved_piece(m); // Use a slower but simpler function for uncommon cases if (type_of(m) != NORMAL) return MoveList(*this).contains(m); // Is not a promotion, so promotion piece must be empty if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE) return false; // If the 'from' square is not occupied by a piece belonging to the side to // move, the move is obviously not legal. if (pc == NO_PIECE || color_of(pc) != us) return false; // The destination square cannot be occupied by a friendly piece if (pieces(us) & to) return false; // Handle the special case of a pawn move if (type_of(pc) == PAWN) { // We have already handled promotion moves, so destination // cannot be on the 8th/1st rank. if ((Rank8BB | Rank1BB) & to) return false; if ( !(attacks_from(from, us) & pieces(~us) & to) // Not a capture && !((from + pawn_push(us) == to) && empty(to)) // Not a single push && !( (from + 2 * pawn_push(us) == to) // Not a double push && (rank_of(from) == relative_rank(us, RANK_2)) && empty(to) && empty(to - pawn_push(us)))) return false; } else if (!(attacks_from(type_of(pc), from) & to)) return false; // Evasions generator already takes care to avoid some kind of illegal moves // and legal() relies on this. We therefore have to take care that the same // kind of moves are filtered out here. if (checkers()) { if (type_of(pc) != KING) { // Double check? In this case a king move is required if (more_than_one(checkers())) return false; // Our move must be a blocking evasion or a capture of the checking piece if (!((between_bb(lsb(checkers()), square(us)) | checkers()) & to)) return false; } // In case of king moves under check we have to remove king so as to catch // invalid moves like b1a1 when opposite queen is on c1. else if (attackers_to(to, pieces() ^ from) & pieces(~us)) return false; } return true; } /// Position::gives_check() tests whether a pseudo-legal move gives a check bool Position::gives_check(Move m) const { assert(is_ok(m)); assert(color_of(moved_piece(m)) == sideToMove); Square from = from_sq(m); Square to = to_sq(m); // Is there a direct check? if (st->checkSquares[type_of(piece_on(from))] & to) return true; // Is there a discovered check? if ( (st->blockersForKing[~sideToMove] & from) && !aligned(from, to, square(~sideToMove))) return true; switch (type_of(m)) { case NORMAL: return false; case PROMOTION: return attacks_bb(promotion_type(m), to, pieces() ^ from) & square(~sideToMove); // En passant capture with check? We have already handled the case // of direct checks and ordinary discovered check, so the only case we // need to handle is the unusual case of a discovered check through // the captured pawn. case ENPASSANT: { Square capsq = make_square(file_of(to), rank_of(from)); Bitboard b = (pieces() ^ from ^ capsq) | to; return (attacks_bb< ROOK>(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) | (attacks_bb(square(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP)); } case CASTLING: { Square kfrom = from; Square rfrom = to; // Castling is encoded as 'King captures the rook' Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1); Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1); return (PseudoAttacks[ROOK][rto] & square(~sideToMove)) && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square(~sideToMove)); } default: assert(false); return false; } } /// Position::do_move() makes a move, and saves all information necessary /// to a StateInfo object. The move is assumed to be legal. Pseudo-legal /// moves should be filtered out before this function is called. void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { assert(is_ok(m)); assert(&newSt != st); thisThread->nodes.fetch_add(1, std::memory_order_relaxed); Key k = st->key ^ Zobrist::side; // Copy some fields of the old state to our new StateInfo object except the // ones which are going to be recalculated from scratch anyway and then switch // our state pointer to point to the new (ready to be updated) state. std::memcpy(&newSt, st, offsetof(StateInfo, key)); newSt.previous = st; st = &newSt; // Increment ply counters. In particular, rule50 will be reset to zero later on // in case of a capture or a pawn move. ++gamePly; ++st->rule50; ++st->pliesFromNull; Color us = sideToMove; Color them = ~us; Square from = from_sq(m); Square to = to_sq(m); Piece pc = piece_on(from); Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); assert(color_of(pc) == us); assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); assert(type_of(captured) != KING); if (type_of(m) == CASTLING) { assert(pc == make_piece(us, KING)); assert(captured == make_piece(us, ROOK)); Square rfrom, rto; do_castling(us, from, to, rfrom, rto); k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; captured = NO_PIECE; } if (captured) { Square capsq = to; // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. if (type_of(captured) == PAWN) { if (type_of(m) == ENPASSANT) { capsq -= pawn_push(us); assert(pc == make_piece(us, PAWN)); assert(to == st->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(to) == NO_PIECE); assert(piece_on(capsq) == make_piece(them, PAWN)); board[capsq] = NO_PIECE; // Not done by remove_piece() } st->pawnKey ^= Zobrist::psq[captured][capsq]; } else st->nonPawnMaterial[them] -= PieceValue[MG][captured]; // Update board and piece lists remove_piece(captured, capsq); // Update material hash key and prefetch access to materialTable k ^= Zobrist::psq[captured][capsq]; st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; prefetch(thisThread->materialTable[st->materialKey]); // Reset rule 50 counter st->rule50 = 0; } // Update hash key k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; // Reset en passant square if (st->epSquare != SQ_NONE) { k ^= Zobrist::enpassant[file_of(st->epSquare)]; st->epSquare = SQ_NONE; } // Update castling rights if needed if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to])) { int cr = castlingRightsMask[from] | castlingRightsMask[to]; k ^= Zobrist::castling[st->castlingRights & cr]; st->castlingRights &= ~cr; } // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) move_piece(pc, from, to); // If the moving piece is a pawn do some special extra work if (type_of(pc) == PAWN) { // Set en-passant square if the moved pawn can be captured if ( (int(to) ^ int(from)) == 16 && (attacks_from(to - pawn_push(us), us) & pieces(them, PAWN))) { st->epSquare = to - pawn_push(us); k ^= Zobrist::enpassant[file_of(st->epSquare)]; } else if (type_of(m) == PROMOTION) { Piece promotion = make_piece(us, promotion_type(m)); assert(relative_rank(us, to) == RANK_8); assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); remove_piece(pc, to); put_piece(promotion, to); // Update hash keys k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; st->pawnKey ^= Zobrist::psq[pc][to]; st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1] ^ Zobrist::psq[pc][pieceCount[pc]]; // Update material st->nonPawnMaterial[us] += PieceValue[MG][promotion]; } // Update pawn hash key st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; // Reset rule 50 draw counter st->rule50 = 0; } // Set capture piece st->capturedPiece = captured; // Update the key with the final value st->key = k; // Calculate checkers bitboard (if move gives check) st->checkersBB = givesCheck ? attackers_to(square(them)) & pieces(us) : 0; sideToMove = ~sideToMove; // Update king attacks used for fast check detection set_check_info(st); // Calculate the repetition info. It is the ply distance from the previous // occurrence of the same position, negative in the 3-fold case, or zero // if the position was not repeated. st->repetition = 0; int end = std::min(st->rule50, st->pliesFromNull); if (end >= 4) { StateInfo* stp = st->previous->previous; for (int i = 4; i <= end; i += 2) { stp = stp->previous->previous; if (stp->key == st->key) { st->repetition = stp->repetition ? -i : i; break; } } } assert(pos_is_ok()); } /// Position::undo_move() unmakes a move. When it returns, the position should /// be restored to exactly the same state as before the move was made. void Position::undo_move(Move m) { assert(is_ok(m)); sideToMove = ~sideToMove; Color us = sideToMove; Square from = from_sq(m); Square to = to_sq(m); Piece pc = piece_on(to); assert(empty(from) || type_of(m) == CASTLING); assert(type_of(st->capturedPiece) != KING); if (type_of(m) == PROMOTION) { assert(relative_rank(us, to) == RANK_8); assert(type_of(pc) == promotion_type(m)); assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); remove_piece(pc, to); pc = make_piece(us, PAWN); put_piece(pc, to); } if (type_of(m) == CASTLING) { Square rfrom, rto; do_castling(us, from, to, rfrom, rto); } else { move_piece(pc, to, from); // Put the piece back at the source square if (st->capturedPiece) { Square capsq = to; if (type_of(m) == ENPASSANT) { capsq -= pawn_push(us); assert(type_of(pc) == PAWN); assert(to == st->previous->epSquare); assert(relative_rank(us, to) == RANK_6); assert(piece_on(capsq) == NO_PIECE); assert(st->capturedPiece == make_piece(~us, PAWN)); } put_piece(st->capturedPiece, capsq); // Restore the captured piece } } // Finally point our state pointer back to the previous state st = st->previous; --gamePly; assert(pos_is_ok()); } /// Position::do_castling() is a helper used to do/undo a castling move. This /// is a bit tricky in Chess960 where from/to squares can overlap. template void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) { bool kingSide = to > from; rfrom = to; // Castling is encoded as "king captures friendly rook" rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); // Remove both pieces first since squares could overlap in Chess960 remove_piece(make_piece(us, KING), Do ? from : to); remove_piece(make_piece(us, ROOK), Do ? rfrom : rto); board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us put_piece(make_piece(us, KING), Do ? to : from); put_piece(make_piece(us, ROOK), Do ? rto : rfrom); } /// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips /// the side to move without executing any move on the board. void Position::do_null_move(StateInfo& newSt) { assert(!checkers()); assert(&newSt != st); std::memcpy(&newSt, st, sizeof(StateInfo)); newSt.previous = st; st = &newSt; if (st->epSquare != SQ_NONE) { st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; st->epSquare = SQ_NONE; } st->key ^= Zobrist::side; prefetch(TT.first_entry(st->key)); ++st->rule50; st->pliesFromNull = 0; sideToMove = ~sideToMove; set_check_info(st); st->repetition = 0; assert(pos_is_ok()); } void Position::undo_null_move() { assert(!checkers()); st = st->previous; sideToMove = ~sideToMove; } /// Position::key_after() computes the new hash key after the given move. Needed /// for speculative prefetch. It doesn't recognize special moves like castling, /// en-passant and promotions. Key Position::key_after(Move m) const { Square from = from_sq(m); Square to = to_sq(m); Piece pc = piece_on(from); Piece captured = piece_on(to); Key k = st->key ^ Zobrist::side; if (captured) k ^= Zobrist::psq[captured][to]; return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; } /// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the /// SEE value of move is greater or equal to the given threshold. We'll use an /// algorithm similar to alpha-beta pruning with a null window. bool Position::see_ge(Move m, Value threshold) const { assert(is_ok(m)); // Only deal with normal moves, assume others pass a simple see if (type_of(m) != NORMAL) return VALUE_ZERO >= threshold; Square from = from_sq(m), to = to_sq(m); int swap = PieceValue[MG][piece_on(to)] - threshold; if (swap < 0) return false; swap = PieceValue[MG][piece_on(from)] - swap; if (swap <= 0) return true; Bitboard occupied = pieces() ^ from ^ to; Color stm = color_of(piece_on(from)); Bitboard attackers = attackers_to(to, occupied); Bitboard stmAttackers, bb; int res = 1; while (true) { stm = ~stm; attackers &= occupied; // If stm has no more attackers then give up: stm loses if (!(stmAttackers = attackers & pieces(stm))) break; // Don't allow pinned pieces to attack (except the king) as long as // there are pinners on their original square. if (st->pinners[~stm] & occupied) stmAttackers &= ~st->blockersForKing[stm]; if (!stmAttackers) break; res ^= 1; // Locate and remove the next least valuable attacker, and add to // the bitboard 'attackers' any X-ray attackers behind it. if ((bb = stmAttackers & pieces(PAWN))) { if ((swap = PawnValueMg - swap) < res) break; occupied ^= lsb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(KNIGHT))) { if ((swap = KnightValueMg - swap) < res) break; occupied ^= lsb(bb); } else if ((bb = stmAttackers & pieces(BISHOP))) { if ((swap = BishopValueMg - swap) < res) break; occupied ^= lsb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(ROOK))) { if ((swap = RookValueMg - swap) < res) break; occupied ^= lsb(bb); attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); } else if ((bb = stmAttackers & pieces(QUEEN))) { if ((swap = QueenValueMg - swap) < res) break; occupied ^= lsb(bb); attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) | (attacks_bb(to, occupied) & pieces(ROOK , QUEEN)); } else // KING // If we "capture" with the king but opponent still has attackers, // reverse the result. return (attackers & ~pieces(stm)) ? res ^ 1 : res; } return bool(res); } /// Position::is_draw() tests whether the position is drawn by 50-move rule /// or by repetition. It does not detect stalemates. bool Position::is_draw(int ply) const { if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) return true; // Return a draw score if a position repeats once earlier but strictly // after the root, or repeats twice before or at the root. if (st->repetition && st->repetition < ply) return true; return false; } // Position::has_repeated() tests whether there has been at least one repetition // of positions since the last capture or pawn move. bool Position::has_repeated() const { StateInfo* stc = st; int end = std::min(st->rule50, st->pliesFromNull); while (end-- >= 4) { if (stc->repetition) return true; stc = stc->previous; } return false; } /// Position::has_game_cycle() tests if the position has a move which draws by repetition, /// or an earlier position has a move that directly reaches the current position. bool Position::has_game_cycle(int ply) const { int j; int end = std::min(st->rule50, st->pliesFromNull); if (end < 3) return false; Key originalKey = st->key; StateInfo* stp = st->previous; for (int i = 3; i <= end; i += 2) { stp = stp->previous->previous; Key moveKey = originalKey ^ stp->key; if ( (j = H1(moveKey), cuckoo[j] == moveKey) || (j = H2(moveKey), cuckoo[j] == moveKey)) { Move move = cuckooMove[j]; Square s1 = from_sq(move); Square s2 = to_sq(move); if (!(between_bb(s1, s2) & pieces())) { if (ply > i) return true; // For nodes before or at the root, check that the move is a // repetition rather than a move to the current position. // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in // the same location, so we have to select which square to check. if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) continue; // For repetitions before or at the root, require one more if (stp->repetition) return true; } } } return false; } /// Position::flip() flips position with the white and black sides reversed. This /// is only useful for debugging e.g. for finding evaluation symmetry bugs. void Position::flip() { string f, token; std::stringstream ss(fen()); for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement { std::getline(ss, token, r > RANK_1 ? '/' : ' '); f.insert(0, token + (f.empty() ? " " : "/")); } ss >> token; // Active color f += (token == "w" ? "B " : "W "); // Will be lowercased later ss >> token; // Castling availability f += token + " "; std::transform(f.begin(), f.end(), f.begin(), [](char c) { return char(islower(c) ? toupper(c) : tolower(c)); }); ss >> token; // En passant square f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3")); std::getline(ss, token); // Half and full moves f += token; set(f, is_chess960(), st, this_thread()); assert(pos_is_ok()); } /// Position::pos_is_ok() performs some consistency checks for the /// position object and raises an asserts if something wrong is detected. /// This is meant to be helpful when debugging. bool Position::pos_is_ok() const { constexpr bool Fast = true; // Quick (default) or full check? if ( (sideToMove != WHITE && sideToMove != BLACK) || piece_on(square(WHITE)) != W_KING || piece_on(square(BLACK)) != B_KING || ( ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6)) assert(0 && "pos_is_ok: Default"); if (Fast) return true; if ( pieceCount[W_KING] != 1 || pieceCount[B_KING] != 1 || attackers_to(square(~sideToMove)) & pieces(sideToMove)) assert(0 && "pos_is_ok: Kings"); if ( (pieces(PAWN) & (Rank1BB | Rank8BB)) || pieceCount[W_PAWN] > 8 || pieceCount[B_PAWN] > 8) assert(0 && "pos_is_ok: Pawns"); if ( (pieces(WHITE) & pieces(BLACK)) || (pieces(WHITE) | pieces(BLACK)) != pieces() || popcount(pieces(WHITE)) > 16 || popcount(pieces(BLACK)) > 16) assert(0 && "pos_is_ok: Bitboards"); for (PieceType p1 = PAWN; p1 <= KING; ++p1) for (PieceType p2 = PAWN; p2 <= KING; ++p2) if (p1 != p2 && (pieces(p1) & pieces(p2))) assert(0 && "pos_is_ok: Bitboards"); StateInfo si = *st; set_state(&si); if (std::memcmp(&si, st, sizeof(StateInfo))) assert(0 && "pos_is_ok: State"); for (Piece pc : Pieces) { if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))) || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc)) assert(0 && "pos_is_ok: Pieces"); for (int i = 0; i < pieceCount[pc]; ++i) if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i) assert(0 && "pos_is_ok: Index"); } for (Color c : { WHITE, BLACK }) for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE}) { if (!can_castle(cr)) continue; if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK) || castlingRightsMask[castlingRookSquare[cr]] != cr || (castlingRightsMask[square(c)] & cr) != cr) assert(0 && "pos_is_ok: Castling"); } return true; } stockfish-11.orig/src/endgame.cpp0000644000175000017500000007332513610452365015265 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "bitboard.h" #include "endgame.h" #include "movegen.h" using std::string; namespace { // Table used to drive the king towards the edge of the board // in KX vs K and KQ vs KR endgames. constexpr int PushToEdges[SQUARE_NB] = { 100, 90, 80, 70, 70, 80, 90, 100, 90, 70, 60, 50, 50, 60, 70, 90, 80, 60, 40, 30, 30, 40, 60, 80, 70, 50, 30, 20, 20, 30, 50, 70, 70, 50, 30, 20, 20, 30, 50, 70, 80, 60, 40, 30, 30, 40, 60, 80, 90, 70, 60, 50, 50, 60, 70, 90, 100, 90, 80, 70, 70, 80, 90, 100 }; // Table used to drive the king towards a corner square of the // right color in KBN vs K endgames. constexpr int PushToCorners[SQUARE_NB] = { 6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160, 6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480, 5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800, 5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120, 5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440, 4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760, 4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080, 4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400 }; // Tables used to drive a piece towards or away from another piece constexpr int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; constexpr int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 }; // Pawn Rank based scaling factors used in KRPPKRP endgame constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 }; #ifndef NDEBUG bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) { return pos.non_pawn_material(c) == npm && pos.count(c) == pawnsCnt; } #endif // Map the square as if strongSide is white and strongSide's only pawn // is on the left half of the board. Square normalize(const Position& pos, Color strongSide, Square sq) { assert(pos.count(strongSide) == 1); if (file_of(pos.square(strongSide)) >= FILE_E) sq = Square(int(sq) ^ 7); // Mirror SQ_H1 -> SQ_A1 return strongSide == WHITE ? sq : ~sq; } } // namespace namespace Endgames { std::pair, Map> maps; void init() { add("KPK"); add("KNNK"); add("KBNK"); add("KRKP"); add("KRKB"); add("KRKN"); add("KQKP"); add("KQKR"); add("KNNKP"); add("KNPK"); add("KNPKB"); add("KRPKR"); add("KRPKB"); add("KBPKB"); add("KBPKN"); add("KBPPKB"); add("KRPPKRP"); } } /// Mate with KX vs K. This function is used to evaluate positions with /// king and plenty of material vs a lone king. It simply gives the /// attacking side a bonus for driving the defending king towards the edge /// of the board, and for keeping the distance between the two kings small. template<> Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(!pos.checkers()); // Eval is never called when in check // Stalemate detection with lone king if (pos.side_to_move() == weakSide && !MoveList(pos).size()) return VALUE_DRAW; Square winnerKSq = pos.square(strongSide); Square loserKSq = pos.square(weakSide); Value result = pos.non_pawn_material(strongSide) + pos.count(strongSide) * PawnValueEg + PushToEdges[loserKSq] + PushClose[distance(winnerKSq, loserKSq)]; if ( pos.count(strongSide) || pos.count(strongSide) ||(pos.count(strongSide) && pos.count(strongSide)) || ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares) && (pos.pieces(strongSide, BISHOP) & DarkSquares))) result = std::min(result + VALUE_KNOWN_WIN, VALUE_MATE_IN_MAX_PLY - 1); return strongSide == pos.side_to_move() ? result : -result; } /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the /// defending king towards a corner square that our bishop attacks. template<> 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); // If our bishop does not attack A1/H8, we flip the enemy king square // to drive to opposite corners (A8/H1). Value result = VALUE_KNOWN_WIN + PushClose[distance(winnerKSq, loserKSq)] + PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq]; assert(abs(result) < VALUE_MATE_IN_MAX_PLY); return strongSide == pos.side_to_move() ? result : -result; } /// KP vs K. This endgame is evaluated with the help of a bitbase template<> Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); 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)); Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; if (!Bitbases::probe(wksq, psq, bksq, us)) return VALUE_DRAW; Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); return strongSide == pos.side_to_move() ? result : -result; } /// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without /// a bitbase. The function below returns drawish scores when the pawn is /// far advanced with support of the king, while the attacking king is far /// away. template<> 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); 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 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); // 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 result = Value(200) - 8 * ( distance(wksq, psq + SOUTH) - distance(bksq, psq + SOUTH) - distance(psq, queeningSq)); return strongSide == pos.side_to_move() ? result : -result; } /// KR vs KB. This is very simple, and always returns drawish scores. The /// score is slightly bigger when the defending king is close to the edge. template<> Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); Value result = Value(PushToEdges[pos.square(weakSide)]); return strongSide == pos.side_to_move() ? result : -result; } /// KR vs KN. The attacking side has slightly better winning chances than /// in KR vs KB, particularly if the king and the knight are far apart. template<> 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(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]); return strongSide == pos.side_to_move() ? result : -result; } /// KQ vs KP. In general, this is a win for the stronger side, but there are a /// few important exceptions. A pawn on 7th rank and on the A,C,F or H files /// with a king positioned next to it can be a draw, so in that case, we only /// use the distance between the kings. template<> 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); Value result = Value(PushClose[distance(winnerKSq, loserKSq)]); if ( relative_rank(weakSide, pawnSq) != RANK_7 || distance(loserKSq, pawnSq) != 1 || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) result += QueenValueEg - PawnValueEg; return strongSide == pos.side_to_move() ? result : -result; } /// 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. template<> 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); Value result = QueenValueEg - RookValueEg + PushToEdges[loserKSq] + PushClose[distance(winnerKSq, loserKSq)]; return strongSide == pos.side_to_move() ? result : -result; } /// KNN vs KP. Simply push the opposing king to the corner template<> Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); Value result = 2 * KnightValueEg - PawnValueEg + PushToEdges[pos.square(weakSide)]; return strongSide == pos.side_to_move() ? result : -result; } /// Some cases of trivial draws template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } /// KB and one or more pawns vs K. It checks for draws with rook pawns and /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling /// will be used. template<> ScaleFactor Endgame::operator()(const Position& pos) const { assert(pos.non_pawn_material(strongSide) == BishopValueMg); assert(pos.count(strongSide) >= 1); // No assertions about the material of weakSide, because we want draws to // be detected even when the weaker side has some pawns. Bitboard pawns = pos.pieces(strongSide, PAWN); File pawnsFile = file_of(lsb(pawns)); // All pawns are on a single rook file? if ( (pawnsFile == FILE_A || pawnsFile == FILE_H) && !(pawns & ~file_bb(pawnsFile))) { Square bishopSq = pos.square(strongSide); Square queeningSq = relative_square(strongSide, make_square(pawnsFile, RANK_8)); Square kingSq = pos.square(weakSide); if ( opposite_colors(queeningSq, bishopSq) && distance(queeningSq, kingSq) <= 1) return SCALE_FACTOR_DRAW; } // If all the pawns are on the same B or G file, then it's potentially a draw if ( (pawnsFile == FILE_B || pawnsFile == FILE_G) && !(pos.pieces(PAWN) & ~file_bb(pawnsFile)) && pos.non_pawn_material(weakSide) == 0 && pos.count(weakSide) >= 1) { // Get weakSide pawn that is closest to the home rank Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); Square strongKingSq = pos.square(strongSide); Square weakKingSq = pos.square(weakSide); Square bishopSq = pos.square(strongSide); // 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 && (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide))) && (opposite_colors(bishopSq, weakPawnSq) || pos.count(strongSide) == 1)) { int strongKingDist = distance(weakPawnSq, strongKingSq); int weakKingDist = distance(weakPawnSq, weakKingSq); // 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 // 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) if ( relative_rank(strongSide, weakKingSq) >= RANK_7 && weakKingDist <= 2 && weakKingDist <= strongKingDist) return SCALE_FACTOR_DRAW; } } return SCALE_FACTOR_NONE; } /// KQ vs KR and one or more pawns. It tests for fortress draws with a rook on /// the third rank defended by a pawn. template<> ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(pos.count(weakSide) == 1); assert(pos.count(weakSide) >= 1); Square kingSq = pos.square(weakSide); Square rsq = pos.square(weakSide); if ( relative_rank(weakSide, kingSq) <= RANK_2 && relative_rank(weakSide, pos.square(strongSide)) >= RANK_4 && relative_rank(weakSide, rsq) == RANK_3 && ( pos.pieces(weakSide, PAWN) & pos.attacks_from(kingSq) & pos.attacks_from(rsq, strongSide))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; } /// KRP vs KR. This function knows a handful of the most important classes of /// drawn positions, but is far from perfect. It would probably be a good idea /// to add more knowledge in the future. /// /// It would also be nice to rewrite the actual code for this function, /// which is mostly copied from Glaurung 1.x, and isn't very pretty. template<> ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 1)); 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)); File f = file_of(wpsq); Rank r = rank_of(wpsq); Square queeningSq = make_square(f, 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))) 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))) return SCALE_FACTOR_DRAW; if ( r >= RANK_6 && bksq == queeningSq && rank_of(brsq) == RANK_1 && (!tempo || distance(wksq, wpsq) >= 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)) 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) 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)); // 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)))) return ScaleFactor( SCALE_FACTOR_MAX - 8 * distance(wpsq, queeningSq) - 2 * distance(wksq, queeningSq)); // 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 (file_of(bksq) == file_of(wpsq)) return ScaleFactor(10); if ( distance(bksq, wpsq) == 1 && distance(wksq, bksq) > 2) return ScaleFactor(24 - 2 * distance(wksq, bksq)); } return SCALE_FACTOR_NONE; } template<> ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 1)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); // 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); Direction push = pawn_push(strongSide); // If the pawn is on the 5th rank and the pawn (currently) is on // the same color square as the bishop then there is a chance of // 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)) { int d = distance(psq + 3 * push, ksq); if (d <= 2 && !(d == 0 && ksq == pos.square(strongSide) + 2 * push)) return ScaleFactor(24); else return ScaleFactor(48); } // When the pawn has moved to the 6th rank we can be fairly sure // 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 && (PseudoAttacks[BISHOP][bsq] & (psq + push)) && distance(bsq, psq) >= 2) return ScaleFactor(8); } return SCALE_FACTOR_NONE; } /// KRPP vs KRP. There is just a single rule: if the stronger side has no passed /// pawns and the defending king is actively placed, the position is drawish. template<> 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); // Does the stronger side have a passed pawn? if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) return SCALE_FACTOR_NONE; Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); if ( distance(bksq, wpsq1) <= 1 && distance(bksq, wpsq2) <= 1 && relative_rank(strongSide, bksq) > r) { assert(r > RANK_1 && r < RANK_7); return ScaleFactor(KRPPKRPScaleFactors[r]); } return SCALE_FACTOR_NONE; } /// 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 { assert(pos.non_pawn_material(strongSide) == VALUE_ZERO); assert(pos.count(strongSide) >= 2); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); Square ksq = pos.square(weakSide); Bitboard pawns = pos.pieces(strongSide, PAWN); // If all pawns are ahead of the king, on a single rook file and // the king is within one file of the pawns, it's a draw. if ( !(pawns & ~forward_ranks_bb(weakSide, ksq)) && !((pawns & ~FileABB) && (pawns & ~FileHBB)) && distance(ksq, lsb(pawns)) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; } /// KBP vs KB. There are two rules: 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. If the two bishops have opposite color, /// it's almost always a draw. template<> 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); // Case 1: Defending king blocks the pawn, and cannot be driven away 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)) return SCALE_FACTOR_DRAW; // Case 2: Opposite colored bishops if (opposite_colors(strongBishopSq, weakBishopSq)) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; } /// KBPP vs KB. It detects a few basic draws with opposite-colored bishops template<> 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); if (!opposite_colors(wbsq, bbsq)) return SCALE_FACTOR_NONE; Square ksq = pos.square(weakSide); Square psq1 = pos.squares(strongSide)[0]; Square psq2 = pos.squares(strongSide)[1]; Square blockSq1, blockSq2; if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) { blockSq1 = psq1 + pawn_push(strongSide); blockSq2 = make_square(file_of(psq2), rank_of(psq1)); } else { blockSq1 = psq2 + pawn_push(strongSide); blockSq2 = make_square(file_of(psq1), rank_of(psq2)); } switch (distance(psq1, psq2)) { 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)) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; case 1: // 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 || (pos.attacks_from(blockSq2) & pos.pieces(weakSide, BISHOP)) || distance(psq1, psq2) >= 2)) return SCALE_FACTOR_DRAW; else if ( ksq == blockSq2 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq1 || (pos.attacks_from(blockSq1) & pos.pieces(weakSide, BISHOP)))) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; default: // The pawns are not on the same file or adjacent files. No scaling. return SCALE_FACTOR_NONE; } } /// 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<> 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); 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)) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; } /// KNP vs K. There is a single rule: if the pawn is a rook pawn on the 7th rank /// and the defending king prevents the pawn from advancing, the position is drawn. template<> ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, KnightValueMg, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); // Assume strongSide is white and the pawn is on files A-D Square pawnSq = normalize(pos, strongSide, pos.square(strongSide)); Square weakKingSq = normalize(pos, strongSide, pos.square(weakSide)); if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; } /// KNP vs KB. If knight can block bishop from taking pawn, it's a win. /// Otherwise the position is drawn. template<> ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, KnightValueMg, 1)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); Square pawnSq = pos.square(strongSide); Square bishopSq = pos.square(weakSide); Square weakKingSq = pos.square(weakSide); // King needs to get close to promoting pawn to prevent knight from blocking. // Rules for this are very tricky, so just approximate. if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from(bishopSq)) return ScaleFactor(distance(weakKingSq, pawnSq)); return SCALE_FACTOR_NONE; } /// 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 /// 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). template<> ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); 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)); 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) 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; } stockfish-11.orig/src/bitboard.cpp0000644000175000017500000001712113610452365015443 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "bitboard.h" #include "misc.h" uint8_t PopCnt16[1 << 16]; uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; Bitboard SquareBB[SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; Magic RookMagics[SQUARE_NB]; Magic BishopMagics[SQUARE_NB]; namespace { Bitboard RookTable[0x19000]; // To store rook attacks Bitboard BishopTable[0x1480]; // To store bishop attacks void init_magics(Bitboard table[], Magic magics[], Direction directions[]); } /// Bitboards::pretty() returns an ASCII representation of a bitboard suitable /// to be printed to standard output. Useful for debugging. const std::string Bitboards::pretty(Bitboard b) { std::string s = "+---+---+---+---+---+---+---+---+\n"; for (Rank r = RANK_8; r >= RANK_1; --r) { for (File f = FILE_A; f <= FILE_H; ++f) s += b & make_square(f, r) ? "| X " : "| "; s += "|\n+---+---+---+---+---+---+---+---+\n"; } return s; } /// Bitboards::init() initializes various bitboard tables. It is called at /// startup and relies on global objects to be already zero-initialized. void Bitboards::init() { for (unsigned i = 0; i < (1 << 16); ++i) PopCnt16[i] = std::bitset<16>(i).count(); for (Square s = SQ_A1; s <= SQ_H8; ++s) SquareBB[s] = (1ULL << s); for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); for (Square s = SQ_A1; s <= SQ_H8; ++s) { PawnAttacks[WHITE][s] = pawn_attacks_bb(square_bb(s)); PawnAttacks[BLACK][s] = pawn_attacks_bb(square_bb(s)); } // Helper returning the target bitboard of a step from a square auto landing_square_bb = [&](Square s, int step) { Square to = Square(s + step); return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); }; for (Square s = SQ_A1; s <= SQ_H8; ++s) { for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} ) PseudoAttacks[KING][s] |= landing_square_bb(s, step); for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} ) PseudoAttacks[KNIGHT][s] |= landing_square_bb(s, step); } 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); for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); for (PieceType pt : { BISHOP, ROOK }) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) if (PseudoAttacks[pt][s1] & s2) LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; } } namespace { Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) { Bitboard attack = 0; for (int i = 0; i < 4; ++i) for (Square s = sq + directions[i]; is_ok(s) && distance(s, s - directions[i]) == 1; s += directions[i]) { attack |= s; if (occupied & s) break; } return attack; } // init_magics() computes all rook and bishop attacks at startup. Magic // bitboards are used to look up attacks of sliding pieces. As a reference see // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so // called "fancy" approach. void init_magics(Bitboard table[], Magic magics[], Direction directions[]) { // Optimal PRNG seeds to pick the correct magics in the shortest time int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } }; Bitboard occupancy[4096], reference[4096], edges, b; int epoch[4096] = {}, cnt = 0, size = 0; for (Square s = SQ_A1; s <= SQ_H8; ++s) { // Board edges are not considered in the relevant occupancies edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s)); // Given a square 's', the mask is the bitboard of sliding attacks from // 's' computed on an empty board. The index must be big enough to contain // all the attacks for each possible subset of the mask and so is 2 power // 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.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); // Set the offset for the attacks table of the square. We have individual // table sizes for each square with "Fancy Magic Bitboards". m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size; // Use Carry-Rippler trick to enumerate all subsets of masks[s] and // store the corresponding sliding attack bitboard in reference[]. b = size = 0; do { occupancy[size] = b; reference[size] = sliding_attack(directions, s, b); if (HasPext) m.attacks[pext(b, m.mask)] = reference[size]; size++; b = (b - m.mask) & m.mask; } while (b); if (HasPext) continue; PRNG rng(seeds[Is64Bit][rank_of(s)]); // Find a magic for square 's' picking up an (almost) random number // until we find the one that passes the verification test. for (int i = 0; i < size; ) { for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; ) m.magic = rng.sparse_rand(); // A good magic must map every possible occupancy to an index that // looks up the correct sliding attack in the attacks[s] database. // Note that we build up the database for square 's' as a side // effect of verifying the magic. Keep track of the attempt count // and save it in epoch[], little speed-up trick to avoid resetting // m.attacks[] after every failed attempt. for (++cnt, i = 0; i < size; ++i) { unsigned idx = m.index(occupancy[i]); if (epoch[idx] < cnt) { epoch[idx] = cnt; m.attacks[idx] = reference[i]; } else if (m.attacks[idx] != reference[i]) break; } } } } } stockfish-11.orig/src/misc.h0000644000175000017500000000667413610452365014270 0ustar pdmpdm/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED #include #include #include #include #include #include "types.h" const std::string engine_info(bool to_uci = false); const std::string compiler_info(); void prefetch(void* addr); void start_logger(const std::string& fname); void dbg_hit_on(bool b); void dbg_hit_on(bool c, bool b); void dbg_mean_of(int v); void dbg_print(); typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); inline TimePoint now() { return std::chrono::duration_cast (std::chrono::steady_clock::now().time_since_epoch()).count(); } template struct HashTable { Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } private: std::vector table = std::vector(Size); // Allocate on the heap }; enum SyncCout { IO_LOCK, IO_UNLOCK }; std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated /// to the public domain by Sebastiano Vigna (2014). /// It has the following characteristics: /// /// - Outputs 64-bit numbers /// - Passes Dieharder and SmallCrush test batteries /// - Does not require warm-up, no zeroland to escape /// - Internal state is a single 64-bit integer /// - Period is 2^64 - 1 /// - Speed: 1.60 ns/call (Core i7 @3.40GHz) /// /// For further analysis see /// class PRNG { uint64_t s; uint64_t rand64() { s ^= s >> 12, s ^= s << 25, s ^= s >> 27; return s * 2685821657736338717LL; } public: PRNG(uint64_t seed) : s(seed) { assert(seed); } template T rand() { return T(rand64()); } /// Special generator used to fast init magic numbers. /// Output values only have 1/8th of their bits set on average. template T sparse_rand() { return T(rand64() & rand64() & rand64()); } }; /// 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 /// cores. To overcome this, some special platform specific API should be /// called to set group affinity for each thread. Original code from Texel by /// Peter Österlund. namespace WinProcGroup { void bindThisThread(size_t idx); } #endif // #ifndef MISC_H_INCLUDED stockfish-11.orig/AUTHORS0000644000175000017500000000675413610452365013444 0ustar pdmpdm# List of authors for Stockfish, as of January 7, 2020 Tord Romstad (romstad) Marco Costalba (mcostalba) Joona Kiiski (zamar) Gary Linscott (glinscott) Aditya (absimaldata) Adrian Petrescu (apetresc) Ajith Chandy Jose (ajithcj) Alain Savard (Rocky640) Alayan Feh (Alayan-stk-2) Alexander Kure Alexander Pagel (Lolligerhans) Ali AlZhrani (Cooffe) Andrew Grant (AndyGrant) Andrey Neporada (nepal) Andy Duplain Aram Tumanian (atumanian) Arjun Temurnikar Auguste Pop Balint Pfliegel Ben Koshy (BKSpurgeon) Bill Henry (VoyagerOne) Bojun Guo (noobpwnftw, Nooby) braich Brian Sheppard (SapphireBrand, briansheppard-toast) Bryan Cross (crossbr) candirufish Chess13234 Chris Cain (ceebo) Dan Schmidt (dfannius) Daniel Axtens (daxtens) Daniel Dugovic (ddugovic) Dariusz Orzechowski David Zar Daylen Yang (daylen) DiscanX double-beep Eduardo Cáceres (eduherminio) Eelco de Groot (KingDefender) Elvin Liu (solarlight2) erbsenzaehler Ernesto Gatti Fabian Beuke (madnight) Fabian Fichter (ianfab) fanon Fauzi Akram Dabat (FauziAkram) Felix Wittmann gamander gguliash Gian-Carlo Pascutto (gcp) Gontran Lemaire (gonlem) Goodkov Vasiliy Aleksandrovich (goodkov) Gregor Cramer GuardianRM Günther Demetz (pb00067, pb00068) Guy Vreuls (gvreuls) Henri Wiechers Hiraoka Takuya (HiraokaTakuya) homoSapiensSapiens Hongzhi Cheng Ivan Ivec (IIvec) Jacques B. (Timshel) Jan Ondruš (hxim) Jared Kish (Kurtbusch) Jarrod Torriero (DU-jdto) Jean Gauthier (OuaisBla) Jean-Francois Romang (jromang) Jekaa Jerry Donald Watson (jerrydonaldwatson) Jonathan Calovski (Mysseno) Jonathan Dumale (SFisGOD) Joost VandeVondele (vondele) Jörg Oster (joergoster) Joseph Ellis (jhellis3) Joseph R. Prostko jundery Justin Blanchard (UncombedCoconut) Kelly Wilson Ken Takusagawa kinderchocolate Kiran Panditrao (Krgp) Kojirion Leonardo Ljubičić (ICCF World Champion) Leonid Pechenik (lp--) Linus Arver (listx) loco-loco Lub van den Berg (ElbertoOne) Luca Brivio (lucabrivio) Lucas Braesch (lucasart) Lyudmil Antonov (lantonov) Maciej Żenczykowski (zenczykowski) Malcolm Campbell (xoto10) Mark Tenzer (31m059) marotear Matthew Lai (matthewlai) Matthew Sullivan (Matt14916) Michael An (man) Michael Byrne (MichaelB7) Michael Chaly (Vizvezdenec) Michael Stembera (mstembera) Michael Whiteley (protonspring) Michel Van den Bergh (vdbergh) Miguel Lahoz (miguel-l) Mikael Bäckman (mbootsector) Mira Miroslav Fontán (Hexik) Moez Jellouli (MJZ1977) Mohammed Li (tthsqe12) Nathan Rugg (nmrugg) Nick Pelling (nickpelling) Nicklas Persson (NicklasPersson) Niklas Fiekas (niklasf) Nikolay Kostov (NikolayIT) Ondrej Mosnáček (WOnder93) Oskar Werkelin Ahlin Pablo Vazquez Panthee Pascal Romaret Pasquale Pigazzini (ppigazzini) Patrick Jansen (mibere) pellanda Peter Zsifkovits (CoffeeOne) Ralph Stößer (Ralph Stoesser) Raminder Singh renouve Reuven Peleg Richard Lloyd Rodrigo Exterckötter Tjäder Ron Britvich (Britvich) Ronald de Man (syzygy1, syzygy) Ryan Schmitt Ryan Takker Sami Kiminki (skiminki) Sebastian Buchwald (UniQP) Sergei Antonov (saproj) Sergei Ivanov (svivanov72) sf-x Shane Booth (shane31) Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) Steinar Gunderson (sesse) Stéphane Nicolet (snicolet) Thanar2 thaspel theo77186 Tom Truscott Tom Vijlbrief (tomtor) Torsten Franz (torfranz, tfranzer) Tracey Emery (basepr1me) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) # Additionally, we acknowledge the authors and maintainers of fishtest, # an amazing and essential framework for the development of Stockfish! # # https://github.com/glinscott/fishtest/blob/master/AUTHORS stockfish-11.orig/Readme.md0000644000175000017500000002153513610452365014105 0ustar pdmpdm## Overview [![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish) [![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master) [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine derived from Glaurung 2.1. It is not a complete chess program and requires a UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably. Read the documentation for your GUI of choice for information about how to use Stockfish with it. ## Files This distribution of Stockfish consists of the following files: * Readme.md, the file you are currently reading. * Copying.txt, a text file containing the GNU General Public License version 3. * src, a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. ## UCI parameters Currently, Stockfish has the following UCI options: * #### Debug Log File Write all communication to and from the engine into a text file. * #### Contempt A positive value for contempt favors middle game positions and avoids draws. * #### Analysis Contempt By default, contempt is set to prefer the side to move. Set this option to "White" or "Black" to analyse with contempt for that side, or "Off" to disable contempt. * #### Threads The number of CPU threads used for searching a position. For best performance, set this equal to the number of CPU cores available. * #### Hash The size of the hash table in MB. * #### Clear Hash Clear the hash table. * #### Ponder Let Stockfish ponder its next move while the opponent is thinking. * #### MultiPV Output the N best lines (principal variations, PVs) when searching. Leave at 1 for best performance. * #### Skill Level Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength). Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a weaker move will be played. * #### UCI_LimitStrength Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level. * #### UCI_Elo If enabled by UCI_LimitStrength, aim for an engine strength of the given Elo. This Elo rating has been calibrated at a time control of 60s+0.6s and anchored to CCRL 40/4. * #### Move Overhead Assume a time delay of x ms due to network and GUI overheads. This is useful to avoid losses on time in those cases. * #### Minimum Thinking Time Search for at least x ms per move. * #### Slow Mover Lower values will make Stockfish take less time in games, higher values will make it think longer. * #### nodestime Tells the engine to use nodes searched instead of wall time to account for elapsed time. Useful for engine testing. * #### UCI_Chess960 An option handled by your GUI. If true, Stockfish will play Chess960. * #### UCI_AnalyseMode An option handled by your GUI. * #### SyzygyPath Path to the folders/directories storing the Syzygy tablebase files. Multiple directories are to be separated by ";" on Windows and by ":" on Unix-based operating systems. Do not use spaces around the ";" or ":". Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6` It is recommended to store .rtbw files on an SSD. There is no loss in storing the .rtbz files on a regular HD. It is recommended to verify all md5 checksums of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will lead to engine crashes. * #### SyzygyProbeDepth Minimum remaining search depth for which a position is probed. Set this option to a higher value to probe less agressively if you experience too much slowdown (in terms of nps) due to TB probing. * #### Syzygy50MoveRule Disable to let fifty-move rule draws detected by Syzygy tablebase probes count as wins or losses. This is useful for ICCF correspondence games. * #### SyzygyProbeLimit Limit Syzygy tablebase probing to positions with at most this many pieces left (including kings and pawns). ## What to expect from Syzygybases? If the engine is searching a position that is not in the tablebases (e.g. a position with 8 pieces), it will access the tablebases during the search. If the engine reports a very large score (typically 153.xx), this means that it has found a winning line into a tablebase position. If the engine is given a position to search that is in the tablebases, it will use the tablebases at the beginning of the search to preselect all good moves, i.e. all moves that preserve the win or preserve the draw while taking into account the 50-move rule. It will then perform a search only on those moves. **The engine will not move immediately**, unless there is only a single good move. **The engine likely will not report a mate score even if the position is known to be won.** It is therefore clear that this behaviour is not identical to what one might be used to with Nalimov tablebases. There are technical reasons for this difference, the main technical reason being that Nalimov tablebases use the DTM metric (distance-to-mate), while Syzygybases use a variation of the DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move counter). This special metric is one of the reasons that Syzygybases are more compact than Nalimov tablebases, while still storing all information needed for optimal play and in addition being able to take into account the 50-move rule. ## 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, the hardware POPCNT instruction, big-endian machines such as Power PC, and other platforms. 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. When reporting an issue or a bug, please tell us which version and compiler you used to create your executable. These informations can be found by typing the following commands in a console: ``` ./stockfish compiler ``` ## Understanding the code base and participating in the project Stockfish's improvement over the last couple of years has been a great community effort. There are a few ways to help contribute to its growth. ### Donating hardware Improving Stockfish requires a massive amount of testing. You can donate your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview) and view the current tests on [Fishtest](https://tests.stockfishchess.org/tests). ### Improving the code If you want to help improve the code, there are several valuable resources: * [In this wiki,](https://www.chessprogramming.org) many techniques used in Stockfish are explained with a lot of background information. * [The section on Stockfish](https://www.chessprogramming.org/Stockfish) describes many features and techniques used by Stockfish. However, it is generic rather than being focused on Stockfish's precise implementation. Nevertheless, a helpful resource. * The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish). Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) group and engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests). If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test) first, where the basics of Stockfish development are explained. ## Terms of use Stockfish is free, and distributed under the **GNU General Public License version 3** (GPL v3). Essentially, this means that you are free to do almost exactly what you want with the program, including distributing it among your friends, making it available for download from your web site, selling it (either by itself or as part of some bigger software package), or using it as the starting point for a software project of your own. The only real limitation is that whenever you distribute Stockfish in some way, you must always include the full source code, or a pointer to where the source code can be found. If you make any changes to the source code, these changes must also be made available under the GPL. For full details, read the copy of the GPL v3 found in the file named *Copying.txt*. stockfish-11.orig/Copying.txt0000644000175000017500000010575513610452365014546 0ustar pdmpdm GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . stockfish-11.orig/Top CPU Contributors.txt0000644000175000017500000001713313610452365016756 0ustar pdmpdmContributors with >10,000 CPU hours as of January 7, 2020 Thank you! Username CPU Hours Games played -------------------------------------------------- noobpwnftw 9305707 695548021 mlang 780050 61648867 dew 621626 43921547 mibere 524702 42238645 crunchy 354587 27344275 cw 354495 27274181 fastgm 332801 22804359 JojoM 295750 20437451 CSU_Dynasty 262015 21828122 Fisherman 232181 18939229 ctoks 218866 17622052 glinscott 201989 13780820 tvijlbrief 201204 15337115 velislav 188630 14348485 gvreuls 187164 15149976 bking_US 180289 11876016 nordlandia 172076 13467830 leszek 157152 11443978 Thanar 148021 12365359 spams 141975 10319326 drabel 138073 11121749 vdv 137850 9394330 mgrabiak 133578 10454324 TueRens 132485 10878471 bcross 129683 11557084 marrco 126078 9356740 sqrt2 125830 9724586 robal 122873 9593418 vdbergh 120766 8926915 malala 115926 8002293 CoffeeOne 114241 5004100 dsmith 113189 7570238 BrunoBanani 104644 7436849 Data 92328 8220352 mhoram 89333 6695109 davar 87924 7009424 xoto 81094 6869316 ElbertoOne 80899 7023771 grandphish2 78067 6160199 brabos 77212 6186135 psk 75733 5984901 BRAVONE 73875 5054681 sunu 70771 5597972 sterni1971 70605 5590573 MaZePallas 66886 5188978 Vizvezdenec 63708 4967313 nssy 63462 5259388 jromang 61634 4940891 teddybaer 61231 5407666 Pking_cda 60099 5293873 solarlight 57469 5028306 dv8silencer 56913 3883992 tinker 54936 4086118 renouve 49732 3501516 Freja 49543 3733019 robnjr 46972 4053117 rap 46563 3219146 Bobo1239 46036 3817196 ttruscott 45304 3649765 racerschmacer 44881 3975413 finfish 44764 3370515 eva42 41783 3599691 biffhero 40263 3111352 bigpen0r 39817 3291647 mhunt 38871 2691355 ronaldjerum 38820 3240695 Antihistamine 38785 2761312 pb00067 38038 3086320 speedycpu 37591 3003273 rkl 37207 3289580 VoyagerOne 37050 3441673 jbwiebe 35320 2805433 cuistot 34191 2146279 homyur 33927 2850481 manap 32873 2327384 gri 32538 2515779 oryx 31267 2899051 EthanOConnor 30959 2090311 SC 30832 2730764 csnodgrass 29505 2688994 jmdana 29458 2205261 strelock 28219 2067805 jkiiski 27832 1904470 Pyafue 27533 1902349 Garf 27515 2747562 eastorwest 27421 2317535 slakovv 26903 2021889 Prcuvu 24835 2170122 anst 24714 2190091 hyperbolic.tom 24319 2017394 Patrick_G 23687 1801617 Sharaf_DG 22896 1786697 nabildanial 22195 1519409 chriswk 21931 1868317 achambord 21665 1767323 Zirie 20887 1472937 team-oh 20217 1636708 Isidor 20096 1680691 ncfish1 19931 1520927 nesoneg 19875 1463031 Spprtr 19853 1548165 JanErik 19849 1703875 agg177 19478 1395014 SFTUser 19231 1567999 xor12 19017 1680165 sg4032 18431 1641865 rstoesser 18118 1293588 MazeOfGalious 17917 1629593 j3corre 17743 941444 cisco2015 17725 1690126 ianh2105 17706 1632562 dex 17678 1467203 jundery 17194 1115855 iisiraider 17019 1101015 horst.prack 17012 1465656 Adrian.Schmidt123 16563 1281436 purplefishies 16342 1092533 wei 16274 1745989 ville 16144 1384026 eudhan 15712 1283717 OuaisBla 15581 972000 DragonLord 15559 1162790 dju 14716 875569 chris 14479 1487385 0xB00B1ES 14079 1001120 OssumOpossum 13776 1007129 enedene 13460 905279 bpfliegel 13346 884523 Ente 13198 1156722 IgorLeMasson 13087 1147232 jpulman 13000 870599 ako027ako 12775 1173203 Nikolay.IT 12352 1068349 Andrew Grant 12327 895539 joster 12008 950160 AdrianSA 11996 804972 Nesa92 11455 1111993 fatmurphy 11345 853210 Dark_wizzie 11108 1007152 modolief 10869 896470 mschmidt 10757 803401 infinity 10594 727027 mabichito 10524 749391 Thomas A. Anderson 10474 732094 thijsk 10431 719357 Flopzee 10339 894821 crocogoat 10104 1013854 SapphireBrand 10104 969604 stocky 10017 699440 stockfish-11.orig/appveyor.yml0000644000175000017500000000402013610452365014744 0ustar pdmpdmversion: 1.0.{build} clone_depth: 50 branches: only: - master - appveyor # Operating system (build VM template) os: Visual Studio 2017 # Build platform, i.e. x86, x64, AnyCPU. This setting is optional. platform: - x86 - x64 # build Configuration, i.e. Debug, Release, etc. configuration: - Debug - Release matrix: # The build fail immediately once one of the job fails fast_finish: true # Scripts that are called at very beginning, before repo cloning init: - cmake --version - msbuild /version before_build: - ps: | # Get sources $src = get-childitem -Path *.cpp -Recurse | select -ExpandProperty FullName $src = $src -join ' ' $src = $src.Replace("\", "/") # Build CMakeLists.txt $t = 'cmake_minimum_required(VERSION 3.8)', 'project(Stockfish)', 'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)', 'set(source_files', $src, ')', 'add_executable(stockfish ${source_files})' # Write CMakeLists.txt withouth BOM $MyPath = (Get-Item -Path "." -Verbose).FullName + '\CMakeLists.txt' $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False [System.IO.File]::WriteAllLines($MyPath, $t, $Utf8NoBomEncoding) # Obtain bench reference from git log $b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1 $bench = $b -match '\D+(\d+)' | % { $matches[1] } Write-Host "Reference bench:" $bench $g = "Visual Studio 15 2017" If (${env:PLATFORM} -eq 'x64') { $g = $g + ' Win64' } cmake -G "${g}" . Write-Host "Generated files for: " $g build_script: - cmake --build . --config %CONFIGURATION% -- /verbosity:minimal before_test: - cd src/%CONFIGURATION% - stockfish bench 2> out.txt >NUL - ps: | # Verify bench number $s = (gc "./out.txt" | out-string) $r = ($s -match 'Nodes searched \D+(\d+)' | % { $matches[1] }) Write-Host "Engine bench:" $r Write-Host "Reference bench:" $bench If ($r -ne $bench) { exit 1 }