From b9ad648cbb560d93564a67052277f7bace350cf5 Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Mon, 29 Aug 2016 19:39:55 +0200 Subject: [PATCH] DroidFish: Updated stockfish engine to git version from 2016-08-28. --- DroidFish/jni/stockfish/evaluate.cpp | 66 +++--- DroidFish/jni/stockfish/misc.cpp | 2 +- DroidFish/jni/stockfish/movegen.cpp | 74 +++--- DroidFish/jni/stockfish/movepick.cpp | 10 +- DroidFish/jni/stockfish/movepick.h | 20 ++ DroidFish/jni/stockfish/pawns.cpp | 3 - DroidFish/jni/stockfish/pawns.h | 2 - DroidFish/jni/stockfish/position.cpp | 75 +++--- DroidFish/jni/stockfish/position.h | 31 +-- DroidFish/jni/stockfish/search.cpp | 264 +++++++++++---------- DroidFish/jni/stockfish/syzygy/tbprobe.cpp | 44 ++-- DroidFish/jni/stockfish/thread.h | 1 + DroidFish/jni/stockfish/tt.cpp | 4 +- DroidFish/jni/stockfish/tt.h | 8 +- DroidFish/jni/stockfish/types.h | 14 +- DroidFish/jni/stockfish/uci.cpp | 2 +- 16 files changed, 332 insertions(+), 288 deletions(-) diff --git a/DroidFish/jni/stockfish/evaluate.cpp b/DroidFish/jni/stockfish/evaluate.cpp index 489422f..177d293 100644 --- a/DroidFish/jni/stockfish/evaluate.cpp +++ b/DroidFish/jni/stockfish/evaluate.cpp @@ -119,7 +119,7 @@ namespace { // game, indexed by piece type and number of attacked squares in the MobilityArea. const Score MobilityBonus[][32] = { {}, {}, - { S(-75,-76), S(-56,-54), S(- 9,-26), S( -2,-10), S( 6, 5), S( 15, 11), // Knights + { S(-75,-76), S(-56,-54), S( -9,-26), S( -2,-10), S( 6, 5), S( 15, 11), // Knights S( 22, 26), S( 30, 28), S( 36, 29) }, { S(-48,-58), S(-21,-19), S( 16, -2), S( 26, 12), S( 37, 22), S( 51, 42), // Bishops S( 54, 54), S( 63, 58), S( 65, 63), S( 71, 70), S( 79, 74), S( 81, 86), @@ -188,6 +188,7 @@ namespace { const Score BishopPawns = S( 8, 12); const Score RookOnPawn = S( 8, 24); const Score TrappedRook = S(92, 0); + const Score CloseEnemies = S( 7, 0); const Score SafeCheck = S(20, 20); const Score OtherCheck = S(10, 10); const Score ThreatByHangingPawn = S(71, 61); @@ -359,7 +360,7 @@ namespace { if (Pt == QUEEN) { // Penalty if any relative pin or discovered attack against the queen - if (pos.slider_blockers(pos.pieces(), pos.pieces(Them, ROOK, BISHOP), s)) + if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s)) score -= WeakQueen; } } @@ -379,6 +380,19 @@ namespace { // evaluate_king() assigns bonuses and penalties to a king of a given color + const Bitboard WhiteCamp = Rank1BB | Rank2BB | Rank3BB | Rank4BB | Rank5BB; + const Bitboard BlackCamp = Rank8BB | Rank7BB | Rank6BB | Rank5BB | Rank4BB; + const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB; + const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB; + const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; + + const Bitboard KingFlank[COLOR_NB][FILE_NB] = { + { QueenSide & WhiteCamp, QueenSide & WhiteCamp, QueenSide & WhiteCamp, CenterFiles & WhiteCamp, + CenterFiles & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp }, + { QueenSide & BlackCamp, QueenSide & BlackCamp, QueenSide & BlackCamp, CenterFiles & BlackCamp, + CenterFiles & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp }, + }; + template Score evaluate_king(const Position& pos, const EvalInfo& ei) { @@ -471,6 +485,19 @@ namespace { score -= KingDanger[std::max(std::min(attackUnits, 399), 0)]; } + // King tropism: firstly, find squares that opponent attacks in our king flank + b = ei.attackedBy[Them][ALL_PIECES] & KingFlank[Us][file_of(ksq)]; + + assert(((Us == WHITE ? b << 4 : b >> 4) & b) == 0); + assert(popcount(Us == WHITE ? b << 4 : b >> 4) == popcount(b)); + + // Secondly, add the squares which are attacked twice in that flank and + // which are not defended by our pawns. + b = (Us == WHITE ? b << 4 : b >> 4) + | (b & ei.attackedBy2[Them] & ~ei.attackedBy[Us][PAWN]); + + score -= CloseEnemies * popcount(b); + if (DoTrace) Trace::add(KING, Us, score); @@ -481,19 +508,6 @@ namespace { // evaluate_threats() assigns bonuses according to the types of the attacking // and the attacked pieces. - const Bitboard WhiteCamp = Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB; - const Bitboard BlackCamp = Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB; - const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB; - const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB; - const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; - - const Bitboard KingFlank[COLOR_NB][FILE_NB] = { - { QueenSide & WhiteCamp, QueenSide & WhiteCamp, QueenSide & WhiteCamp, CenterFiles & WhiteCamp, - CenterFiles & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp }, - { QueenSide & BlackCamp, QueenSide & BlackCamp, QueenSide & BlackCamp, CenterFiles & BlackCamp, - CenterFiles & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp }, - }; - template Score evaluate_threats(const Position& pos, const EvalInfo& ei) { @@ -571,18 +585,6 @@ namespace { score += ThreatByPawnPush * popcount(b); - // King tropism: firstly, find squares that we attack in the enemy king flank - b = ei.attackedBy[Us][ALL_PIECES] & KingFlank[Us][file_of(pos.square(Them))]; - - // Secondly, add to the bitboard the squares which we attack twice in that flank - // but which are not protected by a enemy pawn. Note the trick to shift away the - // previous attack bits to the empty part of the bitboard. - b = (b & ei.attackedBy2[Us] & ~ei.attackedBy[Them][PAWN]) - | (Us == WHITE ? b >> 4 : b << 4); - - // Count all these squares with a single popcount - score += make_score(7 * popcount(b), 0); - if (DoTrace) Trace::add(THREAT, Us, score); @@ -703,10 +705,10 @@ namespace { // ...count safe + (behind & safe) with a single popcount int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); - int weight = pos.count(Us) + pos.count(Us) - + pos.count(Them) + pos.count(Them); + bonus = std::min(16, bonus); + int weight = pos.count(Us); - return make_score(bonus * weight * weight * 2 / 11, 0); + return make_score(bonus * weight * weight / 22, 0); } @@ -758,9 +760,9 @@ namespace { // Endings where weaker side can place his king in front of the opponent's // pawns are drawish. else if ( abs(eg) <= BishopValueEg - && ei.pi->pawn_span(strongSide) <= 1 + && pos.count(strongSide) <= 2 && !pos.pawn_passed(~strongSide, pos.square(~strongSide))) - sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(51) : ScaleFactor(37); + sf = ScaleFactor(37 + 7 * pos.count(strongSide)); } return sf; diff --git a/DroidFish/jni/stockfish/misc.cpp b/DroidFish/jni/stockfish/misc.cpp index 2854f25..680e220 100644 --- a/DroidFish/jni/stockfish/misc.cpp +++ b/DroidFish/jni/stockfish/misc.cpp @@ -32,7 +32,7 @@ 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 = "2016-07-16"; +const string Version = "2016-08-28"; /// 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 diff --git a/DroidFish/jni/stockfish/movegen.cpp b/DroidFish/jni/stockfish/movegen.cpp index 3622f18..efa47bd 100644 --- a/DroidFish/jni/stockfish/movegen.cpp +++ b/DroidFish/jni/stockfish/movegen.cpp @@ -26,7 +26,7 @@ namespace { template - ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us, const CheckInfo* ci) { + ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us) { static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO); @@ -57,10 +57,8 @@ namespace { Move m = make(kfrom, rfrom); - if (Checks && !pos.gives_check(m, *ci)) + if (Checks && !pos.gives_check(m)) return moveList; - else - (void)ci; // Silence a warning under MSVC *moveList++ = m; return moveList; @@ -68,7 +66,7 @@ namespace { template - ExtMove* make_promotions(ExtMove* moveList, Square to, const CheckInfo* ci) { + ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) *moveList++ = make(to - Delta, to, QUEEN); @@ -82,18 +80,17 @@ namespace { // 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 && (StepAttacksBB[W_KNIGHT][to] & ci->ksq)) + if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ksq)) *moveList++ = make(to - Delta, to, KNIGHT); else - (void)ci; // Silence a warning under MSVC + (void)ksq; // Silence a warning under MSVC return moveList; } template - ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, - Bitboard target, const CheckInfo* ci) { + ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { // Compute our parametrized parameters at compile time, named according to // the point of view of white side. @@ -129,16 +126,19 @@ namespace { if (Type == QUIET_CHECKS) { - b1 &= pos.attacks_from(ci->ksq, Them); - b2 &= pos.attacks_from(ci->ksq, Them); + Square ksq = pos.square(Them); + + 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. - if (pawnsNotOn7 & ci->dcCandidates) + Bitboard dcCandidates = pos.discovered_check_candidates(); + if (pawnsNotOn7 & dcCandidates) { - Bitboard dc1 = shift_bb(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); + Bitboard dc1 = shift_bb(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq); Bitboard dc2 = shift_bb(dc1 & TRank3BB) & emptySquares; b1 |= dc1; @@ -172,14 +172,16 @@ namespace { Bitboard b2 = shift_bb(pawnsOn7) & enemies; Bitboard b3 = shift_bb(pawnsOn7) & emptySquares; + Square ksq = pos.square(Them); + while (b1) - moveList = make_promotions(moveList, pop_lsb(&b1), ci); + moveList = make_promotions(moveList, pop_lsb(&b1), ksq); while (b2) - moveList = make_promotions(moveList, pop_lsb(&b2), ci); + moveList = make_promotions(moveList, pop_lsb(&b2), ksq); while (b3) - moveList = make_promotions(moveList, pop_lsb(&b3), ci); + moveList = make_promotions(moveList, pop_lsb(&b3), ksq); } // Standard and en-passant captures @@ -225,7 +227,7 @@ namespace { template ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, - Bitboard target, const CheckInfo* ci) { + Bitboard target) { assert(Pt != KING && Pt != PAWN); @@ -236,17 +238,17 @@ namespace { if (Checks) { if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) - && !(PseudoAttacks[Pt][from] & target & ci->checkSquares[Pt])) + && !(PseudoAttacks[Pt][from] & target & pos.check_squares(Pt))) continue; - if (ci->dcCandidates & from) + if (pos.discovered_check_candidates() & from) continue; } Bitboard b = pos.attacks_from(from) & target; if (Checks) - b &= ci->checkSquares[Pt]; + b &= pos.check_squares(Pt); while (b) *moveList++ = make_move(from, pop_lsb(&b)); @@ -257,16 +259,15 @@ namespace { template - ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target, - const CheckInfo* ci = nullptr) { + ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { const bool Checks = Type == QUIET_CHECKS; - moveList = generate_pawn_moves(pos, moveList, target, ci); - moveList = generate_moves(pos, moveList, Us, target, ci); - moveList = generate_moves(pos, moveList, Us, target, ci); - moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target, ci); - moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target, ci); + 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) { @@ -280,13 +281,13 @@ namespace { { if (pos.is_chess960()) { - moveList = generate_castling::right, Checks, true>(pos, moveList, Us, ci); - moveList = generate_castling::right, Checks, true>(pos, moveList, Us, ci); + moveList = generate_castling::right, Checks, true>(pos, moveList, Us); + moveList = generate_castling::right, Checks, true>(pos, moveList, Us); } else { - moveList = generate_castling::right, Checks, false>(pos, moveList, Us, ci); - moveList = generate_castling::right, Checks, false>(pos, moveList, Us, ci); + moveList = generate_castling::right, Checks, false>(pos, moveList, Us); + moveList = generate_castling::right, Checks, false>(pos, moveList, Us); } } @@ -335,8 +336,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { assert(!pos.checkers()); Color us = pos.side_to_move(); - CheckInfo ci(pos); - Bitboard dc = ci.dcCandidates; + Bitboard dc = pos.discovered_check_candidates(); while (dc) { @@ -349,14 +349,14 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces(); if (pt == KING) - b &= ~PseudoAttacks[QUEEN][ci.ksq]; + b &= ~PseudoAttacks[QUEEN][pos.square(~us)]; while (b) *moveList++ = make_move(from, pop_lsb(&b)); } - return us == WHITE ? generate_all(pos, moveList, ~pos.pieces(), &ci) - : generate_all(pos, moveList, ~pos.pieces(), &ci); + return us == WHITE ? generate_all(pos, moveList, ~pos.pieces()) + : generate_all(pos, moveList, ~pos.pieces()); } @@ -411,7 +411,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { : generate(pos, moveList); while (cur != moveList) if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT) - && !pos.legal(*cur, pinned)) + && !pos.legal(*cur)) *cur = (--moveList)->move; else ++cur; diff --git a/DroidFish/jni/stockfish/movepick.cpp b/DroidFish/jni/stockfish/movepick.cpp index 3e90750..78765be 100644 --- a/DroidFish/jni/stockfish/movepick.cpp +++ b/DroidFish/jni/stockfish/movepick.cpp @@ -142,16 +142,20 @@ template<> void MovePicker::score() { const HistoryStats& history = pos.this_thread()->history; + const FromToStats& fromTo = pos.this_thread()->fromTo; const CounterMoveStats* cm = (ss-1)->counterMoves; const CounterMoveStats* fm = (ss-2)->counterMoves; const CounterMoveStats* f2 = (ss-4)->counterMoves; + Color c = pos.side_to_move(); + for (auto& m : *this) m.value = history[pos.moved_piece(m)][to_sq(m)] + (cm ? (*cm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) + (fm ? (*fm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) - + (f2 ? (*f2)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO); + + (f2 ? (*f2)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) + + fromTo.get(c, m); } template<> @@ -160,6 +164,8 @@ void MovePicker::score() { // by history value, then bad captures and quiet moves with a negative SEE ordered // by SEE value. const HistoryStats& history = pos.this_thread()->history; + const FromToStats& fromTo = pos.this_thread()->fromTo; + Color c = pos.side_to_move(); Value see; for (auto& m : *this) @@ -170,7 +176,7 @@ void MovePicker::score() { m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))) + HistoryStats::Max; else - m.value = history[pos.moved_piece(m)][to_sq(m)]; + m.value = history[pos.moved_piece(m)][to_sq(m)] + fromTo.get(c, m); } diff --git a/DroidFish/jni/stockfish/movepick.h b/DroidFish/jni/stockfish/movepick.h index fffd470..8028d48 100644 --- a/DroidFish/jni/stockfish/movepick.h +++ b/DroidFish/jni/stockfish/movepick.h @@ -66,6 +66,26 @@ typedef Stats HistoryStats; typedef Stats CounterMoveStats; typedef Stats CounterMoveHistoryStats; +struct FromToStats { + + Value get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; } + void clear() { std::memset(table, 0, sizeof(table)); } + + void update(Color c, Move m, Value v) + { + if (abs(int(v)) >= 324) + return; + + Square f = from_sq(m); + Square t = to_sq(m); + + table[c][f][t] -= table[c][f][t] * abs(int(v)) / 324; + table[c][f][t] += int(v) * 32; + } + +private: + Value table[COLOR_NB][SQUARE_NB][SQUARE_NB]; +}; /// 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 diff --git a/DroidFish/jni/stockfish/pawns.cpp b/DroidFish/jni/stockfish/pawns.cpp index 23d00bd..72496fc 100644 --- a/DroidFish/jni/stockfish/pawns.cpp +++ b/DroidFish/jni/stockfish/pawns.cpp @@ -172,9 +172,6 @@ namespace { score += Lever[relative_rank(Us, s)]; } - b = e->semiopenFiles[Us] ^ 0xFF; - e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0; - return score; } diff --git a/DroidFish/jni/stockfish/pawns.h b/DroidFish/jni/stockfish/pawns.h index c2f5fc0..24843e3 100644 --- a/DroidFish/jni/stockfish/pawns.h +++ b/DroidFish/jni/stockfish/pawns.h @@ -37,7 +37,6 @@ struct Entry { 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 pawn_span(Color c) const { return pawnSpan[c]; } int pawn_asymmetry() const { return asymmetry; } int semiopen_file(Color c, File f) const { @@ -73,7 +72,6 @@ struct Entry { Score kingSafety[COLOR_NB]; int castlingRights[COLOR_NB]; int semiopenFiles[COLOR_NB]; - int pawnSpan[COLOR_NB]; int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] int asymmetry; }; diff --git a/DroidFish/jni/stockfish/position.cpp b/DroidFish/jni/stockfish/position.cpp index 6022518..bc8e92a 100644 --- a/DroidFish/jni/stockfish/position.cpp +++ b/DroidFish/jni/stockfish/position.cpp @@ -81,25 +81,6 @@ PieceType min_attacker(const Bitboard*, Square, Bitboard, Bitboard&, Bitbo } // namespace -/// CheckInfo constructor - -CheckInfo::CheckInfo(const Position& pos) { - - Color them = ~pos.side_to_move(); - ksq = pos.square(them); - - pinned = pos.pinned_pieces(pos.side_to_move()); - dcCandidates = pos.discovered_check_candidates(); - - checkSquares[PAWN] = pos.attacks_from(ksq, them); - checkSquares[KNIGHT] = pos.attacks_from(ksq); - checkSquares[BISHOP] = pos.attacks_from(ksq); - checkSquares[ROOK] = pos.attacks_from(ksq); - checkSquares[QUEEN] = checkSquares[BISHOP] | checkSquares[ROOK]; - checkSquares[KING] = 0; -} - - /// operator<<(Position) returns an ASCII representation of the position std::ostream& operator<<(std::ostream& os, const Position& pos) { @@ -311,6 +292,24 @@ void Position::set_castling_right(Color c, Square 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->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK)); + + 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 @@ -321,9 +320,10 @@ void Position::set_state(StateInfo* si) const { si->key = si->pawnKey = si->materialKey = 0; si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; si->psq = SCORE_ZERO; - si->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); + set_check_info(si); + for (Bitboard b = pieces(); b; ) { Square s = pop_lsb(&b); @@ -420,14 +420,14 @@ Phase Position::game_phase() const { } -/// Position::slider_blockers() returns a bitboard of all the pieces in 'target' that +/// 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 target, Bitboard sliders, Square s) const { +Bitboard Position::slider_blockers(Bitboard sliders, Square s) const { Bitboard b, pinners, result = 0; @@ -440,7 +440,7 @@ Bitboard Position::slider_blockers(Bitboard target, Bitboard sliders, Square s) b = between_bb(s, pop_lsb(&pinners)) & pieces(); if (!more_than_one(b)) - result |= b & target; + result |= b; } return result; } @@ -462,10 +462,9 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const { /// Position::legal() tests whether a pseudo-legal move is legal -bool Position::legal(Move m, Bitboard pinned) const { +bool Position::legal(Move m) const { assert(is_ok(m)); - assert(pinned == pinned_pieces(sideToMove)); Color us = sideToMove; Square from = from_sq(m); @@ -500,7 +499,7 @@ bool Position::legal(Move m, Bitboard pinned) const { // 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 !(pinned & from) + return !(pinned_pieces(us) & from) || aligned(from, to_sq(m), square(us)); } @@ -579,22 +578,21 @@ bool Position::pseudo_legal(const Move m) const { /// Position::gives_check() tests whether a pseudo-legal move gives a check -bool Position::gives_check(Move m, const CheckInfo& ci) const { +bool Position::gives_check(Move m) const { assert(is_ok(m)); - assert(ci.dcCandidates == discovered_check_candidates()); assert(color_of(moved_piece(m)) == sideToMove); Square from = from_sq(m); Square to = to_sq(m); // Is there a direct check? - if (ci.checkSquares[type_of(piece_on(from))] & to) + if (st->checkSquares[type_of(piece_on(from))] & to) return true; // Is there a discovered check? - if ( (ci.dcCandidates & from) - && !aligned(from, to, ci.ksq)) + if ( (discovered_check_candidates() & from) + && !aligned(from, to, square(~sideToMove))) return true; switch (type_of(m)) @@ -603,7 +601,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const { return false; case PROMOTION: - return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & ci.ksq; + return attacks_bb(Piece(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 @@ -614,8 +612,8 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const { Square capsq = make_square(file_of(to), rank_of(from)); Bitboard b = (pieces() ^ from ^ capsq) | to; - return (attacks_bb< ROOK>(ci.ksq, b) & pieces(sideToMove, QUEEN, ROOK)) - | (attacks_bb(ci.ksq, b) & pieces(sideToMove, QUEEN, BISHOP)); + return (attacks_bb< ROOK>(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) + | (attacks_bb(square(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP)); } case CASTLING: { @@ -624,8 +622,8 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const { 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] & ci.ksq) - && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ci.ksq); + return (PseudoAttacks[ROOK][rto] & square(~sideToMove)) + && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square(~sideToMove)); } default: assert(false); @@ -801,6 +799,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { sideToMove = ~sideToMove; + // Update king attacks used for fast check detection + set_check_info(st); + assert(pos_is_ok()); } @@ -914,6 +915,8 @@ void Position::do_null_move(StateInfo& newSt) { sideToMove = ~sideToMove; + set_check_info(st); + assert(pos_is_ok()); } diff --git a/DroidFish/jni/stockfish/position.h b/DroidFish/jni/stockfish/position.h index b2538e9..7fce1db 100644 --- a/DroidFish/jni/stockfish/position.h +++ b/DroidFish/jni/stockfish/position.h @@ -41,19 +41,6 @@ namespace PSQT { void init(); } -/// CheckInfo struct is initialized at constructor time and keeps info used to -/// detect if a move gives check. - -struct CheckInfo { - - explicit CheckInfo(const Position&); - - Bitboard dcCandidates; - Bitboard pinned; - Bitboard checkSquares[PIECE_TYPE_NB]; - Square ksq; -}; - /// 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 @@ -76,6 +63,8 @@ struct StateInfo { Bitboard checkersBB; PieceType capturedType; StateInfo* previous; + Bitboard blockersForKing[COLOR_NB]; + Bitboard checkSquares[PIECE_TYPE_NB]; }; // In a std::deque references to elements are unaffected upon resizing @@ -124,6 +113,7 @@ public: Bitboard checkers() const; Bitboard discovered_check_candidates() const; Bitboard pinned_pieces(Color c) const; + Bitboard check_squares(PieceType pt) const; // Attacks to/from a given square Bitboard attackers_to(Square s) const; @@ -131,14 +121,14 @@ public: Bitboard attacks_from(Piece pc, Square s) const; template Bitboard attacks_from(Square s) const; template Bitboard attacks_from(Square s, Color c) const; - Bitboard slider_blockers(Bitboard target, Bitboard sliders, Square s) const; + Bitboard slider_blockers(Bitboard sliders, Square s) const; // Properties of moves - bool legal(Move m, Bitboard pinned) const; + 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 CheckInfo& ci) const; + bool gives_check(Move m) const; bool advanced_pawn_push(Move m) const; Piece moved_piece(Move m) const; PieceType captured_piece_type() const; @@ -185,6 +175,7 @@ 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(Color c, PieceType pt, Square s); @@ -311,11 +302,15 @@ inline Bitboard Position::checkers() const { } inline Bitboard Position::discovered_check_candidates() const { - return slider_blockers(pieces(sideToMove), pieces(sideToMove), square(~sideToMove)); + return st->blockersForKing[~sideToMove] & pieces(sideToMove); } inline Bitboard Position::pinned_pieces(Color c) const { - return slider_blockers(pieces(c), pieces(~c), square(c)); + return st->blockersForKing[c] & pieces(c); +} + +inline Bitboard Position::check_squares(PieceType pt) const { + return st->checkSquares[pt]; } inline bool Position::pawn_passed(Color c, Square s) const { diff --git a/DroidFish/jni/stockfish/search.cpp b/DroidFish/jni/stockfish/search.cpp index aaba40e..cf10674 100644 --- a/DroidFish/jni/stockfish/search.cpp +++ b/DroidFish/jni/stockfish/search.cpp @@ -65,14 +65,14 @@ namespace { // Razoring and futility margin based on depth const int razor_margin[4] = { 483, 570, 603, 554 }; - Value futility_margin(Depth d) { return Value(200 * d); } + Value futility_margin(Depth d) { return Value(150 * d / ONE_PLY); } // Futility and reductions lookup tables, initialized at startup - int FutilityMoveCounts[2][16]; // [improving][depth] - Depth Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber] + int FutilityMoveCounts[2][16]; // [improving][depth] + int Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber] template Depth reduction(bool i, Depth d, int mn) { - return Reductions[PvNode][i][std::min(d, 63 * ONE_PLY)][std::min(mn, 63)]; + return Reductions[PvNode][i][std::min(d / ONE_PLY, 63)][std::min(mn, 63)] * ONE_PLY; } // Skill structure is used to implement strength limit @@ -113,8 +113,8 @@ namespace { std::copy(newPv.begin(), newPv.begin() + 3, pv); StateInfo st[2]; - pos.do_move(newPv[0], st[0], pos.gives_check(newPv[0], CheckInfo(pos))); - pos.do_move(newPv[1], st[1], pos.gives_check(newPv[1], CheckInfo(pos))); + pos.do_move(newPv[0], st[0], pos.gives_check(newPv[0])); + pos.do_move(newPv[1], st[1], pos.gives_check(newPv[1])); expectedPosKey = pos.key(); pos.undo_move(newPv[1]); pos.undo_move(newPv[0]); @@ -168,7 +168,8 @@ namespace { Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); void update_pv(Move* pv, Move move, Move* childPv); - void update_stats(const Position& pos, Stack* ss, Move move, Depth depth, Move* quiets, int quietsCnt); + void update_cm_stats(Stack* ss, Piece pc, Square s, Value bonus); + void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, Value bonus); void check_time(); } // namespace @@ -186,12 +187,12 @@ void Search::init() { if (r < 0.80) continue; - Reductions[NonPV][imp][d][mc] = int(round(r)) * ONE_PLY; - Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - ONE_PLY, DEPTH_ZERO); + Reductions[NonPV][imp][d][mc] = int(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 && Reductions[NonPV][imp][d][mc] >= 2 * ONE_PLY) - Reductions[NonPV][imp][d][mc] += ONE_PLY; + if (!imp && Reductions[NonPV][imp][d][mc] >= 2) + Reductions[NonPV][imp][d][mc]++; } for (int d = 0; d < 16; ++d) @@ -213,6 +214,7 @@ void Search::clear() { { th->history.clear(); th->counterMoves.clear(); + th->fromTo.clear(); } Threads.main()->previousScore = VALUE_INFINITE; @@ -226,7 +228,6 @@ uint64_t Search::perft(Position& pos, Depth depth) { StateInfo st; uint64_t cnt, nodes = 0; - CheckInfo ci(pos); const bool leaf = (depth == 2 * ONE_PLY); for (const auto& m : MoveList(pos)) @@ -235,7 +236,7 @@ uint64_t Search::perft(Position& pos, Depth depth) { cnt = 1, nodes++; else { - pos.do_move(m, st, pos.gives_check(m, ci)); + pos.do_move(m, st, pos.gives_check(m)); cnt = leaf ? MoveList(pos).size() : perft(pos, depth - ONE_PLY); nodes += cnt; pos.undo_move(m); @@ -366,15 +367,17 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - // Iterative deepening loop until requested to stop or the target depth is reached. - while (++rootDepth < DEPTH_MAX && !Signals.stop && (!Limits.depth || Threads.main()->rootDepth <= Limits.depth)) + // Iterative deepening loop until requested to stop or the target depth is reached + while ( (rootDepth += ONE_PLY) < DEPTH_MAX + && !Signals.stop + && (!Limits.depth || Threads.main()->rootDepth / ONE_PLY <= Limits.depth)) { // Set up the new depths for the helper threads skipping on average every // 2nd ply (using a half-density matrix). if (!mainThread) { const Row& row = HalfDensity[(idx - 1) % HalfDensitySize]; - if (row[(rootDepth + rootPos.game_ply()) % row.size()]) + if (row[(rootDepth / ONE_PLY + rootPos.game_ply()) % row.size()]) continue; } @@ -550,6 +553,7 @@ namespace { assert(PvNode || (alpha == beta - 1)); assert(DEPTH_ZERO < depth && depth < DEPTH_MAX); assert(!(PvNode && cutNode)); + assert(depth / ONE_PLY * ONE_PLY == depth); Move pv[MAX_PLY+1], quietsSearched[64]; StateInfo st; @@ -635,9 +639,24 @@ namespace { ss->currentMove = ttMove; // Can be MOVE_NONE // If ttMove is quiet, update killers, history, counter move on TT hit - if (ttValue >= beta && ttMove && !pos.capture_or_promotion(ttMove)) - update_stats(pos, ss, ttMove, depth, nullptr, 0); + if (ttValue >= beta && ttMove) + { + int d = depth / ONE_PLY; + if (!pos.capture_or_promotion(ttMove)) + { + Value bonus = Value(d * d + 2 * d - 2); + update_stats(pos, ss, ttMove, nullptr, 0, bonus); + } + + // Extra penalty for a quiet TT move in previous ply when it gets refuted + if ((ss-1)->moveCount == 1 && !pos.captured_piece_type()) + { + Value penalty = Value(d * d + 4 * d + 1); + Square prevSq = to_sq((ss-1)->currentMove); + update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -penalty); + } + } return ttValue; } @@ -706,14 +725,14 @@ namespace { // Step 6. Razoring (skipped when in check) if ( !PvNode && depth < 4 * ONE_PLY - && eval + razor_margin[depth] <= alpha + && eval + razor_margin[depth / ONE_PLY] <= alpha && ttMove == MOVE_NONE) { if ( depth <= ONE_PLY && eval + razor_margin[3 * ONE_PLY] <= alpha) return qsearch(pos, ss, alpha, beta, DEPTH_ZERO); - Value ralpha = alpha - razor_margin[depth]; + Value ralpha = alpha - razor_margin[depth / ONE_PLY]; Value v = qsearch(pos, ss, ralpha, ralpha+1, DEPTH_ZERO); if (v <= ralpha) return v; @@ -729,7 +748,6 @@ namespace { // Step 8. Null move search with verification search (is omitted in PV nodes) if ( !PvNode - && depth >= 2 * ONE_PLY && eval >= beta && (ss->staticEval >= beta - 35 * (depth / ONE_PLY - 6) || depth >= 13 * ONE_PLY) && pos.non_pawn_material(pos.side_to_move())) @@ -740,7 +758,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = ((823 + 67 * depth) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY; + Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY; pos.do_null_move(st); (ss+1)->skipEarlyPruning = true; @@ -785,14 +803,13 @@ namespace { assert((ss-1)->currentMove != MOVE_NULL); MovePicker mp(pos, ttMove, PieceValue[MG][pos.captured_piece_type()]); - CheckInfo ci(pos); while ((move = mp.next_move()) != MOVE_NONE) - if (pos.legal(move, ci.pinned)) + if (pos.legal(move)) { ss->currentMove = move; ss->counterMoves = &CounterMoveHistory[pos.moved_piece(move)][to_sq(move)]; - pos.do_move(move, st, pos.gives_check(move, ci)); + pos.do_move(move, st, pos.gives_check(move)); value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode); pos.undo_move(move); if (value >= rbeta) @@ -801,11 +818,11 @@ namespace { } // Step 10. Internal iterative deepening (skipped when in check) - if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY) + if ( depth >= 6 * ONE_PLY && !ttMove && (PvNode || ss->staticEval + 256 >= beta)) { - Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4); + Depth d = (3 * depth / (4 * ONE_PLY) - 2) * ONE_PLY; ss->skipEarlyPruning = true; search(pos, ss, alpha, beta, d, cutNode); ss->skipEarlyPruning = false; @@ -821,7 +838,6 @@ moves_loop: // When in check search starts from here const CounterMoveStats* fmh2 = (ss-4)->counterMoves; MovePicker mp(pos, ttMove, depth, ss); - CheckInfo ci(pos); value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc improving = ss->staticEval >= (ss-2)->staticEval /* || ss->staticEval == VALUE_NONE Already implicit in the previous condition */ @@ -866,12 +882,12 @@ moves_loop: // When in check search starts from here captureOrPromotion = pos.capture_or_promotion(move); moved_piece = pos.moved_piece(move); - givesCheck = type_of(move) == NORMAL && !ci.dcCandidates - ? ci.checkSquares[type_of(pos.piece_on(from_sq(move)))] & to_sq(move) - : pos.gives_check(move, ci); + givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates() + ? pos.check_squares(type_of(pos.piece_on(from_sq(move)))) & to_sq(move) + : pos.gives_check(move); moveCountPruning = depth < 16 * ONE_PLY - && moveCount >= FutilityMoveCounts[improving][depth]; + && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY]; // Step 12. Extend checks if ( givesCheck @@ -887,12 +903,13 @@ moves_loop: // When in check search starts from here if ( singularExtensionNode && move == ttMove && !extension - && pos.legal(move, ci.pinned)) + && pos.legal(move)) { Value rBeta = ttValue - 2 * depth / ONE_PLY; + Depth d = (depth / (2 * ONE_PLY)) * ONE_PLY; ss->excludedMove = move; ss->skipEarlyPruning = true; - value = search(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode); + value = search(pos, ss, rBeta - 1, rBeta, d, cutNode); ss->skipEarlyPruning = false; ss->excludedMove = MOVE_NONE; @@ -915,31 +932,38 @@ moves_loop: // When in check search starts from here if (moveCountPruning) continue; + predictedDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO); + // Countermoves based pruning - if ( depth <= 4 * ONE_PLY + if ( predictedDepth < 3 * ONE_PLY && move != ss->killers[0] && (!cmh || (*cmh )[moved_piece][to_sq(move)] < VALUE_ZERO) && (!fmh || (*fmh )[moved_piece][to_sq(move)] < VALUE_ZERO) && (!fmh2 || (*fmh2)[moved_piece][to_sq(move)] < VALUE_ZERO || (cmh && fmh))) continue; - predictedDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO); - // Futility pruning: parent node if ( predictedDepth < 7 * ONE_PLY - && ss->staticEval + futility_margin(predictedDepth) + 256 <= alpha) + && ss->staticEval + 256 + 200 * predictedDepth / ONE_PLY <= alpha) continue; - // Prune moves with negative SEE at low depths - if (predictedDepth < 4 * ONE_PLY && pos.see_sign(move) < VALUE_ZERO) - continue; + // Prune moves with negative SEE at low depths and below a decreasing + // threshold at higher depths. + if (predictedDepth < 8 * ONE_PLY) + { + Value see_v = predictedDepth < 4 * ONE_PLY ? VALUE_ZERO + : -PawnValueMg * 2 * int(predictedDepth - 3 * ONE_PLY) / ONE_PLY; + + if (pos.see_sign(move) < see_v) + continue; + } } // 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, ci.pinned)) + if (!rootNode && !pos.legal(move)) { ss->moveCount = --moveCount; continue; @@ -955,36 +979,42 @@ moves_loop: // When in check search starts from here // re-searched at full depth. if ( depth >= 3 * ONE_PLY && moveCount > 1 - && !captureOrPromotion) + && (!captureOrPromotion || moveCountPruning)) { Depth r = reduction(improving, depth, moveCount); - Value val = thisThread->history[moved_piece][to_sq(move)] - + (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO) - + (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO) - + (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO); - // Increase reduction for cut nodes - if (cutNode) - r += 2 * ONE_PLY; + if (captureOrPromotion) + r -= r ? ONE_PLY : DEPTH_ZERO; + else + { + // Increase reduction for cut nodes + if (cutNode) + r += 2 * ONE_PLY; - // 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(). Also use see() instead of see_sign(), - // because the destination square is empty. - else if ( type_of(move) == NORMAL - && type_of(pos.piece_on(to_sq(move))) != PAWN - && pos.see(make_move(to_sq(move), from_sq(move))) < VALUE_ZERO) - r -= 2 * ONE_PLY; + // 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(). Also use see() instead of see_sign(), + // because the destination square is empty. + else if ( type_of(move) == NORMAL + && type_of(pos.piece_on(to_sq(move))) != PAWN + && pos.see(make_move(to_sq(move), from_sq(move))) < VALUE_ZERO) + r -= 2 * ONE_PLY; - // Decrease/increase reduction for moves with a good/bad history - int rHist = (val - 10000) / 20000; - r = std::max(DEPTH_ZERO, r - rHist * ONE_PLY); + // Decrease/increase reduction for moves with a good/bad history + Value val = thisThread->history[moved_piece][to_sq(move)] + + (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO) + + (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO) + + (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO) + + thisThread->fromTo.get(~pos.side_to_move(), move); + int rHist = (val - 8000) / 20000; + r = std::max(DEPTH_ZERO, (r / ONE_PLY - rHist) * ONE_PLY); + } Depth d = std::max(newDepth - r, ONE_PLY); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - doFullDepthSearch = (value > alpha && r != DEPTH_ZERO); + doFullDepthSearch = (value > alpha && d != newDepth); } else doFullDepthSearch = !PvNode || moveCount > 1; @@ -1098,27 +1128,34 @@ moves_loop: // When in check search starts from here if (!moveCount) bestValue = excludedMove ? alpha : inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; + else if (bestMove) + { + int d = depth / ONE_PLY; - // Quiet best move: update killers, history and countermoves - else if (bestMove && !pos.capture_or_promotion(bestMove)) - update_stats(pos, ss, bestMove, depth, quietsSearched, quietCount); + // Quiet best move: update killers, history and countermoves + if (!pos.capture_or_promotion(bestMove)) + { + Value bonus = Value(d * d + 2 * d - 2); + update_stats(pos, ss, bestMove, quietsSearched, quietCount, bonus); + } + // Extra penalty for a quiet TT move in previous ply when it gets refuted + if ((ss-1)->moveCount == 1 && !pos.captured_piece_type()) + { + Value penalty = Value(d * d + 4 * d + 1); + Square prevSq = to_sq((ss-1)->currentMove); + update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, -penalty); + } + } // Bonus for prior countermove that caused the fail low else if ( depth >= 3 * ONE_PLY - && !bestMove && !pos.captured_piece_type() && is_ok((ss-1)->currentMove)) { + int d = depth / ONE_PLY; + Value bonus = Value(d * d + 2 * d - 2); Square prevSq = to_sq((ss-1)->currentMove); - Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + 2 * depth / ONE_PLY - 2); - if ((ss-2)->counterMoves) - (ss-2)->counterMoves->update(pos.piece_on(prevSq), prevSq, bonus); - - if ((ss-3)->counterMoves) - (ss-3)->counterMoves->update(pos.piece_on(prevSq), prevSq, bonus); - - if ((ss-5)->counterMoves) - (ss-5)->counterMoves->update(pos.piece_on(prevSq), prevSq, bonus); + update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, bonus); } tte->save(posKey, value_to_tt(bestValue, ss->ply), @@ -1145,6 +1182,7 @@ moves_loop: // When in check search starts from here assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); assert(depth <= DEPTH_ZERO); + assert(depth / ONE_PLY * ONE_PLY == depth); Move pv[MAX_PLY+1]; StateInfo st; @@ -1240,16 +1278,15 @@ moves_loop: // When in check search starts from here // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // be generated. MovePicker mp(pos, ttMove, depth, to_sq((ss-1)->currentMove)); - CheckInfo ci(pos); // 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 = type_of(move) == NORMAL && !ci.dcCandidates - ? ci.checkSquares[type_of(pos.piece_on(from_sq(move)))] & to_sq(move) - : pos.gives_check(move, ci); + givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates() + ? pos.check_squares(type_of(pos.piece_on(from_sq(move)))) & to_sq(move) + : pos.gives_check(move); // Futility pruning if ( !InCheck @@ -1289,7 +1326,7 @@ moves_loop: // When in check search starts from here prefetch(TT.first_entry(pos.key_after(move))); // Check for legality just before making the move - if (!pos.legal(move, ci.pinned)) + if (!pos.legal(move)) continue; ss->currentMove = move; @@ -1378,11 +1415,30 @@ moves_loop: // When in check search starts from here } + // update_cm_stats() updates countermove and follow-up move history + + void update_cm_stats(Stack* ss, Piece pc, Square s, Value bonus) { + + CounterMoveStats* cmh = (ss-1)->counterMoves; + CounterMoveStats* fmh1 = (ss-2)->counterMoves; + CounterMoveStats* fmh2 = (ss-4)->counterMoves; + + if (cmh) + cmh->update(pc, s, bonus); + + if (fmh1) + fmh1->update(pc, s, bonus); + + if (fmh2) + fmh2->update(pc, s, bonus); + } + + // update_stats() updates killers, history, countermove and countermove plus // follow-up move history when a new quiet best move is found. void update_stats(const Position& pos, Stack* ss, Move move, - Depth depth, Move* quiets, int quietsCnt) { + Move* quiets, int quietsCnt, Value bonus) { if (ss->killers[0] != move) { @@ -1390,54 +1446,24 @@ moves_loop: // When in check search starts from here ss->killers[0] = move; } - Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + 2 * depth / ONE_PLY - 2); - - Square prevSq = to_sq((ss-1)->currentMove); - CounterMoveStats* cmh = (ss-1)->counterMoves; - CounterMoveStats* fmh = (ss-2)->counterMoves; - CounterMoveStats* fmh2 = (ss-4)->counterMoves; + Color c = pos.side_to_move(); Thread* thisThread = pos.this_thread(); - + thisThread->fromTo.update(c, move, bonus); thisThread->history.update(pos.moved_piece(move), to_sq(move), bonus); + update_cm_stats(ss, pos.moved_piece(move), to_sq(move), bonus); - if (cmh) + if ((ss-1)->counterMoves) { + Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves.update(pos.piece_on(prevSq), prevSq, move); - cmh->update(pos.moved_piece(move), to_sq(move), bonus); } - if (fmh) - fmh->update(pos.moved_piece(move), to_sq(move), bonus); - - if (fmh2) - fmh2->update(pos.moved_piece(move), to_sq(move), bonus); - // Decrease all the other played quiet moves for (int i = 0; i < quietsCnt; ++i) { + thisThread->fromTo.update(c, quiets[i], -bonus); thisThread->history.update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); - - if (cmh) - cmh->update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); - - if (fmh) - fmh->update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); - - if (fmh2) - fmh2->update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); - } - - // Extra penalty for a quiet TT move in previous ply when it gets refuted - if ((ss-1)->moveCount == 1 && !pos.captured_piece_type()) - { - if ((ss-2)->counterMoves) - (ss-2)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY - 1); - - if ((ss-3)->counterMoves) - (ss-3)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY - 1); - - if ((ss-5)->counterMoves) - (ss-5)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY - 1); + update_cm_stats(ss, pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); } } @@ -1572,7 +1598,7 @@ bool RootMove::extract_ponder_from_tt(Position& pos) assert(pv.size() == 1); - pos.do_move(pv[0], st, pos.gives_check(pv[0], CheckInfo(pos))); + pos.do_move(pv[0], st, pos.gives_check(pv[0])); TTEntry* tte = TT.probe(pos.key(), ttHit); if (ttHit) diff --git a/DroidFish/jni/stockfish/syzygy/tbprobe.cpp b/DroidFish/jni/stockfish/syzygy/tbprobe.cpp index 1b05db9..6f6627a 100644 --- a/DroidFish/jni/stockfish/syzygy/tbprobe.cpp +++ b/DroidFish/jni/stockfish/syzygy/tbprobe.cpp @@ -361,14 +361,12 @@ static int probe_ab(Position& pos, int alpha, int beta, int *success) } else end = generate(pos, stack); - CheckInfo ci(pos); - for (moves = stack; moves < end; moves++) { Move capture = moves->move; if (!pos.capture(capture) || type_of(capture) == ENPASSANT - || !pos.legal(capture, ci.pinned)) + || !pos.legal(capture)) continue; - pos.do_move(capture, st, pos.gives_check(capture, ci)); + pos.do_move(capture, st, pos.gives_check(capture)); v = -probe_ab(pos, -beta, -alpha, success); pos.undo_move(capture); if (*success == 0) return 0; @@ -424,14 +422,12 @@ int Tablebases::probe_wdl(Position& pos, int *success) else end = generate(pos, stack); - CheckInfo ci(pos); - for (moves = stack; moves < end; moves++) { Move capture = moves->move; if (type_of(capture) != ENPASSANT - || !pos.legal(capture, ci.pinned)) + || !pos.legal(capture)) continue; - pos.do_move(capture, st, pos.gives_check(capture, ci)); + pos.do_move(capture, st, pos.gives_check(capture)); int v0 = -probe_ab(pos, -2, 2, success); pos.undo_move(capture); if (*success == 0) return 0; @@ -444,13 +440,13 @@ int Tablebases::probe_wdl(Position& pos, int *success) for (moves = stack; moves < end; moves++) { Move capture = moves->move; if (type_of(capture) == ENPASSANT) continue; - if (pos.legal(capture, ci.pinned)) break; + if (pos.legal(capture)) break; } if (moves == end && !pos.checkers()) { end = generate(pos, end); for (; moves < end; moves++) { Move move = moves->move; - if (pos.legal(move, ci.pinned)) + if (pos.legal(move)) break; } } @@ -479,7 +475,6 @@ static int probe_dtz_no_ep(Position& pos, int *success) ExtMove stack[192]; ExtMove *moves, *end = NULL; StateInfo st; - CheckInfo ci(pos); if (wdl > 0) { // Generate at least all legal non-capturing pawn moves @@ -492,9 +487,9 @@ static int probe_dtz_no_ep(Position& pos, int *success) for (moves = stack; moves < end; moves++) { Move move = moves->move; if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move) - || !pos.legal(move, ci.pinned)) + || !pos.legal(move)) continue; - pos.do_move(move, st, pos.gives_check(move, ci)); + pos.do_move(move, st, pos.gives_check(move)); int v = -Tablebases::probe_wdl(pos, success); pos.undo_move(move); if (*success == 0) return 0; @@ -514,9 +509,9 @@ static int probe_dtz_no_ep(Position& pos, int *success) for (moves = stack; moves < end; moves++) { Move move = moves->move; if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN - || !pos.legal(move, ci.pinned)) + || !pos.legal(move)) continue; - pos.do_move(move, st, pos.gives_check(move, ci)); + pos.do_move(move, st, pos.gives_check(move)); int v = -Tablebases::probe_dtz(pos, success); pos.undo_move(move); if (*success == 0) return 0; @@ -533,9 +528,9 @@ static int probe_dtz_no_ep(Position& pos, int *success) for (moves = stack; moves < end; moves++) { int v; Move move = moves->move; - if (!pos.legal(move, ci.pinned)) + if (!pos.legal(move)) continue; - pos.do_move(move, st, pos.gives_check(move, ci)); + pos.do_move(move, st, pos.gives_check(move)); if (st.rule50 == 0) { if (wdl == -2) v = -1; else { @@ -604,14 +599,13 @@ int Tablebases::probe_dtz(Position& pos, int *success) end = generate(pos, stack); else end = generate(pos, stack); - CheckInfo ci(pos); for (moves = stack; moves < end; moves++) { Move capture = moves->move; if (type_of(capture) != ENPASSANT - || !pos.legal(capture, ci.pinned)) + || !pos.legal(capture)) continue; - pos.do_move(capture, st, pos.gives_check(capture, ci)); + pos.do_move(capture, st, pos.gives_check(capture)); int v0 = -probe_ab(pos, -2, 2, success); pos.undo_move(capture); if (*success == 0) return 0; @@ -637,13 +631,13 @@ int Tablebases::probe_dtz(Position& pos, int *success) for (moves = stack; moves < end; moves++) { Move move = moves->move; if (type_of(move) == ENPASSANT) continue; - if (pos.legal(move, ci.pinned)) break; + if (pos.legal(move)) break; } if (moves == end && !pos.checkers()) { end = generate(pos, end); for (; moves < end; moves++) { Move move = moves->move; - if (pos.legal(move, ci.pinned)) + if (pos.legal(move)) break; } } @@ -696,12 +690,11 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& if (!success) return false; StateInfo st; - CheckInfo ci(pos); // Probe each move. for (size_t i = 0; i < rootMoves.size(); i++) { Move move = rootMoves[i].pv[0]; - pos.do_move(move, st, pos.gives_check(move, ci)); + pos.do_move(move, st, pos.gives_check(move)); int v = 0; if (pos.checkers() && dtz > 0) { ExtMove s[192]; @@ -804,14 +797,13 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Val score = wdl_to_Value[wdl + 2]; StateInfo st; - CheckInfo ci(pos); int best = -2; // Probe each move. for (size_t i = 0; i < rootMoves.size(); i++) { Move move = rootMoves[i].pv[0]; - pos.do_move(move, st, pos.gives_check(move, ci)); + pos.do_move(move, st, pos.gives_check(move)); int v = -Tablebases::probe_wdl(pos, &success); pos.undo_move(move); if (!success) return false; diff --git a/DroidFish/jni/stockfish/thread.h b/DroidFish/jni/stockfish/thread.h index 4e63fe4..8181163 100644 --- a/DroidFish/jni/stockfish/thread.h +++ b/DroidFish/jni/stockfish/thread.h @@ -68,6 +68,7 @@ public: Depth rootDepth; HistoryStats history; MoveStats counterMoves; + FromToStats fromTo; Depth completedDepth; std::atomic_bool resetCalls; }; diff --git a/DroidFish/jni/stockfish/tt.cpp b/DroidFish/jni/stockfish/tt.cpp index 151b71f..3c43e12 100644 --- a/DroidFish/jni/stockfish/tt.cpp +++ b/DroidFish/jni/stockfish/tt.cpp @@ -92,8 +92,8 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { // nature we add 259 (256 is the modulus plus 3 to keep the lowest // two bound bits from affecting the result) to calculate the entry // age correctly even after generation8 overflows into the next cycle. - if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 * ONE_PLY - > tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2 * ONE_PLY) + if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 + > tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2) replace = &tte[i]; return found = false, replace; diff --git a/DroidFish/jni/stockfish/tt.h b/DroidFish/jni/stockfish/tt.h index 70283fc..677f38e 100644 --- a/DroidFish/jni/stockfish/tt.h +++ b/DroidFish/jni/stockfish/tt.h @@ -39,18 +39,20 @@ 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 depth() const { return (Depth)(depth8 * int(ONE_PLY)); } Bound bound() const { return (Bound)(genBound8 & 0x3); } void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) { + assert(d / ONE_PLY * ONE_PLY == d); + // Preserve any existing move for the same position if (m || (k >> 48) != key16) move16 = (uint16_t)m; // Don't overwrite more valuable entries if ( (k >> 48) != key16 - || d > depth8 - 4 + || d / ONE_PLY > depth8 - 4 /* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */ || b == BOUND_EXACT) { @@ -58,7 +60,7 @@ struct TTEntry { value16 = (int16_t)v; eval16 = (int16_t)ev; genBound8 = (uint8_t)(g | b); - depth8 = (int8_t)d; + depth8 = (int8_t)(d / ONE_PLY); } } diff --git a/DroidFish/jni/stockfish/types.h b/DroidFish/jni/stockfish/types.h index 10dfdb0..1440f73 100644 --- a/DroidFish/jni/stockfish/types.h +++ b/DroidFish/jni/stockfish/types.h @@ -209,15 +209,17 @@ enum Depth { ONE_PLY = 1, - DEPTH_ZERO = 0, - DEPTH_QS_CHECKS = 0, - DEPTH_QS_NO_CHECKS = -1, - DEPTH_QS_RECAPTURES = -5, + DEPTH_ZERO = 0 * ONE_PLY, + DEPTH_QS_CHECKS = 0 * ONE_PLY, + DEPTH_QS_NO_CHECKS = -1 * ONE_PLY, + DEPTH_QS_RECAPTURES = -5 * ONE_PLY, - DEPTH_NONE = -6, - DEPTH_MAX = MAX_PLY + DEPTH_NONE = -6 * ONE_PLY, + DEPTH_MAX = MAX_PLY * ONE_PLY }; +static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2"); + enum Square { 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, diff --git a/DroidFish/jni/stockfish/uci.cpp b/DroidFish/jni/stockfish/uci.cpp index 4ae6662..b195b87 100644 --- a/DroidFish/jni/stockfish/uci.cpp +++ b/DroidFish/jni/stockfish/uci.cpp @@ -75,7 +75,7 @@ namespace { while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) { States->push_back(StateInfo()); - pos.do_move(m, States->back(), pos.gives_check(m, CheckInfo(pos))); + pos.do_move(m, States->back(), pos.gives_check(m)); } }