DroidFish: Updated stockfish engine to git version from 2016-08-28.

This commit is contained in:
Peter Osterlund 2016-08-29 19:39:55 +02:00
parent e37b5db229
commit b9ad648cbb
16 changed files with 332 additions and 288 deletions

View File

@ -119,7 +119,7 @@ namespace {
// game, indexed by piece type and number of attacked squares in the MobilityArea. // game, indexed by piece type and number of attacked squares in the MobilityArea.
const Score MobilityBonus[][32] = { 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( 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(-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), 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 BishopPawns = S( 8, 12);
const Score RookOnPawn = S( 8, 24); const Score RookOnPawn = S( 8, 24);
const Score TrappedRook = S(92, 0); const Score TrappedRook = S(92, 0);
const Score CloseEnemies = S( 7, 0);
const Score SafeCheck = S(20, 20); const Score SafeCheck = S(20, 20);
const Score OtherCheck = S(10, 10); const Score OtherCheck = S(10, 10);
const Score ThreatByHangingPawn = S(71, 61); const Score ThreatByHangingPawn = S(71, 61);
@ -359,7 +360,7 @@ namespace {
if (Pt == QUEEN) if (Pt == QUEEN)
{ {
// Penalty if any relative pin or discovered attack against the 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; score -= WeakQueen;
} }
} }
@ -379,6 +380,19 @@ namespace {
// evaluate_king() assigns bonuses and penalties to a king of a given color // 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<Color Us, bool DoTrace> template<Color Us, bool DoTrace>
Score evaluate_king(const Position& pos, const EvalInfo& ei) { Score evaluate_king(const Position& pos, const EvalInfo& ei) {
@ -471,6 +485,19 @@ namespace {
score -= KingDanger[std::max(std::min(attackUnits, 399), 0)]; 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) if (DoTrace)
Trace::add(KING, Us, score); Trace::add(KING, Us, score);
@ -481,19 +508,6 @@ namespace {
// evaluate_threats() assigns bonuses according to the types of the attacking // evaluate_threats() assigns bonuses according to the types of the attacking
// and the attacked pieces. // 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<Color Us, bool DoTrace> template<Color Us, bool DoTrace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei) { Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
@ -571,18 +585,6 @@ namespace {
score += ThreatByPawnPush * popcount(b); 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<KING>(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) if (DoTrace)
Trace::add(THREAT, Us, score); Trace::add(THREAT, Us, score);
@ -703,10 +705,10 @@ namespace {
// ...count safe + (behind & safe) with a single popcount // ...count safe + (behind & safe) with a single popcount
int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
int weight = pos.count<KNIGHT>(Us) + pos.count<BISHOP>(Us) bonus = std::min(16, bonus);
+ pos.count<KNIGHT>(Them) + pos.count<BISHOP>(Them); int weight = pos.count<ALL_PIECES>(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 // Endings where weaker side can place his king in front of the opponent's
// pawns are drawish. // pawns are drawish.
else if ( abs(eg) <= BishopValueEg else if ( abs(eg) <= BishopValueEg
&& ei.pi->pawn_span(strongSide) <= 1 && pos.count<PAWN>(strongSide) <= 2
&& !pos.pawn_passed(~strongSide, pos.square<KING>(~strongSide))) && !pos.pawn_passed(~strongSide, pos.square<KING>(~strongSide)))
sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(51) : ScaleFactor(37); sf = ScaleFactor(37 + 7 * pos.count<PAWN>(strongSide));
} }
return sf; return sf;

View File

@ -32,7 +32,7 @@ namespace {
/// Version number. If Version is left empty, then compile date in the format /// Version number. If Version is left empty, then compile date in the format
/// DD-MM-YY and show in engine_info. /// 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 /// 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 /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We

View File

@ -26,7 +26,7 @@
namespace { namespace {
template<CastlingRight Cr, bool Checks, bool Chess960> template<CastlingRight Cr, bool Checks, bool Chess960>
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); static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);
@ -57,10 +57,8 @@ namespace {
Move m = make<CASTLING>(kfrom, rfrom); Move m = make<CASTLING>(kfrom, rfrom);
if (Checks && !pos.gives_check(m, *ci)) if (Checks && !pos.gives_check(m))
return moveList; return moveList;
else
(void)ci; // Silence a warning under MSVC
*moveList++ = m; *moveList++ = m;
return moveList; return moveList;
@ -68,7 +66,7 @@ namespace {
template<GenType Type, Square Delta> template<GenType Type, Square Delta>
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) if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
*moveList++ = make<PROMOTION>(to - Delta, to, QUEEN); *moveList++ = make<PROMOTION>(to - Delta, to, QUEEN);
@ -82,18 +80,17 @@ namespace {
// Knight promotion is the only promotion that can give a direct check // Knight promotion is the only promotion that can give a direct check
// that's not already included in the queen promotion. // 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<PROMOTION>(to - Delta, to, KNIGHT); *moveList++ = make<PROMOTION>(to - Delta, to, KNIGHT);
else else
(void)ci; // Silence a warning under MSVC (void)ksq; // Silence a warning under MSVC
return moveList; return moveList;
} }
template<Color Us, GenType Type> template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
Bitboard target, const CheckInfo* ci) {
// Compute our parametrized parameters at compile time, named according to // Compute our parametrized parameters at compile time, named according to
// the point of view of white side. // the point of view of white side.
@ -129,16 +126,19 @@ namespace {
if (Type == QUIET_CHECKS) if (Type == QUIET_CHECKS)
{ {
b1 &= pos.attacks_from<PAWN>(ci->ksq, Them); Square ksq = pos.square<KING>(Them);
b2 &= pos.attacks_from<PAWN>(ci->ksq, Them);
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn pushes which give discovered check. This is possible only // 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 // 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 // don't generate captures. Note that a possible discovery check
// promotion has been already generated amongst the captures. // 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<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); Bitboard dc1 = shift_bb<Up>(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq);
Bitboard dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares; Bitboard dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1; b1 |= dc1;
@ -172,14 +172,16 @@ namespace {
Bitboard b2 = shift_bb<Left >(pawnsOn7) & enemies; Bitboard b2 = shift_bb<Left >(pawnsOn7) & enemies;
Bitboard b3 = shift_bb<Up >(pawnsOn7) & emptySquares; Bitboard b3 = shift_bb<Up >(pawnsOn7) & emptySquares;
Square ksq = pos.square<KING>(Them);
while (b1) while (b1)
moveList = make_promotions<Type, Right>(moveList, pop_lsb(&b1), ci); moveList = make_promotions<Type, Right>(moveList, pop_lsb(&b1), ksq);
while (b2) while (b2)
moveList = make_promotions<Type, Left >(moveList, pop_lsb(&b2), ci); moveList = make_promotions<Type, Left >(moveList, pop_lsb(&b2), ksq);
while (b3) while (b3)
moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ci); moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ksq);
} }
// Standard and en-passant captures // Standard and en-passant captures
@ -225,7 +227,7 @@ namespace {
template<PieceType Pt, bool Checks> template<PieceType Pt, bool Checks>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
Bitboard target, const CheckInfo* ci) { Bitboard target) {
assert(Pt != KING && Pt != PAWN); assert(Pt != KING && Pt != PAWN);
@ -236,17 +238,17 @@ namespace {
if (Checks) if (Checks)
{ {
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
&& !(PseudoAttacks[Pt][from] & target & ci->checkSquares[Pt])) && !(PseudoAttacks[Pt][from] & target & pos.check_squares(Pt)))
continue; continue;
if (ci->dcCandidates & from) if (pos.discovered_check_candidates() & from)
continue; continue;
} }
Bitboard b = pos.attacks_from<Pt>(from) & target; Bitboard b = pos.attacks_from<Pt>(from) & target;
if (Checks) if (Checks)
b &= ci->checkSquares[Pt]; b &= pos.check_squares(Pt);
while (b) while (b)
*moveList++ = make_move(from, pop_lsb(&b)); *moveList++ = make_move(from, pop_lsb(&b));
@ -257,16 +259,15 @@ namespace {
template<Color Us, GenType Type> template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target, ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) {
const CheckInfo* ci = nullptr) {
const bool Checks = Type == QUIET_CHECKS; const bool Checks = Type == QUIET_CHECKS;
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target, ci); moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target, ci); moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target, ci); moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target);
moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target, ci); moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target);
moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target, ci); moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target);
if (Type != QUIET_CHECKS && Type != EVASIONS) if (Type != QUIET_CHECKS && Type != EVASIONS)
{ {
@ -280,13 +281,13 @@ namespace {
{ {
if (pos.is_chess960()) if (pos.is_chess960())
{ {
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, moveList, Us, ci); moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, moveList, Us);
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, moveList, Us, ci); moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, moveList, Us);
} }
else else
{ {
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, moveList, Us, ci); moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, moveList, Us);
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, moveList, Us, ci); moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, moveList, Us);
} }
} }
@ -335,8 +336,7 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
assert(!pos.checkers()); assert(!pos.checkers());
Color us = pos.side_to_move(); Color us = pos.side_to_move();
CheckInfo ci(pos); Bitboard dc = pos.discovered_check_candidates();
Bitboard dc = ci.dcCandidates;
while (dc) while (dc)
{ {
@ -349,14 +349,14 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces(); Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces();
if (pt == KING) if (pt == KING)
b &= ~PseudoAttacks[QUEEN][ci.ksq]; b &= ~PseudoAttacks[QUEEN][pos.square<KING>(~us)];
while (b) while (b)
*moveList++ = make_move(from, pop_lsb(&b)); *moveList++ = make_move(from, pop_lsb(&b));
} }
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci) return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces())
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci); : generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces());
} }
@ -411,7 +411,7 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
: generate<NON_EVASIONS>(pos, moveList); : generate<NON_EVASIONS>(pos, moveList);
while (cur != moveList) while (cur != moveList)
if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT) if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT)
&& !pos.legal(*cur, pinned)) && !pos.legal(*cur))
*cur = (--moveList)->move; *cur = (--moveList)->move;
else else
++cur; ++cur;

