DroidFish: Update to stockfish 9.

This commit is contained in:
Peter Osterlund 2018-02-01 18:45:14 +01:00
parent e75d83de94
commit 1404bdf264
37 changed files with 654 additions and 550 deletions

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -111,7 +111,7 @@ namespace {
ksq[WHITE] = Square((idx >> 0) & 0x3F);
ksq[BLACK] = Square((idx >> 6) & 0x3F);
us = Color ((idx >> 12) & 0x01);
psq = make_square(File((idx >> 13) & 0x3), RANK_7 - Rank((idx >> 15) & 0x7));
psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7)));
// Check if two pieces are on the same square or if a king can be captured
if ( distance(ksq[WHITE], ksq[BLACK]) <= 1

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -54,7 +54,7 @@ namespace {
Bitboard RookTable[0x19000]; // To store rook attacks
Bitboard BishopTable[0x1480]; // To store bishop attacks
void init_magics(Bitboard table[], Magic magics[], Square deltas[]);
void init_magics(Bitboard table[], Magic magics[], Direction directions[]);
// bsf_index() returns the index into BSFTable[] to look up the bitscan. Uses
// Matt Taylor's folding for 32 bit case, extended to 64 bit by Kim Walisch.
@ -188,7 +188,7 @@ void Bitboards::init() {
for (Square s = SQ_A1; s <= SQ_H8; ++s)
for (int i = 0; steps[pt][i]; ++i)
{
Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]);
Square to = s + Direction(c == WHITE ? steps[pt][i] : -steps[pt][i]);
if (is_ok(to) && distance(s, to) < 3)
{
@ -199,11 +199,11 @@ void Bitboards::init() {
}
}
Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST };
Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
init_magics(RookTable, RookMagics, RookDeltas);
init_magics(BishopTable, BishopMagics, BishopDeltas);
init_magics(RookTable, RookMagics, RookDirections);
init_magics(BishopTable, BishopMagics, BishopDirections);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{
@ -225,14 +225,14 @@ void Bitboards::init() {
namespace {
Bitboard sliding_attack(Square deltas[], Square sq, Bitboard occupied) {
Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) {
Bitboard attack = 0;
for (int i = 0; i < 4; ++i)
for (Square s = sq + deltas[i];
is_ok(s) && distance(s, s - deltas[i]) == 1;
s += deltas[i])
for (Square s = sq + directions[i];
is_ok(s) && distance(s, s - directions[i]) == 1;
s += directions[i])
{
attack |= s;
@ -249,7 +249,7 @@ namespace {
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
// use the so called "fancy" approach.
void init_magics(Bitboard table[], Magic magics[], Square deltas[]) {
void init_magics(Bitboard table[], Magic magics[], Direction directions[]) {
// Optimal PRNG seeds to pick the correct magics in the shortest time
int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
@ -269,7 +269,7 @@ namespace {
// the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index.
Magic& m = magics[s];
m.mask = sliding_attack(deltas, s, 0) & ~edges;
m.mask = sliding_attack(directions, s, 0) & ~edges;
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
// Set the offset for the attacks table of the square. We have individual
@ -281,7 +281,7 @@ namespace {
b = size = 0;
do {
occupancy[size] = b;
reference[size] = sliding_attack(deltas, s, b);
reference[size] = sliding_attack(directions, s, b);
if (HasPext)
m.attacks[pext(b, m.mask)] = reference[size];

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -126,11 +126,10 @@ inline Bitboard& operator^=(Bitboard& b, Square s) {
return b ^= SquareBB[s];
}
inline bool more_than_one(Bitboard b) {
constexpr bool more_than_one(Bitboard b) {
return b & (b - 1);
}
/// rank_bb() and file_bb() return a bitboard representing all the squares on
/// the given file or rank.
@ -153,8 +152,8 @@ inline Bitboard file_bb(Square s) {
/// shift() moves a bitboard one step along direction D. Mainly for pawns
template<Square D>
inline Bitboard shift(Bitboard b) {
template<Direction D>
constexpr 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

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -524,7 +524,7 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
Square bsq = pos.square<BISHOP>(weakSide);
Square psq = pos.square<PAWN>(strongSide);
Rank rk = relative_rank(strongSide, psq);
Square push = pawn_push(strongSide);
Direction push = pawn_push(strongSide);
// If the pawn is on the 5th rank and the pawn (currently) is on
// the same color square as the bishop then there is a chance of

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -31,12 +31,21 @@
namespace {
const Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
const Bitboard KingFlank[FILE_NB] = {
QueenSide, QueenSide, QueenSide, CenterFiles, CenterFiles, KingSide, KingSide, KingSide
};
namespace Trace {
enum Tracing {NO_TRACE, TRACE};
enum Term { // The first 8 entries are for PieceType
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERM_NB
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB
};
double scores[TERM_NB][COLOR_NB][PHASE_NB];
@ -54,7 +63,7 @@ namespace {
std::ostream& operator<<(std::ostream& os, Term t) {
if (t == MATERIAL || t == IMBALANCE || t == Term(PAWN) || t == TOTAL)
if (t == MATERIAL || t == IMBALANCE || t == Term(PAWN) || t == INITIATIVE || t == TOTAL)
os << " --- --- | --- --- | ";
else
os << std::setw(5) << scores[t][WHITE][MG] << " "
@ -88,6 +97,7 @@ namespace {
template<Color Us> void initialize();
template<Color Us> Score evaluate_king();
template<Color Us> Score evaluate_threats();
int king_distance(Color c, Square s);
template<Color Us> Score evaluate_passed_pawns();
template<Color Us> Score evaluate_space();
template<Color Us, PieceType Pt> Score evaluate_pieces();
@ -102,7 +112,8 @@ namespace {
Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO };
// attackedBy[color][piece type] is a bitboard representing all squares
// attacked by a given color and piece type (can be also ALL_PIECES).
// attacked by a given color and piece type. Special "piece types" which are
// also calculated are QUEEN_DIAGONAL and ALL_PIECES.
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
// attackedBy2[color] are the squares attacked by 2 pieces of a given color,
@ -162,8 +173,8 @@ namespace {
// supported by a pawn. If the minor piece occupies an outpost square
// then score is doubled.
const Score Outpost[][2] = {
{ S(22, 6), S(33, 9) }, // Knight
{ S( 9, 2), S(14, 4) } // Bishop
{ S(22, 6), S(36,12) }, // Knight
{ S( 9, 2), S(15, 5) } // Bishop
};
// RookOnFile[semiopen/open] contains bonuses for each rook when there is no
@ -188,8 +199,8 @@ namespace {
// Passed[mg/eg][Rank] contains midgame and endgame bonuses for passed pawns.
// We don't use a Score because we process the two components independently.
const Value Passed[][RANK_NB] = {
{ V(5), V( 5), V(31), V(73), V(166), V(252) },
{ V(7), V(14), V(38), V(73), V(166), V(252) }
{ V(0), V(5), V( 5), V(31), V(73), V(166), V(252) },
{ V(0), V(7), V(14), V(38), V(73), V(166), V(252) }
};
// PassedFile[File] contains a bonus according to the file of a passed pawn
@ -198,25 +209,29 @@ namespace {
S(-20,-12), S( 1, -8), S( 2, 10), S( 9, 10)
};
// Rank factor applied on some bonus for passed pawn on rank 4 or beyond
const int RankFactor[RANK_NB] = {0, 0, 0, 2, 6, 11, 16};
// KingProtector[PieceType-2] contains a bonus according to distance from king
const Score KingProtector[] = { S(-3, -5), S(-4, -3), S(-3, 0), S(-1, 1) };
// Assorted bonuses and penalties used by evaluation
const Score MinorBehindPawn = S( 16, 0);
const Score BishopPawns = S( 8, 12);
const Score RookOnPawn = S( 8, 24);
const Score TrappedRook = S( 92, 0);
const Score WeakQueen = S( 50, 10);
const Score OtherCheck = S( 10, 10);
const Score CloseEnemies = S( 7, 0);
const Score PawnlessFlank = S( 20, 80);
const Score ThreatByHangingPawn = S( 71, 61);
const Score ThreatBySafePawn = S(182,175);
const Score ThreatByRank = S( 16, 3);
const Score Hanging = S( 48, 27);
const Score ThreatByPawnPush = S( 38, 22);
const Score HinderPassedPawn = S( 7, 0);
const Score TrappedBishopA1H1 = S( 50, 50);
const Score MinorBehindPawn = S( 16, 0);
const Score BishopPawns = S( 8, 12);
const Score LongRangedBishop = S( 22, 0);
const Score RookOnPawn = S( 8, 24);
const Score TrappedRook = S( 92, 0);
const Score WeakQueen = S( 50, 10);
const Score CloseEnemies = S( 7, 0);
const Score PawnlessFlank = S( 20, 80);
const Score ThreatBySafePawn = S(192,175);
const Score ThreatByRank = S( 16, 3);
const Score Hanging = S( 48, 27);
const Score WeakUnopposedPawn = S( 5, 25);
const Score ThreatByPawnPush = S( 38, 22);
const Score ThreatByAttackOnQueen = S( 38, 22);
const Score HinderPassedPawn = S( 7, 0);
const Score TrappedBishopA1H1 = S( 50, 50);
#undef S
#undef V
@ -225,10 +240,10 @@ namespace {
const int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 78, 56, 45, 11 };
// Penalties for enemy's safe checks
const int QueenCheck = 780;
const int RookCheck = 880;
const int BishopCheck = 435;
const int KnightCheck = 790;
const int QueenSafeCheck = 780;
const int RookSafeCheck = 880;
const int BishopSafeCheck = 435;
const int KnightSafeCheck = 790;
// Threshold for lazy and space evaluation
const Value LazyThreshold = Value(1500);
@ -241,9 +256,9 @@ namespace {
template<Tracing T> template<Color Us>
void Evaluation<T>::initialize() {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? NORTH : SOUTH);
const Square Down = (Us == WHITE ? SOUTH : NORTH);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Direction Up = (Us == WHITE ? NORTH : SOUTH);
const Direction Down = (Us == WHITE ? SOUTH : NORTH);
const Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB: Rank7BB | Rank6BB);
// Find our pawns on the first two ranks, and those which are blocked
@ -292,11 +307,14 @@ namespace {
attackedBy[Us][Pt] = 0;
if (Pt == QUEEN)
attackedBy[Us][QUEEN_DIAGONAL] = 0;
while ((s = *pl++) != SQ_NONE)
{
// Find attacked squares, including x-ray attacks for bishops and rooks
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(Us, QUEEN))
: Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN))
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
: Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
: pos.attacks_from<Pt>(s);
if (pos.pinned_pieces(Us) & s)
@ -305,6 +323,9 @@ namespace {
attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b;
attackedBy[Us][ALL_PIECES] |= attackedBy[Us][Pt] |= b;
if (Pt == QUEEN)
attackedBy[Us][QUEEN_DIAGONAL] |= b & PseudoAttacks[BISHOP][s];
if (b & kingRing[Them])
{
kingAttackersCount[Us]++;
@ -324,12 +345,12 @@ namespace {
// Bonus for outpost squares
bb = OutpostRanks & ~pe->pawn_attacks_span(Them);
if (bb & s)
score += Outpost[Pt == BISHOP][!!(attackedBy[Us][PAWN] & s)] * 2;
score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & s)] * 2;
else
{
bb &= b & ~pos.pieces(Us);
if (bb)
score += Outpost[Pt == BISHOP][!!(attackedBy[Us][PAWN] & bb)];
score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & bb)];
}
// Bonus when behind a pawn
@ -337,10 +358,16 @@ namespace {
&& (pos.pieces(PAWN) & (s + pawn_push(Us))))
score += MinorBehindPawn;
// Penalty for pawns on the same color square as the bishop
if (Pt == BISHOP)
{
// Penalty for pawns on the same color square as the bishop
score -= BishopPawns * pe->pawns_on_same_color_squares(Us, s);
// Bonus for bishop on a long diagonal which can "see" both center squares
if (more_than_one(Center & (attacks_bb<BISHOP>(s, pos.pieces(PAWN)) | s)))
score += LongRangedBishop;
}
// An important Chess960 pattern: A cornered bishop blocked by a friendly
// pawn diagonally in front of it is a very serious problem, especially
// when that pawn is also blocked.
@ -348,7 +375,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 ? EAST : WEST);
Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2
@ -364,7 +391,7 @@ namespace {
// Bonus when on an open or semi-open file
if (pe->semiopen_file(Us, file_of(s)))
score += RookOnFile[!!pe->semiopen_file(Them, file_of(s))];
score += RookOnFile[bool(pe->semiopen_file(Them, file_of(s)))];
// Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3)
@ -395,25 +422,15 @@ namespace {
// evaluate_king() assigns bonuses and penalties to a king of a given color
const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
const Bitboard KingFlank[FILE_NB] = {
QueenSide, QueenSide, QueenSide, CenterFiles, CenterFiles, KingSide, KingSide, KingSide
};
template<Tracing T> template<Color Us>
Score Evaluation<T>::evaluate_king() {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? NORTH : SOUTH);
const Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
: AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
: AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
const Square ksq = pos.square<KING>(Us);
Bitboard kingOnlyDefended, undefended, b, b1, b2, safe, other;
int kingDanger;
Bitboard weak, b, b1, b2, safe, unsafeChecks;
// King shelter and enemy pawns storm
Score score = pe->king_safety<Us>(pos, ksq);
@ -421,78 +438,65 @@ namespace {
// Main king safety evaluation
if (kingAttackersCount[Them] > (1 - pos.count<QUEEN>(Them)))
{
// Find the attacked squares which are defended only by our king...
kingOnlyDefended = attackedBy[Them][ALL_PIECES]
& attackedBy[Us][KING]
& ~attackedBy2[Us];
// Attacked squares defended at most once by our queen or king
weak = attackedBy[Them][ALL_PIECES]
& ~attackedBy2[Us]
& (attackedBy[Us][KING] | attackedBy[Us][QUEEN] | ~attackedBy[Us][ALL_PIECES]);
// ... and those which are not defended at all in the larger king ring
undefended = attackedBy[Them][ALL_PIECES]
& ~attackedBy[Us][ALL_PIECES]
& kingRing[Us]
& ~pos.pieces(Them);
// Initialize the 'kingDanger' variable, which will be transformed
// later into a king danger score. The initial value is based on the
// number and types of the enemy's attacking pieces, the number of
// attacked and weak squares around our king, the absence of queen and
// the quality of the pawn shelter (current 'score' value).
kingDanger = kingAttackersCount[Them] * kingAttackersWeight[Them]
+ 102 * kingAdjacentZoneAttacksCount[Them]
+ 191 * popcount(kingOnlyDefended | undefended)
+ 143 * !!pos.pinned_pieces(Us)
- 848 * !pos.count<QUEEN>(Them)
- 9 * mg_value(score) / 8
+ 40;
int kingDanger = unsafeChecks = 0;
// Analyse the safe enemy's checks which are possible on next move
safe = ~pos.pieces(Them);
safe &= ~attackedBy[Us][ALL_PIECES] | (kingOnlyDefended & attackedBy2[Them]);
safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
b1 = pos.attacks_from< ROOK>(ksq);
b2 = pos.attacks_from<BISHOP>(ksq);
b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
// Enemy queen safe checks
if ((b1 | b2) & attackedBy[Them][QUEEN] & safe)
kingDanger += QueenCheck;
if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN])
kingDanger += QueenSafeCheck;
// For minors and rooks, also consider the square safe if attacked twice,
// and only defended by our queen.
safe |= attackedBy2[Them]
& ~(attackedBy2[Us] | pos.pieces(Them))
& attackedBy[Us][QUEEN];
b1 &= attackedBy[Them][ROOK];
b2 &= attackedBy[Them][BISHOP];
// Some other potential checks are also analysed, even from squares
// currently occupied by the opponent own pieces, as long as the square
// is not attacked by our pawns, and is not occupied by a blocked pawn.
other = ~( attackedBy[Us][PAWN]
| (pos.pieces(Them, PAWN) & shift<Up>(pos.pieces(PAWN))));
// Enemy rooks checks
if (b1 & safe)
kingDanger += RookSafeCheck;
else
unsafeChecks |= b1;
// Enemy rooks safe and other checks
if (b1 & attackedBy[Them][ROOK] & safe)
kingDanger += RookCheck;
// Enemy bishops checks
if (b2 & safe)
kingDanger += BishopSafeCheck;
else
unsafeChecks |= b2;
else if (b1 & attackedBy[Them][ROOK] & other)
score -= OtherCheck;
// Enemy bishops safe and other checks
if (b2 & attackedBy[Them][BISHOP] & safe)
kingDanger += BishopCheck;
else if (b2 & attackedBy[Them][BISHOP] & other)
score -= OtherCheck;
// Enemy knights safe and other checks
// Enemy knights checks
b = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
if (b & safe)
kingDanger += KnightCheck;
kingDanger += KnightSafeCheck;
else
unsafeChecks |= b;
else if (b & other)
score -= OtherCheck;
// Unsafe or occupied checking squares will also be considered, as long as
// the square is in the attacker's mobility area.
unsafeChecks &= mobilityArea[Them];
// Transform the kingDanger units into a Score, and substract it from the evaluation
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
+ 102 * kingAdjacentZoneAttacksCount[Them]
+ 191 * popcount(kingRing[Us] & weak)
+ 143 * popcount(pos.pinned_pieces(Us) | unsafeChecks)
- 848 * !pos.count<QUEEN>(Them)
- 9 * mg_value(score) / 8
+ 40;
// Transform the kingDanger units into a Score, and subtract it from the evaluation
if (kingDanger > 0)
{
int mobilityDanger = mg_value(mobility[Them] - mobility[Us]);
kingDanger = std::max(0, kingDanger + mobilityDanger);
score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
}
}
// King tropism: firstly, find squares that opponent attacks in our king flank
@ -526,11 +530,11 @@ namespace {
template<Tracing T> template<Color Us>
Score Evaluation<T>::evaluate_threats() {
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 TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Direction Up = (Us == WHITE ? NORTH : SOUTH);
const Direction Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
const Direction Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
Bitboard b, weak, defended, stronglyProtected, safeThreats;
Score score = SCORE_ZERO;
@ -546,9 +550,6 @@ namespace {
safeThreats = (shift<Right>(b) | shift<Left>(b)) & weak;
score += ThreatBySafePawn * popcount(safeThreats);
if (weak ^ safeThreats)
score += ThreatByHangingPawn;
}
// Squares strongly protected by the opponent, either because they attack the
@ -593,6 +594,10 @@ namespace {
score += ThreatByKing[more_than_one(b)];
}
// Bonus for opponent unopposed weak pawns
if (pos.pieces(Us, ROOK, QUEEN))
score += WeakUnopposedPawn * pe->weak_unopposed(Them);
// Find squares where our pawns can push on the next move
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
b |= shift<Up>(b & TRank3BB) & ~pos.pieces();
@ -608,12 +613,24 @@ namespace {
score += ThreatByPawnPush * popcount(b);
// Add a bonus for safe slider attack threats on opponent queen
safeThreats = ~pos.pieces(Us) & ~attackedBy2[Them] & attackedBy2[Us];
b = (attackedBy[Us][BISHOP] & attackedBy[Them][QUEEN_DIAGONAL])
| (attackedBy[Us][ROOK ] & attackedBy[Them][QUEEN] & ~attackedBy[Them][QUEEN_DIAGONAL]);
score += ThreatByAttackOnQueen * popcount(b & safeThreats);
if (T)
Trace::add(THREAT, Us, score);
return score;
}
// helper used by evaluate_passed_pawns to cap the distance
template<Tracing T>
int Evaluation<T>::king_distance(Color c, Square s) {
return std::min(distance(pos.square<KING>(c), s), 5);
}
// evaluate_passed_pawns() evaluates the passed pawns and candidate passed
// pawns of the given color.
@ -621,8 +638,8 @@ namespace {
template<Tracing T> template<Color Us>
Score Evaluation<T>::evaluate_passed_pawns() {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? NORTH : SOUTH);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Direction Up = (Us == WHITE ? NORTH : SOUTH);
Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares;
Score score = SCORE_ZERO;
@ -638,8 +655,8 @@ namespace {
bb = forward_file_bb(Us, s) & (attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
score -= HinderPassedPawn * popcount(bb);
int r = relative_rank(Us, s) - RANK_2;
int rr = r * (r - 1);
int r = relative_rank(Us, s);
int rr = RankFactor[r];
Value mbonus = Passed[MG][r], ebonus = Passed[EG][r];
@ -648,12 +665,11 @@ namespace {
Square blockSq = s + Up;
// Adjust bonus based on the king's proximity
ebonus += distance(pos.square<KING>(Them), blockSq) * 5 * rr
- distance(pos.square<KING>( Us), blockSq) * 2 * rr;
ebonus += (king_distance(Them, blockSq) * 5 - king_distance(Us, blockSq) * 2) * rr;
// If blockSq is not the queening square then consider also a second push
if (relative_rank(Us, blockSq) != RANK_8)
ebonus -= distance(pos.square<KING>(Us), blockSq + Up) * rr;
if (r != RANK_7)
ebonus -= king_distance(Us, blockSq + Up) * rr;
// If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq))
@ -762,6 +778,9 @@ namespace {
// that the endgame score will never change sign after the bonus.
int v = ((eg > 0) - (eg < 0)) * std::max(initiative, -abs(eg));
if (T)
Trace::add(INITIATIVE, make_score(0, v));
return make_score(0, v);
}
@ -822,7 +841,7 @@ namespace {
// Initialize score by reading the incrementally updated scores included in
// the position object (material + piece square tables) and the material
// imbalance. Score is computed internally from the white point of view.
Score score = pos.psq_score() + me->imbalance();
Score score = pos.psq_score() + me->imbalance() + Eval::Contempt;
// Probe the pawn hash table
pe = Pawns::probe(pos);
@ -880,18 +899,19 @@ namespace {
Trace::add(TOTAL, score);
}
return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; // Side to move point of view
return pos.side_to_move() == WHITE ? v : -v; // Side to move point of view
}
} // namespace
Score Eval::Contempt = SCORE_ZERO;
/// evaluate() is the evaluator for the outer world. It returns a static evaluation
/// of the position from the point of view of the side to move.
Value Eval::evaluate(const Position& pos)
{
return Evaluation<>(pos).value();
return Evaluation<>(pos).value() + Eval::Tempo;
}
/// trace() is like evaluate(), but instead of returning a value, it returns
@ -902,7 +922,7 @@ std::string Eval::trace(const Position& pos) {
std::memset(scores, 0, sizeof(scores));
Value v = Evaluation<TRACE>(pos).value();
Value v = Evaluation<TRACE>(pos).value() + Eval::Tempo;
v = pos.side_to_move() == WHITE ? v : -v; // White's point of view
std::stringstream ss;
@ -922,6 +942,7 @@ std::string Eval::trace(const Position& pos) {
<< " Threats | " << Term(THREAT)
<< " Passed pawns | " << Term(PASSED)
<< " Space | " << Term(SPACE)
<< " Initiative | " << Term(INITIATIVE)
<< "----------------+-------------+-------------+-------------\n"
<< " Total | " << Term(TOTAL);

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -31,6 +31,8 @@ namespace Eval {
const Value Tempo = Value(20); // Must be visible to search
extern Score Contempt;
std::string trace(const Position& pos);
Value evaluate(const Position& pos);

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -45,11 +45,11 @@ int main(int argc, char* argv[]) {
Pawns::init();
Tablebases::init(Options["SyzygyPath"]);
TT.resize(Options["Hash"]);
Threads.init(Options["Threads"]);
Threads.set(Options["Threads"]);
Search::clear(); // After threads are up
UCI::loop(argc, argv);
Threads.exit();
Threads.set(0);
return 0;
}

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -39,7 +39,7 @@ namespace {
{ 32, 255, -3 }, // Knight OUR PIECES
{ 0, 104, 4, 0 }, // Bishop
{ -26, -2, 47, 105, -149 }, // Rook
{-185, 24, 122, 137, -134, 0 } // Queen
{-189, 24, 117, 133, -134, -10 } // Queen
};
const int QuadraticTheirs[][PIECE_TYPE_NB] = {
@ -50,18 +50,7 @@ namespace {
{ 9, 63, 0 }, // Knight OUR PIECES
{ 59, 65, 42, 0 }, // Bishop
{ 46, 39, 24, -24, 0 }, // Rook
{ 101, 100, -37, 141, 268, 0 } // Queen
};
// PawnSet[pawn count] contains a bonus/malus indexed by number of pawns
const int PawnSet[] = {
24, -32, 107, -51, 117, -9, -126, -21, 31
};
// QueenMinorsImbalance[opp_minor_count] is applied when only one side has a queen.
// It contains a bonus/malus for the side with the queen.
const int QueenMinorsImbalance[13] = {
31, -8, -15, -25, -5
{ 97, 100, -42, 137, 268, 0 } // Queen
};
// Endgame evaluation and scaling functions are accessed directly and not through
@ -100,9 +89,9 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
int bonus = PawnSet[pieceCount[Us][PAWN]];
int bonus = 0;
// Second-degree polynomial material imbalance by Tord Romstad
// Second-degree polynomial material imbalance, by Tord Romstad
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
{
if (!pieceCount[Us][pt1])
@ -117,10 +106,6 @@ namespace {
bonus += pieceCount[Us][pt1] * v;
}
// Special handling of Queen vs. Minors
if (pieceCount[Us][QUEEN] == 1 && pieceCount[Them][QUEEN] == 0)
bonus += QueenMinorsImbalance[pieceCount[Them][KNIGHT] + pieceCount[Them][BISHOP]];
return bonus;
}

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -51,7 +51,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 = "2017-09-06";
const string Version = "9";
/// 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
@ -293,14 +293,6 @@ int get_group(size_t idx) {
void bindThisThread(size_t idx) {
// If OS already scheduled us on a different group than 0 then don't overwrite
// the choice, eventually we are one of many one-threaded processes running on
// some Windows NUMA hardware, for instance in fishtest. To make it simple,
// just check if running threads are below a threshold, in this case all this
// NUMA machinery is not needed.
if (Threads.size() < 8)
return;
// Use only local variables to be thread-safe
int group = get_group(idx);

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -42,8 +42,8 @@ namespace {
assert(!pos.checkers());
const Square K = Chess960 ? kto > kfrom ? WEST : EAST
: KingSide ? WEST : EAST;
const Direction K = Chess960 ? kto > kfrom ? WEST : EAST
: KingSide ? WEST : EAST;
for (Square s = kto; s != kfrom; s += K)
if (pos.attackers_to(s) & enemies)
@ -65,7 +65,7 @@ namespace {
}
template<GenType Type, Square D>
template<GenType Type, Direction D>
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
@ -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 ? NORTH : SOUTH);
const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
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 Direction Up = (Us == WHITE ? NORTH : SOUTH);
const Direction Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
const Direction Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
Bitboard emptySquares;

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -45,7 +45,7 @@ struct ExtMove {
// Inhibit unwanted implicit conversions to Move
// with an ambiguity that yields to a compile error.
operator float() const;
operator float() const = delete;
};
inline bool operator<(const ExtMove& f, const ExtMove& s) {

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -68,8 +68,8 @@ namespace {
/// MovePicker constructor for the main search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
const PieceToHistory** ch, Move cm, Move* killers_p)
: pos(p), mainHistory(mh), contHistory(ch), countermove(cm),
const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers_p)
: pos(p), mainHistory(mh), captureHistory(cph), contHistory(ch), countermove(cm),
killers{killers_p[0], killers_p[1]}, depth(d){
assert(d > DEPTH_ZERO);
@ -80,8 +80,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
}
/// MovePicker constructor for quiescence search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, Square s)
: pos(p), mainHistory(mh) {
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, Square s)
: pos(p), mainHistory(mh), captureHistory(cph) {
assert(d <= DEPTH_ZERO);
@ -107,8 +107,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
/// MovePicker constructor for ProbCut: we generate captures with SEE higher
/// than or equal to the given threshold.
MovePicker::MovePicker(const Position& p, Move ttm, Value th)
: pos(p), threshold(th) {
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph)
: pos(p), captureHistory(cph), threshold(th) {
assert(!pos.checkers());
@ -123,7 +123,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th)
/// score() assigns a numerical value to each move in a list, used for sorting.
/// Captures are ordered by Most Valuable Victim (MVV), preferring captures
/// near our home rank. Quiets are ordered using the histories.
/// with a good history. Quiets are ordered using the histories.
template<GenType Type>
void MovePicker::score() {
@ -132,7 +132,7 @@ void MovePicker::score() {
for (auto& m : *this)
if (Type == CAPTURES)
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(200 * relative_rank(pos.side_to_move(), to_sq(m)));
+ Value((*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]);
else if (Type == QUIETS)
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
@ -179,7 +179,7 @@ Move MovePicker::next_move(bool skipQuiets) {
move = pick_best(cur++, endMoves);
if (move != ttMove)
{
if (pos.see_ge(move))
if (pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)))
return move;
// Losing capture, move it to the beginning of the array

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -22,6 +22,7 @@
#define MOVEPICK_H_INCLUDED
#include <array>
#include <limits>
#include "movegen.h"
#include "position.h"
@ -39,7 +40,7 @@ struct StatBoards : public std::array<std::array<T, Size2>, Size1> {
void update(T& entry, int bonus, const int D) {
assert(abs(bonus) <= D); // Ensure range is [-32 * D, 32 * D]
assert(abs(32 * D) < INT16_MAX); // Ensure we don't overflow
assert(abs(32 * D) < (std::numeric_limits<T>::max)()); // Ensure we don't overflow
entry += bonus * 32 - entry * abs(bonus) / D;
@ -47,6 +48,26 @@ struct StatBoards : public std::array<std::array<T, Size2>, Size1> {
}
};
/// StatCubes is a generic 3-dimensional array used to store various statistics
template<int Size1, int Size2, int Size3, typename T = int16_t>
struct StatCubes : public std::array<std::array<std::array<T, Size3>, Size2>, Size1> {
void fill(const T& v) {
T* p = &(*this)[0][0][0];
std::fill(p, p + sizeof(*this) / sizeof(*p), v);
}
void update(T& entry, int bonus, const int D, const int W) {
assert(abs(bonus) <= D); // Ensure range is [-W * D, W * D]
assert(abs(W * D) < (std::numeric_limits<T>::max)()); // Ensure we don't overflow
entry += bonus * W - entry * abs(bonus) / D;
assert(abs(entry) <= W * D);
}
};
/// ButterflyBoards are 2 tables (one for each color) indexed by the move's from
/// and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards
typedef StatBoards<COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyBoards;
@ -54,6 +75,9 @@ typedef StatBoards<COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyBoards;
/// PieceToBoards are addressed by a move's [piece][to] information
typedef StatBoards<PIECE_NB, SQUARE_NB> PieceToBoards;
/// CapturePieceToBoards are addressed by a move's [piece][to][captured piece type] information
typedef StatCubes<PIECE_NB, SQUARE_NB, PIECE_TYPE_NB> CapturePieceToBoards;
/// ButterflyHistory records how often quiet moves have been successful or
/// unsuccessful during the current search, and is used for reduction and move
/// ordering decisions. It uses ButterflyBoards as backing store.
@ -72,6 +96,14 @@ struct PieceToHistory : public PieceToBoards {
}
};
/// CapturePieceToHistory is like PieceToHistory, but is based on CapturePieceToBoards
struct CapturePieceToHistory : public CapturePieceToBoards {
void update(Piece pc, Square to, PieceType captured, int bonus) {
StatCubes::update((*this)[pc][to][captured], bonus, 324, 2);
}
};
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic
typedef StatBoards<PIECE_NB, SQUARE_NB, Move> CounterMoveHistory;
@ -93,9 +125,9 @@ class MovePicker {
public:
MovePicker(const MovePicker&) = delete;
MovePicker& operator=(const MovePicker&) = delete;
MovePicker(const Position&, Move, Value);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, Square);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const PieceToHistory**, Move, Move*);
MovePicker(const Position&, Move, Value, const CapturePieceToHistory*);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, Square);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Move, Move*);
Move next_move(bool skipQuiets = false);
private:
@ -105,6 +137,7 @@ private:
const Position& pos;
const ButterflyHistory* mainHistory;
const CapturePieceToHistory* captureHistory;
const PieceToHistory** contHistory;
Move ttMove, countermove, killers[2];
ExtMove *cur, *endMoves, *endBadCaptures;

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -31,11 +31,11 @@ namespace {
#define V Value
#define S(mg, eg) make_score(mg, eg)
// Isolated pawn penalty by opposed flag
const Score Isolated[] = { S(27, 30), S(13, 18) };
// Isolated pawn penalty
const Score Isolated = S(13, 18);
// Backward pawn penalty by opposed flag
const Score Backward[] = { S(40, 26), S(24, 12) };
// Backward pawn penalty
const Score Backward = S(24, 12);
// Connected pawn bonus by opposed, phalanx, #support and rank
Score Connected[2][2][3][RANK_NB];
@ -43,12 +43,6 @@ namespace {
// Doubled pawn penalty
const Score Doubled = S(18, 38);
// 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)
};
// Weakness of our pawn shelter in front of the king by [isKingFile][distance from edge][rank].
// RANK_1 = 0 is used for files where we have no pawns or our pawn is behind our king.
const Value ShelterWeakness[][int(FILE_NB) / 2][RANK_NB] = {
@ -94,10 +88,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 ? NORTH : SOUTH);
const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Direction Up = (Us == WHITE ? NORTH : SOUTH);
const Direction Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
const Direction Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
Bitboard b, neighbours, stoppers, doubled, supported, phalanx;
Bitboard lever, leverPush;
@ -109,7 +103,7 @@ namespace {
Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
e->semiopenFiles[Us] = 0xFF;
e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = shift<Right>(ourPawns) | shift<Left>(ourPawns);
@ -174,19 +168,16 @@ namespace {
// Score this pawn
if (supported | phalanx)
score += Connected[opposed][!!phalanx][popcount(supported)][relative_rank(Us, s)];
score += Connected[opposed][bool(phalanx)][popcount(supported)][relative_rank(Us, s)];
else if (!neighbours)
score -= Isolated[opposed];
score -= Isolated, e->weakUnopposed[Us] += !opposed;
else if (backward)
score -= Backward[opposed];
score -= Backward, e->weakUnopposed[Us] += !opposed;
if (doubled && !supported)
score -= Doubled;
if (lever)
score += Lever[relative_rank(Us, s)];
}
return score;
@ -254,7 +245,7 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
Value safety = MaxSafetyBonus;
File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
for (File f = center - File(1); f <= center + File(1); ++f)
for (File f = File(center - 1); f <= File(center + 1); ++f)
{
b = ourPawns & file_bb(f);
Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
@ -262,7 +253,7 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
b = theirPawns & file_bb(f);
Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
int d = std::min(f, FILE_H - f);
int d = std::min(f, ~f);
safety -= ShelterWeakness[f == file_of(ksq)][d][rkUs]
+ StormDanger
[f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing :

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -37,6 +37,7 @@ struct Entry {
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
int weak_unopposed(Color c) const { return weakUnopposed[c]; }
int pawn_asymmetry() const { return asymmetry; }
int open_files() const { return openFiles; }
@ -49,7 +50,7 @@ struct Entry {
}
int pawns_on_same_color_squares(Color c, Square s) const {
return pawnsOnSquares[c][!!(DarkSquares & s)];
return pawnsOnSquares[c][bool(DarkSquares & s)];
}
template<Color Us>
@ -71,6 +72,7 @@ struct Entry {
Bitboard pawnAttacksSpan[COLOR_NB];
Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB];
int weakUnopposed[COLOR_NB];
int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -211,10 +211,10 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
while ((ss >> token) && !isspace(token))
{
if (isdigit(token))
sq += Square(token - '0'); // Advance the given number of files
sq += (token - '0') * EAST; // Advance the given number of files
else if (token == '/')
sq -= Square(16);
sq += 2 * SOUTH;
else if ((idx = PieceToChar.find(token)) != string::npos)
{
@ -272,7 +272,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
// 5-6. Halfmove clock and fullmove number
ss >> std::skipws >> st->rule50 >> gamePly;
// Convert from fullmove starting from 1 to ply starting from 0,
// Convert from fullmove starting from 1 to gamePly starting from 0,
// handle also common incorrect FEN with fullmove = 0.
gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
@ -787,7 +787,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
if ( (int(to) ^ int(from)) == 16
&& (attacks_from<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN)))
{
st->epSquare = (from + to) / 2;
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
}
@ -1003,17 +1003,21 @@ bool Position::see_ge(Move m, Value threshold) const {
Value balance; // Values of the pieces taken by us minus opponent's ones
Bitboard occupied, stmAttackers;
balance = PieceValue[MG][piece_on(to)];
// The opponent may be able to recapture so this is the best result
// we can hope for.
balance = PieceValue[MG][piece_on(to)] - threshold;
if (balance < threshold)
if (balance < VALUE_ZERO)
return false;
// Now assume the worst possible result: that the opponent can
// capture our piece for free.
balance -= PieceValue[MG][nextVictim];
if (balance >= threshold) // Always true if nextVictim == KING
if (balance >= VALUE_ZERO) // Always true if nextVictim == KING
return true;
bool relativeStm = true; // True if the opponent is to move
bool opponentToMove = true;
occupied = pieces() ^ from ^ to;
// Find all attackers to the destination square, with the moving piece removed,
@ -1022,6 +1026,12 @@ bool Position::see_ge(Move m, Value threshold) const {
while (true)
{
// The balance is negative only because we assumed we could win
// the last piece for free. We are truly winning only if we can
// win the last piece _cheaply enough_. Test if we can actually
// do this otherwise "give up".
assert(balance < VALUE_ZERO);
stmAttackers = attackers & pieces(stm);
// Don't allow pinned pieces to attack pieces except the king as long all
@ -1029,25 +1039,40 @@ bool Position::see_ge(Move m, Value threshold) const {
if (!(st->pinnersForKing[stm] & ~occupied))
stmAttackers &= ~st->blockersForKing[stm];
// If we have no more attackers we must give up
if (!stmAttackers)
return relativeStm;
break;
// Locate and remove the next least valuable attacker
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
if (nextVictim == KING)
return relativeStm == bool(attackers & pieces(~stm));
{
// Our only attacker is the king. If the opponent still has
// attackers we must give up. Otherwise we make the move and
// (having no more attackers) the opponent must give up.
if (!(attackers & pieces(~stm)))
opponentToMove = !opponentToMove;
break;
}
balance += relativeStm ? PieceValue[MG][nextVictim]
: -PieceValue[MG][nextVictim];
// Assume the opponent can win the next piece for free and switch sides
balance += PieceValue[MG][nextVictim];
opponentToMove = !opponentToMove;
relativeStm = !relativeStm;
if (relativeStm == (balance >= threshold))
return relativeStm;
// If balance is negative after receiving a free piece then give up
if (balance < VALUE_ZERO)
break;
// Complete the process of switching sides. The first line swaps
// all negative numbers with non-negative numbers. The compiler
// probably knows that it is just the bitwise negation ~balance.
balance = -balance-1;
stm = ~stm;
}
// If the opponent gave up we win, otherwise we lose.
return opponentToMove;
}
@ -1071,11 +1096,10 @@ bool Position::is_draw(int ply) const {
{
stp = stp->previous->previous;
// At root position ply is 1, so return a draw score if a position
// repeats once earlier but strictly after the root, or repeats twice
// before or at the root.
// Return a draw score if a position repeats once earlier but strictly
// after the root, or repeats twice before or at the root.
if ( stp->key == st->key
&& ++cnt + (ply - 1 > i) == 2)
&& ++cnt + (ply > i) == 2)
return true;
}

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -116,7 +116,7 @@ void init() {
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
File f = std::min(file_of(s), FILE_H - file_of(s));
File f = std::min(file_of(s), ~file_of(s));
psq[ pc][ s] = v + Bonus[pc][rank_of(s)][f];
psq[~pc][~s] = -psq[pc][s];
}

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -67,8 +67,7 @@ namespace {
const int skipPhase[] = { 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7 };
// Razoring and futility margin based on depth
// razor_margin[0] is unused as long as depth >= ONE_PLY in search
const int razor_margin[] = { 0, 570, 603, 554 };
const int razor_margin = 600;
Value futility_margin(Depth d) { return Value(150 * d / ONE_PLY); }
// Futility and reductions lookup tables, initialized at startup
@ -87,58 +86,15 @@ namespace {
// Skill structure is used to implement strength limit
struct Skill {
Skill(int l) : level(l) {}
explicit Skill(int l) : level(l) {}
bool enabled() const { return level < 20; }
bool time_to_pick(Depth depth) const { return depth / ONE_PLY == 1 + level; }
Move best_move(size_t multiPV) { return best ? best : pick_best(multiPV); }
Move pick_best(size_t multiPV);
int level;
Move best = MOVE_NONE;
};
// EasyMoveManager structure is used to detect an 'easy move'. When the PV is stable
// across multiple search iterations, we can quickly return the best move.
struct EasyMoveManager {
void clear() {
stableCnt = 0;
expectedPosKey = 0;
pv[0] = pv[1] = pv[2] = MOVE_NONE;
}
Move get(Key key) const {
return expectedPosKey == key ? pv[2] : MOVE_NONE;
}
void update(Position& pos, const std::vector<Move>& newPv) {
assert(newPv.size() >= 3);
// Keep track of how many times in a row the 3rd ply remains stable
stableCnt = (newPv[2] == pv[2]) ? stableCnt + 1 : 0;
if (!std::equal(newPv.begin(), newPv.begin() + 3, pv))
{
std::copy(newPv.begin(), newPv.begin() + 3, pv);
StateInfo st[2];
pos.do_move(newPv[0], st[0]);
pos.do_move(newPv[1], st[1]);
expectedPosKey = pos.key();
pos.undo_move(newPv[1]);
pos.undo_move(newPv[0]);
}
}
Key expectedPosKey;
int stableCnt;
Move pv[3];
};
EasyMoveManager EasyMove;
Value DrawValue[COLOR_NB];
template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning);
@ -150,6 +106,8 @@ namespace {
void update_pv(Move* pv, Move move, Move* childPv);
void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus);
void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus);
void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCnt, int bonus);
bool pv_is_draw(Position& pos);
// perft() is our utility to verify move generation. All the leaf nodes up
// to the given depth are generated and counted, and the sum is returned.
@ -190,7 +148,7 @@ void Search::init() {
{
double r = log(d) * log(mc) / 1.95;
Reductions[NonPV][imp][d][mc] = int(round(r));
Reductions[NonPV][imp][d][mc] = int(std::round(r));
Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - 1, 0);
// Increase reduction for non-PV nodes when eval is not improving
@ -214,12 +172,7 @@ void Search::clear() {
Time.availableNodes = 0;
TT.clear();
for (Thread* th : Threads)
th->clear();
Threads.main()->callsCnt = 0;
Threads.main()->previousScore = VALUE_INFINITE;
Threads.clear();
}
@ -240,8 +193,9 @@ void MainThread::search() {
TT.new_search();
int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns
DrawValue[ us] = VALUE_DRAW - Value(contempt);
DrawValue[~us] = VALUE_DRAW + Value(contempt);
Eval::Contempt = (us == WHITE ? make_score(contempt, contempt / 2)
: -make_score(contempt, contempt / 2));
if (rootMoves.empty())
{
@ -259,11 +213,6 @@ void MainThread::search() {
Thread::search(); // Let's start searching!
}
// When playing in 'nodes as time' mode, subtract the searched nodes from
// the available ones before exiting.
if (Limits.npmsec)
Time.availableNodes += Limits.inc[us] - Threads.nodes_searched();
// When we reach the maximum depth, we can arrive here without a raise of
// Threads.stop. However, if we are pondering or in an infinite search,
// the UCI protocol states that we shouldn't print the best move before the
@ -283,10 +232,14 @@ void MainThread::search() {
if (th != this)
th->wait_for_search_finished();
// When playing in 'nodes as time' mode, subtract the searched nodes from
// the available ones before exiting.
if (Limits.npmsec)
Time.availableNodes += Limits.inc[us] - Threads.nodes_searched();
// Check if there are threads with a better score than main thread
Thread* bestThread = this;
if ( !this->easyMovePlayed
&& Options["MultiPV"] == 1
if ( Options["MultiPV"] == 1
&& !Limits.depth
&& !Skill(Options["Skill Level"]).enabled()
&& rootMoves[0].pv[0] != MOVE_NONE)
@ -324,10 +277,12 @@ void MainThread::search() {
void Thread::search() {
Stack stack[MAX_PLY+7], *ss = stack+4; // To allow referencing (ss-4) and (ss+2)
Stack stack[MAX_PLY+7], *ss = stack+4; // To reference from (ss-4) to (ss+2)
Value bestValue, alpha, beta, delta;
Move easyMove = MOVE_NONE;
Move lastBestMove = MOVE_NONE;
Depth lastBestMoveDepth = DEPTH_ZERO;
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
double timeReduction = 1.0;
std::memset(ss-4, 0, 7 * sizeof(Stack));
for (int i = 4; i > 0; i--)
@ -338,9 +293,7 @@ void Thread::search() {
if (mainThread)
{
easyMove = EasyMove.get(rootPos.key());
EasyMove.clear();
mainThread->easyMovePlayed = mainThread->failedLow = false;
mainThread->failedLow = false;
mainThread->bestMoveChanges = 0;
}
@ -453,6 +406,11 @@ void Thread::search() {
if (!Threads.stop)
completedDepth = rootDepth;
if (rootMoves[0].pv[0] != lastBestMove) {
lastBestMove = rootMoves[0].pv[0];
lastBestMoveDepth = rootDepth;
}
// Have we found a "mate in x"?
if ( Limits.mate
&& bestValue >= VALUE_MATE_IN_MAX_PLY
@ -472,21 +430,29 @@ void Thread::search() {
if (!Threads.stop && !Threads.stopOnPonderhit)
{
// Stop the search if only one legal move is available, or if all
// of the available time has been used, or if we matched an easyMove
// from the previous search and just did a fast verification.
// of the available time has been used
const int F[] = { mainThread->failedLow,
bestValue - mainThread->previousScore };
int improvingFactor = std::max(229, std::min(715, 357 + 119 * F[0] - 6 * F[1]));
double unstablePvFactor = 1 + mainThread->bestMoveChanges;
bool doEasyMove = rootMoves[0].pv[0] == easyMove
&& mainThread->bestMoveChanges < 0.03
&& Time.elapsed() > Time.optimum() * 5 / 44;
Color us = rootPos.side_to_move();
bool thinkHard = bestValue == VALUE_DRAW
&& Limits.time[us] - Time.elapsed() > Limits.time[~us]
&& ::pv_is_draw(rootPos);
double unstablePvFactor = 1 + mainThread->bestMoveChanges + thinkHard;
// if the bestMove is stable over several iterations, reduce time for this move,
// the longer the move has been stable, the more.
// Use part of the gained time from a previous stable move for the current move.
timeReduction = 1;
for (int i : {3, 4, 5})
if (lastBestMoveDepth * i < completedDepth && !thinkHard)
timeReduction *= 1.3;
unstablePvFactor *= std::pow(mainThread->previousTimeReduction, 0.51) / timeReduction;
if ( rootMoves.size() == 1
|| Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628
|| (mainThread->easyMovePlayed = doEasyMove, doEasyMove))
|| Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628)
{
// If we are allowed to ponder do not stop the search now but
// keep pondering until the GUI sends "ponderhit" or "stop".
@ -496,26 +462,18 @@ void Thread::search() {
Threads.stop = true;
}
}
if (rootMoves[0].pv.size() >= 3)
EasyMove.update(rootPos, rootMoves[0].pv);
else
EasyMove.clear();
}
}
if (!mainThread)
return;
// Clear any candidate easy move that wasn't stable for the last search
// iterations; the second condition prevents consecutive fast moves.
if (EasyMove.stableCnt < 6 || mainThread->easyMovePlayed)
EasyMove.clear();
mainThread->previousTimeReduction = timeReduction;
// If skill level is enabled, swap best PV line with the sub-optimal one
if (skill.enabled())
std::swap(rootMoves[0], *std::find(rootMoves.begin(),
rootMoves.end(), skill.best_move(multiPV)));
std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(),
skill.best ? skill.best : skill.pick_best(multiPV)));
}
@ -527,7 +485,7 @@ namespace {
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning) {
const bool PvNode = NT == PV;
const bool rootNode = PvNode && (ss-1)->ply == 0;
const bool rootNode = PvNode && ss->ply == 0;
assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE);
assert(PvNode || (alpha == beta - 1));
@ -535,7 +493,7 @@ namespace {
assert(!(PvNode && cutNode));
assert(depth / ONE_PLY * ONE_PLY == depth);
Move pv[MAX_PLY+1], quietsSearched[64];
Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64];
StateInfo st;
TTEntry* tte;
Key posKey;
@ -543,33 +501,31 @@ namespace {
Depth extension, newDepth;
Value bestValue, value, ttValue, eval, maxValue;
bool ttHit, inCheck, givesCheck, singularExtensionNode, improving;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact;
Piece movedPiece;
int moveCount, quietCount;
int moveCount, captureCount, quietCount;
// Step 1. Initialize node
Thread* thisThread = pos.this_thread();
inCheck = pos.checkers();
moveCount = quietCount = ss->moveCount = 0;
moveCount = captureCount = quietCount = ss->moveCount = 0;
ss->statScore = 0;
bestValue = -VALUE_INFINITE;
maxValue = VALUE_INFINITE;
ss->ply = (ss-1)->ply + 1;
// Check for the available remaining time
if (thisThread == Threads.main())
static_cast<MainThread*>(thisThread)->check_time();
// Used to send selDepth info to GUI
if (PvNode && thisThread->selDepth < ss->ply)
thisThread->selDepth = ss->ply;
// Used to send selDepth info to GUI (selDepth counts from 1, ply from 0)
if (PvNode && thisThread->selDepth < ss->ply + 1)
thisThread->selDepth = ss->ply + 1;
if (!rootNode)
{
// Step 2. Check for aborted search and immediate draw
if (Threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY)
return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos)
: DrawValue[pos.side_to_move()];
return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : VALUE_DRAW;
// Step 3. Mate distance pruning. Even if we mate at the next move our score
// would be at best mate_in(ss->ply+1), but if alpha is already bigger because
@ -585,6 +541,7 @@ namespace {
assert(0 <= ss->ply && ss->ply < MAX_PLY);
(ss+1)->ply = ss->ply + 1;
ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
ss->contHistory = &thisThread->contHistory[NO_PIECE][0];
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
@ -594,7 +551,7 @@ namespace {
// search to overwrite a previous full search TT value, so we use a different
// position key in case of an excluded move.
excludedMove = ss->excludedMove;
posKey = pos.key() ^ Key(excludedMove);
posKey = pos.key() ^ Key(excludedMove << 16); // isn't a very good hash
tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ttMove = rootNode ? thisThread->rootMoves[thisThread->PVIdx].pv[0]
@ -650,8 +607,8 @@ namespace {
int drawScore = TB::UseRule50 ? 1 : 0;
value = wdl < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply
: wdl > drawScore ? VALUE_MATE - MAX_PLY - ss->ply
value = wdl < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply + 1
: wdl > drawScore ? VALUE_MATE - MAX_PLY - ss->ply - 1
: VALUE_DRAW + 2 * wdl * drawScore;
Bound b = wdl < -drawScore ? BOUND_UPPER
@ -706,18 +663,18 @@ namespace {
ss->staticEval, TT.generation());
}
if (skipEarlyPruning)
if (skipEarlyPruning || !pos.non_pawn_material(pos.side_to_move()))
goto moves_loop;
// Step 6. Razoring (skipped when in check)
if ( !PvNode
&& depth < 4 * ONE_PLY
&& eval + razor_margin[depth / ONE_PLY] <= alpha)
&& eval + razor_margin <= alpha)
{
if (depth <= ONE_PLY)
return qsearch<NonPV, false>(pos, ss, alpha, alpha+1);
Value ralpha = alpha - razor_margin[depth / ONE_PLY];
Value ralpha = alpha - razor_margin;
Value v = qsearch<NonPV, false>(pos, ss, ralpha, ralpha+1);
if (v <= ralpha)
return v;
@ -727,15 +684,14 @@ namespace {
if ( !rootNode
&& depth < 7 * ONE_PLY
&& eval - futility_margin(depth) >= beta
&& eval < VALUE_KNOWN_WIN // Do not return unproven wins
&& pos.non_pawn_material(pos.side_to_move()))
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
return eval;
// Step 8. Null move search with verification search (is omitted in PV nodes)
if ( !PvNode
&& eval >= beta
&& (ss->staticEval >= beta - 35 * (depth / ONE_PLY - 6) || depth >= 13 * ONE_PLY)
&& pos.non_pawn_material(pos.side_to_move()))
&& ss->staticEval >= beta - 36 * depth / ONE_PLY + 225
&& (ss->ply >= thisThread->nmp_ply || ss->ply % 2 != thisThread->nmp_odd))
{
assert(eval - beta >= 0);
@ -757,13 +713,19 @@ namespace {
if (nullValue >= VALUE_MATE_IN_MAX_PLY)
nullValue = beta;
if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN)
if (abs(beta) < VALUE_KNOWN_WIN && (depth < 12 * ONE_PLY || thisThread->nmp_ply))
return nullValue;
// Do verification search at high depths
// disable null move pruning for side to move for the first part of the remaining search tree
thisThread->nmp_ply = ss->ply + 3 * (depth-R) / 4;
thisThread->nmp_odd = ss->ply % 2;
Value v = depth-R < ONE_PLY ? qsearch<NonPV, false>(pos, ss, beta-1, beta)
: search<NonPV>(pos, ss, beta-1, beta, depth-R, false, true);
thisThread->nmp_odd = thisThread->nmp_ply = 0;
if (v >= beta)
return nullValue;
}
@ -780,7 +742,7 @@ namespace {
assert(is_ok((ss-1)->currentMove));
MovePicker mp(pos, ttMove, rbeta - ss->staticEval);
MovePicker mp(pos, ttMove, rbeta - ss->staticEval, &thisThread->captureHistory);
while ((move = mp.next_move()) != MOVE_NONE)
if (pos.legal(move))
@ -814,7 +776,7 @@ moves_loop: // When in check search starts from here
const PieceToHistory* contHist[] = { (ss-1)->contHistory, (ss-2)->contHistory, nullptr, (ss-4)->contHistory };
Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, contHist, countermove, ss->killers);
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, countermove, ss->killers);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
improving = ss->staticEval >= (ss-2)->staticEval
/* || ss->staticEval == VALUE_NONE Already implicit in the previous condition */
@ -829,6 +791,7 @@ moves_loop: // When in check search starts from here
&& tte->depth() >= depth - 3 * ONE_PLY;
skipQuiets = false;
ttCapture = false;
pvExact = PvNode && ttHit && tte->bound() == BOUND_EXACT;
// Step 11. Loop through moves
// Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs
@ -861,7 +824,7 @@ moves_loop: // When in check search starts from here
movedPiece = pos.moved_piece(move);
givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates()
? pos.check_squares(type_of(pos.piece_on(from_sq(move)))) & to_sq(move)
? pos.check_squares(type_of(movedPiece)) & to_sq(move)
: pos.gives_check(move);
moveCountPruning = depth < 16 * ONE_PLY
@ -973,6 +936,10 @@ moves_loop: // When in check search starts from here
if ((ss-1)->moveCount > 15)
r -= ONE_PLY;
// Decrease reduction for exact PV nodes
if (pvExact)
r -= ONE_PLY;
// Increase reduction if ttMove is a capture
if (ttCapture)
r += ONE_PLY;
@ -995,10 +962,10 @@ moves_loop: // When in check search starts from here
- 4000;
// Decrease/increase reduction by comparing opponent's stat score
if (ss->statScore > 0 && (ss-1)->statScore < 0)
if (ss->statScore >= 0 && (ss-1)->statScore < 0)
r -= ONE_PLY;
else if (ss->statScore < 0 && (ss-1)->statScore > 0)
else if ((ss-1)->statScore >= 0 && ss->statScore < 0)
r += ONE_PLY;
// Decrease/increase reduction for moves with a good/bad history
@ -1100,6 +1067,8 @@ moves_loop: // When in check search starts from here
if (!captureOrPromotion && move != bestMove && quietCount < 64)
quietsSearched[quietCount++] = move;
else if (captureOrPromotion && move != bestMove && captureCount < 32)
capturesSearched[captureCount++] = move;
}
// The following condition would detect a stop only after move loop has been
@ -1119,12 +1088,14 @@ moves_loop: // When in check search starts from here
if (!moveCount)
bestValue = excludedMove ? alpha
: inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()];
: inCheck ? mated_in(ss->ply) : VALUE_DRAW;
else if (bestMove)
{
// Quiet best move: update move sorting heuristics
if (!pos.capture_or_promotion(bestMove))
update_stats(pos, ss, bestMove, quietsSearched, quietCount, stat_bonus(depth));
else
update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth));
// Extra penalty for a quiet TT move in previous ply when it gets refuted
if ((ss-1)->moveCount == 1 && !pos.captured_piece())
@ -1159,7 +1130,7 @@ moves_loop: // When in check search starts from here
const bool PvNode = NT == PV;
assert(InCheck == !!pos.checkers());
assert(InCheck == bool(pos.checkers()));
assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
assert(PvNode || (alpha == beta - 1));
assert(depth <= DEPTH_ZERO);
@ -1183,13 +1154,12 @@ moves_loop: // When in check search starts from here
}
ss->currentMove = bestMove = MOVE_NONE;
ss->ply = (ss-1)->ply + 1;
(ss+1)->ply = ss->ply + 1;
moveCount = 0;
// Check for an instant draw or if the maximum ply has been reached
if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY)
return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos)
: DrawValue[pos.side_to_move()];
return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : VALUE_DRAW;
assert(0 <= ss->ply && ss->ply < MAX_PLY);
@ -1198,7 +1168,6 @@ moves_loop: // When in check search starts from here
// only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS.
ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
: DEPTH_QS_NO_CHECKS;
// Transposition table lookup
posKey = pos.key();
tte = TT.probe(posKey, ttHit);
@ -1241,7 +1210,7 @@ moves_loop: // When in check search starts from here
if (bestValue >= beta)
{
if (!ttHit)
tte->save(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER,
tte->save(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER,
DEPTH_NONE, MOVE_NONE, ss->staticEval, TT.generation());
return bestValue;
@ -1257,7 +1226,7 @@ moves_loop: // When in check search starts from here
// to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
// be generated.
MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, to_sq((ss-1)->currentMove));
MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, &pos.this_thread()->captureHistory, to_sq((ss-1)->currentMove));
// Loop through the moves until no moves remain or a beta cutoff occurs
while ((move = mp.next_move()) != MOVE_NONE)
@ -1265,7 +1234,7 @@ moves_loop: // When in check search starts from here
assert(is_ok(move));
givesCheck = type_of(move) == NORMAL && !pos.discovered_check_candidates()
? pos.check_squares(type_of(pos.piece_on(from_sq(move)))) & to_sq(move)
? pos.check_squares(type_of(pos.moved_piece(move))) & to_sq(move)
: pos.gives_check(move);
moveCount++;
@ -1301,7 +1270,6 @@ 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_ge(move))
continue;
@ -1412,6 +1380,26 @@ moves_loop: // When in check search starts from here
}
// update_capture_stats() updates move sorting heuristics when a new capture best move is found
void update_capture_stats(const Position& pos, Move move,
Move* captures, int captureCnt, int bonus) {
CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
Piece moved_piece = pos.moved_piece(move);
PieceType captured = type_of(pos.piece_on(to_sq(move)));
captureHistory.update(moved_piece, to_sq(move), captured, bonus);
// Decrease all the other played capture moves
for (int i = 0; i < captureCnt; ++i)
{
moved_piece = pos.moved_piece(captures[i]);
captured = type_of(pos.piece_on(to_sq(captures[i])));
captureHistory.update(moved_piece, to_sq(captures[i]), captured, -bonus);
}
}
// update_stats() updates move sorting heuristics when a new quiet best move is found
void update_stats(const Position& pos, Stack* ss, Move move,
@ -1443,6 +1431,24 @@ moves_loop: // When in check search starts from here
}
// Is the PV leading to a draw position? Assumes all pv moves are legal
bool pv_is_draw(Position& pos) {
StateInfo st[MAX_PLY];
auto& pv = pos.this_thread()->rootMoves[0].pv;
for (size_t i = 0; i < pv.size(); ++i)
pos.do_move(pv[i], st[i]);
bool isDraw = pos.is_draw(pv.size());
for (size_t i = pv.size(); i > 0; --i)
pos.undo_move(pv[i-1]);
return isDraw;
}
// When playing with strength handicap, choose best move among a set of RootMoves
// using a statistical rule dependent on 'level'. Idea by Heinz van Saanen.
@ -1466,7 +1472,7 @@ moves_loop: // When in check search starts from here
int push = ( weakness * int(topScore - rootMoves[i].score)
+ delta * (rng.rand<unsigned>() % weakness)) / 128;
if (rootMoves[i].score + push > maxScore)
if (rootMoves[i].score + push >= maxScore)
{
maxScore = rootMoves[i].score + push;
best = rootMoves[i].pv[0];
@ -1505,7 +1511,7 @@ moves_loop: // When in check search starts from here
if (Threads.ponder)
return;
if ( (Limits.use_time_management() && elapsed > Time.maximum())
if ( (Limits.use_time_management() && elapsed > Time.maximum() - 10)
|| (Limits.movetime && elapsed >= Limits.movetime)
|| (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes))
Threads.stop = true;
@ -1614,6 +1620,10 @@ void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves)
if (Cardinality < popcount(pos.pieces()) || pos.can_castle(ANY_CASTLING))
return;
// Don't filter any moves if the user requested analysis on multiple
if (Options["MultiPV"] != 1)
return;
// If the current root position is in the tablebases, then RootMoves
// contains only moves that preserve the draw or the win.
RootInTB = root_probe(pos, rootMoves, TB::Score);

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (c) 2013 Ronald de Man
Copyright (C) 2016-2017 Marco Costalba, Lucas Braesch
Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -133,16 +133,16 @@ struct Atomic {
std::atomic_bool ready;
};
// We define types for the different parts of the WLDEntry and DTZEntry with
// We define types for the different parts of the WDLEntry and DTZEntry with
// corresponding specializations for pieces or pawns.
struct WLDEntryPiece {
struct WDLEntryPiece {
PairsData* precomp;
};
struct WDLEntryPawn {
uint8_t pawnCount[2]; // [Lead color / other color]
WLDEntryPiece file[2][4]; // [wtm / btm][FILE_A..FILE_D]
WDLEntryPiece file[2][4]; // [wtm / btm][FILE_A..FILE_D]
};
struct DTZEntryPiece {
@ -172,7 +172,7 @@ struct WDLEntry : public TBEntry {
WDLEntry(const std::string& code);
~WDLEntry();
union {
WLDEntryPiece pieceTable[2]; // [wtm / btm]
WDLEntryPiece pieceTable[2]; // [wtm / btm]
WDLEntryPawn pawnTable;
};
};
@ -1379,9 +1379,8 @@ void Tablebases::init(const std::string& paths) {
for (PieceType p3 = PAWN; p3 <= p2; ++p3) {
EntryTable.insert({KING, p1, p2, p3, KING});
if (sizeof(char*) >= 8)
for (PieceType p4 = PAWN; p4 <= p3; ++p4)
EntryTable.insert({KING, p1, p2, p3, p4, KING});
for (PieceType p4 = PAWN; p4 <= p3; ++p4)
EntryTable.insert({KING, p1, p2, p3, p4, KING});
for (PieceType p4 = PAWN; p4 < KING; ++p4)
EntryTable.insert({KING, p1, p2, p3, KING, p4});

View File

@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (c) 2013 Ronald de Man
Copyright (C) 2016-2017 Marco Costalba, Lucas Braesch
Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -24,6 +24,7 @@
#include "movegen.h"
#include "search.h"
#include "thread.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
ThreadPool Threads; // Global object
@ -35,7 +36,6 @@ ThreadPool Threads; // Global object
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
wait_for_search_finished();
clear(); // Zero-init histories (based on std::array)
}
@ -58,6 +58,7 @@ void Thread::clear() {
counterMoves.fill(MOVE_NONE);
mainHistory.fill(0);
captureHistory.fill(0);
for (auto& to : contHistory)
for (auto& h : to)
@ -91,7 +92,13 @@ void Thread::wait_for_search_finished() {
void Thread::idle_loop() {
WinProcGroup::bindThisThread(idx);
// If OS already scheduled us on a different group than 0 then don't overwrite
// the choice, eventually we are one of many one-threaded processes running on
// some Windows NUMA hardware, for instance in fishtest. To make it simple,
// just check if running threads are below a threshold, in this case all this
// NUMA machinery is not needed.
if (Options["Threads"] >= 8)
WinProcGroup::bindThisThread(idx);
while (true)
{
@ -109,41 +116,39 @@ void Thread::idle_loop() {
}
}
/// ThreadPool::init() creates and launches the threads that will go
/// immediately to sleep in idle_loop. We cannot use the c'tor because
/// Threads is a static object and we need a fully initialized engine at
/// this point due to allocation of Endgames in the Thread constructor.
void ThreadPool::init(size_t requested) {
push_back(new MainThread(0));
set(requested);
}
/// ThreadPool::exit() terminates threads before the program exits. Cannot be
/// done in the destructor because threads must be terminated before deleting
/// any static object, so before main() returns.
void ThreadPool::exit() {
main()->wait_for_search_finished();
set(0);
}
/// ThreadPool::set() creates/destroys threads to match the requested number
/// ThreadPool::set() creates/destroys threads to match the requested number.
/// Created and launced threads wil go immediately to sleep in idle_loop.
/// Upon resizing, threads are recreated to allow for binding if necessary.
void ThreadPool::set(size_t requested) {
while (size() < requested)
push_back(new Thread(size()));
if (size() > 0) { // destroy any existing thread(s)
main()->wait_for_search_finished();
while (size() > requested)
delete back(), pop_back();
while (size() > 0)
delete back(), pop_back();
}
if (requested > 0) { // create new thread(s)
push_back(new MainThread(0));
while (size() < requested)
push_back(new Thread(size()));
clear();
}
}
/// ThreadPool::clear() sets threadPool data to initial values.
void ThreadPool::clear() {
for (Thread* th : *this)
th->clear();
main()->callsCnt = 0;
main()->previousScore = VALUE_INFINITE;
main()->previousTimeReduction = 1;
}
/// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and
/// returns immediately. Main thread will wake up other threads and start the search.
@ -180,9 +185,9 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
// is shared by threads but is accessed in read-only mode.
StateInfo tmp = setupStates->back();
for (Thread* th : Threads)
for (Thread* th : *this)
{
th->nodes = th->tbHits = 0;
th->nodes = th->tbHits = th->nmp_ply = th->nmp_odd = 0;
th->rootDepth = th->completedDepth = DEPTH_ZERO;
th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -61,7 +61,7 @@ public:
Material::Table materialTable;
Endgames endgames;
size_t PVIdx;
int selDepth;
int selDepth, nmp_ply, nmp_odd;
std::atomic<uint64_t> nodes, tbHits;
Position rootPos;
@ -69,6 +69,7 @@ public:
Depth rootDepth, completedDepth;
CounterMoveHistory counterMoves;
ButterflyHistory mainHistory;
CapturePieceToHistory captureHistory;
ContinuationHistory contHistory;
};
@ -82,8 +83,8 @@ struct MainThread : public Thread {
void search() override;
void check_time();
bool easyMovePlayed, failedLow;
double bestMoveChanges;
bool failedLow;
double bestMoveChanges, previousTimeReduction;
Value previousScore;
int callsCnt;
};
@ -95,9 +96,8 @@ struct MainThread : public Thread {
struct ThreadPool : public std::vector<Thread*> {
void init(size_t); // No constructor and destructor, threads rely on globals that should
void exit(); // be initialized and valid during the whole thread lifetime.
void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false);
void clear();
void set(size_t);
MainThread* main() const { return static_cast<MainThread*>(front()); }

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,6 +19,8 @@
*/
#include <algorithm>
#include <cfloat>
#include <cmath>
#include "search.h"
#include "timeman.h"
@ -30,43 +32,41 @@ namespace {
enum TimeType { OptimumTime, MaxTime };
int remaining(int myTime, int myInc, int moveOverhead, int movesToGo,
int moveNum, bool ponder, TimeType type) {
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const double MaxRatio = 7.09; // When in trouble, we can step over reserved time with this ratio
const double StealRatio = 0.35; // However we must not steal time from remaining moves over this ratio
if (myTime <= 0)
return 0;
double ratio; // Which ratio of myTime we are going to use
// move_importance() is a skew-logistic function based on naive statistical
// analysis of "how many games are still undecided after n half-moves". Game
// is considered "undecided" as long as neither side has >275cp advantage.
// Data was extracted from the CCRL game database with some simple filtering criteria.
// Usage of increment follows quadratic distribution with the maximum at move 25
double inc = myInc * std::max(55.0, 120 - 0.12 * (moveNum - 25) * (moveNum - 25));
double move_importance(int ply) {
// In moves-to-go we distribute time according to a quadratic function with
// the maximum around move 20 for 40 moves in y time case.
if (movesToGo)
{
ratio = (type == OptimumTime ? 1.0 : 6.0) / std::min(50, movesToGo);
const double XScale = 7.64;
const double XShift = 58.4;
const double Skew = 0.183;
if (moveNum <= 40)
ratio *= 1.1 - 0.001 * (moveNum - 20) * (moveNum - 20);
else
ratio *= 1.5;
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
}
ratio *= 1 + inc / (myTime * 8.5);
}
// Otherwise we increase usage of remaining time as the game goes on
else
{
double k = 1 + 20 * moveNum / (500.0 + moveNum);
ratio = (type == OptimumTime ? 0.017 : 0.07) * (k + inc / myTime);
}
template<TimeType T>
int remaining(int myTime, int movesToGo, int ply, int slowMover) {
int time = int(std::min(1.0, ratio) * std::max(0, myTime - moveOverhead));
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
if (type == OptimumTime && ponder)
time = 5 * time / 4;
double moveImportance = (move_importance(ply) * slowMover) / 100;
double otherMovesImportance = 0;
return time;
for (int i = 1; i < movesToGo; ++i)
otherMovesImportance += move_importance(ply + 2 * i);
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast
}
} // namespace
@ -81,11 +81,12 @@ 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)
{
int moveOverhead = Options["Move Overhead"];
int npmsec = Options["nodestime"];
bool ponder = Options["Ponder"];
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"];
int npmsec = Options["nodestime"];
// If we have to play in 'nodes as time' mode, then convert from time
// to nodes, and use resulting values in time management formulas.
@ -102,11 +103,30 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
limits.npmsec = npmsec;
}
int moveNum = (ply + 1) / 2;
startTime = limits.startTime;
optimumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
limits.movestogo, moveNum, ponder, OptimumTime);
maximumTime = remaining(limits.time[us], limits.inc[us], moveOverhead,
limits.movestogo, moveNum, ponder, MaxTime);
optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime);
const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
// We calculate optimum time usage for different hypothetical "moves to go"-values
// and choose the minimum of calculated search time values. Usually the greatest
// hypMTG gives the minimum values.
for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG)
{
// Calculate thinking time for hypothetical "moves to go"-value
int hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1)
- moveOverhead * (2 + std::min(hypMTG, 40));
hypMyTime = std::max(hypMyTime, 0);
int t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
optimumTime = std::min(t1, optimumTime);
maximumTime = std::min(t2, maximumTime);
}
if (Options["Ponder"])
optimumTime += optimumTime / 4;
}

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -33,7 +33,7 @@ TranspositionTable TT; // Our global transposition table
void TranspositionTable::resize(size_t mbSize) {
size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(Cluster));
size_t newClusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
if (newClusterCount == clusterCount)
return;
@ -41,7 +41,7 @@ void TranspositionTable::resize(size_t mbSize) {
clusterCount = newClusterCount;
free(mem);
mem = calloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1, 1);
mem = malloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1);
if (!mem)
{
@ -51,6 +51,7 @@ void TranspositionTable::resize(size_t mbSize) {
}
table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1));
clear();
}

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -104,9 +104,9 @@ public:
void resize(size_t mbSize);
void clear();
// The lowest order bits of the key are used to get the index of the cluster
// The 32 lowest order bits of the key are used to get the index of the cluster
TTEntry* first_entry(const Key key) const {
return &table[(size_t)key & (clusterCount - 1)].entry[0];
return &table[(uint32_t(key) * uint64_t(clusterCount)) >> 32].entry[0];
}
private:

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -146,7 +146,7 @@ enum CastlingRight {
};
template<Color C, CastlingSide S> struct MakeCastling {
static const CastlingRight
static constexpr CastlingRight
right = C == WHITE ? S == QUEEN_SIDE ? WHITE_OOO : WHITE_OO
: S == QUEEN_SIDE ? BLACK_OOO : BLACK_OO;
};
@ -195,6 +195,7 @@ enum Value : int {
enum PieceType {
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
ALL_PIECES = 0,
QUEEN_DIAGONAL = 7,
PIECE_TYPE_NB = 8
};
@ -222,7 +223,7 @@ enum Depth : int {
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
enum Square {
enum Square : int {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
@ -233,8 +234,10 @@ enum Square {
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
SQ_NONE,
SQUARE_NB = 64,
SQUARE_NB = 64
};
enum Direction : int {
NORTH = 8,
EAST = 1,
SOUTH = -NORTH,
@ -261,7 +264,7 @@ enum Rank : int {
/// 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) {
constexpr Score make_score(int mg, int eg) {
return Score((int)((unsigned int)eg << 16) + mg);
}
@ -269,58 +272,68 @@ inline Score make_score(int mg, int eg) {
/// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer.
inline Value eg_value(Score s) {
union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) };
return Value(eg.s);
}
inline Value mg_value(Score s) {
union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) };
return Value(mg.s);
}
#define ENABLE_BASE_OPERATORS_ON(T) \
inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \
inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \
inline T operator-(T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } \
#define ENABLE_BASE_OPERATORS_ON(T) \
constexpr T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \
constexpr T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \
constexpr T operator-(T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; }
#define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_OPERATORS_ON(T) \
inline T operator*(int i, T d) { return T(i * int(d)); } \
inline T operator*(T d, int i) { return T(int(d) * i); } \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T& operator--(T& d) { return d = T(int(d) - 1); } \
inline T operator/(T d, int i) { return T(int(d) / i); } \
inline int operator/(T d1, T d2) { return int(d1) / int(d2); } \
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \
#define ENABLE_INCR_OPERATORS_ON(T) \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T& operator--(T& d) { return d = T(int(d) - 1); }
#define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_OPERATORS_ON(T) \
ENABLE_INCR_OPERATORS_ON(T) \
constexpr T operator*(int i, T d) { return T(i * int(d)); } \
constexpr T operator*(T d, int i) { return T(int(d) * i); } \
constexpr T operator/(T d, int i) { return T(int(d) / i); } \
constexpr int operator/(T d1, T d2) { return int(d1) / int(d2); } \
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
ENABLE_FULL_OPERATORS_ON(Value)
ENABLE_FULL_OPERATORS_ON(PieceType)
ENABLE_FULL_OPERATORS_ON(Piece)
ENABLE_FULL_OPERATORS_ON(Color)
ENABLE_FULL_OPERATORS_ON(Depth)
ENABLE_FULL_OPERATORS_ON(Square)
ENABLE_FULL_OPERATORS_ON(File)
ENABLE_FULL_OPERATORS_ON(Rank)
ENABLE_FULL_OPERATORS_ON(Direction)
ENABLE_INCR_OPERATORS_ON(PieceType)
ENABLE_INCR_OPERATORS_ON(Piece)
ENABLE_INCR_OPERATORS_ON(Color)
ENABLE_INCR_OPERATORS_ON(Square)
ENABLE_INCR_OPERATORS_ON(File)
ENABLE_INCR_OPERATORS_ON(Rank)
ENABLE_BASE_OPERATORS_ON(Score)
#undef ENABLE_FULL_OPERATORS_ON
#undef ENABLE_INCR_OPERATORS_ON
#undef ENABLE_BASE_OPERATORS_ON
/// Additional operators to add integers to a Value
inline Value operator+(Value v, int i) { return Value(int(v) + i); }
inline Value operator-(Value v, int i) { return Value(int(v) - i); }
constexpr Value operator+(Value v, int i) { return Value(int(v) + i); }
constexpr Value operator-(Value v, int i) { return Value(int(v) - i); }
inline Value& operator+=(Value& v, int i) { return v = v + i; }
inline Value& operator-=(Value& v, int i) { return v = v - i; }
/// Additional operators to add a Direction to a Square
inline Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
inline Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }
inline Square& operator+=(Square &s, Direction d) { return s = s + d; }
inline Square& operator-=(Square &s, Direction d) { return s = s - d; }
/// Only declared but not defined. We don't want to multiply two scores due to
/// a very high risk of overflow. So user should explicitly convert to integer.
inline Score operator*(Score s1, Score s2);
Score operator*(Score, Score) = delete;
/// Division of a Score must be handled separately for each term
inline Score operator/(Score s, int i) {
@ -339,39 +352,43 @@ inline Score operator*(Score s, int i) {
return result;
}
inline Color operator~(Color c) {
constexpr Color operator~(Color c) {
return Color(c ^ BLACK); // Toggle color
}
inline Square operator~(Square s) {
constexpr Square operator~(Square s) {
return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
}
inline Piece operator~(Piece pc) {
constexpr File operator~(File f) {
return File(f ^ FILE_H); // Horizontal flip FILE_A -> FILE_H
}
constexpr Piece operator~(Piece pc) {
return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
}
inline CastlingRight operator|(Color c, CastlingSide s) {
constexpr CastlingRight operator|(Color c, CastlingSide s) {
return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
}
inline Value mate_in(int ply) {
constexpr Value mate_in(int ply) {
return VALUE_MATE - ply;
}
inline Value mated_in(int ply) {
constexpr Value mated_in(int ply) {
return -VALUE_MATE + ply;
}
inline Square make_square(File f, Rank r) {
constexpr Square make_square(File f, Rank r) {
return Square((r << 3) + f);
}
inline Piece make_piece(Color c, PieceType pt) {
constexpr Piece make_piece(Color c, PieceType pt) {
return Piece((c << 3) + pt);
}
inline PieceType type_of(Piece pc) {
constexpr PieceType type_of(Piece pc) {
return PieceType(pc & 7);
}
@ -380,27 +397,27 @@ inline Color color_of(Piece pc) {
return Color(pc >> 3);
}
inline bool is_ok(Square s) {
constexpr bool is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8;
}
inline File file_of(Square s) {
constexpr File file_of(Square s) {
return File(s & 7);
}
inline Rank rank_of(Square s) {
constexpr Rank rank_of(Square s) {
return Rank(s >> 3);
}
inline Square relative_square(Color c, Square s) {
constexpr Square relative_square(Color c, Square s) {
return Square(s ^ (c * 56));
}
inline Rank relative_rank(Color c, Rank r) {
constexpr Rank relative_rank(Color c, Rank r) {
return Rank(r ^ (c * 7));
}
inline Rank relative_rank(Color c, Square s) {
constexpr Rank relative_rank(Color c, Square s) {
return relative_rank(c, rank_of(s));
}
@ -409,27 +426,27 @@ inline bool opposite_colors(Square s1, Square s2) {
return ((s >> 3) ^ s) & 1;
}
inline Square pawn_push(Color c) {
constexpr Direction pawn_push(Color c) {
return c == WHITE ? NORTH : SOUTH;
}
inline Square from_sq(Move m) {
constexpr Square from_sq(Move m) {
return Square((m >> 6) & 0x3F);
}
inline Square to_sq(Move m) {
constexpr Square to_sq(Move m) {
return Square(m & 0x3F);
}
inline int from_to(Move m) {
constexpr int from_to(Move m) {
return m & 0xFFF;
}
inline MoveType type_of(Move m) {
constexpr MoveType type_of(Move m) {
return MoveType(m & (3 << 14));
}
inline PieceType promotion_type(Move m) {
constexpr PieceType promotion_type(Move m) {
return PieceType(((m >> 12) & 3) + KNIGHT);
}
@ -438,11 +455,11 @@ inline Move make_move(Square from, Square to) {
}
template<MoveType T>
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
}
inline bool is_ok(Move m) {
constexpr bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
}

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -55,17 +55,20 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
void init(OptionsMap& o) {
const int MaxHashMB = Is64Bit ? 1024 * 1024 : 2048;
// at most 2^32 clusters.
const int MaxHashMB = Is64Bit ? 131072 : 2048;
o["Debug Log File"] << Option("", on_logger);
o["Contempt"] << Option(0, -100, 100);
o["Contempt"] << Option(20, -100, 100);
o["Threads"] << Option(1, 1, 512, on_threads);
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
o["Clear Hash"] << Option(on_clear_hash);
o["Ponder"] << Option(false);
o["MultiPV"] << Option(1, 1, 500);
o["Skill Level"] << Option(20, 0, 20);
o["Move Overhead"] << Option(60, 0, 5000);
o["Move Overhead"] << Option(30, 0, 5000);
o["Minimum Thinking Time"] << Option(20, 0, 5000);
o["Slow Mover"] << Option(89, 10, 1000);
o["nodestime"] << Option(0, 0, 10000);
o["UCI_Chess960"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path);