diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 13735e3d..f66d971b 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -19,12 +19,13 @@ */ #include +#include #include "bitboard.h" #include "misc.h" uint8_t PopCnt16[1 << 16]; -int8_t SquareDistance[SQUARE_NB][SQUARE_NB]; +uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; Bitboard SquareBB[SQUARE_NB]; Bitboard ForwardRanksBB[COLOR_NB][RANK_NB]; @@ -34,6 +35,12 @@ Bitboard DistanceRingBB[SQUARE_NB][8]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; +Bitboard KingFlank[FILE_NB] = { + QueenSide ^ FileDBB, QueenSide, QueenSide, + CenterFiles, CenterFiles, + KingSide, KingSide, KingSide ^ FileEBB +}; + Magic RookMagics[SQUARE_NB]; Magic BishopMagics[SQUARE_NB]; @@ -43,15 +50,6 @@ namespace { Bitboard BishopTable[0x1480]; // To store bishop attacks void init_magics(Bitboard table[], Magic magics[], Direction directions[]); - - // popcount16() counts the non-zero bits using SWAR-Popcount algorithm - - unsigned popcount16(unsigned u) { - u -= (u >> 1) & 0x5555U; - u = ((u >> 2) & 0x3333U) + (u & 0x3333U); - u = ((u >> 4) + u) & 0x0F0FU; - return (u * 0x0101U) >> 8; - } } @@ -80,17 +78,13 @@ const std::string Bitboards::pretty(Bitboard b) { void Bitboards::init() { for (unsigned i = 0; i < (1 << 16); ++i) - PopCnt16[i] = (uint8_t) popcount16(i); + PopCnt16[i] = std::bitset<16>(i).count(); for (Square s = SQ_A1; s <= SQ_H8; ++s) SquareBB[s] = (1ULL << s); - for (Rank r = RANK_1; r < RANK_8; ++r) - ForwardRanksBB[WHITE][r] = ~(ForwardRanksBB[BLACK][r + 1] = ForwardRanksBB[BLACK][r] | rank_bb(r)); - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - if (s1 != s2) { SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); DistanceRingBB[s1][SquareDistance[s1][s2]] |= s2; @@ -127,13 +121,11 @@ void Bitboards::init() { for (PieceType pt : { BISHOP, ROOK }) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - { - if (!(PseudoAttacks[pt][s1] & s2)) - continue; - - LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; - BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]); - } + if (PseudoAttacks[pt][s1] & s2) + { + LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; + BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]); + } } } diff --git a/src/bitboard.h b/src/bitboard.h index cf52b39d..af7b592a 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -60,15 +60,21 @@ constexpr Bitboard Rank6BB = Rank1BB << (8 * 5); constexpr Bitboard Rank7BB = Rank1BB << (8 * 6); constexpr Bitboard Rank8BB = Rank1BB << (8 * 7); -extern int8_t SquareDistance[SQUARE_NB][SQUARE_NB]; +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); + +extern uint8_t PopCnt16[1 << 16]; +extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern Bitboard SquareBB[SQUARE_NB]; -extern Bitboard ForwardRanksBB[COLOR_NB][RANK_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard DistanceRingBB[SQUARE_NB][8]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; +extern Bitboard KingFlank[FILE_NB]; /// Magic holds all magic bitboards relevant data for a single square @@ -133,6 +139,7 @@ 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. @@ -192,48 +199,49 @@ inline Bitboard adjacent_files_bb(File f) { return shift(file_bb(f)) | shift(file_bb(f)); } + /// between_bb() returns a bitboard representing all the squares between the two /// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with -/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, file -/// or diagonal, 0 is returned. +/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, +/// file or diagonal, 0 is returned. inline Bitboard between_bb(Square s1, Square s2) { return BetweenBB[s1][s2]; } -/// forward_ranks_bb() returns a bitboard representing the squares on all the ranks +/// 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 ForwardRanksBB[c][rank_of(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: -/// ForwardFileBB[c][s] = forward_ranks_bb(c, s) & file_bb(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 ForwardRanksBB[c][rank_of(s)] & file_bb(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: +/// 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(file_of(s)); } -/// passed_pawn_mask() returns a bitboard mask which can be used to test if a -/// pawn of the given color and on the given square is a passed pawn: +/// 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_mask(Color c, Square s) { - return pawn_attack_span(c, s) | forward_file_bb(c, s); +inline Bitboard passed_pawn_span(Color c, Square s) { + return forward_ranks_bb(c, s) & (adjacent_files_bb(file_of(s)) | file_bb(s)); } @@ -248,7 +256,7 @@ inline bool aligned(Square s1, Square s2, Square s3) { /// distance() functions return the distance between x and y, defined as the /// number of steps for a king in x to reach y. Works with squares, ranks, files. -template inline int distance(T x, T y) { return x < y ? y - x : x - y; } +template inline int distance(T x, T y) { return std::abs(x - y); } template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } template inline int distance(T2 x, T2 y); @@ -286,7 +294,6 @@ inline int popcount(Bitboard b) { #ifndef USE_POPCNT - extern uint8_t PopCnt16[1 << 16]; 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]]; diff --git a/src/endgame.cpp b/src/endgame.cpp index 835173c4..3e572205 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -286,6 +286,21 @@ Value Endgame::operator()(const Position& pos) const { } +/// 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; } diff --git a/src/endgame.h b/src/endgame.h index 941cb310..2a48488f 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -37,6 +37,7 @@ 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 @@ -125,6 +126,7 @@ public: add("KRKN"); add("KQKP"); add("KQKR"); + add("KNNKP"); add("KNPK"); add("KNPKB"); diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 98082c4c..27e1dd3d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -73,17 +73,6 @@ using namespace Trace; namespace { - 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 - }; - // Threshold for lazy and space evaluation constexpr Value LazyThreshold = Value(1500); constexpr Value SpaceThreshold = Value(12222); @@ -152,6 +141,7 @@ namespace { constexpr Score KnightOnQueen = S( 16, 12); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); + constexpr Score Outpost = S( 9, 3); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnPawn = S( 10, 32); @@ -163,7 +153,6 @@ namespace { constexpr Score TrappedRook = S( 47, 4); constexpr Score WeakQueen = S( 49, 15); constexpr Score WeakUnopposedPawn = S( 12, 23); - constexpr Score Outpost = S( 9, 3); #undef S @@ -402,7 +391,8 @@ namespace { constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); - Bitboard weak, b, b1, b2, safe, unsafeChecks = 0; + Bitboard weak, b1, b2, safe, unsafeChecks = 0; + Bitboard rookChecks, queenChecks, bishopChecks, knightChecks; int kingDanger = 0; const Square ksq = pos.square(Us); @@ -422,45 +412,43 @@ namespace { b2 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); // Enemy rooks checks - Bitboard RookCheck = b1 - & safe - & attackedBy[Them][ROOK]; + rookChecks = b1 & safe & attackedBy[Them][ROOK]; - if (RookCheck) + 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. - Bitboard QueenCheck = (b1 | b2) - & attackedBy[Them][QUEEN] - & safe - & ~attackedBy[Us][QUEEN] - & ~RookCheck; + queenChecks = (b1 | b2) + & attackedBy[Them][QUEEN] + & safe + & ~attackedBy[Us][QUEEN] + & ~rookChecks; - if (QueenCheck) + 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. - Bitboard BishopCheck = b2 - & attackedBy[Them][BISHOP] - & safe - & ~QueenCheck; + bishopChecks = b2 + & attackedBy[Them][BISHOP] + & safe + & ~queenChecks; - if (BishopCheck) + if (bishopChecks) kingDanger += BishopSafeCheck; else unsafeChecks |= b2 & attackedBy[Them][BISHOP]; // Enemy knights checks - b = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; + knightChecks = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; - if (b & safe) + if (knightChecks & safe) kingDanger += KnightSafeCheck; else - unsafeChecks |= b; + unsafeChecks |= knightChecks; // Unsafe or occupied checking squares will also be considered, as long as // the square is in the attacker's mobility area. @@ -511,7 +499,7 @@ namespace { constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe, restricted; + Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe; Score score = SCORE_ZERO; // Non-pawn enemies @@ -561,10 +549,11 @@ namespace { } // Bonus for restricting their piece moves - restricted = attackedBy[Them][ALL_PIECES] - & ~stronglyProtected - & attackedBy[Us][ALL_PIECES]; - score += RestrictedPiece * popcount(restricted); + b = attackedBy[Them][ALL_PIECES] + & ~stronglyProtected + & attackedBy[Us][ALL_PIECES]; + + score += RestrictedPiece * popcount(b); // Bonus for enemy unopposed weak pawns if (pos.pieces(Us, ROOK, QUEEN)) @@ -901,7 +890,6 @@ std::string Eval::trace(const Position& pos) { << " ------------+-------------+-------------+------------\n" << " Material | " << Term(MATERIAL) << " Imbalance | " << Term(IMBALANCE) - << " Initiative | " << Term(INITIATIVE) << " Pawns | " << Term(PAWN) << " Knights | " << Term(KNIGHT) << " Bishops | " << Term(BISHOP) @@ -912,6 +900,7 @@ std::string Eval::trace(const Position& pos) { << " Threats | " << Term(THREAT) << " Passed | " << Term(PASSED) << " Space | " << Term(SPACE) + << " Initiative | " << Term(INITIATIVE) << " ------------+-------------+-------------+------------\n" << " Total | " << Term(TOTAL); diff --git a/src/movegen.cpp b/src/movegen.cpp index 2f7c55c8..4c609352 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -252,14 +252,11 @@ namespace { } // namespace -/// generate generates all pseudo-legal captures and queen -/// promotions. Returns a pointer to the end of the move list. +/// Generates all pseudo-legal captures and queen promotions +/// Generates all pseudo-legal non-captures and underpromotions +/// Generates all pseudo-legal captures and non-captures /// -/// generate generates all pseudo-legal non-captures and -/// underpromotions. Returns a pointer to the end of the move list. -/// -/// generate generates all pseudo-legal captures and -/// non-captures. Returns a pointer to the end of the move list. +/// Returns a pointer to the end of the move list. template ExtMove* generate(const Position& pos, ExtMove* moveList) { diff --git a/src/movepick.cpp b/src/movepick.cpp index 87c3f6fa..86eb98aa 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -114,7 +114,8 @@ void MovePicker::score() { m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] - + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]; + + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] / 2; else // Type == EVASIONS { diff --git a/src/pawns.cpp b/src/pawns.cpp index c581c8e7..6072745b 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -96,7 +96,7 @@ namespace { // Flag the pawn opposed = theirPawns & forward_file_bb(Us, s); - stoppers = theirPawns & passed_pawn_mask(Us, s); + stoppers = theirPawns & passed_pawn_span(Us, s); lever = theirPawns & PawnAttacks[Us][s]; leverPush = theirPawns & PawnAttacks[Us][s + Up]; doubled = ourPawns & (s - Up); diff --git a/src/position.h b/src/position.h index 74173d03..b65522b7 100644 --- a/src/position.h +++ b/src/position.h @@ -310,7 +310,7 @@ inline Bitboard Position::check_squares(PieceType pt) const { } inline bool Position::pawn_passed(Color c, Square s) const { - return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); + return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); } inline bool Position::advanced_pawn_push(Move m) const { diff --git a/src/search.cpp b/src/search.cpp index e12c6f51..064be66d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -62,22 +62,22 @@ namespace { // Different node types, used as a template parameter enum NodeType { NonPV, PV }; - // Sizes and phases of the skip-blocks, used for distributing search depths across the threads - constexpr int SkipSize[] = { 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 }; - constexpr int SkipPhase[] = { 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7 }; - // Razor and futility margins constexpr int RazorMargin = 600; Value futility_margin(Depth d, bool improving) { return Value((175 - 50 * improving) * d / ONE_PLY); } - // Futility and reductions lookup tables, initialized at startup - int FutilityMoveCounts[2][16]; // [improving][depth] - int Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber] + // Reductions lookup table, initialized at startup + int Reductions[64]; // [depth or moveNumber] template Depth reduction(bool i, Depth d, int mn) { - return Reductions[PvNode][i][std::min(d / ONE_PLY, 63)][std::min(mn, 63)] * ONE_PLY; + int r = Reductions[std::min(d / ONE_PLY, 63)] * Reductions[std::min(mn, 63)] / 1024; + return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY; + } + + constexpr int futility_move_count(bool improving, int depth) { + return (5 + depth * depth) * (1 + improving) / 2; } // History and stats update bonus, based on depth @@ -157,25 +157,8 @@ namespace { void Search::init() { - for (int imp = 0; imp <= 1; ++imp) - for (int d = 1; d < 64; ++d) - for (int mc = 1; mc < 64; ++mc) - { - double r = log(d) * log(mc) / 1.95; - - Reductions[NonPV][imp][d][mc] = int(std::round(r)); - Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - 1, 0); - - // Increase reduction for non-PV nodes when eval is not improving - if (!imp && r > 1.0) - Reductions[NonPV][imp][d][mc]++; - } - - for (int d = 0; d < 16; ++d) - { - FutilityMoveCounts[0][d] = int(2.4 + 0.74 * pow(d, 1.78)); - FutilityMoveCounts[1][d] = int(5.0 + 1.00 * pow(d, 2.00)); - } + for (int i = 1; i < 64; ++i) + Reductions[i] = int(1024 * std::log(i) / std::sqrt(1.95)); } @@ -331,7 +314,7 @@ void Thread::search() { // The former is needed to allow update_continuation_histories(ss-1, ...), // which accesses its argument at ss-4, also near the root. // The latter is needed for statScores and killer initialization. - Stack stack[MAX_PLY+8], *ss = stack+5; + Stack stack[MAX_PLY+10], *ss = stack+7; Move pv[MAX_PLY+1]; Value bestValue, alpha, beta, delta; Move lastBestMove = MOVE_NONE; @@ -339,10 +322,9 @@ void Thread::search() { MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1.0; Color us = rootPos.side_to_move(); - bool failedLow; - std::memset(ss-5, 0, 8 * sizeof(Stack)); - for (int i = 5; i > 0; i--) + std::memset(ss-7, 0, 10 * sizeof(Stack)); + for (int i = 7; i > 0; i--) (ss-i)->continuationHistory = &this->continuationHistory[NO_PIECE][0]; // Use as sentinel ss->pv = pv; @@ -350,7 +332,7 @@ void Thread::search() { beta = VALUE_INFINITE; if (mainThread) - mainThread->bestMoveChanges = 0, failedLow = false; + mainThread->bestMoveChanges = 0; size_t multiPV = Options["MultiPV"]; Skill skill(Options["Skill Level"]); @@ -381,17 +363,9 @@ void Thread::search() { && !Threads.stop && !(Limits.depth && mainThread && Cluster::is_root() && rootDepth / ONE_PLY > Limits.depth)) { - // Distribute search depths across the helper threads - if (idx + Cluster::rank() > 0) - { - int i = (idx + Cluster::rank() * (int)Options["Threads"] - 1) % 20; - if (((rootDepth / ONE_PLY + SkipPhase[i]) / SkipSize[i]) % 2) - continue; // Retry with an incremented rootDepth - } - // Age out PV variability metric if (mainThread) - mainThread->bestMoveChanges *= 0.517, failedLow = false; + mainThread->bestMoveChanges *= 0.517; // Save the last iteration's scores before first PV line is searched and @@ -476,7 +450,6 @@ void Thread::search() { if (mainThread) { failedHighCnt = 0; - failedLow = true; mainThread->stopOnPonderhit = false; } } @@ -531,19 +504,19 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (306 + 119 * failedLow + 6 * (mainThread->previousScore - bestValue)) / 581.0; + double fallingEval = (306 + 9 * (mainThread->previousScore - bestValue)) / 581.0; fallingEval = std::max(0.5, std::min(1.5, fallingEval)); // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = lastBestMoveDepth + 10 * ONE_PLY < completedDepth ? 1.95 : 1.0; + double reduction = std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction; // Use part of the gained time from a previous stable move for the current move double bestMoveInstability = 1.0 + mainThread->bestMoveChanges; - bestMoveInstability *= std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction; // Stop the search if we have only one legal move, or if available time elapsed if ( rootMoves.size() == 1 - || Time.elapsed() > Time.optimum() * bestMoveInstability * fallingEval) + || 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". @@ -607,7 +580,7 @@ namespace { Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, pureStaticEval; bool ttHit, ttPv, inCheck, givesCheck, improving; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -871,7 +844,7 @@ namespace { int probCutCount = 0; while ( (move = mp.next_move()) != MOVE_NONE - && probCutCount < 3) + && probCutCount < 2 + 2 * cutNode) if (move != excludedMove && pos.legal(move)) { probCutCount++; @@ -886,7 +859,7 @@ namespace { // 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 the qsearch held, perform the regular search if (value >= raisedBeta) value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4 * ONE_PLY, !cutNode); @@ -910,7 +883,9 @@ namespace { moves_loop: // When in check, search starts from here - const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr, (ss-4)->continuationHistory }; + 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, @@ -920,12 +895,12 @@ moves_loop: // When in check, search starts from here ss->killers); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc - skipQuiets = false; + moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. - while ((move = mp.next_move(skipQuiets)) != MOVE_NONE) + while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { assert(is_ok(move)); @@ -954,9 +929,6 @@ moves_loop: // When in check, search starts from here movedPiece = pos.moved_piece(move); givesCheck = gives_check(pos, move); - moveCountPruning = depth < 16 * ONE_PLY - && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; - // Step 13. Extensions (~70 Elo) // Singular extension search (~60 Elo). If all moves but one fail low on a @@ -968,12 +940,13 @@ moves_loop: // When in check, search starts from here && move == ttMove && !rootNode && !excludedMove // Avoid recursive singular search - && ttValue != VALUE_NONE + /* && ttValue != VALUE_NONE Already implicit in the next condition */ + && abs(ttValue) < VALUE_KNOWN_WIN && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3 * ONE_PLY && pos.legal(move)) { - Value singularBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE); + Value singularBeta = ttValue - 2 * depth / ONE_PLY; ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, depth / 2, cutNode); ss->excludedMove = MOVE_NONE; @@ -1007,16 +980,16 @@ moves_loop: // When in check, search starts from here && 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 / ONE_PLY); + if ( !captureOrPromotion && !givesCheck && !pos.advanced_pawn_push(move)) { // Move count based pruning (~30 Elo) if (moveCountPruning) - { - skipQuiets = true; continue; - } // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; @@ -1037,8 +1010,7 @@ moves_loop: // When in check, search starts from here if (!pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth))) continue; } - else if ( !extension // (~20 Elo) - && !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) + else if (!pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo) continue; } @@ -1365,7 +1337,9 @@ moves_loop: // When in check, search starts from here futilityBase = bestValue + 128; } - const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr, (ss-4)->continuationHistory }; + 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, @@ -1516,7 +1490,7 @@ moves_loop: // When in check, search starts from here void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - for (int i : {1, 2, 4}) + for (int i : {1, 2, 4, 6}) if (is_ok((ss-i)->currentMove)) (*(ss-i)->continuationHistory)[pc][to] << bonus; } diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index c1619d3d..247113bd 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch + Copyright (C) 2016-2019 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 @@ -33,7 +33,7 @@ #include "../movegen.h" #include "../position.h" #include "../search.h" -#include "../thread_win32.h" +#include "../thread_win32_osx.h" #include "../types.h" #include "../uci.h" diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 572265b5..264f6e84 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch + Copyright (C) 2016-2019 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 diff --git a/src/thread.h b/src/thread.h index b3bed46a..147f592c 100644 --- a/src/thread.h +++ b/src/thread.h @@ -33,7 +33,7 @@ #include "pawns.h" #include "position.h" #include "search.h" -#include "thread_win32.h" +#include "thread_win32_osx.h" /// Thread class keeps together all the thread-related stuff. We use @@ -47,7 +47,7 @@ class Thread { ConditionVariable cv; size_t idx; bool exit = false, searching = true; // Set before starting std::thread - std::thread stdThread; + NativeThread stdThread; public: explicit Thread(size_t); diff --git a/src/thread_win32.h b/src/thread_win32_osx.h similarity index 66% rename from src/thread_win32.h rename to src/thread_win32_osx.h index 5c914df3..88900540 100644 --- a/src/thread_win32.h +++ b/src/thread_win32_osx.h @@ -18,8 +18,8 @@ along with this program. If not, see . */ -#ifndef THREAD_WIN32_H_INCLUDED -#define THREAD_WIN32_H_INCLUDED +#ifndef THREAD_WIN32_OSX_H_INCLUDED +#define THREAD_WIN32_OSX_H_INCLUDED /// STL thread library used by mingw and gcc when cross compiling for Windows /// relies on libwinpthread. Currently libwinpthread implements mutexes directly @@ -33,6 +33,7 @@ #include #include +#include #if defined(_WIN32) && !defined(_MSC_VER) @@ -67,4 +68,45 @@ typedef std::condition_variable ConditionVariable; #endif -#endif // #ifndef THREAD_WIN32_H_INCLUDED +/// On OSX threads other than the main thread are created with a reduced stack +/// size of 512KB by default, this is dangerously low for deep searches, so +/// adjust it to TH_STACK_SIZE. The implementation calls pthread_create() with +/// proper stack size parameter. + +#if defined(__APPLE__) + +#include + +static const size_t TH_STACK_SIZE = 2 * 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 diff --git a/src/types.h b/src/types.h index a7415519..2e36279d 100644 --- a/src/types.h +++ b/src/types.h @@ -180,7 +180,7 @@ enum Value : int { VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, - PawnValueMg = 136, PawnValueEg = 208, + PawnValueMg = 128, PawnValueEg = 213, KnightValueMg = 782, KnightValueEg = 865, BishopValueMg = 830, BishopValueEg = 918, RookValueMg = 1289, RookValueEg = 1378, diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 1c6ef777..813a0890 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "misc.h" #include "search.h" @@ -136,8 +137,8 @@ Option::operator std::string() const { bool Option::operator==(const char* s) const { assert(type == "combo"); - return !CaseInsensitiveLess()(currentValue, s) - && !CaseInsensitiveLess()(s, currentValue); + return !CaseInsensitiveLess()(currentValue, s) + && !CaseInsensitiveLess()(s, currentValue); } @@ -153,8 +154,8 @@ void Option::operator<<(const Option& o) { /// 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. +/// 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) { @@ -165,6 +166,17 @@ Option& Option::operator=(const string& v) { || (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;