View File

@ -142,16 +142,20 @@ template<>
void MovePicker::score<QUIETS>() { void MovePicker::score<QUIETS>() {
const HistoryStats& history = pos.this_thread()->history; const HistoryStats& history = pos.this_thread()->history;
const FromToStats& fromTo = pos.this_thread()->fromTo;
const CounterMoveStats* cm = (ss-1)->counterMoves; const CounterMoveStats* cm = (ss-1)->counterMoves;
const CounterMoveStats* fm = (ss-2)->counterMoves; const CounterMoveStats* fm = (ss-2)->counterMoves;
const CounterMoveStats* f2 = (ss-4)->counterMoves; const CounterMoveStats* f2 = (ss-4)->counterMoves;
Color c = pos.side_to_move();
for (auto& m : *this) for (auto& m : *this)
m.value = history[pos.moved_piece(m)][to_sq(m)] m.value = history[pos.moved_piece(m)][to_sq(m)]
+ (cm ? (*cm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO) + (cm ? (*cm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO)
+ (fm ? (*fm)[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<> template<>
@ -160,6 +164,8 @@ void MovePicker::score<EVASIONS>() {
// by history value, then bad captures and quiet moves with a negative SEE ordered // by history value, then bad captures and quiet moves with a negative SEE ordered
// by SEE value. // by SEE value.
const HistoryStats& history = pos.this_thread()->history; const HistoryStats& history = pos.this_thread()->history;
const FromToStats& fromTo = pos.this_thread()->fromTo;
Color c = pos.side_to_move();
Value see; Value see;
for (auto& m : *this) for (auto& m : *this)
@ -170,7 +176,7 @@ void MovePicker::score<EVASIONS>() {
m.value = PieceValue[MG][pos.piece_on(to_sq(m))] m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(type_of(pos.moved_piece(m))) + HistoryStats::Max; - Value(type_of(pos.moved_piece(m))) + HistoryStats::Max;
else 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);
} }

View File

@ -66,6 +66,26 @@ typedef Stats<Value, false> HistoryStats;
typedef Stats<Value, true> CounterMoveStats; typedef Stats<Value, true> CounterMoveStats;
typedef Stats<CounterMoveStats> CounterMoveHistoryStats; typedef Stats<CounterMoveStats> 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 /// 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 /// current position. The most important method is next_move(), which returns a

View File

@ -172,9 +172,6 @@ namespace {
score += Lever[relative_rank(Us, s)]; score += Lever[relative_rank(Us, s)];
} }
b = e->semiopenFiles[Us] ^ 0xFF;
e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0;
return score; return score;
} }

View File

@ -37,7 +37,6 @@ struct Entry {
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[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 pawn_asymmetry() const { return asymmetry; }
int semiopen_file(Color c, File f) const { int semiopen_file(Color c, File f) const {
@ -73,7 +72,6 @@ struct Entry {
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int castlingRights[COLOR_NB]; int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB]; int semiopenFiles[COLOR_NB];
int pawnSpan[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
int asymmetry; int asymmetry;
}; };

View File

@ -81,25 +81,6 @@ PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitbo
} // namespace } // namespace
/// CheckInfo constructor
CheckInfo::CheckInfo(const Position& pos) {
Color them = ~pos.side_to_move();
ksq = pos.square<KING>(them);
pinned = pos.pinned_pieces(pos.side_to_move());
dcCandidates = pos.discovered_check_candidates();
checkSquares[PAWN] = pos.attacks_from<PAWN>(ksq, them);
checkSquares[KNIGHT] = pos.attacks_from<KNIGHT>(ksq);
checkSquares[BISHOP] = pos.attacks_from<BISHOP>(ksq);
checkSquares[ROOK] = pos.attacks_from<ROOK>(ksq);
checkSquares[QUEEN] = checkSquares[BISHOP] | checkSquares[ROOK];
checkSquares[KING] = 0;
}
/// operator<<(Position) returns an ASCII representation of the position /// operator<<(Position) returns an ASCII representation of the position
std::ostream& operator<<(std::ostream& os, const Position& pos) { 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<KING>(WHITE));
si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK));
Square ksq = square<KING>(~sideToMove);
si->checkSquares[PAWN] = attacks_from<PAWN>(ksq, ~sideToMove);
si->checkSquares[KNIGHT] = attacks_from<KNIGHT>(ksq);
si->checkSquares[BISHOP] = attacks_from<BISHOP>(ksq);
si->checkSquares[ROOK] = attacks_from<ROOK>(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 /// Position::set_state() computes the hash keys of the position, and other
/// data that once computed is updated incrementally as moves are made. /// 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 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->key = si->pawnKey = si->materialKey = 0;
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
si->psq = SCORE_ZERO; si->psq = SCORE_ZERO;
si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove); si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
set_check_info(si);
for (Bitboard b = pieces(); b; ) for (Bitboard b = pieces(); b; )
{ {
Square s = pop_lsb(&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 /// 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' /// 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 /// 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 /// a discovered check piece, according if its color is the opposite or the same of
/// the color of the slider. /// 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; 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(); b = between_bb(s, pop_lsb(&pinners)) & pieces();
if (!more_than_one(b)) if (!more_than_one(b))
result |= b & target; result |= b;
} }
return result; 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 /// 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(is_ok(m));
assert(pinned == pinned_pieces(sideToMove));
Color us = sideToMove; Color us = sideToMove;
Square from = from_sq(m); 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 // 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. // 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<KING>(us)); || aligned(from, to_sq(m), square<KING>(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 /// 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(is_ok(m));
assert(ci.dcCandidates == discovered_check_candidates());
assert(color_of(moved_piece(m)) == sideToMove); assert(color_of(moved_piece(m)) == sideToMove);
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
// Is there a direct check? // 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; return true;
// Is there a discovered check? // Is there a discovered check?
if ( (ci.dcCandidates & from) if ( (discovered_check_candidates() & from)
&& !aligned(from, to, ci.ksq)) && !aligned(from, to, square<KING>(~sideToMove)))
return true; return true;
switch (type_of(m)) switch (type_of(m))
@ -603,7 +601,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
return false; return false;
case PROMOTION: 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<KING>(~sideToMove);
// En passant capture with check? We have already handled the case // En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, so the only case we // 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)); Square capsq = make_square(file_of(to), rank_of(from));
Bitboard b = (pieces() ^ from ^ capsq) | to; Bitboard b = (pieces() ^ from ^ capsq) | to;
return (attacks_bb< ROOK>(ci.ksq, b) & pieces(sideToMove, QUEEN, ROOK)) return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
| (attacks_bb<BISHOP>(ci.ksq, b) & pieces(sideToMove, QUEEN, BISHOP)); | (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
} }
case CASTLING: 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 kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1); Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
return (PseudoAttacks[ROOK][rto] & ci.ksq) return (PseudoAttacks[ROOK][rto] & square<KING>(~sideToMove))
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ci.ksq); && (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
} }
default: default:
assert(false); assert(false);
@ -801,6 +799,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
sideToMove = ~sideToMove; sideToMove = ~sideToMove;
// Update king attacks used for fast check detection
set_check_info(st);
assert(pos_is_ok()); assert(pos_is_ok());
} }
@ -914,6 +915,8 @@ void Position::do_null_move(StateInfo& newSt) {
sideToMove = ~sideToMove; sideToMove = ~sideToMove;
set_check_info(st);
assert(pos_is_ok()); assert(pos_is_ok());
} }

