mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-02-17 09:37:50 +01:00
DroidFish: Updated stockfish engine to version 8.
This commit is contained in:
parent
503390c19f
commit
3d227b6bee
|
@ -76,7 +76,14 @@ const vector<string> Defaults = {
|
|||
"8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw
|
||||
|
||||
// 7-man positions
|
||||
"8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124" // Draw
|
||||
"8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
|
||||
|
||||
// Mate and stalemate positions
|
||||
"8/8/8/8/8/6k1/6p1/6K1 w - -",
|
||||
"5k2/5P2/5K2/8/8/8/8/8 b - -",
|
||||
"8/8/8/8/8/4k3/4p3/4K3 w - -",
|
||||
"8/8/8/8/8/5K2/8/3Q1k2 b - -",
|
||||
"7k/7P/6K1/8/3B4/8/8/8 b - -"
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -123,9 +123,9 @@ namespace {
|
|||
// Immediate win if a pawn can be promoted without getting captured
|
||||
else if ( us == WHITE
|
||||
&& rank_of(psq) == RANK_7
|
||||
&& ksq[us] != psq + DELTA_N
|
||||
&& ( distance(ksq[~us], psq + DELTA_N) > 1
|
||||
|| (StepAttacksBB[KING][ksq[us]] & (psq + DELTA_N))))
|
||||
&& ksq[us] != psq + NORTH
|
||||
&& ( distance(ksq[~us], psq + NORTH) > 1
|
||||
|| (StepAttacksBB[KING][ksq[us]] & (psq + NORTH))))
|
||||
result = WIN;
|
||||
|
||||
// Immediate draw if it is a stalemate or a king captures undefended pawn
|
||||
|
@ -166,12 +166,12 @@ namespace {
|
|||
if (Us == WHITE)
|
||||
{
|
||||
if (rank_of(psq) < RANK_7) // Single push
|
||||
r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N)];
|
||||
r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH)];
|
||||
|
||||
if ( rank_of(psq) == RANK_2 // Double push
|
||||
&& psq + DELTA_N != ksq[Us]
|
||||
&& psq + DELTA_N != ksq[Them])
|
||||
r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N + DELTA_N)];
|
||||
&& psq + NORTH != ksq[Us]
|
||||
&& psq + NORTH != ksq[Them])
|
||||
r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH + NORTH)];
|
||||
}
|
||||
|
||||
return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;
|
||||
|
|
|
@ -205,8 +205,8 @@ void Bitboards::init() {
|
|||
StepAttacksBB[make_piece(c, pt)][s] |= to;
|
||||
}
|
||||
|
||||
Square RookDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W };
|
||||
Square BishopDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW };
|
||||
Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST };
|
||||
Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
|
||||
|
||||
init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index<ROOK>);
|
||||
init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index<BISHOP>);
|
||||
|
|
|
@ -124,13 +124,13 @@ inline Bitboard file_bb(Square s) {
|
|||
}
|
||||
|
||||
|
||||
/// shift_bb() moves a bitboard one step along direction Delta. Mainly for pawns
|
||||
/// shift() moves a bitboard one step along direction D. Mainly for pawns
|
||||
|
||||
template<Square Delta>
|
||||
inline Bitboard shift_bb(Bitboard b) {
|
||||
return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8
|
||||
: Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7
|
||||
: Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9
|
||||
template<Square D>
|
||||
inline Bitboard shift(Bitboard b) {
|
||||
return D == NORTH ? b << 8 : D == SOUTH ? b >> 8
|
||||
: D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7
|
||||
: D == NORTH_WEST ? (b & ~FileABB) << 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -259,8 +259,8 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
|
|||
result = Value(80) - 8 * distance(wksq, psq);
|
||||
|
||||
else
|
||||
result = Value(200) - 8 * ( distance(wksq, psq + DELTA_S)
|
||||
- distance(bksq, psq + DELTA_S)
|
||||
result = Value(200) - 8 * ( distance(wksq, psq + SOUTH)
|
||||
- distance(bksq, psq + SOUTH)
|
||||
- distance(psq, queeningSq));
|
||||
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
|
@ -496,7 +496,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
|||
// If the defending king blocks the pawn and the attacking king is too far
|
||||
// away, it's a draw.
|
||||
if ( r <= RANK_5
|
||||
&& bksq == wpsq + DELTA_N
|
||||
&& bksq == wpsq + NORTH
|
||||
&& distance(wksq, wpsq) - tempo >= 2
|
||||
&& distance(wksq, brsq) - tempo >= 2)
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
@ -517,10 +517,10 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
|||
&& file_of(wrsq) == f
|
||||
&& wrsq < wpsq
|
||||
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
|
||||
&& (distance(wksq, wpsq + DELTA_N) < distance(bksq, wpsq + DELTA_N) - 2 + tempo)
|
||||
&& (distance(wksq, wpsq + NORTH) < distance(bksq, wpsq + NORTH) - 2 + tempo)
|
||||
&& ( distance(bksq, wrsq) + tempo >= 3
|
||||
|| ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo
|
||||
&& (distance(wksq, wpsq + DELTA_N) < distance(bksq, wrsq) + tempo))))
|
||||
&& (distance(wksq, wpsq + NORTH) < distance(bksq, wrsq) + tempo))))
|
||||
return ScaleFactor( SCALE_FACTOR_MAX
|
||||
- 8 * distance(wpsq, queeningSq)
|
||||
- 2 * distance(wksq, queeningSq));
|
||||
|
|
|
@ -156,7 +156,8 @@ namespace {
|
|||
// ThreatBySafePawn[PieceType] contains bonuses according to which piece
|
||||
// type is attacked by a pawn which is protected or is not attacked.
|
||||
const Score ThreatBySafePawn[PIECE_TYPE_NB] = {
|
||||
S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215) };
|
||||
S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215)
|
||||
};
|
||||
|
||||
// Threat[by minor/by rook][attacked PieceType] contains
|
||||
// bonuses according to which piece type attacks which one.
|
||||
|
@ -197,6 +198,8 @@ namespace {
|
|||
const Score Hanging = S(48, 27);
|
||||
const Score ThreatByPawnPush = S(38, 22);
|
||||
const Score Unstoppable = S( 0, 20);
|
||||
const Score PawnlessFlank = S(20, 80);
|
||||
const Score HinderPassedPawn = S( 7, 0);
|
||||
|
||||
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
|
||||
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
|
||||
|
@ -223,8 +226,8 @@ namespace {
|
|||
template<Color Us>
|
||||
void eval_init(const Position& pos, EvalInfo& ei) {
|
||||
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Square Down = (Us == WHITE ? DELTA_S : DELTA_N);
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Square Down = (Us == WHITE ? SOUTH : NORTH);
|
||||
|
||||
ei.pinnedPieces[Us] = pos.pinned_pieces(Us);
|
||||
Bitboard b = ei.attackedBy[Them][KING];
|
||||
|
@ -235,7 +238,7 @@ namespace {
|
|||
// Init king safety tables only if we are going to use them
|
||||
if (pos.non_pawn_material(Us) >= QueenValueMg)
|
||||
{
|
||||
ei.kingRing[Them] = b | shift_bb<Down>(b);
|
||||
ei.kingRing[Them] = b | shift<Down>(b);
|
||||
b &= ei.attackedBy[Us][PAWN];
|
||||
ei.kingAttackersCount[Us] = popcount(b);
|
||||
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
|
||||
|
@ -321,7 +324,7 @@ namespace {
|
|||
&& pos.is_chess960()
|
||||
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
|
||||
{
|
||||
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
|
||||
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
|
||||
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
|
||||
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2
|
||||
|
@ -391,8 +394,8 @@ namespace {
|
|||
template<Color Us, bool DoTrace>
|
||||
Score evaluate_king(const Position& pos, const EvalInfo& ei) {
|
||||
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Square Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
|
||||
Bitboard undefended, b, b1, b2, safe, other;
|
||||
int kingDanger;
|
||||
|
@ -438,7 +441,7 @@ namespace {
|
|||
// ... and some other potential checks, only requiring the square to be
|
||||
// safe from pawn-attacks, and not being occupied by a blocked pawn.
|
||||
other = ~( ei.attackedBy[Us][PAWN]
|
||||
| (pos.pieces(Them, PAWN) & shift_bb<Up>(pos.pieces(PAWN))));
|
||||
| (pos.pieces(Them, PAWN) & shift<Up>(pos.pieces(PAWN))));
|
||||
|
||||
b1 = pos.attacks_from<ROOK >(ksq);
|
||||
b2 = pos.attacks_from<BISHOP>(ksq);
|
||||
|
@ -481,7 +484,8 @@ namespace {
|
|||
}
|
||||
|
||||
// King tropism: firstly, find squares that opponent attacks in our king flank
|
||||
b = ei.attackedBy[Them][ALL_PIECES] & KingFlank[Us][file_of(ksq)];
|
||||
File kf = file_of(ksq);
|
||||
b = ei.attackedBy[Them][ALL_PIECES] & KingFlank[Us][kf];
|
||||
|
||||
assert(((Us == WHITE ? b << 4 : b >> 4) & b) == 0);
|
||||
assert(popcount(Us == WHITE ? b << 4 : b >> 4) == popcount(b));
|
||||
|
@ -493,6 +497,10 @@ namespace {
|
|||
|
||||
score -= CloseEnemies * popcount(b);
|
||||
|
||||
// Penalty when our king is on a pawnless flank
|
||||
if (!(pos.pieces(PAWN) & (KingFlank[WHITE][kf] | KingFlank[BLACK][kf])))
|
||||
score -= PawnlessFlank;
|
||||
|
||||
if (DoTrace)
|
||||
Trace::add(KING, Us, score);
|
||||
|
||||
|
@ -506,12 +514,12 @@ namespace {
|
|||
template<Color Us, bool DoTrace>
|
||||
Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
|
||||
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
|
||||
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
|
||||
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
|
||||
const Bitboard TRank2BB = (Us == WHITE ? Rank2BB : Rank7BB);
|
||||
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Square Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
|
||||
const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
|
||||
const Bitboard TRank2BB = (Us == WHITE ? Rank2BB : Rank7BB);
|
||||
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
|
||||
enum { Minor, Rook };
|
||||
|
||||
|
@ -531,7 +539,7 @@ namespace {
|
|||
b = pos.pieces(Us, PAWN) & ( ~ei.attackedBy[Them][ALL_PIECES]
|
||||
| ei.attackedBy[Us][ALL_PIECES]);
|
||||
|
||||
safeThreats = (shift_bb<Right>(b) | shift_bb<Left>(b)) & weak;
|
||||
safeThreats = (shift<Right>(b) | shift<Left>(b)) & weak;
|
||||
|
||||
if (weak ^ safeThreats)
|
||||
score += ThreatByHangingPawn;
|
||||
|
@ -568,13 +576,13 @@ namespace {
|
|||
|
||||
// Bonus if some pawns can safely push and attack an enemy piece
|
||||
b = pos.pieces(Us, PAWN) & ~TRank7BB;
|
||||
b = shift_bb<Up>(b | (shift_bb<Up>(b & TRank2BB) & ~pos.pieces()));
|
||||
b = shift<Up>(b | (shift<Up>(b & TRank2BB) & ~pos.pieces()));
|
||||
|
||||
b &= ~pos.pieces()
|
||||
& ~ei.attackedBy[Them][PAWN]
|
||||
& (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]);
|
||||
|
||||
b = (shift_bb<Left>(b) | shift_bb<Right>(b))
|
||||
b = (shift<Left>(b) | shift<Right>(b))
|
||||
& pos.pieces(Them)
|
||||
& ~ei.attackedBy[Us][PAWN];
|
||||
|
||||
|
@ -594,7 +602,7 @@ namespace {
|
|||
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
|
||||
Bitboard b, squaresToQueen, defendedSquares, unsafeSquares;
|
||||
Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares;
|
||||
Score score = SCORE_ZERO;
|
||||
|
||||
b = ei.pi->passed_pawns(Us);
|
||||
|
@ -606,6 +614,9 @@ namespace {
|
|||
assert(pos.pawn_passed(Us, s));
|
||||
assert(!(pos.pieces(PAWN) & forward_bb(Us, s)));
|
||||
|
||||
bb = forward_bb(Us, s) & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
|
||||
score -= HinderPassedPawn * popcount(bb);
|
||||
|
||||
int r = relative_rank(Us, s) - RANK_2;
|
||||
int rr = r * (r - 1);
|
||||
|
||||
|
@ -631,7 +642,7 @@ namespace {
|
|||
// in the pawn's path attacked or occupied by the enemy.
|
||||
defendedSquares = unsafeSquares = squaresToQueen = forward_bb(Us, s);
|
||||
|
||||
Bitboard bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
|
||||
bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
|
||||
|
||||
if (!(pos.pieces(Us) & bb))
|
||||
defendedSquares &= ei.attackedBy[Us][ALL_PIECES];
|
||||
|
@ -803,8 +814,8 @@ Value Eval::evaluate(const Position& pos) {
|
|||
|
||||
// Pawns blocked or on ranks 2 and 3 will be excluded from the mobility area
|
||||
Bitboard blockedPawns[] = {
|
||||
pos.pieces(WHITE, PAWN) & (shift_bb<DELTA_S>(pos.pieces()) | Rank2BB | Rank3BB),
|
||||
pos.pieces(BLACK, PAWN) & (shift_bb<DELTA_N>(pos.pieces()) | Rank7BB | Rank6BB)
|
||||
pos.pieces(WHITE, PAWN) & (shift<SOUTH>(pos.pieces()) | Rank2BB | Rank3BB),
|
||||
pos.pieces(BLACK, PAWN) & (shift<NORTH>(pos.pieces()) | Rank7BB | Rank6BB)
|
||||
};
|
||||
|
||||
// Do not include in mobility area squares protected by enemy pawns, or occupied
|
||||
|
|
|
@ -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-09-17";
|
||||
const string Version = "8";
|
||||
|
||||
/// 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
|
||||
|
|
|
@ -42,8 +42,8 @@ namespace {
|
|||
|
||||
assert(!pos.checkers());
|
||||
|
||||
const Square K = Chess960 ? kto > kfrom ? DELTA_W : DELTA_E
|
||||
: KingSide ? DELTA_W : DELTA_E;
|
||||
const Square K = Chess960 ? kto > kfrom ? WEST : EAST
|
||||
: KingSide ? WEST : EAST;
|
||||
|
||||
for (Square s = kto; s != kfrom; s += K)
|
||||
if (pos.attackers_to(s) & enemies)
|
||||
|
@ -65,23 +65,23 @@ namespace {
|
|||
}
|
||||
|
||||
|
||||
template<GenType Type, Square Delta>
|
||||
template<GenType Type, Square D>
|
||||
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
|
||||
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
*moveList++ = make<PROMOTION>(to - Delta, to, QUEEN);
|
||||
*moveList++ = make<PROMOTION>(to - D, to, QUEEN);
|
||||
|
||||
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
*moveList++ = make<PROMOTION>(to - Delta, to, ROOK);
|
||||
*moveList++ = make<PROMOTION>(to - Delta, to, BISHOP);
|
||||
*moveList++ = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
|
||||
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
|
||||
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
||||
}
|
||||
|
||||
// Knight promotion is the only promotion that can give a direct check
|
||||
// that's not already included in the queen promotion.
|
||||
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ksq))
|
||||
*moveList++ = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
||||
else
|
||||
(void)ksq; // Silence a warning under MSVC
|
||||
|
||||
|
@ -94,13 +94,13 @@ namespace {
|
|||
|
||||
// Compute our parametrized parameters at compile time, named according to
|
||||
// the point of view of white side.
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
||||
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
|
||||
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
|
||||
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
|
||||
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
||||
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
|
||||
const Square Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
|
||||
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
|
||||
|
||||
Bitboard emptySquares;
|
||||
|
||||
|
@ -115,8 +115,8 @@ namespace {
|
|||
{
|
||||
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
|
||||
|
||||
Bitboard b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
|
||||
Bitboard b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
|
||||
Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
|
||||
Bitboard b2 = shift<Up>(b1 & TRank3BB) & emptySquares;
|
||||
|
||||
if (Type == EVASIONS) // Consider only blocking squares
|
||||
{
|
||||
|
@ -138,8 +138,8 @@ namespace {
|
|||
Bitboard dcCandidates = pos.discovered_check_candidates();
|
||||
if (pawnsNotOn7 & dcCandidates)
|
||||
{
|
||||
Bitboard dc1 = shift_bb<Up>(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq);
|
||||
Bitboard dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
|
||||
Bitboard dc1 = shift<Up>(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq);
|
||||
Bitboard dc2 = shift<Up>(dc1 & TRank3BB) & emptySquares;
|
||||
|
||||
b1 |= dc1;
|
||||
b2 |= dc2;
|
||||
|
@ -168,9 +168,9 @@ namespace {
|
|||
if (Type == EVASIONS)
|
||||
emptySquares &= target;
|
||||
|
||||
Bitboard b1 = shift_bb<Right>(pawnsOn7) & enemies;
|
||||
Bitboard b2 = shift_bb<Left >(pawnsOn7) & enemies;
|
||||
Bitboard b3 = shift_bb<Up >(pawnsOn7) & emptySquares;
|
||||
Bitboard b1 = shift<Right>(pawnsOn7) & enemies;
|
||||
Bitboard b2 = shift<Left >(pawnsOn7) & enemies;
|
||||
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
|
||||
|
||||
Square ksq = pos.square<KING>(Them);
|
||||
|
||||
|
@ -187,8 +187,8 @@ namespace {
|
|||
// Standard and en-passant captures
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
Bitboard b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
|
||||
Bitboard b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
|
||||
Bitboard b1 = shift<Right>(pawnsNotOn7) & enemies;
|
||||
Bitboard b2 = shift<Left >(pawnsNotOn7) & enemies;
|
||||
|
||||
while (b1)
|
||||
{
|
||||
|
|
|
@ -115,7 +115,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th)
|
|||
ttMove = ttm
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& pos.capture(ttm)
|
||||
&& pos.see(ttm) > threshold ? ttm : MOVE_NONE;
|
||||
&& pos.see_ge(ttm, threshold + 1)? ttm : MOVE_NONE;
|
||||
|
||||
stage += (ttMove == MOVE_NONE);
|
||||
}
|
||||
|
@ -159,19 +159,13 @@ void MovePicker::score<QUIETS>() {
|
|||
|
||||
template<>
|
||||
void MovePicker::score<EVASIONS>() {
|
||||
// Try winning and equal captures ordered by MVV/LVA, then non-captures ordered
|
||||
// by history value, then bad captures and quiet moves with a negative SEE ordered
|
||||
// by SEE value.
|
||||
// Try captures ordered by MVV/LVA, then non-captures ordered by history 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)
|
||||
if ((see = pos.see_sign(m)) < VALUE_ZERO)
|
||||
m.value = see - HistoryStats::Max; // At the bottom
|
||||
|
||||
else if (pos.capture(m))
|
||||
if (pos.capture(m))
|
||||
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||
- Value(type_of(pos.moved_piece(m))) + HistoryStats::Max;
|
||||
else
|
||||
|
@ -207,7 +201,7 @@ Move MovePicker::next_move() {
|
|||
move = pick_best(cur++, endMoves);
|
||||
if (move != ttMove)
|
||||
{
|
||||
if (pos.see_sign(move) >= VALUE_ZERO)
|
||||
if (pos.see_ge(move, VALUE_ZERO))
|
||||
return move;
|
||||
|
||||
// Losing capture, move it to the beginning of the array
|
||||
|
@ -277,8 +271,7 @@ Move MovePicker::next_move() {
|
|||
case EVASIONS_INIT:
|
||||
cur = moves;
|
||||
endMoves = generate<EVASIONS>(pos, cur);
|
||||
if (endMoves - cur - (ttMove != MOVE_NONE) > 1)
|
||||
score<EVASIONS>();
|
||||
score<EVASIONS>();
|
||||
++stage;
|
||||
|
||||
case ALL_EVASIONS:
|
||||
|
@ -301,7 +294,7 @@ Move MovePicker::next_move() {
|
|||
{
|
||||
move = pick_best(cur++, endMoves);
|
||||
if ( move != ttMove
|
||||
&& pos.see(move) > threshold)
|
||||
&& pos.see_ge(move, threshold + 1))
|
||||
return move;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -37,9 +37,8 @@ namespace {
|
|||
// Backward pawn penalty by opposed flag
|
||||
const Score Backward[2] = { S(56, 33), S(41, 19) };
|
||||
|
||||
// Unsupported pawn penalty for pawns which are neither isolated or backward,
|
||||
// by number of pawns it supports [less than 2 / exactly 2].
|
||||
const Score Unsupported[2] = { S(17, 8), S(21, 12) };
|
||||
// Unsupported pawn penalty for pawns which are neither isolated or backward
|
||||
const Score Unsupported = S(17, 8);
|
||||
|
||||
// Connected pawn bonus by opposed, phalanx, twice supported and rank
|
||||
Score Connected[2][2][2][RANK_NB];
|
||||
|
@ -50,14 +49,16 @@ namespace {
|
|||
// Lever bonus by rank
|
||||
const Score Lever[RANK_NB] = {
|
||||
S( 0, 0), S( 0, 0), S(0, 0), S(0, 0),
|
||||
S(17, 16), S(33, 32), S(0, 0), S(0, 0) };
|
||||
S(17, 16), S(33, 32), S(0, 0), S(0, 0)
|
||||
};
|
||||
|
||||
// Weakness of our pawn shelter in front of the king by [distance from edge][rank]
|
||||
const Value ShelterWeakness[][RANK_NB] = {
|
||||
{ V( 97), V(21), V(26), V(51), V(87), V( 89), V( 99) },
|
||||
{ V(120), V( 0), V(28), V(76), V(88), V(103), V(104) },
|
||||
{ V(101), V( 7), V(54), V(78), V(77), V( 92), V(101) },
|
||||
{ V( 80), V(11), V(44), V(68), V(87), V( 90), V(119) } };
|
||||
{ V( 80), V(11), V(44), V(68), V(87), V( 90), V(119) }
|
||||
};
|
||||
|
||||
// Danger of enemy pawns moving toward our king by [type][distance from edge][rank]
|
||||
const Value StormDanger[][4][RANK_NB] = {
|
||||
|
@ -76,7 +77,8 @@ namespace {
|
|||
{ { V( 0), V(-283), V(-281), V(57), V(31) },
|
||||
{ V( 0), V( 58), V( 141), V(39), V(18) },
|
||||
{ V( 0), V( 65), V( 142), V(48), V(32) },
|
||||
{ V( 0), V( 60), V( 126), V(51), V(19) } } };
|
||||
{ V( 0), V( 60), V( 126), V(51), V(19) } }
|
||||
};
|
||||
|
||||
// Max bonus for king safety. Corresponds to start position with all the pawns
|
||||
// in front of the king and no enemy pawn on the horizon.
|
||||
|
@ -88,10 +90,10 @@ namespace {
|
|||
template<Color Us>
|
||||
Score evaluate(const Position& pos, Pawns::Entry* e) {
|
||||
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
|
||||
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
|
||||
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Square Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
|
||||
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
|
||||
|
||||
Bitboard b, neighbours, stoppers, doubled, supported, phalanx;
|
||||
Square s;
|
||||
|
@ -103,10 +105,10 @@ namespace {
|
|||
Bitboard ourPawns = pos.pieces(Us , PAWN);
|
||||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
||||
|
||||
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
|
||||
e->kingSquares[Us] = SQ_NONE;
|
||||
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
|
||||
e->semiopenFiles[Us] = 0xFF;
|
||||
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
|
||||
e->kingSquares[Us] = SQ_NONE;
|
||||
e->pawnAttacks[Us] = shift<Right>(ourPawns) | shift<Left>(ourPawns);
|
||||
e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares);
|
||||
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
|
||||
|
||||
|
@ -117,7 +119,7 @@ namespace {
|
|||
|
||||
File f = file_of(s);
|
||||
|
||||
e->semiopenFiles[Us] &= ~(1 << f);
|
||||
e->semiopenFiles[Us] &= ~(1 << f);
|
||||
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
||||
|
||||
// Flag the pawn
|
||||
|
@ -142,7 +144,7 @@ namespace {
|
|||
// The pawn is backward when it cannot safely progress to that rank:
|
||||
// either there is a stopper in the way on this rank, or there is a
|
||||
// stopper on adjacent file which controls the way to that rank.
|
||||
backward = (b | shift_bb<Up>(b & adjacent_files_bb(f))) & stoppers;
|
||||
backward = (b | shift<Up>(b & adjacent_files_bb(f))) & stoppers;
|
||||
|
||||
assert(!backward || !(pawn_attack_span(Them, s + Up) & neighbours));
|
||||
}
|
||||
|
@ -160,7 +162,7 @@ namespace {
|
|||
score -= Backward[opposed];
|
||||
|
||||
else if (!supported)
|
||||
score -= Unsupported[more_than_one(neighbours & pawnAttacksBB[s])];
|
||||
score -= Unsupported;
|
||||
|
||||
if (connected)
|
||||
score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)];
|
||||
|
@ -183,8 +185,8 @@ namespace Pawns {
|
|||
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
|
||||
/// to reduce independent parameters and to allow easier tuning and better insight.
|
||||
|
||||
void init()
|
||||
{
|
||||
void init() {
|
||||
|
||||
static const int Seed[RANK_NB] = { 0, 8, 19, 13, 71, 94, 169, 324 };
|
||||
|
||||
for (int opposed = 0; opposed <= 1; ++opposed)
|
||||
|
|
|
@ -425,23 +425,28 @@ Phase Position::game_phase() const {
|
|||
/// 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. The pinners bitboard get filled with
|
||||
/// real and potential pinners.
|
||||
/// or the same of the color of the slider.
|
||||
|
||||
Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const {
|
||||
|
||||
Bitboard b, p, result = 0;
|
||||
Bitboard result = 0;
|
||||
pinners = 0;
|
||||
|
||||
// Pinners are sliders that attack 's' when a pinned piece is removed
|
||||
pinners = p = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK))
|
||||
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
|
||||
// Snipers are sliders that attack 's' when a piece is removed
|
||||
Bitboard snipers = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK))
|
||||
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
|
||||
|
||||
while (p)
|
||||
while (snipers)
|
||||
{
|
||||
b = between_bb(s, pop_lsb(&p)) & pieces();
|
||||
Square sniperSq = pop_lsb(&snipers);
|
||||
Bitboard b = between_bb(s, sniperSq) & pieces();
|
||||
|
||||
if (!more_than_one(b))
|
||||
result |= b;
|
||||
if (!more_than_one(b))
|
||||
{
|
||||
result |= b;
|
||||
if (b & pieces(color_of(piece_on(s))))
|
||||
pinners |= sniperSq;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -950,102 +955,83 @@ Key Position::key_after(Move m) const {
|
|||
}
|
||||
|
||||
|
||||
/// Position::see() is a static exchange evaluator: It tries to estimate the
|
||||
/// material gain or loss resulting from a move.
|
||||
/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
|
||||
/// SEE value of move is greater or equal to the given value. We'll use an
|
||||
/// algorithm similar to alpha-beta pruning with a null window.
|
||||
|
||||
Value Position::see_sign(Move m) const {
|
||||
bool Position::see_ge(Move m, Value v) const {
|
||||
|
||||
assert(is_ok(m));
|
||||
|
||||
// Early return if SEE cannot be negative because captured piece value
|
||||
// is not less then capturing one. Note that king moves always return
|
||||
// here because king midgame value is set to 0.
|
||||
if (PieceValue[MG][moved_piece(m)] <= PieceValue[MG][piece_on(to_sq(m))])
|
||||
return VALUE_KNOWN_WIN;
|
||||
|
||||
return see(m);
|
||||
}
|
||||
|
||||
Value Position::see(Move m) const {
|
||||
|
||||
Square from, to;
|
||||
Bitboard occupied, attackers, stmAttackers;
|
||||
Value swapList[32];
|
||||
int slIndex = 1;
|
||||
PieceType captured;
|
||||
Color stm;
|
||||
|
||||
assert(is_ok(m));
|
||||
|
||||
from = from_sq(m);
|
||||
to = to_sq(m);
|
||||
swapList[0] = PieceValue[MG][piece_on(to)];
|
||||
stm = color_of(piece_on(from));
|
||||
occupied = pieces() ^ from;
|
||||
|
||||
// Castling moves are implemented as king capturing the rook so cannot
|
||||
// be handled correctly. Simply return VALUE_ZERO that is always correct
|
||||
// unless in the rare case the rook ends up under attack.
|
||||
// Castling moves are implemented as king capturing the rook so cannot be
|
||||
// handled correctly. Simply assume the SEE value is VALUE_ZERO that is always
|
||||
// correct unless in the rare case the rook ends up under attack.
|
||||
if (type_of(m) == CASTLING)
|
||||
return VALUE_ZERO;
|
||||
return VALUE_ZERO >= v;
|
||||
|
||||
Square from = from_sq(m), to = to_sq(m);
|
||||
PieceType nextVictim = type_of(piece_on(from));
|
||||
Color stm = ~color_of(piece_on(from)); // First consider opponent's move
|
||||
Value balance; // Values of the pieces taken by us minus opponent's ones
|
||||
Bitboard occupied, stmAttackers;
|
||||
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
occupied ^= to - pawn_push(stm); // Remove the captured pawn
|
||||
swapList[0] = PieceValue[MG][PAWN];
|
||||
occupied = SquareBB[to - pawn_push(~stm)]; // Remove the captured pawn
|
||||
balance = PieceValue[MG][PAWN];
|
||||
}
|
||||
else
|
||||
{
|
||||
balance = PieceValue[MG][piece_on(to)];
|
||||
occupied = 0;
|
||||
}
|
||||
|
||||
// Find all attackers to the destination square, with the moving piece
|
||||
// removed, but possibly an X-ray attacker added behind it.
|
||||
attackers = attackers_to(to, occupied) & occupied;
|
||||
if (balance < v)
|
||||
return false;
|
||||
|
||||
// If the opponent has no attackers we are finished
|
||||
stm = ~stm;
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
occupied ^= to; // For the case when captured piece is a pinner
|
||||
if (nextVictim == KING)
|
||||
return true;
|
||||
|
||||
// Don't allow pinned pieces to attack as long all pinners (this includes also
|
||||
// potential ones) are on their original square. When a pinner moves to the
|
||||
// exchange-square or get captured on it, we fall back to standard SEE behaviour.
|
||||
if ( (stmAttackers & pinned_pieces(stm))
|
||||
&& (st->pinnersForKing[stm] & occupied) == st->pinnersForKing[stm])
|
||||
stmAttackers &= ~pinned_pieces(stm);
|
||||
balance -= PieceValue[MG][nextVictim];
|
||||
|
||||
if (!stmAttackers)
|
||||
return swapList[0];
|
||||
if (balance >= v)
|
||||
return true;
|
||||
|
||||
// The destination square is defended, which makes things rather more
|
||||
// difficult to compute. We proceed by building up a "swap list" containing
|
||||
// the material gain or loss at each stop in a sequence of captures to the
|
||||
// destination square, where the sides alternately capture, and always
|
||||
// capture with the least valuable piece. After each capture, we look for
|
||||
// new X-ray attacks from behind the capturing piece.
|
||||
captured = type_of(piece_on(from));
|
||||
bool relativeStm = true; // True if the opponent is to move
|
||||
occupied ^= pieces() ^ from ^ to;
|
||||
|
||||
do {
|
||||
assert(slIndex < 32);
|
||||
// Find all attackers to the destination square, with the moving piece removed,
|
||||
// but possibly an X-ray attacker added behind it.
|
||||
Bitboard attackers = attackers_to(to, occupied) & occupied;
|
||||
|
||||
// Add the new entry to the swap list
|
||||
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
|
||||
while (true)
|
||||
{
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
|
||||
// Don't allow pinned pieces to attack pieces except the king as long all
|
||||
// pinners are on their original square.
|
||||
if (!(st->pinnersForKing[stm] & ~occupied))
|
||||
stmAttackers &= ~st->blockersForKing[stm];
|
||||
|
||||
if (!stmAttackers)
|
||||
return relativeStm;
|
||||
|
||||
// Locate and remove the next least valuable attacker
|
||||
captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
|
||||
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
|
||||
|
||||
if (nextVictim == KING)
|
||||
return relativeStm == bool(attackers & pieces(~stm));
|
||||
|
||||
balance += relativeStm ? PieceValue[MG][nextVictim]
|
||||
: -PieceValue[MG][nextVictim];
|
||||
|
||||
relativeStm = !relativeStm;
|
||||
|
||||
if (relativeStm == (balance >= v))
|
||||
return relativeStm;
|
||||
|
||||
stm = ~stm;
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
if ( (stmAttackers & pinned_pieces(stm))
|
||||
&& (st->pinnersForKing[stm] & occupied) == st->pinnersForKing[stm])
|
||||
stmAttackers &= ~pinned_pieces(stm);
|
||||
|
||||
++slIndex;
|
||||
|
||||
} while (stmAttackers && (captured != KING || (--slIndex, false))); // Stop before a king capture
|
||||
|
||||
// Having built the swap list, we negamax through it to find the best
|
||||
// achievable score from the point of view of the side to move.
|
||||
while (--slIndex)
|
||||
swapList[slIndex - 1] = std::min(-swapList[slIndex], swapList[slIndex - 1]);
|
||||
|
||||
return swapList[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -133,8 +133,7 @@ public:
|
|||
void undo_null_move();
|
||||
|
||||
// Static Exchange Evaluation
|
||||
Value see(Move m) const;
|
||||
Value see_sign(Move m) const;
|
||||
bool see_ge(Move m, Value value) const;
|
||||
|
||||
// Accessing hash keys
|
||||
Key key() const;
|
||||
|
@ -149,7 +148,6 @@ public:
|
|||
bool is_chess960() const;
|
||||
Thread* this_thread() const;
|
||||
uint64_t nodes_searched() const;
|
||||
void set_nodes_searched(uint64_t n);
|
||||
bool is_draw() const;
|
||||
int rule50_count() const;
|
||||
Score psq_score() const;
|
||||
|
@ -342,10 +340,6 @@ inline uint64_t Position::nodes_searched() const {
|
|||
return nodes;
|
||||
}
|
||||
|
||||
inline void Position::set_nodes_searched(uint64_t n) {
|
||||
nodes = n;
|
||||
}
|
||||
|
||||
inline bool Position::opposite_bishops() const {
|
||||
return pieceCount[W_BISHOP] == 1
|
||||
&& pieceCount[B_BISHOP] == 1
|
||||
|
|
|
@ -23,8 +23,9 @@
|
|||
#include "types.h"
|
||||
|
||||
Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
|
||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
|
||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg }
|
||||
};
|
||||
|
||||
namespace PSQT {
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ namespace Search {
|
|||
namespace Tablebases {
|
||||
|
||||
int Cardinality;
|
||||
uint64_t Hits;
|
||||
bool RootInTB;
|
||||
bool UseRule50;
|
||||
Depth ProbeDepth;
|
||||
|
@ -462,11 +461,7 @@ void Thread::search() {
|
|||
if (!mainThread)
|
||||
continue;
|
||||
|
||||
if (Signals.stop)
|
||||
sync_cout << "info nodes " << Threads.nodes_searched()
|
||||
<< " time " << Time.elapsed() << sync_endl;
|
||||
|
||||
else if (PVIdx + 1 == multiPV || Time.elapsed() > 3000)
|
||||
if (Signals.stop || PVIdx + 1 == multiPV || Time.elapsed() > 3000)
|
||||
sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl;
|
||||
}
|
||||
|
||||
|
@ -571,6 +566,7 @@ namespace {
|
|||
Thread* thisThread = pos.this_thread();
|
||||
inCheck = pos.checkers();
|
||||
moveCount = quietCount = ss->moveCount = 0;
|
||||
ss->history = VALUE_ZERO;
|
||||
bestValue = -VALUE_INFINITE;
|
||||
ss->ply = (ss-1)->ply + 1;
|
||||
|
||||
|
@ -636,8 +632,6 @@ namespace {
|
|||
&& (ttValue >= beta ? (tte->bound() & BOUND_LOWER)
|
||||
: (tte->bound() & BOUND_UPPER)))
|
||||
{
|
||||
ss->currentMove = ttMove; // Can be MOVE_NONE
|
||||
|
||||
// If ttMove is quiet, update killers, history, counter move on TT hit
|
||||
if (ttValue >= beta && ttMove)
|
||||
{
|
||||
|
@ -674,7 +668,7 @@ namespace {
|
|||
|
||||
if (found)
|
||||
{
|
||||
TB::Hits++;
|
||||
thisThread->tbHits++;
|
||||
|
||||
int drawScore = TB::UseRule50 ? 1 : 0;
|
||||
|
||||
|
@ -728,8 +722,7 @@ namespace {
|
|||
&& ttMove == MOVE_NONE
|
||||
&& eval + razor_margin[depth / ONE_PLY] <= alpha)
|
||||
{
|
||||
if ( depth <= ONE_PLY
|
||||
&& eval + razor_margin[3 * ONE_PLY] <= alpha)
|
||||
if (depth <= ONE_PLY)
|
||||
return qsearch<NonPV, false>(pos, ss, alpha, beta, DEPTH_ZERO);
|
||||
|
||||
Value ralpha = alpha - razor_margin[depth / ONE_PLY];
|
||||
|
@ -744,7 +737,7 @@ namespace {
|
|||
&& eval - futility_margin(depth) >= beta
|
||||
&& eval < VALUE_KNOWN_WIN // Do not return unproven wins
|
||||
&& pos.non_pawn_material(pos.side_to_move()))
|
||||
return eval - futility_margin(depth);
|
||||
return eval;
|
||||
|
||||
// Step 8. Null move search with verification search (is omitted in PV nodes)
|
||||
if ( !PvNode
|
||||
|
@ -845,8 +838,7 @@ moves_loop: // When in check search starts from here
|
|||
singularExtensionNode = !rootNode
|
||||
&& depth >= 8 * ONE_PLY
|
||||
&& ttMove != MOVE_NONE
|
||||
/* && ttValue != VALUE_NONE Already implicit in the next condition */
|
||||
&& abs(ttValue) < VALUE_KNOWN_WIN
|
||||
&& ttValue != VALUE_NONE
|
||||
&& !excludedMove // Recursive singular search is not allowed
|
||||
&& (tte->bound() & BOUND_LOWER)
|
||||
&& tte->depth() >= depth - 3 * ONE_PLY;
|
||||
|
@ -891,7 +883,7 @@ moves_loop: // When in check search starts from here
|
|||
// Step 12. Extend checks
|
||||
if ( givesCheck
|
||||
&& !moveCountPruning
|
||||
&& pos.see_sign(move) >= VALUE_ZERO)
|
||||
&& pos.see_ge(move, VALUE_ZERO))
|
||||
extension = ONE_PLY;
|
||||
|
||||
// Singular extension search. If all moves but one fail low on a search of
|
||||
|
@ -904,7 +896,7 @@ moves_loop: // When in check search starts from here
|
|||
&& !extension
|
||||
&& pos.legal(move))
|
||||
{
|
||||
Value rBeta = ttValue - 2 * depth / ONE_PLY;
|
||||
Value rBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE);
|
||||
Depth d = (depth / (2 * ONE_PLY)) * ONE_PLY;
|
||||
ss->excludedMove = move;
|
||||
ss->skipEarlyPruning = true;
|
||||
|
@ -921,8 +913,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
// Step 13. Pruning at shallow depth
|
||||
if ( !rootNode
|
||||
&& !inCheck
|
||||
&& bestValue > VALUE_MATED_IN_MAX_PLY)
|
||||
&& bestValue > VALUE_MATED_IN_MAX_PLY)
|
||||
{
|
||||
if ( !captureOrPromotion
|
||||
&& !givesCheck
|
||||
|
@ -944,16 +935,18 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
// Futility pruning: parent node
|
||||
if ( lmrDepth < 7
|
||||
&& !inCheck
|
||||
&& ss->staticEval + 256 + 200 * lmrDepth <= alpha)
|
||||
continue;
|
||||
|
||||
// Prune moves with negative SEE
|
||||
if ( lmrDepth < 8
|
||||
&& pos.see_sign(move) < Value(-35 * lmrDepth * lmrDepth))
|
||||
&& !pos.see_ge(move, Value(-35 * lmrDepth * lmrDepth)))
|
||||
continue;
|
||||
}
|
||||
else if ( depth < 7 * ONE_PLY
|
||||
&& pos.see_sign(move) < Value(-35 * depth / ONE_PLY * depth / ONE_PLY))
|
||||
&& !extension
|
||||
&& !pos.see_ge(move, Value(-35 * depth / ONE_PLY * depth / ONE_PLY)))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -995,17 +988,25 @@ moves_loop: // When in check search starts from here
|
|||
// 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)
|
||||
&& !pos.see_ge(make_move(to_sq(move), from_sq(move)), VALUE_ZERO))
|
||||
r -= 2 * ONE_PLY;
|
||||
|
||||
ss->history = 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)
|
||||
- 8000; // Correction factor
|
||||
|
||||
// Decrease/increase reduction by comparing opponent's stat score
|
||||
if (ss->history > VALUE_ZERO && (ss-1)->history < VALUE_ZERO)
|
||||
r -= ONE_PLY;
|
||||
|
||||
else if (ss->history < VALUE_ZERO && (ss-1)->history > VALUE_ZERO)
|
||||
r += 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);
|
||||
r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->history / 20000) * ONE_PLY);
|
||||
}
|
||||
|
||||
Depth d = std::max(newDepth - r, ONE_PLY);
|
||||
|
@ -1123,6 +1124,9 @@ moves_loop: // When in check search starts from here
|
|||
// All legal moves have been searched and if there are no legal moves, it
|
||||
// must be a mate or a stalemate. If we are in a singular extension search then
|
||||
// return a fail low score.
|
||||
|
||||
assert(moveCount || !inCheck || excludedMove || !MoveList<LEGAL>(pos).size());
|
||||
|
||||
if (!moveCount)
|
||||
bestValue = excludedMove ? alpha
|
||||
: inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()];
|
||||
|
@ -1226,10 +1230,7 @@ moves_loop: // When in check search starts from here
|
|||
&& ttValue != VALUE_NONE // Only in case of TT access race
|
||||
&& (ttValue >= beta ? (tte->bound() & BOUND_LOWER)
|
||||
: (tte->bound() & BOUND_UPPER)))
|
||||
{
|
||||
ss->currentMove = ttMove; // Can be MOVE_NONE
|
||||
return ttValue;
|
||||
}
|
||||
|
||||
// Evaluate the position statically
|
||||
if (InCheck)
|
||||
|
@ -1302,7 +1303,7 @@ moves_loop: // When in check search starts from here
|
|||
continue;
|
||||
}
|
||||
|
||||
if (futilityBase <= alpha && pos.see(move) <= VALUE_ZERO)
|
||||
if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1))
|
||||
{
|
||||
bestValue = std::max(bestValue, futilityBase);
|
||||
continue;
|
||||
|
@ -1317,7 +1318,7 @@ moves_loop: // When in check search starts from here
|
|||
// Don't search moves with negative SEE values
|
||||
if ( (!InCheck || evasionPrunable)
|
||||
&& type_of(move) != PROMOTION
|
||||
&& pos.see_sign(move) < VALUE_ZERO)
|
||||
&& !pos.see_ge(move, VALUE_ZERO))
|
||||
continue;
|
||||
|
||||
// Speculative prefetch as early as possible
|
||||
|
@ -1522,7 +1523,7 @@ moves_loop: // When in check search starts from here
|
|||
|
||||
if ( (Limits.use_time_management() && elapsed > Time.maximum() - 10)
|
||||
|| (Limits.movetime && elapsed >= Limits.movetime)
|
||||
|| (Limits.nodes && Threads.nodes_searched() >= Limits.nodes))
|
||||
|| (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes))
|
||||
Signals.stop = true;
|
||||
}
|
||||
|
||||
|
@ -1539,7 +1540,8 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
|
|||
const RootMoves& rootMoves = pos.this_thread()->rootMoves;
|
||||
size_t PVIdx = pos.this_thread()->PVIdx;
|
||||
size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size());
|
||||
uint64_t nodes_searched = Threads.nodes_searched();
|
||||
uint64_t nodesSearched = Threads.nodes_searched();
|
||||
uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0);
|
||||
|
||||
for (size_t i = 0; i < multiPV; ++i)
|
||||
{
|
||||
|
@ -1566,13 +1568,13 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
|
|||
if (!tb && i == PVIdx)
|
||||
ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
|
||||
|
||||
ss << " nodes " << nodes_searched
|
||||
<< " nps " << nodes_searched * 1000 / elapsed;
|
||||
ss << " nodes " << nodesSearched
|
||||
<< " nps " << nodesSearched * 1000 / elapsed;
|
||||
|
||||
if (elapsed > 1000) // Earlier makes little sense
|
||||
ss << " hashfull " << TT.hashfull();
|
||||
|
||||
ss << " tbhits " << TB::Hits
|
||||
ss << " tbhits " << tbHits
|
||||
<< " time " << elapsed
|
||||
<< " pv";
|
||||
|
||||
|
@ -1589,13 +1591,16 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
|
|||
/// fail high at root. We try hard to have a ponder move to return to the GUI,
|
||||
/// otherwise in case of 'ponder on' we have nothing to think on.
|
||||
|
||||
bool RootMove::extract_ponder_from_tt(Position& pos)
|
||||
{
|
||||
bool RootMove::extract_ponder_from_tt(Position& pos) {
|
||||
|
||||
StateInfo st;
|
||||
bool ttHit;
|
||||
|
||||
assert(pv.size() == 1);
|
||||
|
||||
if (!pv[0])
|
||||
return false;
|
||||
|
||||
pos.do_move(pv[0], st, pos.gives_check(pv[0]));
|
||||
TTEntry* tte = TT.probe(pos.key(), ttHit);
|
||||
|
||||
|
@ -1612,7 +1617,6 @@ bool RootMove::extract_ponder_from_tt(Position& pos)
|
|||
|
||||
void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) {
|
||||
|
||||
Hits = 0;
|
||||
RootInTB = false;
|
||||
UseRule50 = Options["Syzygy50MoveRule"];
|
||||
ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY;
|
||||
|
@ -1645,13 +1649,8 @@ void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves)
|
|||
Cardinality = 0;
|
||||
}
|
||||
|
||||
if (RootInTB)
|
||||
{
|
||||
Hits = rootMoves.size();
|
||||
|
||||
if (!UseRule50)
|
||||
TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1
|
||||
: TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1
|
||||
: VALUE_DRAW;
|
||||
}
|
||||
if (RootInTB && !UseRule50)
|
||||
TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1
|
||||
: TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1
|
||||
: VALUE_DRAW;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ struct Stack {
|
|||
Move excludedMove;
|
||||
Move killers[2];
|
||||
Value staticEval;
|
||||
Value history;
|
||||
bool skipEarlyPruning;
|
||||
int moveCount;
|
||||
CounterMoveStats* counterMoves;
|
||||
|
|
|
@ -36,6 +36,7 @@ Thread::Thread() {
|
|||
|
||||
resetCalls = exit = false;
|
||||
maxPly = callsCnt = 0;
|
||||
tbHits = 0;
|
||||
history.clear();
|
||||
counterMoves.clear();
|
||||
idx = Threads.size(); // Start from 0
|
||||
|
@ -158,15 +159,26 @@ void ThreadPool::read_uci_options() {
|
|||
|
||||
/// ThreadPool::nodes_searched() returns the number of nodes searched
|
||||
|
||||
int64_t ThreadPool::nodes_searched() {
|
||||
uint64_t ThreadPool::nodes_searched() const {
|
||||
|
||||
int64_t nodes = 0;
|
||||
uint64_t nodes = 0;
|
||||
for (Thread* th : *this)
|
||||
nodes += th->rootPos.nodes_searched();
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
||||
/// ThreadPool::tb_hits() returns the number of TB hits
|
||||
|
||||
uint64_t ThreadPool::tb_hits() const {
|
||||
|
||||
uint64_t hits = 0;
|
||||
for (Thread* th : *this)
|
||||
hits += th->tbHits;
|
||||
return hits;
|
||||
}
|
||||
|
||||
|
||||
/// ThreadPool::start_thinking() wakes up the main thread sleeping in idle_loop()
|
||||
/// and starts a new search, then returns immediately.
|
||||
|
||||
|
@ -184,7 +196,8 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
|||
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
|
||||
rootMoves.push_back(Search::RootMove(m));
|
||||
|
||||
Tablebases::filter_root_moves(pos, rootMoves);
|
||||
if (!rootMoves.empty())
|
||||
Tablebases::filter_root_moves(pos, rootMoves);
|
||||
|
||||
// After ownership transfer 'states' becomes empty, so if we stop the search
|
||||
// and call 'go' again without setting a new position states.get() == NULL.
|
||||
|
@ -198,6 +211,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
|||
for (Thread* th : Threads)
|
||||
{
|
||||
th->maxPly = 0;
|
||||
th->tbHits = 0;
|
||||
th->rootDepth = DEPTH_ZERO;
|
||||
th->rootMoves = rootMoves;
|
||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
Endgames endgames;
|
||||
size_t idx, PVIdx;
|
||||
int maxPly, callsCnt;
|
||||
uint64_t tbHits;
|
||||
|
||||
Position rootPos;
|
||||
Search::RootMoves rootMoves;
|
||||
|
@ -98,7 +99,8 @@ struct ThreadPool : public std::vector<Thread*> {
|
|||
MainThread* main() { return static_cast<MainThread*>(at(0)); }
|
||||
void start_thinking(Position&, StateListPtr&, const Search::LimitsType&);
|
||||
void read_uci_options();
|
||||
int64_t nodes_searched();
|
||||
uint64_t nodes_searched() const;
|
||||
uint64_t tb_hits() const;
|
||||
|
||||
private:
|
||||
StateListPtr setupStates;
|
||||
|
|
|
@ -52,8 +52,8 @@ namespace {
|
|||
}
|
||||
|
||||
template<TimeType T>
|
||||
int remaining(int myTime, int movesToGo, int ply, int slowMover)
|
||||
{
|
||||
int remaining(int myTime, int movesToGo, int ply, int slowMover) {
|
||||
|
||||
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
|
||||
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
|
||||
|
||||
|
@ -81,8 +81,8 @@ namespace {
|
|||
/// inc > 0 && movestogo == 0 means: x basetime + z increment
|
||||
/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
|
||||
|
||||
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
|
||||
{
|
||||
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
||||
|
||||
int minThinkingTime = Options["Minimum Thinking Time"];
|
||||
int moveOverhead = Options["Move Overhead"];
|
||||
int slowMover = Options["Slow Mover"];
|
||||
|
|
|
@ -100,11 +100,11 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
|||
}
|
||||
|
||||
|
||||
/// Returns an approximation of the hashtable occupation during a search. The
|
||||
/// hash is x permill full, as per UCI protocol.
|
||||
/// TranspositionTable::hashfull() returns an approximation of the hashtable
|
||||
/// occupation during a search. The hash is x permill full, as per UCI protocol.
|
||||
|
||||
int TranspositionTable::hashfull() const {
|
||||
|
||||
int TranspositionTable::hashfull() const
|
||||
{
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < 1000 / ClusterSize; i++)
|
||||
{
|
||||
|
|
|
@ -115,7 +115,7 @@ const int MAX_PLY = 128;
|
|||
/// any normal move destination square is always different from origin square
|
||||
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
|
||||
|
||||
enum Move {
|
||||
enum Move : int {
|
||||
MOVE_NONE,
|
||||
MOVE_NULL = 65
|
||||
};
|
||||
|
@ -237,35 +237,34 @@ enum Square {
|
|||
|
||||
SQUARE_NB = 64,
|
||||
|
||||
DELTA_N = 8,
|
||||
DELTA_E = 1,
|
||||
DELTA_S = -8,
|
||||
DELTA_W = -1,
|
||||
NORTH = 8,
|
||||
EAST = 1,
|
||||
SOUTH = -8,
|
||||
WEST = -1,
|
||||
|
||||
DELTA_NN = DELTA_N + DELTA_N,
|
||||
DELTA_NE = DELTA_N + DELTA_E,
|
||||
DELTA_SE = DELTA_S + DELTA_E,
|
||||
DELTA_SS = DELTA_S + DELTA_S,
|
||||
DELTA_SW = DELTA_S + DELTA_W,
|
||||
DELTA_NW = DELTA_N + DELTA_W
|
||||
NORTH_EAST = NORTH + EAST,
|
||||
SOUTH_EAST = SOUTH + EAST,
|
||||
SOUTH_WEST = SOUTH + WEST,
|
||||
NORTH_WEST = NORTH + WEST
|
||||
};
|
||||
|
||||
enum File {
|
||||
enum File : int {
|
||||
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB
|
||||
};
|
||||
|
||||
enum Rank {
|
||||
enum Rank : int {
|
||||
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
|
||||
};
|
||||
|
||||
|
||||
/// Score enum stores a middlegame and an endgame value in a single integer
|
||||
/// (enum). The least significant 16 bits are used to store the endgame value
|
||||
/// and the upper 16 bits are used to store the middlegame value.
|
||||
/// and the upper 16 bits are used to store the middlegame value. Take some
|
||||
/// care to avoid left-shifting a signed int to avoid undefined behavior.
|
||||
enum Score : int { SCORE_ZERO };
|
||||
|
||||
inline Score make_score(int mg, int eg) {
|
||||
return Score((eg << 16) + mg);
|
||||
return Score((int)((unsigned int)eg << 16) + mg);
|
||||
}
|
||||
|
||||
/// Extracting the signed lower and upper 16 bits is not so trivial because
|
||||
|
@ -401,7 +400,7 @@ inline bool opposite_colors(Square s1, Square s2) {
|
|||
}
|
||||
|
||||
inline Square pawn_push(Color c) {
|
||||
return c == WHITE ? DELTA_N : DELTA_S;
|
||||
return c == WHITE ? NORTH : SOUTH;
|
||||
}
|
||||
|
||||
inline Square from_sq(Move m) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user