View File

@ -41,19 +41,6 @@ namespace PSQT {
void init(); 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 /// 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 /// its previous state when we retract a move. Whenever a move is made on the
@ -76,6 +63,8 @@ struct StateInfo {
Bitboard checkersBB; Bitboard checkersBB;
PieceType capturedType; PieceType capturedType;
StateInfo* previous; StateInfo* previous;
Bitboard blockersForKing[COLOR_NB];
Bitboard checkSquares[PIECE_TYPE_NB];
}; };
// In a std::deque references to elements are unaffected upon resizing // In a std::deque references to elements are unaffected upon resizing
@ -124,6 +113,7 @@ public:
Bitboard checkers() const; Bitboard checkers() const;
Bitboard discovered_check_candidates() const; Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces(Color c) const; Bitboard pinned_pieces(Color c) const;
Bitboard check_squares(PieceType pt) const;
// Attacks to/from a given square // Attacks to/from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
@ -131,14 +121,14 @@ public:
Bitboard attacks_from(Piece pc, Square s) const; Bitboard attacks_from(Piece pc, Square s) const;
template<PieceType> Bitboard attacks_from(Square s) const; template<PieceType> Bitboard attacks_from(Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const; template<PieceType> 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 // Properties of moves
bool legal(Move m, Bitboard pinned) const; bool legal(Move m) const;
bool pseudo_legal(const Move m) const; bool pseudo_legal(const Move m) const;
bool capture(Move m) const; bool capture(Move m) const;
bool capture_or_promotion(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; bool advanced_pawn_push(Move m) const;
Piece moved_piece(Move m) const; Piece moved_piece(Move m) const;
PieceType captured_piece_type() const; PieceType captured_piece_type() const;
@ -185,6 +175,7 @@ private:
// Initialization helpers (used while setting up a position) // Initialization helpers (used while setting up a position)
void set_castling_right(Color c, Square rfrom); void set_castling_right(Color c, Square rfrom);
void set_state(StateInfo* si) const; void set_state(StateInfo* si) const;
void set_check_info(StateInfo* si) const;
// Other helpers // Other helpers
void put_piece(Color c, PieceType pt, Square s); 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 { inline Bitboard Position::discovered_check_candidates() const {
return slider_blockers(pieces(sideToMove), pieces(sideToMove), square<KING>(~sideToMove)); return st->blockersForKing[~sideToMove] & pieces(sideToMove);
} }
inline Bitboard Position::pinned_pieces(Color c) const { inline Bitboard Position::pinned_pieces(Color c) const {
return slider_blockers(pieces(c), pieces(~c), square<KING>(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 { inline bool Position::pawn_passed(Color c, Square s) const {

View File

@ -65,14 +65,14 @@ namespace {
// Razoring and futility margin based on depth // Razoring and futility margin based on depth
const int razor_margin[4] = { 483, 570, 603, 554 }; 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 // Futility and reductions lookup tables, initialized at startup
int FutilityMoveCounts[2][16]; // [improving][depth] int FutilityMoveCounts[2][16]; // [improving][depth]
Depth Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber] int Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
template <bool PvNode> Depth reduction(bool i, Depth d, int mn) { template <bool PvNode> 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 // Skill structure is used to implement strength limit
@ -113,8 +113,8 @@ namespace {
std::copy(newPv.begin(), newPv.begin() + 3, pv); std::copy(newPv.begin(), newPv.begin() + 3, pv);
StateInfo st[2]; StateInfo st[2];
pos.do_move(newPv[0], st[0], pos.gives_check(newPv[0], 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], CheckInfo(pos))); pos.do_move(newPv[1], st[1], pos.gives_check(newPv[1]));
expectedPosKey = pos.key(); expectedPosKey = pos.key();
pos.undo_move(newPv[1]); pos.undo_move(newPv[1]);
pos.undo_move(newPv[0]); pos.undo_move(newPv[0]);
@ -168,7 +168,8 @@ namespace {
Value value_to_tt(Value v, int ply); Value value_to_tt(Value v, int ply);
Value value_from_tt(Value v, int ply); Value value_from_tt(Value v, int ply);
void update_pv(Move* pv, Move move, Move* childPv); 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(); void check_time();
} // namespace } // namespace
@ -186,12 +187,12 @@ void Search::init() {
if (r < 0.80) if (r < 0.80)
continue; continue;
Reductions[NonPV][imp][d][mc] = int(round(r)) * ONE_PLY; Reductions[NonPV][imp][d][mc] = int(round(r));
Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - ONE_PLY, DEPTH_ZERO); 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 // Increase reduction for non-PV nodes when eval is not improving
if (!imp && Reductions[NonPV][imp][d][mc] >= 2 * ONE_PLY) if (!imp && Reductions[NonPV][imp][d][mc] >= 2)
Reductions[NonPV][imp][d][mc] += ONE_PLY; Reductions[NonPV][imp][d][mc]++;
} }
for (int d = 0; d < 16; ++d) for (int d = 0; d < 16; ++d)
@ -213,6 +214,7 @@ void Search::clear() {
{ {
th->history.clear(); th->history.clear();
th->counterMoves.clear(); th->counterMoves.clear();
th->fromTo.clear();
} }
Threads.main()->previousScore = VALUE_INFINITE; Threads.main()->previousScore = VALUE_INFINITE;
@ -226,7 +228,6 @@ uint64_t Search::perft(Position& pos, Depth depth) {
StateInfo st; StateInfo st;
uint64_t cnt, nodes = 0; uint64_t cnt, nodes = 0;
CheckInfo ci(pos);
const bool leaf = (depth == 2 * ONE_PLY); const bool leaf = (depth == 2 * ONE_PLY);
for (const auto& m : MoveList<LEGAL>(pos)) for (const auto& m : MoveList<LEGAL>(pos))
@ -235,7 +236,7 @@ uint64_t Search::perft(Position& pos, Depth depth) {
cnt = 1, nodes++; cnt = 1, nodes++;
else else
{ {
pos.do_move(m, st, pos.gives_check(m, ci)); pos.do_move(m, st, pos.gives_check(m));
cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - ONE_PLY); cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - ONE_PLY);
nodes += cnt; nodes += cnt;
pos.undo_move(m); pos.undo_move(m);
@ -366,15 +367,17 @@ void Thread::search() {
multiPV = std::min(multiPV, rootMoves.size()); multiPV = std::min(multiPV, rootMoves.size());
// Iterative deepening loop until requested to stop or the target depth is reached. // 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)) 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 // Set up the new depths for the helper threads skipping on average every
// 2nd ply (using a half-density matrix). // 2nd ply (using a half-density matrix).
if (!mainThread) if (!mainThread)
{ {
const Row& row = HalfDensity[(idx - 1) % HalfDensitySize]; 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; continue;
} }
@ -550,6 +553,7 @@ namespace {
assert(PvNode || (alpha == beta - 1)); assert(PvNode || (alpha == beta - 1));
assert(DEPTH_ZERO < depth && depth < DEPTH_MAX); assert(DEPTH_ZERO < depth && depth < DEPTH_MAX);
assert(!(PvNode && cutNode)); assert(!(PvNode && cutNode));
assert(depth / ONE_PLY * ONE_PLY == depth);
Move pv[MAX_PLY+1], quietsSearched[64]; Move pv[MAX_PLY+1], quietsSearched[64];
StateInfo st; StateInfo st;
@ -635,9 +639,24 @@ namespace {
ss->currentMove = ttMove; // Can be MOVE_NONE ss->currentMove = ttMove; // Can be MOVE_NONE
// If ttMove is quiet, update killers, history, counter move on TT hit // If ttMove is quiet, update killers, history, counter move on TT hit
if (ttValue >= beta && ttMove && !pos.capture_or_promotion(ttMove)) if (ttValue >= beta && ttMove)
update_stats(pos, ss, ttMove, depth, nullptr, 0); {
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; return ttValue;
} }
@ -706,14 +725,14 @@ namespace {
// Step 6. Razoring (skipped when in check) // Step 6. Razoring (skipped when in check)
if ( !PvNode if ( !PvNode
&& depth < 4 * ONE_PLY && depth < 4 * ONE_PLY
&& eval + razor_margin[depth] <= alpha && eval + razor_margin[depth / ONE_PLY] <= alpha
&& ttMove == MOVE_NONE) && ttMove == MOVE_NONE)
{ {
if ( depth <= ONE_PLY if ( depth <= ONE_PLY
&& eval + razor_margin[3 * ONE_PLY] <= alpha) && eval + razor_margin[3 * ONE_PLY] <= alpha)
return qsearch<NonPV, false>(pos, ss, alpha, beta, DEPTH_ZERO); return qsearch<NonPV, false>(pos, ss, alpha, beta, DEPTH_ZERO);
Value ralpha = alpha - razor_margin[depth]; Value ralpha = alpha - razor_margin[depth / ONE_PLY];
Value v = qsearch<NonPV, false>(pos, ss, ralpha, ralpha+1, DEPTH_ZERO); Value v = qsearch<NonPV, false>(pos, ss, ralpha, ralpha+1, DEPTH_ZERO);
if (v <= ralpha) if (v <= ralpha)
return v; return v;
@ -729,7 +748,6 @@ namespace {
// Step 8. Null move search with verification search (is omitted in PV nodes) // Step 8. Null move search with verification search (is omitted in PV nodes)
if ( !PvNode if ( !PvNode
&& depth >= 2 * ONE_PLY
&& eval >= beta && eval >= beta
&& (ss->staticEval >= beta - 35 * (depth / ONE_PLY - 6) || depth >= 13 * ONE_PLY) && (ss->staticEval >= beta - 35 * (depth / ONE_PLY - 6) || depth >= 13 * ONE_PLY)
&& pos.non_pawn_material(pos.side_to_move())) && pos.non_pawn_material(pos.side_to_move()))
@ -740,7 +758,7 @@ namespace {
assert(eval - beta >= 0); assert(eval - beta >= 0);
// Null move dynamic reduction based on depth and value // 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); pos.do_null_move(st);
(ss+1)->skipEarlyPruning = true; (ss+1)->skipEarlyPruning = true;
@ -785,14 +803,13 @@ namespace {
assert((ss-1)->currentMove != MOVE_NULL); assert((ss-1)->currentMove != MOVE_NULL);
MovePicker mp(pos, ttMove, PieceValue[MG][pos.captured_piece_type()]); MovePicker mp(pos, ttMove, PieceValue[MG][pos.captured_piece_type()]);
CheckInfo ci(pos);
while ((move = mp.next_move()) != MOVE_NONE) while ((move = mp.next_move()) != MOVE_NONE)
if (pos.legal(move, ci.pinned)) if (pos.legal(move))
{ {
ss->currentMove = move; ss->currentMove = move;
ss->counterMoves = &CounterMoveHistory[pos.moved_piece(move)][to_sq(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<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode); value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode);
pos.undo_move(move); pos.undo_move(move);
if (value >= rbeta) if (value >= rbeta)
@ -801,11 +818,11 @@ namespace {
} }
// Step 10. Internal iterative deepening (skipped when in check) // Step 10. Internal iterative deepening (skipped when in check)
if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY) if ( depth >= 6 * ONE_PLY
&& !ttMove && !ttMove
&& (PvNode || ss->staticEval + 256 >= beta)) && (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; ss->skipEarlyPruning = true;
search<NT>(pos, ss, alpha, beta, d, cutNode); search<NT>(pos, ss, alpha, beta, d, cutNode);
ss->skipEarlyPruning = false; ss->skipEarlyPruning = false;
@ -821,7 +838,6 @@ moves_loop: // When in check search starts from here
const CounterMoveStats* fmh2 = (ss-4)->counterMoves; const CounterMoveStats* fmh2 = (ss-4)->counterMoves;
MovePicker mp(pos, ttMove, depth, ss); MovePicker mp(pos, ttMove, depth, ss);
CheckInfo ci(pos);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
improving = ss->staticEval >= (ss-2)->staticEval improving = ss->staticEval >= (ss-2)->staticEval
/* || ss->staticEval == VALUE_NONE Already implicit in the previous condition */ /* || 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); captureOrPromotion = pos.capture_or_promotion(move);
moved_piece = pos.moved_piece(move); moved_piece = pos.moved_piece(move);
givesCheck = type_of(move) == NORMAL && !ci.dcCandidates givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates()
? ci.checkSquares[type_of(pos.piece_on(from_sq(move)))] & to_sq(move) ? pos.check_squares(type_of(pos.piece_on(from_sq(move)))) & to_sq(move)
: pos.gives_check(move, ci); : pos.gives_check(move);
moveCountPruning = depth < 16 * ONE_PLY moveCountPruning = depth < 16 * ONE_PLY
&& moveCount >= FutilityMoveCounts[improving][depth]; && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY];
// Step 12. Extend checks // Step 12. Extend checks
if ( givesCheck if ( givesCheck
@ -887,12 +903,13 @@ moves_loop: // When in check search starts from here
if ( singularExtensionNode if ( singularExtensionNode
&& move == ttMove && move == ttMove
&& !extension && !extension
&& pos.legal(move, ci.pinned)) && pos.legal(move))
{ {
Value rBeta = ttValue - 2 * depth / ONE_PLY; Value rBeta = ttValue - 2 * depth / ONE_PLY;
Depth d = (depth / (2 * ONE_PLY)) * ONE_PLY;
ss->excludedMove = move; ss->excludedMove = move;
ss->skipEarlyPruning = true; ss->skipEarlyPruning = true;
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode); value = search<NonPV>(pos, ss, rBeta - 1, rBeta, d, cutNode);
ss->skipEarlyPruning = false; ss->skipEarlyPruning = false;
ss->excludedMove = MOVE_NONE; ss->excludedMove = MOVE_NONE;
@ -915,31 +932,38 @@ moves_loop: // When in check search starts from here
if (moveCountPruning) if (moveCountPruning)
continue; continue;
predictedDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO);
// Countermoves based pruning // Countermoves based pruning
if ( depth <= 4 * ONE_PLY if ( predictedDepth < 3 * ONE_PLY
&& move != ss->killers[0] && move != ss->killers[0]
&& (!cmh || (*cmh )[moved_piece][to_sq(move)] < VALUE_ZERO) && (!cmh || (*cmh )[moved_piece][to_sq(move)] < VALUE_ZERO)
&& (!fmh || (*fmh )[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))) && (!fmh2 || (*fmh2)[moved_piece][to_sq(move)] < VALUE_ZERO || (cmh && fmh)))
continue; continue;
predictedDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO);
// Futility pruning: parent node // Futility pruning: parent node
if ( predictedDepth < 7 * ONE_PLY if ( predictedDepth < 7 * ONE_PLY
&& ss->staticEval + futility_margin(predictedDepth) + 256 <= alpha) && ss->staticEval + 256 + 200 * predictedDepth / ONE_PLY <= alpha)
continue; continue;
// Prune moves with negative SEE at low depths // Prune moves with negative SEE at low depths and below a decreasing
if (predictedDepth < 4 * ONE_PLY && pos.see_sign(move) < VALUE_ZERO) // threshold at higher depths.
continue; 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 // Speculative prefetch as early as possible
prefetch(TT.first_entry(pos.key_after(move))); prefetch(TT.first_entry(pos.key_after(move)));
// Check for legality just before making the move // Check for legality just before making the move
if (!rootNode && !pos.legal(move, ci.pinned)) if (!rootNode && !pos.legal(move))
{ {
ss->moveCount = --moveCount; ss->moveCount = --moveCount;
continue; continue;
@ -955,36 +979,42 @@ moves_loop: // When in check search starts from here
// re-searched at full depth. // re-searched at full depth.
if ( depth >= 3 * ONE_PLY if ( depth >= 3 * ONE_PLY
&& moveCount > 1 && moveCount > 1
&& !captureOrPromotion) && (!captureOrPromotion || moveCountPruning))
{ {
Depth r = reduction<PvNode>(improving, depth, moveCount); Depth r = reduction<PvNode>(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 (captureOrPromotion)
if (cutNode) r -= r ? ONE_PLY : DEPTH_ZERO;
r += 2 * ONE_PLY; else
{
// Increase reduction for cut nodes
if (cutNode)
r += 2 * ONE_PLY;
// Decrease reduction for moves that escape a capture. Filter out // Decrease reduction for moves that escape a capture. Filter out
// castling moves, because they are coded as "king captures rook" and // castling moves, because they are coded as "king captures rook" and
// hence break make_move(). Also use see() instead of see_sign(), // hence break make_move(). Also use see() instead of see_sign(),
// because the destination square is empty. // because the destination square is empty.
else if ( type_of(move) == NORMAL else if ( type_of(move) == NORMAL
&& type_of(pos.piece_on(to_sq(move))) != PAWN && type_of(pos.piece_on(to_sq(move))) != PAWN
&& pos.see(make_move(to_sq(move), from_sq(move))) < VALUE_ZERO) && pos.see(make_move(to_sq(move), from_sq(move))) < VALUE_ZERO)
r -= 2 * ONE_PLY; r -= 2 * ONE_PLY;
// Decrease/increase reduction for moves with a good/bad history // Decrease/increase reduction for moves with a good/bad history
int rHist = (val - 10000) / 20000; Value val = thisThread->history[moved_piece][to_sq(move)]
r = std::max(DEPTH_ZERO, r - rHist * ONE_PLY); + (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); Depth d = std::max(newDepth - r, ONE_PLY);
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true); value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
doFullDepthSearch = (value > alpha && r != DEPTH_ZERO); doFullDepthSearch = (value > alpha && d != newDepth);
} }
else else
doFullDepthSearch = !PvNode || moveCount > 1; doFullDepthSearch = !PvNode || moveCount > 1;
@ -1098,27 +1128,34 @@ moves_loop: // When in check search starts from here
if (!moveCount) if (!moveCount)
bestValue = excludedMove ? alpha bestValue = excludedMove ? alpha
: inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; : 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 // Quiet best move: update killers, history and countermoves
else if (bestMove && !pos.capture_or_promotion(bestMove)) if (!pos.capture_or_promotion(bestMove))
update_stats(pos, ss, bestMove, depth, quietsSearched, quietCount); {
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 // Bonus for prior countermove that caused the fail low
else if ( depth >= 3 * ONE_PLY else if ( depth >= 3 * ONE_PLY
&& !bestMove
&& !pos.captured_piece_type() && !pos.captured_piece_type()
&& is_ok((ss-1)->currentMove)) && 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); Square prevSq = to_sq((ss-1)->currentMove);
Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + 2 * depth / ONE_PLY - 2); update_cm_stats(ss-1, pos.piece_on(prevSq), prevSq, bonus);
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);
} }
tte->save(posKey, value_to_tt(bestValue, ss->ply), 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(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
assert(PvNode || (alpha == beta - 1)); assert(PvNode || (alpha == beta - 1));
assert(depth <= DEPTH_ZERO); assert(depth <= DEPTH_ZERO);
assert(depth / ONE_PLY * ONE_PLY == depth);
Move pv[MAX_PLY+1]; Move pv[MAX_PLY+1];
StateInfo st; 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 // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
// be generated. // be generated.
MovePicker mp(pos, ttMove, depth, to_sq((ss-1)->currentMove)); 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 // Loop through the moves until no moves remain or a beta cutoff occurs
while ((move = mp.next_move()) != MOVE_NONE) while ((move = mp.next_move()) != MOVE_NONE)
{ {
assert(is_ok(move)); assert(is_ok(move));
givesCheck = type_of(move) == NORMAL && !ci.dcCandidates givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates()
? ci.checkSquares[type_of(pos.piece_on(from_sq(move)))] & to_sq(move) ? pos.check_squares(type_of(pos.piece_on(from_sq(move)))) & to_sq(move)
: pos.gives_check(move, ci); : pos.gives_check(move);
// Futility pruning // Futility pruning
if ( !InCheck if ( !InCheck
@ -1289,7 +1326,7 @@ moves_loop: // When in check search starts from here
prefetch(TT.first_entry(pos.key_after(move))); prefetch(TT.first_entry(pos.key_after(move)));
// Check for legality just before making the move // Check for legality just before making the move
if (!pos.legal(move, ci.pinned)) if (!pos.legal(move))
continue; continue;
ss->currentMove = move; 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 // update_stats() updates killers, history, countermove and countermove plus
// follow-up move history when a new quiet best move is found. // follow-up move history when a new quiet best move is found.
void update_stats(const Position& pos, Stack* ss, Move move, 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) if (ss->killers[0] != move)
{ {
@ -1390,54 +1446,24 @@ moves_loop: // When in check search starts from here
ss->killers[0] = move; ss->killers[0] = move;
} }
Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + 2 * depth / ONE_PLY - 2); Color c = pos.side_to_move();
Square prevSq = to_sq((ss-1)->currentMove);
CounterMoveStats* cmh = (ss-1)->counterMoves;
CounterMoveStats* fmh = (ss-2)->counterMoves;
CounterMoveStats* fmh2 = (ss-4)->counterMoves;
Thread* thisThread = pos.this_thread(); Thread* thisThread = pos.this_thread();
thisThread->fromTo.update(c, move, bonus);
thisThread->history.update(pos.moved_piece(move), to_sq(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); 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 // Decrease all the other played quiet moves
for (int i = 0; i < quietsCnt; ++i) 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); thisThread->history.update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
update_cm_stats(ss, 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);
} }
} }
@ -1572,7 +1598,7 @@ bool RootMove::extract_ponder_from_tt(Position& pos)
assert(pv.size() == 1); 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); TTEntry* tte = TT.probe(pos.key(), ttHit);
if (ttHit) if (ttHit)

View File

@ -361,14 +361,12 @@ static int probe_ab(Position& pos, int alpha, int beta, int *success)
} else } else
end = generate<EVASIONS>(pos, stack); end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move capture = moves->move; Move capture = moves->move;
if (!pos.capture(capture) || type_of(capture) == ENPASSANT if (!pos.capture(capture) || type_of(capture) == ENPASSANT
|| !pos.legal(capture, ci.pinned)) || !pos.legal(capture))
continue; 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); v = -probe_ab(pos, -beta, -alpha, success);
pos.undo_move(capture); pos.undo_move(capture);
if (*success == 0) return 0; if (*success == 0) return 0;
@ -424,14 +422,12 @@ int Tablebases::probe_wdl(Position& pos, int *success)
else else
end = generate<EVASIONS>(pos, stack); end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move capture = moves->move; Move capture = moves->move;
if (type_of(capture) != ENPASSANT if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned)) || !pos.legal(capture))
continue; 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); int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture); pos.undo_move(capture);
if (*success == 0) return 0; if (*success == 0) return 0;
@ -444,13 +440,13 @@ int Tablebases::probe_wdl(Position& pos, int *success)
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move capture = moves->move; Move capture = moves->move;
if (type_of(capture) == ENPASSANT) continue; if (type_of(capture) == ENPASSANT) continue;
if (pos.legal(capture, ci.pinned)) break; if (pos.legal(capture)) break;
} }
if (moves == end && !pos.checkers()) { if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end); end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) { for (; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (pos.legal(move, ci.pinned)) if (pos.legal(move))
break; break;
} }
} }
@ -479,7 +475,6 @@ static int probe_dtz_no_ep(Position& pos, int *success)
ExtMove stack[192]; ExtMove stack[192];
ExtMove *moves, *end = NULL; ExtMove *moves, *end = NULL;
StateInfo st; StateInfo st;
CheckInfo ci(pos);
if (wdl > 0) { if (wdl > 0) {
// Generate at least all legal non-capturing pawn moves // 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++) { for (moves = stack; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move) if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move)
|| !pos.legal(move, ci.pinned)) || !pos.legal(move))
continue; 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); int v = -Tablebases::probe_wdl(pos, success);
pos.undo_move(move); pos.undo_move(move);
if (*success == 0) return 0; 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++) { for (moves = stack; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN
|| !pos.legal(move, ci.pinned)) || !pos.legal(move))
continue; 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); int v = -Tablebases::probe_dtz(pos, success);
pos.undo_move(move); pos.undo_move(move);
if (*success == 0) return 0; 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++) { for (moves = stack; moves < end; moves++) {
int v; int v;
Move move = moves->move; Move move = moves->move;
if (!pos.legal(move, ci.pinned)) if (!pos.legal(move))
continue; 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 (st.rule50 == 0) {
if (wdl == -2) v = -1; if (wdl == -2) v = -1;
else { else {
@ -604,14 +599,13 @@ int Tablebases::probe_dtz(Position& pos, int *success)
end = generate<CAPTURES>(pos, stack); end = generate<CAPTURES>(pos, stack);
else else
end = generate<EVASIONS>(pos, stack); end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move capture = moves->move; Move capture = moves->move;
if (type_of(capture) != ENPASSANT if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned)) || !pos.legal(capture))
continue; 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); int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture); pos.undo_move(capture);
if (*success == 0) return 0; if (*success == 0) return 0;
@ -637,13 +631,13 @@ int Tablebases::probe_dtz(Position& pos, int *success)
for (moves = stack; moves < end; moves++) { for (moves = stack; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (type_of(move) == ENPASSANT) continue; if (type_of(move) == ENPASSANT) continue;
if (pos.legal(move, ci.pinned)) break; if (pos.legal(move)) break;
} }
if (moves == end && !pos.checkers()) { if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end); end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) { for (; moves < end; moves++) {
Move move = moves->move; Move move = moves->move;
if (pos.legal(move, ci.pinned)) if (pos.legal(move))
break; break;
} }
} }
@ -696,12 +690,11 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value&
if (!success) return false; if (!success) return false;
StateInfo st; StateInfo st;
CheckInfo ci(pos);
// Probe each move. // Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) { for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0]; 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; int v = 0;
if (pos.checkers() && dtz > 0) { if (pos.checkers() && dtz > 0) {
ExtMove s[192]; ExtMove s[192];
@ -804,14 +797,13 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Val
score = wdl_to_Value[wdl + 2]; score = wdl_to_Value[wdl + 2];
StateInfo st; StateInfo st;
CheckInfo ci(pos);
int best = -2; int best = -2;
// Probe each move. // Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) { for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0]; 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); int v = -Tablebases::probe_wdl(pos, &success);
pos.undo_move(move); pos.undo_move(move);
if (!success) return false; if (!success) return false;

View File

@ -68,6 +68,7 @@ public:
Depth rootDepth; Depth rootDepth;
HistoryStats history; HistoryStats history;
MoveStats counterMoves; MoveStats counterMoves;
FromToStats fromTo;
Depth completedDepth; Depth completedDepth;
std::atomic_bool resetCalls; std::atomic_bool resetCalls;
}; };

View File

@ -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 // 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 // two bound bits from affecting the result) to calculate the entry
// age correctly even after generation8 overflows into the next cycle. // age correctly even after generation8 overflows into the next cycle.
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 * ONE_PLY if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2 * ONE_PLY) > tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2)
replace = &tte[i]; replace = &tte[i];
return found = false, replace; return found = false, replace;

View File

@ -39,18 +39,20 @@ struct TTEntry {
Move move() const { return (Move )move16; } Move move() const { return (Move )move16; }
Value value() const { return (Value)value16; } Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; } 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); } Bound bound() const { return (Bound)(genBound8 & 0x3); }
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) { 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 // Preserve any existing move for the same position
if (m || (k >> 48) != key16) if (m || (k >> 48) != key16)
move16 = (uint16_t)m; move16 = (uint16_t)m;
// Don't overwrite more valuable entries // Don't overwrite more valuable entries
if ( (k >> 48) != key16 if ( (k >> 48) != key16
|| d > depth8 - 4 || d / ONE_PLY > depth8 - 4
/* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */ /* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
|| b == BOUND_EXACT) || b == BOUND_EXACT)
{ {
@ -58,7 +60,7 @@ struct TTEntry {
value16 = (int16_t)v; value16 = (int16_t)v;
eval16 = (int16_t)ev; eval16 = (int16_t)ev;
genBound8 = (uint8_t)(g | b); genBound8 = (uint8_t)(g | b);
depth8 = (int8_t)d; depth8 = (int8_t)(d / ONE_PLY);
} }
} }

View File

@ -209,15 +209,17 @@ enum Depth {
ONE_PLY = 1, ONE_PLY = 1,
DEPTH_ZERO = 0, DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = 0, DEPTH_QS_CHECKS = 0 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -1, DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5, DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_NONE = -6, DEPTH_NONE = -6 * ONE_PLY,
DEPTH_MAX = MAX_PLY DEPTH_MAX = MAX_PLY * ONE_PLY
}; };
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
enum Square { enum Square {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, 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_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,

View File

@ -75,7 +75,7 @@ namespace {
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
{ {
States->push_back(StateInfo()); 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));
} }
} }