Update to Stockfish 11

This commit is contained in:
Peter Osterlund
2020-01-18 08:00:03 +01:00
parent c19a3b3777
commit 9c1d76faa1
38 changed files with 1777 additions and 1605 deletions

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -61,6 +61,10 @@ const vector<string> Defaults = {
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1", "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1", "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
"5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90",
"4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21",
"r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16",
"3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
// 5-man positions // 5-man positions
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
@@ -113,7 +117,7 @@ vector<string> setup_bench(const Position& current, istream& is) {
string fenFile = (is >> token) ? token : "default"; string fenFile = (is >> token) ? token : "default";
string limitType = (is >> token) ? token : "depth"; string limitType = (is >> token) ? token : "depth";
go = "go " + limitType + " " + limit; go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
if (fenFile == "default") if (fenFile == "default")
fens = Defaults; fens = Defaults;
@@ -139,9 +143,9 @@ vector<string> setup_bench(const Position& current, istream& is) {
file.close(); file.close();
} }
list.emplace_back("ucinewgame");
list.emplace_back("setoption name Threads value " + threads); list.emplace_back("setoption name Threads value " + threads);
list.emplace_back("setoption name Hash value " + ttSize); list.emplace_back("setoption name Hash value " + ttSize);
list.emplace_back("ucinewgame");
for (const string& fen : fens) for (const string& fen : fens)
if (fen.find("setoption") != string::npos) if (fen.find("setoption") != string::npos)

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <numeric> #include <numeric>
#include <vector> #include <vector>
@@ -28,7 +27,8 @@
namespace { namespace {
// There are 24 possible pawn squares: the first 4 files and ranks from 2 to 7 // There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
// Positions with the pawn on files E to H will be mirrored before probing.
constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608 constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
// Each uint32_t stores results of 32 positions, one per bit // Each uint32_t stores results of 32 positions, one per bit
@@ -44,7 +44,7 @@ namespace {
// bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2) // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
unsigned index(Color us, Square bksq, Square wksq, Square psq) { unsigned index(Color us, Square bksq, Square wksq, Square psq) {
return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); return int(wksq) | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
} }
enum Result { enum Result {

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,24 +19,16 @@
*/ */
#include <algorithm> #include <algorithm>
#include <bitset>
#include "bitboard.h" #include "bitboard.h"
#include "misc.h" #include "misc.h"
uint8_t PopCnt16[1 << 16]; uint8_t PopCnt16[1 << 16];
int SquareDistance[SQUARE_NB][SQUARE_NB]; uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB]; Bitboard SquareBB[SQUARE_NB];
Bitboard FileBB[FILE_NB];
Bitboard RankBB[RANK_NB];
Bitboard AdjacentFilesBB[FILE_NB];
Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingBB[SQUARE_NB][8];
Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
@@ -49,15 +41,6 @@ namespace {
Bitboard BishopTable[0x1480]; // To store bishop attacks Bitboard BishopTable[0x1480]; // To store bishop attacks
void init_magics(Bitboard table[], Magic magics[], Direction directions[]); void init_magics(Bitboard table[], Magic magics[], Direction directions[]);
// popcount16() counts the non-zero bits using SWAR-Popcount algorithm
unsigned popcount16(unsigned u) {
u -= (u >> 1) & 0x5555U;
u = ((u >> 2) & 0x3333U) + (u & 0x3333U);
u = ((u >> 4) + u) & 0x0F0FU;
return (u * 0x0101U) >> 8;
}
} }
@@ -86,55 +69,35 @@ const std::string Bitboards::pretty(Bitboard b) {
void Bitboards::init() { void Bitboards::init() {
for (unsigned i = 0; i < (1 << 16); ++i) for (unsigned i = 0; i < (1 << 16); ++i)
PopCnt16[i] = (uint8_t) popcount16(i); PopCnt16[i] = std::bitset<16>(i).count();
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
SquareBB[s] = (1ULL << s); SquareBB[s] = (1ULL << s);
for (File f = FILE_A; f <= FILE_H; ++f)
FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB;
for (Rank r = RANK_1; r <= RANK_8; ++r)
RankBB[r] = r > RANK_1 ? RankBB[r - 1] << 8 : Rank1BB;
for (File f = FILE_A; f <= FILE_H; ++f)
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
for (Rank r = RANK_1; r < RANK_8; ++r)
ForwardRanksBB[WHITE][r] = ~(ForwardRanksBB[BLACK][r + 1] = ForwardRanksBB[BLACK][r] | RankBB[r]);
for (Color c = WHITE; c <= BLACK; ++c)
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
ForwardFileBB [c][s] = ForwardRanksBB[c][rank_of(s)] & FileBB[file_of(s)];
PawnAttackSpan[c][s] = ForwardRanksBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
PassedPawnMask[c][s] = ForwardFileBB [c][s] | PawnAttackSpan[c][s];
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
if (s1 != s2)
{
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2)); SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
DistanceRingBB[s1][SquareDistance[s1][s2]] |= s2;
}
int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt : { PAWN, KNIGHT, KING })
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
for (int i = 0; steps[pt][i]; ++i)
{ {
Square to = s + Direction(c == WHITE ? steps[pt][i] : -steps[pt][i]); PawnAttacks[WHITE][s] = pawn_attacks_bb<WHITE>(square_bb(s));
PawnAttacks[BLACK][s] = pawn_attacks_bb<BLACK>(square_bb(s));
if (is_ok(to) && distance(s, to) < 3)
{
if (pt == PAWN)
PawnAttacks[c][s] |= to;
else
PseudoAttacks[pt][s] |= to;
} }
// Helper returning the target bitboard of a step from a square
auto landing_square_bb = [&](Square s, int step)
{
Square to = Square(s + step);
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
};
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} )
PseudoAttacks[KING][s] |= landing_square_bb(s, step);
for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} )
PseudoAttacks[KNIGHT][s] |= landing_square_bb(s, step);
} }
Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST }; Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
@@ -150,13 +113,8 @@ void Bitboards::init() {
for (PieceType pt : { BISHOP, ROOK }) for (PieceType pt : { BISHOP, ROOK })
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{ if (PseudoAttacks[pt][s1] & s2)
if (!(PseudoAttacks[pt][s1] & s2))
continue;
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]);
}
} }
} }
@@ -184,8 +142,8 @@ namespace {
// init_magics() computes all rook and bishop attacks at startup. Magic // init_magics() computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see // bitboards are used to look up attacks of sliding pieces. As a reference see
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
// use the so called "fancy" approach. // called "fancy" approach.
void init_magics(Bitboard table[], Magic magics[], Direction directions[]) { void init_magics(Bitboard table[], Magic magics[], Direction directions[]) {

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -60,19 +60,22 @@ constexpr Bitboard Rank6BB = Rank1BB << (8 * 5);
constexpr Bitboard Rank7BB = Rank1BB << (8 * 6); constexpr Bitboard Rank7BB = Rank1BB << (8 * 6);
constexpr Bitboard Rank8BB = Rank1BB << (8 * 7); constexpr Bitboard Rank8BB = Rank1BB << (8 * 7);
extern int SquareDistance[SQUARE_NB][SQUARE_NB]; constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
constexpr Bitboard KingFlank[FILE_NB] = {
QueenSide ^ FileDBB, QueenSide, QueenSide,
CenterFiles, CenterFiles,
KingSide, KingSide, KingSide ^ FileEBB
};
extern uint8_t PopCnt16[1 << 16];
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB]; extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard FileBB[FILE_NB];
extern Bitboard RankBB[RANK_NB];
extern Bitboard AdjacentFilesBB[FILE_NB];
extern Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingBB[SQUARE_NB][8];
extern Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
@@ -102,64 +105,61 @@ struct Magic {
extern Magic RookMagics[SQUARE_NB]; extern Magic RookMagics[SQUARE_NB];
extern Magic BishopMagics[SQUARE_NB]; extern Magic BishopMagics[SQUARE_NB];
inline Bitboard square_bb(Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return SquareBB[s];
}
/// Overloads of bitwise operators between a Bitboard and a Square for testing /// Overloads of bitwise operators between a Bitboard and a Square for testing
/// whether a given bit is set in a bitboard, and for setting and clearing bits. /// whether a given bit is set in a bitboard, and for setting and clearing bits.
inline Bitboard operator&(Bitboard b, Square s) { inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); }
assert(s >= SQ_A1 && s <= SQ_H8); inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); }
return b & SquareBB[s]; inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); }
} inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
inline Bitboard operator|(Bitboard b, Square s) { inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
assert(s >= SQ_A1 && s <= SQ_H8); inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
return b | SquareBB[s]; inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
}
inline Bitboard operator^(Bitboard b, Square s) { inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | square_bb(s2); }
assert(s >= SQ_A1 && s <= SQ_H8);
return b ^ SquareBB[s];
}
inline Bitboard& operator|=(Bitboard& b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b |= SquareBB[s];
}
inline Bitboard& operator^=(Bitboard& b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b ^= SquareBB[s];
}
constexpr bool more_than_one(Bitboard b) { constexpr bool more_than_one(Bitboard b) {
return b & (b - 1); return b & (b - 1);
} }
inline bool opposite_colors(Square s1, Square s2) {
return bool(DarkSquares & s1) != bool(DarkSquares & s2);
}
/// rank_bb() and file_bb() return a bitboard representing all the squares on /// rank_bb() and file_bb() return a bitboard representing all the squares on
/// the given file or rank. /// the given file or rank.
inline Bitboard rank_bb(Rank r) { inline Bitboard rank_bb(Rank r) {
return RankBB[r]; return Rank1BB << (8 * r);
} }
inline Bitboard rank_bb(Square s) { inline Bitboard rank_bb(Square s) {
return RankBB[rank_of(s)]; return rank_bb(rank_of(s));
} }
inline Bitboard file_bb(File f) { inline Bitboard file_bb(File f) {
return FileBB[f]; return FileABB << f;
} }
inline Bitboard file_bb(Square s) { inline Bitboard file_bb(Square s) {
return FileBB[file_of(s)]; return file_bb(file_of(s));
} }
/// shift() moves a bitboard one step along direction D (mainly for pawns) /// shift() moves a bitboard one step along direction D
template<Direction D> template<Direction D>
constexpr Bitboard shift(Bitboard b) { constexpr Bitboard shift(Bitboard b) {
return D == NORTH ? b << 8 : D == SOUTH ? b >> 8 return D == NORTH ? b << 8 : D == SOUTH ? b >> 8
: D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16
: D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1 : D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1
: D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7 : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7
: D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9
@@ -167,8 +167,8 @@ constexpr Bitboard shift(Bitboard b) {
} }
/// pawn_attacks_bb() returns the pawn attacks for the given color from the /// pawn_attacks_bb() returns the squares attacked by pawns of the given color
/// squares in the given bitboard. /// from the squares in the given bitboard.
template<Color C> template<Color C>
constexpr Bitboard pawn_attacks_bb(Bitboard b) { constexpr Bitboard pawn_attacks_bb(Bitboard b) {
@@ -177,58 +177,65 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) {
} }
/// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the
/// given color from the squares in the given bitboard.
template<Color C>
constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
return C == WHITE ? shift<NORTH_WEST>(b) & shift<NORTH_EAST>(b)
: shift<SOUTH_WEST>(b) & shift<SOUTH_EAST>(b);
}
/// adjacent_files_bb() returns a bitboard representing all the squares on the /// adjacent_files_bb() returns a bitboard representing all the squares on the
/// adjacent files of the given one. /// adjacent files of the given one.
inline Bitboard adjacent_files_bb(File f) { inline Bitboard adjacent_files_bb(Square s) {
return AdjacentFilesBB[f]; return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
} }
/// between_bb() returns a bitboard representing all the squares between the two /// between_bb() returns squares that are linearly between the given squares
/// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with /// If the given squares are not on a same file/rank/diagonal, return 0.
/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, file
/// or diagonal, 0 is returned.
inline Bitboard between_bb(Square s1, Square s2) { inline Bitboard between_bb(Square s1, Square s2) {
return BetweenBB[s1][s2]; return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2)))
^(AllSquares << (s2 + !(s1 < s2))));
} }
/// forward_ranks_bb() returns a bitboard representing the squares on all the ranks /// forward_ranks_bb() returns a bitboard representing the squares on the ranks
/// in front of the given one, from the point of view of the given color. For instance, /// in front of the given one, from the point of view of the given color. For instance,
/// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
inline Bitboard forward_ranks_bb(Color c, Square s) { inline Bitboard forward_ranks_bb(Color c, Square s) {
return ForwardRanksBB[c][rank_of(s)]; return c == WHITE ? ~Rank1BB << 8 * (rank_of(s) - RANK_1)
: ~Rank8BB >> 8 * (RANK_8 - rank_of(s));
} }
/// forward_file_bb() returns a bitboard representing all the squares along the line /// forward_file_bb() returns a bitboard representing all the squares along the
/// in front of the given one, from the point of view of the given color: /// line in front of the given one, from the point of view of the given color.
/// ForwardFileBB[c][s] = forward_ranks_bb(c, s) & file_bb(s)
inline Bitboard forward_file_bb(Color c, Square s) { inline Bitboard forward_file_bb(Color c, Square s) {
return ForwardFileBB[c][s]; return forward_ranks_bb(c, s) & file_bb(s);
} }
/// pawn_attack_span() returns a bitboard representing all the squares that can be /// pawn_attack_span() returns a bitboard representing all the squares that can
/// attacked by a pawn of the given color when it moves along its file, starting /// be attacked by a pawn of the given color when it moves along its file,
/// from the given square: /// starting from the given square.
/// PawnAttackSpan[c][s] = forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s));
inline Bitboard pawn_attack_span(Color c, Square s) { inline Bitboard pawn_attack_span(Color c, Square s) {
return PawnAttackSpan[c][s]; return forward_ranks_bb(c, s) & adjacent_files_bb(s);
} }
/// passed_pawn_mask() returns a bitboard mask which can be used to test if a /// passed_pawn_span() returns a bitboard which can be used to test if a pawn of
/// pawn of the given color and on the given square is a passed pawn: /// the given color and on the given square is a passed pawn.
/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_file_bb(c, s)
inline Bitboard passed_pawn_mask(Color c, Square s) { inline Bitboard passed_pawn_span(Color c, Square s) {
return PassedPawnMask[c][s]; return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s));
} }
@@ -241,15 +248,16 @@ inline bool aligned(Square s1, Square s2, Square s3) {
/// distance() functions return the distance between x and y, defined as the /// distance() functions return the distance between x and y, defined as the
/// number of steps for a king in x to reach y. Works with squares, ranks, files. /// number of steps for a king in x to reach y.
template<typename T> inline int distance(T x, T y) { return x < y ? y - x : x - y; } template<typename T1 = Square> inline int distance(Square x, Square y);
template<> inline int distance<File>(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); }
template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; } template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
template<typename T1, typename T2> inline int distance(T2 x, T2 y); template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); } return v < lo ? lo : v > hi ? hi : v;
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); } }
/// attacks_bb() returns a bitboard representing all the squares attacked by a /// attacks_bb() returns a bitboard representing all the squares attacked by a
/// piece of type Pt (bishop or rook) placed on 's'. /// piece of type Pt (bishop or rook) placed on 's'.
@@ -281,7 +289,6 @@ inline int popcount(Bitboard b) {
#ifndef USE_POPCNT #ifndef USE_POPCNT
extern uint8_t PopCnt16[1 << 16];
union { Bitboard bb; uint16_t u[4]; } v = { b }; union { Bitboard bb; uint16_t u[4]; } v = { b };
return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]]; return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]];
@@ -375,10 +382,9 @@ inline Square pop_lsb(Bitboard* b) {
} }
/// frontmost_sq() and backmost_sq() return the square corresponding to the /// frontmost_sq() returns the most advanced square for the given color
/// most/least advanced bit relative to the given color. inline Square frontmost_sq(Color c, Bitboard b) {
return c == WHITE ? msb(b) : lsb(b);
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } }
inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
#endif // #ifndef BITBOARD_H_INCLUDED #endif // #ifndef BITBOARD_H_INCLUDED

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "bitboard.h" #include "bitboard.h"
@@ -45,14 +44,14 @@ namespace {
// Table used to drive the king towards a corner square of the // Table used to drive the king towards a corner square of the
// right color in KBN vs K endgames. // right color in KBN vs K endgames.
constexpr int PushToCorners[SQUARE_NB] = { constexpr int PushToCorners[SQUARE_NB] = {
200, 190, 180, 170, 160, 150, 140, 130, 6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160,
190, 180, 170, 160, 150, 140, 130, 140, 6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480,
180, 170, 155, 140, 140, 125, 140, 150, 5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800,
170, 160, 140, 120, 110, 140, 150, 160, 5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120,
160, 150, 140, 110, 120, 140, 160, 170, 5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440,
150, 140, 125, 140, 140, 155, 170, 180, 4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760,
140, 130, 140, 150, 160, 170, 180, 190, 4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080,
130, 140, 150, 160, 170, 180, 190, 200 4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400
}; };
// Tables used to drive a piece towards or away from another piece // Tables used to drive a piece towards or away from another piece
@@ -75,17 +74,42 @@ namespace {
assert(pos.count<PAWN>(strongSide) == 1); assert(pos.count<PAWN>(strongSide) == 1);
if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E) if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 sq = Square(int(sq) ^ 7); // Mirror SQ_H1 -> SQ_A1
if (strongSide == BLACK) return strongSide == WHITE ? sq : ~sq;
sq = ~sq;
return sq;
} }
} // namespace } // namespace
namespace Endgames {
std::pair<Map<Value>, Map<ScaleFactor>> maps;
void init() {
add<KPK>("KPK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKP>("KQKP");
add<KQKR>("KQKR");
add<KNNKP>("KNNKP");
add<KNPK>("KNPK");
add<KNPKB>("KNPKB");
add<KRPKR>("KRPKR");
add<KRPKB>("KRPKB");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
}
}
/// Mate with KX vs K. This function is used to evaluate positions with /// Mate with KX vs K. This function is used to evaluate positions with
/// king and plenty of material vs a lone king. It simply gives the /// king and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge /// attacking side a bonus for driving the defending king towards the edge
@@ -120,7 +144,7 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
/// defending king towards a corner square of the right color. /// defending king towards a corner square that our bishop attacks.
template<> template<>
Value Endgame<KBNK>::operator()(const Position& pos) const { Value Endgame<KBNK>::operator()(const Position& pos) const {
@@ -131,24 +155,19 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
Square loserKSq = pos.square<KING>(weakSide); Square loserKSq = pos.square<KING>(weakSide);
Square bishopSq = pos.square<BISHOP>(strongSide); Square bishopSq = pos.square<BISHOP>(strongSide);
// kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a // If our bishop does not attack A1/H8, we flip the enemy king square
// bishop that cannot reach the above squares, we flip the kings in order // to drive to opposite corners (A8/H1).
// to drive the enemy toward corners A8 or H1.
if (opposite_colors(bishopSq, SQ_A1))
{
winnerKSq = ~winnerKSq;
loserKSq = ~loserKSq;
}
Value result = VALUE_KNOWN_WIN Value result = VALUE_KNOWN_WIN
+ PushClose[distance(winnerKSq, loserKSq)] + PushClose[distance(winnerKSq, loserKSq)]
+ PushToCorners[loserKSq]; + PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq];
assert(abs(result) < VALUE_MATE_IN_MAX_PLY);
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
/// KP vs K. This endgame is evaluated with the help of a bitbase. /// KP vs K. This endgame is evaluated with the help of a bitbase
template<> template<>
Value Endgame<KPK>::operator()(const Position& pos) const { Value Endgame<KPK>::operator()(const Position& pos) const {
@@ -291,6 +310,21 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
} }
/// KNN vs KP. Simply push the opposing king to the corner
template<>
Value Endgame<KNNKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Value result = 2 * KnightValueEg
- PawnValueEg
+ PushToEdges[pos.square<KING>(weakSide)];
return strongSide == pos.side_to_move() ? result : -result;
}
/// Some cases of trivial draws /// Some cases of trivial draws
template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; } template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
@@ -331,7 +365,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
&& pos.count<PAWN>(weakSide) >= 1) && pos.count<PAWN>(weakSide) >= 1)
{ {
// Get weakSide pawn that is closest to the home rank // Get weakSide pawn that is closest to the home rank
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN)); Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
Square strongKingSq = pos.square<KING>(strongSide); Square strongKingSq = pos.square<KING>(strongSide);
Square weakKingSq = pos.square<KING>(weakSide); Square weakKingSq = pos.square<KING>(weakSide);
@@ -629,8 +663,6 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
Square ksq = pos.square<KING>(weakSide); Square ksq = pos.square<KING>(weakSide);
Square psq1 = pos.squares<PAWN>(strongSide)[0]; Square psq1 = pos.squares<PAWN>(strongSide)[0];
Square psq2 = pos.squares<PAWN>(strongSide)[1]; Square psq2 = pos.squares<PAWN>(strongSide)[1];
Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2; Square blockSq1, blockSq2;
if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2))
@@ -664,7 +696,7 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
&& opposite_colors(ksq, wbsq) && opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2 && ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP)) || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|| distance(r1, r2) >= 2)) || distance<Rank>(psq1, psq2) >= 2))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2 else if ( ksq == blockSq2
@@ -729,6 +761,9 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
template<> template<>
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, KnightValueMg, 1));
assert(verify_material(pos, weakSide, BishopValueMg, 0));
Square pawnSq = pos.square<PAWN>(strongSide); Square pawnSq = pos.square<PAWN>(strongSide);
Square bishopSq = pos.square<BISHOP>(weakSide); Square bishopSq = pos.square<BISHOP>(weakSide);
Square weakKingSq = pos.square<KING>(weakSide); Square weakKingSq = pos.square<KING>(weakSide);

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
#ifndef ENDGAME_H_INCLUDED #ifndef ENDGAME_H_INCLUDED
#define ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED
#include <map> #include <unordered_map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
@@ -37,6 +37,7 @@ enum EndgameCode {
EVALUATION_FUNCTIONS, EVALUATION_FUNCTIONS,
KNNK, // KNN vs K KNNK, // KNN vs K
KNNKP, // KNN vs KP
KXK, // Generic "mate lone king" eval KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K KBNK, // KBN vs K
KPK, // KP vs K KPK, // KP vs K
@@ -90,14 +91,18 @@ struct Endgame : public EndgameBase<T> {
}; };
/// The Endgames class stores the pointers to endgame evaluation and scaling /// The Endgames namespace handles the pointers to endgame evaluation and scaling
/// base objects in two std::map. We use polymorphism to invoke the actual /// base objects in two std::map. We use polymorphism to invoke the actual
/// endgame function by calling its virtual operator(). /// endgame function by calling its virtual operator().
class Endgames { namespace Endgames {
template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>; template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
template<typename T> using Map = std::map<Key, Ptr<T>>; template<typename T> using Map = std::unordered_map<Key, Ptr<T>>;
extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
void init();
template<typename T> template<typename T>
Map<T>& map() { Map<T>& map() {
@@ -112,34 +117,11 @@ class Endgames {
map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK)); map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK));
} }
std::pair<Map<Value>, Map<ScaleFactor>> maps;
public:
Endgames() {
add<KPK>("KPK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKP>("KQKP");
add<KQKR>("KQKR");
add<KNPK>("KNPK");
add<KNPKB>("KNPKB");
add<KRPKR>("KRPKR");
add<KRPKB>("KRPKB");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
}
template<typename T> template<typename T>
const EndgameBase<T>* probe(Key key) { const EndgameBase<T>* probe(Key key) {
return map<T>().count(key) ? map<T>()[key].get() : nullptr; auto it = map<T>().find(key);
return it != map<T>().end() ? it->second.get() : nullptr;
}
} }
};
#endif // #ifndef ENDGAME_H_INCLUDED #endif // #ifndef ENDGAME_H_INCLUDED

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -73,28 +73,17 @@ using namespace Trace;
namespace { namespace {
constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
constexpr Bitboard KingFlank[FILE_NB] = {
QueenSide ^ FileDBB, QueenSide, QueenSide,
CenterFiles, CenterFiles,
KingSide, KingSide, KingSide ^ FileEBB
};
// Threshold for lazy and space evaluation // Threshold for lazy and space evaluation
constexpr Value LazyThreshold = Value(1500); constexpr Value LazyThreshold = Value(1400);
constexpr Value SpaceThreshold = Value(12222); constexpr Value SpaceThreshold = Value(12222);
// KingAttackWeights[PieceType] contains king attack weights by piece type // KingAttackWeights[PieceType] contains king attack weights by piece type
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 77, 55, 44, 10 }; constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
// Penalties for enemy's safe checks // Penalties for enemy's safe checks
constexpr int QueenSafeCheck = 780; constexpr int QueenSafeCheck = 780;
constexpr int RookSafeCheck = 880; constexpr int RookSafeCheck = 1080;
constexpr int BishopSafeCheck = 435; constexpr int BishopSafeCheck = 635;
constexpr int KnightSafeCheck = 790; constexpr int KnightSafeCheck = 790;
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
@@ -117,61 +106,47 @@ namespace {
S(106,184), S(109,191), S(113,206), S(116,212) } S(106,184), S(109,191), S(113,206), S(116,212) }
}; };
// Outpost[knight/bishop][supported by pawn] contains bonuses for minor
// pieces if they occupy or can reach an outpost square, bigger if that
// square is supported by a pawn.
constexpr Score Outpost[][2] = {
{ 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 // RookOnFile[semiopen/open] contains bonuses for each rook when there is
// no (friendly) pawn on the rook file. // no (friendly) pawn on the rook file.
constexpr Score RookOnFile[] = { S(18, 7), S(44, 20) }; constexpr Score RookOnFile[] = { S(21, 4), S(47, 25) };
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
// which piece type attacks which one. Attacks on lesser pieces which are // which piece type attacks which one. Attacks on lesser pieces which are
// pawn-defended are not considered. // pawn-defended are not considered.
constexpr Score ThreatByMinor[PIECE_TYPE_NB] = { constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
S(0, 0), S(0, 31), S(39, 42), S(57, 44), S(68, 112), S(62, 120) S(0, 0), S(6, 32), S(59, 41), S(79, 56), S(90, 119), S(79, 161)
}; };
constexpr Score ThreatByRook[PIECE_TYPE_NB] = { constexpr Score ThreatByRook[PIECE_TYPE_NB] = {
S(0, 0), S(0, 24), S(38, 71), S(38, 61), S(0, 38), S(51, 38) S(0, 0), S(3, 44), S(38, 71), S(38, 61), S(0, 38), S(51, 38)
}; };
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn // PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = { constexpr Score PassedRank[RANK_NB] = {
S(0, 0), S(5, 18), S(12, 23), S(10, 31), S(57, 62), S(163, 167), S(271, 250) S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
};
// PassedFile[File] contains a bonus according to the file of a passed pawn
constexpr Score PassedFile[FILE_NB] = {
S( -1, 7), S( 0, 9), S(-9, -8), S(-30,-14),
S(-30,-14), S(-9, -8), S( 0, 9), S( -1, 7)
}; };
// Assorted bonuses and penalties // Assorted bonuses and penalties
constexpr Score BishopPawns = S( 3, 8); constexpr Score BishopPawns = S( 3, 7);
constexpr Score CloseEnemies = S( 7, 0);
constexpr Score CorneredBishop = S( 50, 50); constexpr Score CorneredBishop = S( 50, 50);
constexpr Score Hanging = S( 62, 34); constexpr Score FlankAttacks = S( 8, 0);
constexpr Score KingProtector = S( 6, 7); constexpr Score Hanging = S( 69, 36);
constexpr Score KnightOnQueen = S( 20, 12); constexpr Score KingProtector = S( 7, 8);
constexpr Score LongDiagonalBishop = S( 44, 0); constexpr Score KnightOnQueen = S( 16, 12);
constexpr Score MinorBehindPawn = S( 16, 0); constexpr Score LongDiagonalBishop = S( 45, 0);
constexpr Score Overload = S( 12, 6); constexpr Score MinorBehindPawn = S( 18, 3);
constexpr Score PawnlessFlank = S( 18, 94); constexpr Score Outpost = S( 30, 21);
constexpr Score RestrictedPiece = S( 7, 6); constexpr Score PassedFile = S( 11, 8);
constexpr Score RookOnPawn = S( 10, 28); constexpr Score PawnlessFlank = S( 17, 95);
constexpr Score SliderOnQueen = S( 49, 21); constexpr Score RestrictedPiece = S( 7, 7);
constexpr Score ThreatByKing = S( 21, 84); constexpr Score ReachableOutpost = S( 32, 10);
constexpr Score ThreatByPawnPush = S( 48, 42); constexpr Score RookOnQueenFile = S( 7, 6);
constexpr Score ThreatByRank = S( 14, 3); constexpr Score SliderOnQueen = S( 59, 18);
constexpr Score ThreatBySafePawn = S(169, 99); constexpr Score ThreatByKing = S( 24, 89);
constexpr Score TrappedRook = S( 98, 5); constexpr Score ThreatByPawnPush = S( 48, 39);
constexpr Score WeakQueen = S( 51, 10); constexpr Score ThreatBySafePawn = S(173, 94);
constexpr Score WeakUnopposedPawn = S( 14, 20); constexpr Score TrappedRook = S( 52, 10);
constexpr Score WeakQueen = S( 49, 15);
#undef S #undef S
@@ -193,7 +168,7 @@ namespace {
template<Color Us> Score passed() const; template<Color Us> Score passed() const;
template<Color Us> Score space() const; template<Color Us> Score space() const;
ScaleFactor scale_factor(Value eg) const; ScaleFactor scale_factor(Value eg) const;
Score initiative(Value eg) const; Score initiative(Score score) const;
const Position& pos; const Position& pos;
Material::Entry* me; Material::Entry* me;
@@ -206,15 +181,12 @@ namespace {
// is also calculated is ALL_PIECES. // is also calculated is ALL_PIECES.
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB]; Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
// attackedBy2[color] are the squares attacked by 2 pieces of a given color, // attackedBy2[color] are the squares attacked by at least 2 units of a given
// possibly via x-ray or by one pawn and one piece. Diagonal x-ray through // color, including x-rays. But diagonal x-rays through pawns are not computed.
// pawn or squares attacked by 2 pawns are not explicitly added.
Bitboard attackedBy2[COLOR_NB]; Bitboard attackedBy2[COLOR_NB];
// kingRing[color] are the squares adjacent to the king, plus (only for a // kingRing[color] are the squares adjacent to the king plus some other
// king on its first rank) the squares two ranks in front. For instance, // very near squares, depending on king position.
// if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6
// and h6. It is set to 0 when king safety evaluation is skipped.
Bitboard kingRing[COLOR_NB]; Bitboard kingRing[COLOR_NB];
// kingAttackersCount[color] is the number of pieces of the given color // kingAttackersCount[color] is the number of pieces of the given color
@@ -242,41 +214,37 @@ namespace {
void Evaluation<T>::initialize() { void Evaluation<T>::initialize() {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Up = pawn_push(Us);
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); constexpr Direction Down = -Up;
constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB); constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB);
const Square ksq = pos.square<KING>(Us);
Bitboard dblAttackByPawn = pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN));
// Find our pawns that are blocked or on the first two ranks // Find our pawns that are blocked or on the first two ranks
Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks); Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks);
// Squares occupied by those pawns, by our king or queen, or controlled by enemy pawns // Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king
// are excluded from the mobility area. // or controlled by enemy pawns are excluded from the mobility area.
mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them)); mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them));
// Initialise attackedBy bitboards for kings and pawns // Initialize attackedBy[] for king and pawns
attackedBy[Us][KING] = pos.attacks_from<KING>(pos.square<KING>(Us)); attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
attackedBy[Us][PAWN] = pe->pawn_attacks(Us); attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
attackedBy2[Us] = attackedBy[Us][KING] & attackedBy[Us][PAWN]; attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
kingRing[Us] = kingAttackersCount[Them] = 0; // Init our king safety tables
Square s = make_square(clamp(file_of(ksq), FILE_B, FILE_G),
// Init our king safety tables only if we are going to use them clamp(rank_of(ksq), RANK_2, RANK_7));
if (pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg) kingRing[Us] = PseudoAttacks[KING][s] | s;
{
kingRing[Us] = attackedBy[Us][KING];
if (relative_rank(Us, pos.square<KING>(Us)) == RANK_1)
kingRing[Us] |= shift<Up>(kingRing[Us]);
if (file_of(pos.square<KING>(Us)) == FILE_H)
kingRing[Us] |= shift<WEST>(kingRing[Us]);
else if (file_of(pos.square<KING>(Us)) == FILE_A)
kingRing[Us] |= shift<EAST>(kingRing[Us]);
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
}
// Remove from kingRing[] the squares defended by two pawns
kingRing[Us] &= ~dblAttackByPawn;
} }
@@ -285,18 +253,17 @@ namespace {
Score Evaluation<T>::pieces() { Score Evaluation<T>::pieces() {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB); : Rank5BB | Rank4BB | Rank3BB);
const Square* pl = pos.squares<Pt>(Us); const Square* pl = pos.squares<Pt>(Us);
Bitboard b, bb; Bitboard b, bb;
Square s;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
attackedBy[Us][Pt] = 0; attackedBy[Us][Pt] = 0;
while ((s = *pl++) != SQ_NONE) for (Square s = *pl; s != SQ_NONE; s = *++pl)
{ {
// Find attacked squares, including x-ray attacks for bishops and rooks // Find attacked squares, including x-ray attacks for bishops and rooks
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN)) b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
@@ -324,12 +291,12 @@ namespace {
if (Pt == BISHOP || Pt == KNIGHT) if (Pt == BISHOP || Pt == KNIGHT)
{ {
// Bonus if piece is on an outpost square or can reach one // Bonus if piece is on an outpost square or can reach one
bb = OutpostRanks & ~pe->pawn_attacks_span(Them); bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
if (bb & s) if (bb & s)
score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & s)] * 2; score += Outpost * (Pt == KNIGHT ? 2 : 1);
else if (bb &= b & ~pos.pieces(Us)) else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & bb)]; score += ReachableOutpost;
// Knight and Bishop bonus for being right behind a pawn // Knight and Bishop bonus for being right behind a pawn
if (shift<Down>(pos.pieces(PAWN)) & s) if (shift<Down>(pos.pieces(PAWN)) & s)
@@ -344,19 +311,17 @@ namespace {
// bishop, bigger when the center files are blocked with pawns. // bishop, bigger when the center files are blocked with pawns.
Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces()); Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
score -= BishopPawns * pe->pawns_on_same_color_squares(Us, s) score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s)
* (1 + popcount(blocked & CenterFiles)); * (1 + popcount(blocked & CenterFiles));
// Bonus for bishop on a long diagonal which can "see" both center squares // Bonus for bishop on a long diagonal which can "see" both center squares
if (more_than_one(attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & Center)) if (more_than_one(attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & Center))
score += LongDiagonalBishop; score += LongDiagonalBishop;
}
// An important Chess960 pattern: A cornered bishop blocked by a friendly // An important Chess960 pattern: a cornered bishop blocked by a friendly
// pawn diagonally in front of it is a very serious problem, especially // pawn diagonally in front of it is a very serious problem, especially
// when that pawn is also blocked. // when that pawn is also blocked.
if ( Pt == BISHOP if ( pos.is_chess960()
&& pos.is_chess960()
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
{ {
Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
@@ -366,23 +331,24 @@ namespace {
: CorneredBishop; : CorneredBishop;
} }
} }
}
if (Pt == ROOK) if (Pt == ROOK)
{ {
// Bonus for aligning rook with enemy pawns on the same rank/file // Bonus for rook on the same file as a queen
if (relative_rank(Us, s) >= RANK_5) if (file_bb(s) & pos.pieces(QUEEN))
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); score += RookOnQueenFile;
// Bonus for rook on an open or semi-open file // Bonus for rook on an open or semi-open file
if (pe->semiopen_file(Us, file_of(s))) if (pos.is_on_semiopen_file(Us, s))
score += RookOnFile[bool(pe->semiopen_file(Them, file_of(s)))]; score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
// Penalty when trapped by the king, even more if the king cannot castle // Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3) else if (mob <= 3)
{ {
File kf = file_of(pos.square<KING>(Us)); File kf = file_of(pos.square<KING>(Us));
if ((kf < FILE_E) == (file_of(s) < kf)) if ((kf < FILE_E) == (file_of(s) < kf))
score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us)); score -= TrappedRook * (1 + !pos.castling_rights(Us));
} }
} }
@@ -409,25 +375,13 @@ namespace {
constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
: AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
const Square ksq = pos.square<KING>(Us); Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0;
Bitboard kingFlank, weak, b, b1, b2, safe, unsafeChecks; Bitboard rookChecks, queenChecks, bishopChecks, knightChecks;
// King shelter and enemy pawns storm
Score score = pe->king_safety<Us>(pos);
// Find the squares that opponent attacks in our king flank, and the squares
// which are attacked twice in that flank.
kingFlank = KingFlank[file_of(ksq)];
b1 = attackedBy[Them][ALL_PIECES] & kingFlank & Camp;
b2 = b1 & attackedBy2[Them];
int tropism = popcount(b1) + popcount(b2);
// Main king safety evaluation
if (kingAttackersCount[Them] > 1 - pos.count<QUEEN>(Them))
{
int kingDanger = 0; int kingDanger = 0;
unsafeChecks = 0; const Square ksq = pos.square<KING>(Us);
// Init the score with king shelter and enemy pawns storm
Score score = pe->king_safety<Us>(pos);
// Attacked squares defended at most once by our queen or king // Attacked squares defended at most once by our queen or king
weak = attackedBy[Them][ALL_PIECES] weak = attackedBy[Them][ALL_PIECES]
@@ -441,57 +395,77 @@ namespace {
b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
b2 = attacks_bb<BISHOP>(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 & ~attackedBy[Us][QUEEN])
kingDanger += QueenSafeCheck;
b1 &= attackedBy[Them][ROOK];
b2 &= attackedBy[Them][BISHOP];
// Enemy rooks checks // Enemy rooks checks
if (b1 & safe) rookChecks = b1 & safe & attackedBy[Them][ROOK];
if (rookChecks)
kingDanger += RookSafeCheck; kingDanger += RookSafeCheck;
else else
unsafeChecks |= b1; unsafeChecks |= b1 & attackedBy[Them][ROOK];
// Enemy bishops checks // Enemy queen safe checks: we count them only if they are from squares from
if (b2 & safe) // which we can't give a rook check, because rook checks are more valuable.
queenChecks = (b1 | b2)
& attackedBy[Them][QUEEN]
& safe
& ~attackedBy[Us][QUEEN]
& ~rookChecks;
if (queenChecks)
kingDanger += QueenSafeCheck;
// Enemy bishops checks: we count them only if they are from squares from
// which we can't give a queen check, because queen checks are more valuable.
bishopChecks = b2
& attackedBy[Them][BISHOP]
& safe
& ~queenChecks;
if (bishopChecks)
kingDanger += BishopSafeCheck; kingDanger += BishopSafeCheck;
else else
unsafeChecks |= b2; unsafeChecks |= b2 & attackedBy[Them][BISHOP];
// Enemy knights checks // Enemy knights checks
b = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT]; knightChecks = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
if (b & safe)
if (knightChecks & safe)
kingDanger += KnightSafeCheck; kingDanger += KnightSafeCheck;
else else
unsafeChecks |= b; unsafeChecks |= knightChecks;
// Unsafe or occupied checking squares will also be considered, as long as // Find the squares that opponent attacks in our king flank, the squares
// the square is in the attacker's mobility area. // which they attack twice in that flank, and the squares that we defend.
unsafeChecks &= mobilityArea[Them]; b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
b2 = b1 & attackedBy2[Them];
b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
int kingFlankAttack = popcount(b1) + popcount(b2);
int kingFlankDefense = popcount(b3);
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
+ 69 * kingAttacksCount[Them]
+ 185 * popcount(kingRing[Us] & weak) + 185 * popcount(kingRing[Us] & weak)
+ 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks) + 148 * popcount(unsafeChecks)
+ tropism * tropism / 4 + 98 * popcount(pos.blockers_for_king(Us))
- 873 * !pos.count<QUEEN>(Them) + 69 * kingAttacksCount[Them]
- 6 * mg_value(score) / 8 + 3 * kingFlankAttack * kingFlankAttack / 8
+ mg_value(mobility[Them] - mobility[Us]) + mg_value(mobility[Them] - mobility[Us])
- 30; - 873 * !pos.count<QUEEN>(Them)
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
- 6 * mg_value(score) / 8
- 4 * kingFlankDefense
+ 37;
// Transform the kingDanger units into a Score, and subtract it from the evaluation // Transform the kingDanger units into a Score, and subtract it from the evaluation
if (kingDanger > 0) if (kingDanger > 100)
score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16); score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
}
// Penalty when our king is on a pawnless flank // Penalty when our king is on a pawnless flank
if (!(pos.pieces(PAWN) & kingFlank)) if (!(pos.pieces(PAWN) & KingFlank[file_of(ksq)]))
score -= PawnlessFlank; score -= PawnlessFlank;
// King tropism bonus, to anticipate slow motion attacks on our king // Penalty if king flank is under attack, potentially moving toward the king
score -= CloseEnemies * tropism; score -= FlankAttacks * kingFlankAttack;
if (T) if (T)
Trace::add(KING, Us, score); Trace::add(KING, Us, score);
@@ -506,14 +480,14 @@ namespace {
Score Evaluation<T>::threats() const { Score Evaluation<T>::threats() const {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Up = pawn_push(Us);
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe, restricted; Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
// Non-pawn enemies // Non-pawn enemies
nonPawnEnemies = pos.pieces(Them) ^ pos.pieces(Them, PAWN); nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(PAWN);
// Squares strongly protected by the enemy, either because they defend the // Squares strongly protected by the enemy, either because they defend the
// square with a pawn, or because they defend the square twice and we don't. // square with a pawn, or because they defend the square twice and we don't.
@@ -526,49 +500,39 @@ namespace {
// Enemies not strongly protected and under our attack // Enemies not strongly protected and under our attack
weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES];
// Safe or protected squares
safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
// Bonus according to the kind of attacking pieces // Bonus according to the kind of attacking pieces
if (defended | weak) if (defended | weak)
{ {
b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]); b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
while (b) while (b)
{ score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))];
Square s = pop_lsb(&b);
score += ThreatByMinor[type_of(pos.piece_on(s))];
if (type_of(pos.piece_on(s)) != PAWN)
score += ThreatByRank * (int)relative_rank(Them, s);
}
b = weak & attackedBy[Us][ROOK]; b = weak & attackedBy[Us][ROOK];
while (b) while (b)
{ score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))];
Square s = pop_lsb(&b);
score += ThreatByRook[type_of(pos.piece_on(s))];
if (type_of(pos.piece_on(s)) != PAWN)
score += ThreatByRank * (int)relative_rank(Them, s);
}
if (weak & attackedBy[Us][KING]) if (weak & attackedBy[Us][KING])
score += ThreatByKing; score += ThreatByKing;
score += Hanging * popcount(weak & ~attackedBy[Them][ALL_PIECES]); b = ~attackedBy[Them][ALL_PIECES]
| (nonPawnEnemies & attackedBy2[Us]);
b = weak & nonPawnEnemies & attackedBy[Them][ALL_PIECES]; score += Hanging * popcount(weak & b);
score += Overload * popcount(b);
} }
// Bonus for restricting their piece moves // Bonus for restricting their piece moves
restricted = attackedBy[Them][ALL_PIECES] b = attackedBy[Them][ALL_PIECES]
& ~attackedBy[Them][PAWN] & ~stronglyProtected
& ~attackedBy2[Them]
& attackedBy[Us][ALL_PIECES]; & attackedBy[Us][ALL_PIECES];
score += RestrictedPiece * popcount(restricted);
// Bonus for enemy unopposed weak pawns score += RestrictedPiece * popcount(b);
if (pos.pieces(Us, ROOK, QUEEN))
score += WeakUnopposedPawn * pe->weak_unopposed(Them); // Protected or unattacked squares
safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
// Bonus for attacking enemy pieces with our relatively safe pawns
b = pos.pieces(Us, PAWN) & safe;
b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
score += ThreatBySafePawn * popcount(b);
// Find squares where our pawns can push on the next move // Find squares where our pawns can push on the next move
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces(); b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
@@ -578,14 +542,8 @@ namespace {
b &= ~attackedBy[Them][PAWN] & safe; b &= ~attackedBy[Them][PAWN] & safe;
// Bonus for safe pawn threats on the next move // Bonus for safe pawn threats on the next move
b = pawn_attacks_bb<Us>(b) & pos.pieces(Them);
score += ThreatByPawnPush * popcount(b);
// Our safe or protected pawns
b = pos.pieces(Us, PAWN) & safe;
b = pawn_attacks_bb<Us>(b) & nonPawnEnemies; b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
score += ThreatBySafePawn * popcount(b); score += ThreatByPawnPush * popcount(b);
// Bonus for threats on the next moves against enemy queen // Bonus for threats on the next moves against enemy queen
if (pos.count<QUEEN>(Them) == 1) if (pos.count<QUEEN>(Them) == 1)
@@ -616,13 +574,13 @@ namespace {
Score Evaluation<T>::passed() const { Score Evaluation<T>::passed() const {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Up = pawn_push(Us);
auto king_proximity = [&](Color c, Square s) { auto king_proximity = [&](Color c, Square s) {
return std::min(distance(pos.square<KING>(c), s), 5); return std::min(distance(pos.square<KING>(c), s), 5);
}; };
Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares; Bitboard b, bb, squaresToQueen, unsafeSquares;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
b = pe->passed_pawns(Us); b = pe->passed_pawns(Us);
@@ -639,11 +597,11 @@ namespace {
if (r > RANK_3) if (r > RANK_3)
{ {
int w = (r-2) * (r-2) + 2; int w = 5 * r - 13;
Square blockSq = s + Up; Square blockSq = s + Up;
// Adjust bonus based on the king's proximity // Adjust bonus based on the king's proximity
bonus += make_score(0, ( king_proximity(Them, blockSq) * 5 bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4
- king_proximity(Us, blockSq) * 2) * w); - king_proximity(Us, blockSq) * 2) * w);
// If blockSq is not the queening square then consider also a second push // If blockSq is not the queening square then consider also a second push
@@ -653,42 +611,37 @@ namespace {
// If the pawn is free to advance, then increase the bonus // If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq)) if (pos.empty(blockSq))
{ {
// If there is a rook or queen attacking/defending the pawn from behind, squaresToQueen = forward_file_bb(Us, s);
// consider all the squaresToQueen. Otherwise consider only the squares unsafeSquares = passed_pawn_span(Us, s);
// in the pawn's path attacked or occupied by the enemy.
defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s); bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
if (!(pos.pieces(Us) & bb))
defendedSquares &= attackedBy[Us][ALL_PIECES];
if (!(pos.pieces(Them) & bb)) if (!(pos.pieces(Them) & bb))
unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); unsafeSquares &= attackedBy[Them][ALL_PIECES];
// If there aren't any enemy attacks, assign a big bonus. Otherwise // If there are no enemy attacks on passed pawn span, assign a big bonus.
// assign a smaller bonus if the block square isn't attacked. // Otherwise assign a smaller bonus if the path to queen is not attacked
int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0; // and even smaller bonus if it is attacked but block square is not.
int k = !unsafeSquares ? 35 :
!(unsafeSquares & squaresToQueen) ? 20 :
!(unsafeSquares & blockSq) ? 9 :
0 ;
// If the path to the queen is fully defended, assign a big bonus. // Assign a larger bonus if the block square is defended
// Otherwise assign a smaller bonus if the block square is defended. if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq))
if (defendedSquares == squaresToQueen) k += 5;
k += 6;
else if (defendedSquares & blockSq)
k += 4;
bonus += make_score(k * w, k * w); bonus += make_score(k * w, k * w);
} }
} // rank > RANK_3 } // r > RANK_3
// Scale down bonus for candidate passers which need more than one // Scale down bonus for candidate passers which need more than one
// pawn push to become passed, or have a pawn in front of them. // pawn push to become passed, or have a pawn in front of them.
if ( !pos.pawn_passed(Us, s + Up) if ( !pos.pawn_passed(Us, s + Up)
|| (pos.pieces(PAWN) & forward_file_bb(Us, s))) || (pos.pieces(PAWN) & (s + Up)))
bonus = bonus / 2; bonus = bonus / 2;
score += bonus + PassedFile[file_of(s)]; score += bonus - PassedFile * map_to_queenside(file_of(s));
} }
if (T) if (T)
@@ -712,6 +665,7 @@ namespace {
return SCORE_ZERO; return SCORE_ZERO;
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard SpaceMask = constexpr Bitboard SpaceMask =
Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB) Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
: CenterFiles & (Rank7BB | Rank6BB | Rank5BB); : CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
@@ -723,12 +677,11 @@ namespace {
// Find all squares which are at most three squares behind some friendly pawn // Find all squares which are at most three squares behind some friendly pawn
Bitboard behind = pos.pieces(Us, PAWN); Bitboard behind = pos.pieces(Us, PAWN);
behind |= (Us == WHITE ? behind >> 8 : behind << 8); behind |= shift<Down>(behind);
behind |= (Us == WHITE ? behind >> 16 : behind << 16); behind |= shift<Down+Down>(behind);
int bonus = popcount(safe) + popcount(behind & safe);
int weight = pos.count<ALL_PIECES>(Us) - 2 * pe->open_files();
int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
int weight = pos.count<ALL_PIECES>(Us) - 1;
Score score = make_score(bonus * weight * weight / 16, 0); Score score = make_score(bonus * weight * weight / 16, 0);
if (T) if (T)
@@ -743,31 +696,44 @@ namespace {
// known attacking/defending status of the players. // known attacking/defending status of the players.
template<Tracing T> template<Tracing T>
Score Evaluation<T>::initiative(Value eg) const { Score Evaluation<T>::initiative(Score score) const {
Value mg = mg_value(score);
Value eg = eg_value(score);
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK)) int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK)); - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
bool infiltration = rank_of(pos.square<KING>(WHITE)) > RANK_4
|| rank_of(pos.square<KING>(BLACK)) < RANK_5;
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
&& (pos.pieces(PAWN) & KingSide); && (pos.pieces(PAWN) & KingSide);
// Compute the initiative bonus for the attacking side bool almostUnwinnable = !pe->passed_count()
int complexity = 8 * pe->pawn_asymmetry() && outflanking < 0
+ 12 * pos.count<PAWN>() && !pawnsOnBothFlanks;
+ 12 * outflanking
+ 16 * pawnsOnBothFlanks
+ 48 * !pos.non_pawn_material()
-118 ;
// Now apply the bonus: note that we find the attacking side by extracting // Compute the initiative bonus for the attacking side
// the sign of the endgame value, and that we carefully cap the bonus so int complexity = 9 * pe->passed_count()
// that the endgame score will never change sign after the bonus. + 11 * pos.count<PAWN>()
+ 9 * outflanking
+ 12 * infiltration
+ 21 * pawnsOnBothFlanks
+ 51 * !pos.non_pawn_material()
- 43 * almostUnwinnable
- 100 ;
// Now apply the bonus: note that we find the attacking side by extracting the
// sign of the midgame or endgame values, and that we carefully cap the bonus
// so that the midgame and endgame scores do not change sign after the bonus.
int u = ((mg > 0) - (mg < 0)) * std::max(std::min(complexity + 50, 0), -abs(mg));
int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
if (T) if (T)
Trace::add(INITIATIVE, make_score(0, v)); Trace::add(INITIATIVE, make_score(u, v));
return make_score(0, v); return make_score(u, v);
} }
@@ -783,12 +749,12 @@ namespace {
if (sf == SCALE_FACTOR_NORMAL) if (sf == SCALE_FACTOR_NORMAL)
{ {
if ( pos.opposite_bishops() if ( pos.opposite_bishops()
&& pos.non_pawn_material(WHITE) == BishopValueMg && pos.non_pawn_material() == 2 * BishopValueMg)
&& pos.non_pawn_material(BLACK) == BishopValueMg) sf = 22 ;
sf = 8 + 4 * pe->pawn_asymmetry();
else else
sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf); sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide));
sf = std::max(0, sf - (pos.rule50_count() - 12) / 4);
} }
return ScaleFactor(sf); return ScaleFactor(sf);
@@ -823,7 +789,7 @@ namespace {
// Early exit if score is high // Early exit if score is high
Value v = (mg_value(score) + eg_value(score)) / 2; Value v = (mg_value(score) + eg_value(score)) / 2;
if (abs(v) > LazyThreshold) if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
return pos.side_to_move() == WHITE ? v : -v; return pos.side_to_move() == WHITE ? v : -v;
// Main evaluation begins here // Main evaluation begins here
@@ -844,14 +810,14 @@ namespace {
+ passed< WHITE>() - passed< BLACK>() + passed< WHITE>() - passed< BLACK>()
+ space< WHITE>() - space< BLACK>(); + space< WHITE>() - space< BLACK>();
score += initiative(eg_value(score)); score += initiative(score);
// Interpolate between a middlegame and a (scaled by 'sf') endgame score // Interpolate between a middlegame and a (scaled by 'sf') endgame score
ScaleFactor sf = scale_factor(eg_value(score)); ScaleFactor sf = scale_factor(eg_value(score));
v = mg_value(score) * int(me->game_phase()) v = mg_value(score) * int(me->game_phase())
+ eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL; + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL;
v /= int(PHASE_MIDGAME); v /= PHASE_MIDGAME;
// In case of tracing add all remaining individual evaluation terms // In case of tracing add all remaining individual evaluation terms
if (T) if (T)
@@ -884,6 +850,9 @@ Value Eval::evaluate(const Position& pos) {
std::string Eval::trace(const Position& pos) { std::string Eval::trace(const Position& pos) {
if (pos.checkers())
return "Total evaluation: none (in check)";
std::memset(scores, 0, sizeof(scores)); std::memset(scores, 0, sizeof(scores));
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
@@ -899,7 +868,6 @@ std::string Eval::trace(const Position& pos) {
<< " ------------+-------------+-------------+------------\n" << " ------------+-------------+-------------+------------\n"
<< " Material | " << Term(MATERIAL) << " Material | " << Term(MATERIAL)
<< " Imbalance | " << Term(IMBALANCE) << " Imbalance | " << Term(IMBALANCE)
<< " Initiative | " << Term(INITIATIVE)
<< " Pawns | " << Term(PAWN) << " Pawns | " << Term(PAWN)
<< " Knights | " << Term(KNIGHT) << " Knights | " << Term(KNIGHT)
<< " Bishops | " << Term(BISHOP) << " Bishops | " << Term(BISHOP)
@@ -910,6 +878,7 @@ std::string Eval::trace(const Position& pos) {
<< " Threats | " << Term(THREAT) << " Threats | " << Term(THREAT)
<< " Passed | " << Term(PASSED) << " Passed | " << Term(PASSED)
<< " Space | " << Term(SPACE) << " Space | " << Term(SPACE)
<< " Initiative | " << Term(INITIATIVE)
<< " ------------+-------------+-------------+------------\n" << " ------------+-------------+-------------+------------\n"
<< " Total | " << Term(TOTAL); << " Total | " << Term(TOTAL);

View File

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

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
#include "uci.h" #include "uci.h"
#include "endgame.h"
#include "syzygy/tbprobe.h" #include "syzygy/tbprobe.h"
namespace PSQT { namespace PSQT {
@@ -41,8 +42,7 @@ int main(int argc, char* argv[]) {
Bitboards::init(); Bitboards::init();
Position::init(); Position::init();
Bitbases::init(); Bitbases::init();
Search::init(); Endgames::init();
Pawns::init();
Threads.set(Options["Threads"]); Threads.set(Options["Threads"]);
Search::clear(); // After threads are up Search::clear(); // After threads are up

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm> // For std::min
#include <cassert> #include <cassert>
#include <cstring> // For std::memset #include <cstring> // For std::memset
@@ -70,14 +69,12 @@ namespace {
bool is_KBPsK(const Position& pos, Color us) { bool is_KBPsK(const Position& pos, Color us) {
return pos.non_pawn_material(us) == BishopValueMg return pos.non_pawn_material(us) == BishopValueMg
&& pos.count<BISHOP>(us) == 1
&& pos.count<PAWN >(us) >= 1; && pos.count<PAWN >(us) >= 1;
} }
bool is_KQKRPs(const Position& pos, Color us) { bool is_KQKRPs(const Position& pos, Color us) {
return !pos.count<PAWN>(us) return !pos.count<PAWN>(us)
&& pos.non_pawn_material(us) == QueenValueMg && pos.non_pawn_material(us) == QueenValueMg
&& pos.count<QUEEN>(us) == 1
&& pos.count<ROOK>(~us) == 1 && pos.count<ROOK>(~us) == 1
&& pos.count<PAWN>(~us) >= 1; && pos.count<PAWN>(~us) >= 1;
} }
@@ -132,7 +129,7 @@ Entry* probe(const Position& pos) {
Value npm_w = pos.non_pawn_material(WHITE); Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK); Value npm_b = pos.non_pawn_material(BLACK);
Value npm = std::max(EndgameLimit, std::min(npm_w + npm_b, MidgameLimit)); Value npm = clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME] // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
@@ -140,10 +137,10 @@ Entry* probe(const Position& pos) {
// Let's look if we have a specialized evaluation function for this particular // Let's look if we have a specialized evaluation function for this particular
// material configuration. Firstly we look for a fixed configuration one, then // material configuration. Firstly we look for a fixed configuration one, then
// for a generic one if the previous search failed. // for a generic one if the previous search failed.
if ((e->evaluationFunction = pos.this_thread()->endgames.probe<Value>(key)) != nullptr) if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
return e; return e;
for (Color c = WHITE; c <= BLACK; ++c) for (Color c : { WHITE, BLACK })
if (is_KXK(pos, c)) if (is_KXK(pos, c))
{ {
e->evaluationFunction = &EvaluateKXK[c]; e->evaluationFunction = &EvaluateKXK[c];
@@ -152,9 +149,9 @@ Entry* probe(const Position& pos) {
// OK, we didn't find any special evaluation function for the current material // OK, we didn't find any special evaluation function for the current material
// configuration. Is there a suitable specialized scaling function? // configuration. Is there a suitable specialized scaling function?
const EndgameBase<ScaleFactor>* sf; const auto* sf = Endgames::probe<ScaleFactor>(key);
if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr) if (sf)
{ {
e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
return e; return e;
@@ -163,7 +160,7 @@ Entry* probe(const Position& pos) {
// We didn't find any specialized scaling function, so fall back on generic // We didn't find any specialized scaling function, so fall back on generic
// ones that refer to more than one material distribution. Note that in this // ones that refer to more than one material distribution. Note that in this
// case we don't return after setting the function. // case we don't return after setting the function.
for (Color c = WHITE; c <= BLACK; ++c) for (Color c : { WHITE, BLACK })
{ {
if (is_KBPsK(pos, c)) if (is_KBPsK(pos, c))
e->scalingFunction[c] = &ScaleKBPsK[c]; e->scalingFunction[c] = &ScaleKBPsK[c];

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -58,7 +58,7 @@ struct Entry {
Key key; Key key;
const EndgameBase<Value>* evaluationFunction; const EndgameBase<Value>* evaluationFunction;
const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
// side (e.g. KPKP, KBPsKs) // side (e.g. KPKP, KBPsK)
int16_t value; int16_t value;
uint8_t factor[COLOR_NB]; uint8_t factor[COLOR_NB];
Phase gamePhase; Phase gamePhase;

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -56,7 +56,7 @@ namespace {
/// Version number. If Version is left empty, then compile date in the format /// Version number. If Version is left empty, then compile date in the format
/// DD-MM-YY and show in engine_info. /// DD-MM-YY and show in engine_info.
const string Version = "10"; const string Version = "11";
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
@@ -102,6 +102,13 @@ public:
if (!fname.empty() && !l.file.is_open()) if (!fname.empty() && !l.file.is_open())
{ {
l.file.open(fname, ifstream::out); l.file.open(fname, ifstream::out);
if (!l.file.is_open())
{
cerr << "Unable to open debug log file " << fname << endl;
exit(EXIT_FAILURE);
}
cin.rdbuf(&l.in); cin.rdbuf(&l.in);
cout.rdbuf(&l.out); cout.rdbuf(&l.out);
} }
@@ -144,8 +151,79 @@ const string engine_info(bool to_uci) {
} }
/// compiler_info() returns a string trying to describe the compiler we use
const std::string compiler_info() {
#define STRINGIFY2(x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define VER_STRING(major, minor, patch) STRINGIFY(major) "." STRINGIFY(minor) "." STRINGIFY(patch)
/// Predefined macros hell:
///
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux
/// __INTEL_COMPILER Compiler is Intel
/// _MSC_VER Compiler is MSVC or Intel on Windows
/// _WIN32 Building on Windows (any)
/// _WIN64 Building on Windows 64 bit
std::string compiler = "\nCompiled by ";
#ifdef __clang__
compiler += "clang++ ";
compiler += VER_STRING(__clang_major__, __clang_minor__, __clang_patchlevel__);
#elif __INTEL_COMPILER
compiler += "Intel compiler ";
compiler += "(version ";
compiler += STRINGIFY(__INTEL_COMPILER) " update " STRINGIFY(__INTEL_COMPILER_UPDATE);
compiler += ")";
#elif _MSC_VER
compiler += "MSVC ";
compiler += "(version ";
compiler += STRINGIFY(_MSC_FULL_VER) "." STRINGIFY(_MSC_BUILD);
compiler += ")";
#elif __GNUC__
compiler += "g++ (GNUC) ";
compiler += VER_STRING(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#else
compiler += "Unknown compiler ";
compiler += "(unknown version)";
#endif
#if defined(__APPLE__)
compiler += " on Apple";
#elif defined(__CYGWIN__)
compiler += " on Cygwin";
#elif defined(__MINGW64__)
compiler += " on MinGW64";
#elif defined(__MINGW32__)
compiler += " on MinGW32";
#elif defined(__ANDROID__)
compiler += " on Android";
#elif defined(__linux__)
compiler += " on Linux";
#elif defined(_WIN64)
compiler += " on Microsoft Windows 64-bit";
#elif defined(_WIN32)
compiler += " on Microsoft Windows 32-bit";
#else
compiler += " on unknown system";
#endif
compiler += "\n __VERSION__ macro expands to: ";
#ifdef __VERSION__
compiler += __VERSION__;
#else
compiler += "(undefined macro)";
#endif
compiler += "\n";
return compiler;
}
/// Debug functions used mainly to collect run-time statistics /// Debug functions used mainly to collect run-time statistics
static int64_t hits[2], means[2]; static std::atomic<int64_t> hits[2], means[2];
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); } void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
@@ -168,7 +246,7 @@ void dbg_print() {
std::ostream& operator<<(std::ostream& os, SyncCout sc) { std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m; static std::mutex m;
if (sc == IO_LOCK) if (sc == IO_LOCK)
m.lock(); m.lock();
@@ -210,12 +288,6 @@ void prefetch(void* addr) {
#endif #endif
void prefetch2(void* addr) {
prefetch(addr);
prefetch((uint8_t*)addr + 64);
}
namespace WinProcGroup { namespace WinProcGroup {
#ifndef _WIN32 #ifndef _WIN32
@@ -257,7 +329,7 @@ int best_group(size_t idx) {
return -1; return -1;
} }
while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength) while (byteOffset < returnLength)
{ {
if (ptr->Relationship == RelationNumaNode) if (ptr->Relationship == RelationNumaNode)
nodes++; nodes++;
@@ -268,6 +340,7 @@ int best_group(size_t idx) {
threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
} }
assert(ptr->Size);
byteOffset += ptr->Size; byteOffset += ptr->Size;
ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size); ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size);
} }

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -30,8 +30,8 @@
#include "types.h" #include "types.h"
const std::string engine_info(bool to_uci = false); const std::string engine_info(bool to_uci = false);
const std::string compiler_info();
void prefetch(void* addr); void prefetch(void* addr);
void prefetch2(void* addr);
void start_logger(const std::string& fname); void start_logger(const std::string& fname);
void dbg_hit_on(bool b); void dbg_hit_on(bool b);
@@ -53,7 +53,7 @@ struct HashTable {
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
private: private:
std::vector<Entry> table = std::vector<Entry>(Size); std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap
}; };

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -25,47 +25,6 @@
namespace { namespace {
template<Color Us, CastlingSide Cs, bool Checks, bool Chess960>
ExtMove* generate_castling(const Position& pos, ExtMove* moveList) {
constexpr CastlingRight Cr = Us | Cs;
constexpr bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);
if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
return moveList;
// After castling, the rook and king final positions are the same in Chess960
// as they would be in standard chess.
Square kfrom = pos.square<KING>(Us);
Square rfrom = pos.castling_rook_square(Cr);
Square kto = relative_square(Us, KingSide ? SQ_G1 : SQ_C1);
Bitboard enemies = pos.pieces(~Us);
assert(!pos.checkers());
const Direction step = Chess960 ? kto > kfrom ? WEST : EAST
: KingSide ? WEST : EAST;
for (Square s = kto; s != kfrom; s += step)
if (pos.attackers_to(s) & enemies)
return moveList;
// Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~Us, ROOK, QUEEN)))
return moveList;
Move m = make<CASTLING>(kfrom, rfrom);
if (Checks && !pos.gives_check(m))
return moveList;
*moveList++ = m;
return moveList;
}
template<GenType Type, Direction D> template<GenType Type, Direction D>
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
@@ -93,16 +52,15 @@ namespace {
template<Color Us, GenType Type> template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
// Compute our parametrized parameters at compile time, named according to // Compute some compile time parameters relative to the white side
// the point of view of white side.
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Up = pawn_push(Us);
constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
const Square ksq = pos.square<KING>(Them);
Bitboard emptySquares; Bitboard emptySquares;
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
@@ -127,8 +85,6 @@ namespace {
if (Type == QUIET_CHECKS) if (Type == QUIET_CHECKS)
{ {
Square ksq = pos.square<KING>(Them);
b1 &= pos.attacks_from<PAWN>(ksq, Them); b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them); b2 &= pos.attacks_from<PAWN>(ksq, Them);
@@ -136,10 +92,10 @@ namespace {
// if the pawn is not on the same file as the enemy king, because we // if the pawn is not on the same file as the enemy king, because we
// don't generate captures. Note that a possible discovery check // don't generate captures. Note that a possible discovery check
// promotion has been already generated amongst the captures. // promotion has been already generated amongst the captures.
Bitboard dcCandidates = pos.blockers_for_king(Them); Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7;
if (pawnsNotOn7 & dcCandidates) if (dcCandidateQuiets)
{ {
Bitboard dc1 = shift<Up>(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq); Bitboard dc1 = shift<Up>(dcCandidateQuiets) & emptySquares & ~file_bb(ksq);
Bitboard dc2 = shift<Up>(dc1 & TRank3BB) & emptySquares; Bitboard dc2 = shift<Up>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1; b1 |= dc1;
@@ -161,7 +117,7 @@ namespace {
} }
// Promotions and underpromotions // Promotions and underpromotions
if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB))) if (pawnsOn7)
{ {
if (Type == CAPTURES) if (Type == CAPTURES)
emptySquares = ~pos.pieces(); emptySquares = ~pos.pieces();
@@ -173,8 +129,6 @@ namespace {
Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies; Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares; Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
Square ksq = pos.square<KING>(Them);
while (b1) while (b1)
moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(&b1), ksq); moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(&b1), ksq);
@@ -230,7 +184,7 @@ namespace {
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
Bitboard target) { Bitboard target) {
assert(Pt != KING && Pt != PAWN); static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
const Square* pl = pos.squares<Pt>(us); const Square* pl = pos.squares<Pt>(us);
@@ -262,7 +216,9 @@ namespace {
template<Color Us, GenType Type> template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) {
constexpr bool Checks = Type == QUIET_CHECKS; constexpr CastlingRights OO = Us & KING_SIDE;
constexpr CastlingRights OOO = Us & QUEEN_SIDE;
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target); moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target); moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
@@ -276,19 +232,14 @@ namespace {
Bitboard b = pos.attacks_from<KING>(ksq) & target; Bitboard b = pos.attacks_from<KING>(ksq) & target;
while (b) while (b)
*moveList++ = make_move(ksq, pop_lsb(&b)); *moveList++ = make_move(ksq, pop_lsb(&b));
}
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us)) if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
{ {
if (pos.is_chess960()) if (!pos.castling_impeded(OO) && pos.can_castle(OO))
{ *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OO));
moveList = generate_castling<Us, KING_SIDE, Checks, true>(pos, moveList);
moveList = generate_castling<Us, QUEEN_SIDE, Checks, true>(pos, moveList); if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
} *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OOO));
else
{
moveList = generate_castling<Us, KING_SIDE, Checks, false>(pos, moveList);
moveList = generate_castling<Us, QUEEN_SIDE, Checks, false>(pos, moveList);
} }
} }
@@ -298,19 +249,16 @@ namespace {
} // namespace } // namespace
/// generate<CAPTURES> generates all pseudo-legal captures and queen /// <CAPTURES> Generates all pseudo-legal captures and queen promotions
/// promotions. Returns a pointer to the end of the move list. /// <QUIETS> Generates all pseudo-legal non-captures and underpromotions
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
/// ///
/// generate<QUIETS> generates all pseudo-legal non-captures and /// Returns a pointer to the end of the move list.
/// underpromotions. Returns a pointer to the end of the move list.
///
/// generate<NON_EVASIONS> generates all pseudo-legal captures and
/// non-captures. Returns a pointer to the end of the move list.
template<GenType Type> template<GenType Type>
ExtMove* generate(const Position& pos, ExtMove* moveList) { ExtMove* generate(const Position& pos, ExtMove* moveList) {
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()");
assert(!pos.checkers()); assert(!pos.checkers());
Color us = pos.side_to_move(); Color us = pos.side_to_move();

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by 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 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -31,9 +31,6 @@ namespace {
QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK
}; };
// Helper filter used with select()
const auto Any = [](){ return true; };
// partial_insertion_sort() sorts moves in descending order up to and including // partial_insertion_sort() sorts moves in descending order up to and including
// a given limit. The order of moves smaller than the limit is left unspecified. // a given limit. The order of moves smaller than the limit is left unspecified.
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
@@ -64,7 +61,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch),
refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) { refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) {
assert(d > DEPTH_ZERO); assert(d > 0);
stage = pos.checkers() ? EVASION_TT : MAIN_TT; stage = pos.checkers() ? EVASION_TT : MAIN_TT;
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
@@ -76,12 +73,12 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs)
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), recaptureSquare(rs), depth(d) { : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), recaptureSquare(rs), depth(d) {
assert(d <= DEPTH_ZERO); assert(d <= 0);
stage = pos.checkers() ? EVASION_TT : QSEARCH_TT; stage = pos.checkers() ? EVASION_TT : QSEARCH_TT;
ttMove = ttm ttMove = ttm
&& pos.pseudo_legal(ttm) && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) ? ttm : MOVE_NONE; && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE); stage += (ttMove == MOVE_NONE);
} }
@@ -94,8 +91,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece
stage = PROBCUT_TT; stage = PROBCUT_TT;
ttMove = ttm ttMove = ttm
&& pos.pseudo_legal(ttm)
&& pos.capture(ttm) && pos.capture(ttm)
&& pos.pseudo_legal(ttm)
&& pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE; && pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE); stage += (ttMove == MOVE_NONE);
} }
@@ -110,14 +107,15 @@ void MovePicker::score() {
for (auto& m : *this) for (auto& m : *this)
if (Type == CAPTURES) if (Type == CAPTURES)
m.value = PieceValue[MG][pos.piece_on(to_sq(m))] m.value = int(PieceValue[MG][pos.piece_on(to_sq(m))]) * 6
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))] / 8; + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
else if (Type == QUIETS) else if (Type == QUIETS)
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]; + 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)];
else // Type == EVASIONS else // Type == EVASIONS
{ {
@@ -141,12 +139,12 @@ Move MovePicker::select(Pred filter) {
if (T == Best) if (T == Best)
std::swap(*cur, *std::max_element(cur, endMoves)); std::swap(*cur, *std::max_element(cur, endMoves));
move = *cur++; if (*cur != ttMove && filter())
return *cur++;
if (move != ttMove && filter()) cur++;
return move;
} }
return move = MOVE_NONE; return MOVE_NONE;
} }
/// MovePicker::next_move() is the most important method of the MovePicker class. It /// MovePicker::next_move() is the most important method of the MovePicker class. It
@@ -176,10 +174,10 @@ top:
case GOOD_CAPTURE: case GOOD_CAPTURE:
if (select<Best>([&](){ if (select<Best>([&](){
return pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)) ? return pos.see_ge(*cur, Value(-55 * cur->value / 1024)) ?
// Move losing capture to endBadCaptures to be tried later // Move losing capture to endBadCaptures to be tried later
true : (*endBadCaptures++ = move, false); })) true : (*endBadCaptures++ = *cur, false); }))
return move; return *(cur - 1);
// Prepare the pointers to loop over the refutations array // Prepare the pointers to loop over the refutations array
cur = std::begin(refutations); cur = std::begin(refutations);
@@ -194,28 +192,32 @@ top:
/* fallthrough */ /* fallthrough */
case REFUTATION: case REFUTATION:
if (select<Next>([&](){ return move != MOVE_NONE if (select<Next>([&](){ return *cur != MOVE_NONE
&& !pos.capture(move) && !pos.capture(*cur)
&& pos.pseudo_legal(move); })) && pos.pseudo_legal(*cur); }))
return move; return *(cur - 1);
++stage; ++stage;
/* fallthrough */ /* fallthrough */
case QUIET_INIT: case QUIET_INIT:
if (!skipQuiets)
{
cur = endBadCaptures; cur = endBadCaptures;
endMoves = generate<QUIETS>(pos, cur); endMoves = generate<QUIETS>(pos, cur);
score<QUIETS>(); score<QUIETS>();
partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY); partial_insertion_sort(cur, endMoves, -3000 * depth);
}
++stage; ++stage;
/* fallthrough */ /* fallthrough */
case QUIET: case QUIET:
if ( !skipQuiets if ( !skipQuiets
&& select<Next>([&](){return move != refutations[0] && select<Next>([&](){return *cur != refutations[0].move
&& move != refutations[1] && *cur != refutations[1].move
&& move != refutations[2];})) && *cur != refutations[2].move;}))
return move; return *(cur - 1);
// Prepare the pointers to loop over the bad captures // Prepare the pointers to loop over the bad captures
cur = moves; cur = moves;
@@ -225,7 +227,7 @@ top:
/* fallthrough */ /* fallthrough */
case BAD_CAPTURE: case BAD_CAPTURE:
return select<Next>(Any); return select<Next>([](){ return true; });
case EVASION_INIT: case EVASION_INIT:
cur = moves; cur = moves;
@@ -236,15 +238,15 @@ top:
/* fallthrough */ /* fallthrough */
case EVASION: case EVASION:
return select<Best>(Any); return select<Best>([](){ return true; });
case PROBCUT: case PROBCUT:
return select<Best>([&](){ return pos.see_ge(move, threshold); }); return select<Best>([&](){ return pos.see_ge(*cur, threshold); });
case QCAPTURE: case QCAPTURE:
if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES
|| to_sq(move) == recaptureSquare; })) || to_sq(*cur) == recaptureSquare; }))
return move; return *(cur - 1);
// If we did not find any move and we do not try checks, we have finished // If we did not find any move and we do not try checks, we have finished
if (depth != DEPTH_QS_CHECKS) if (depth != DEPTH_QS_CHECKS)
@@ -261,7 +263,7 @@ top:
/* fallthrough */ /* fallthrough */
case QCHECK: case QCHECK:
return select<Next>(Any); return select<Next>([](){ return true; });
} }
assert(false); assert(false);

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -80,16 +80,16 @@ struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
/// In stats table, D=0 means that the template parameter is not used /// In stats table, D=0 means that the template parameter is not used
enum StatsParams { NOT_USED = 0 }; enum StatsParams { NOT_USED = 0 };
enum StatsType { NoCaptures, Captures };
/// ButterflyHistory records how often quiet moves have been successful or /// ButterflyHistory records how often quiet moves have been successful or
/// unsuccessful during the current search, and is used for reduction and move /// unsuccessful during the current search, and is used for reduction and move
/// ordering decisions. It uses 2 tables (one for each color) indexed by /// ordering decisions. It uses 2 tables (one for each color) indexed by
/// the move's from and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory; typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic /// move, see www.chessprogramming.org/Countermove_Heuristic
typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory; typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;
/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] /// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
@@ -142,7 +142,6 @@ private:
Move ttMove; Move ttMove;
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
int stage; int stage;
Move move;
Square recaptureSquare; Square recaptureSquare;
Value threshold; Value threshold;
Depth depth; Depth depth;

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -33,11 +33,14 @@ namespace {
// Pawn penalties // Pawn penalties
constexpr Score Backward = S( 9, 24); constexpr Score Backward = S( 9, 24);
constexpr Score BlockedStorm = S(82, 82);
constexpr Score Doubled = S(11, 56); constexpr Score Doubled = S(11, 56);
constexpr Score Isolated = S( 5, 15); constexpr Score Isolated = S( 5, 15);
constexpr Score WeakLever = S( 0, 56);
constexpr Score WeakUnopposed = S(13, 27);
// Connected pawn bonus by opposed, phalanx, #support and rank // Connected pawn bonus
Score Connected[2][2][3][RANK_NB]; constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };
// Strength of pawn shelter for our king by [distance from edge][rank]. // Strength of pawn shelter for our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
@@ -50,12 +53,13 @@ namespace {
// Danger of enemy pawns moving toward our king by [distance from edge][rank]. // Danger of enemy pawns moving toward our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn // RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
// is behind our king. // is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn
// on edge, likely blocked by our king.
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = { constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
{ V( 89), V(107), V(123), V(93), V(57), V( 45), V( 51) }, { V( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) },
{ V( 44), V(-18), V(123), V(46), V(39), V( -7), V( 23) }, { V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) },
{ V( 4), V( 52), V(162), V(37), V( 7), V(-14), V( -2) }, { V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) },
{ V(-10), V(-14), V( 90), V(15), V( 2), V( -7), V(-16) } { V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) }
}; };
#undef S #undef S
@@ -65,80 +69,86 @@ namespace {
Score evaluate(const Position& pos, Pawns::Entry* e) { Score evaluate(const Position& pos, Pawns::Entry* e) {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Up = pawn_push(Us);
Bitboard b, neighbours, stoppers, doubled, supported, phalanx; Bitboard neighbours, stoppers, support, phalanx, opposed;
Bitboard lever, leverPush; Bitboard lever, leverPush, blocked;
Square s; Square s;
bool opposed, backward; bool backward, passed, doubled;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
const Square* pl = pos.squares<PAWN>(Us); const Square* pl = pos.squares<PAWN>(Us);
Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0; Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns);
e->semiopenFiles[Us] = 0xFF;
e->passedPawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE; e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns); e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb<Us>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares);
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
// Loop through all pawns of the current color and score each pawn // Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE) while ((s = *pl++) != SQ_NONE)
{ {
assert(pos.piece_on(s) == make_piece(Us, PAWN)); assert(pos.piece_on(s) == make_piece(Us, PAWN));
File f = file_of(s); Rank r = relative_rank(Us, s);
e->semiopenFiles[Us] &= ~(1 << f);
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// Flag the pawn // Flag the pawn
opposed = theirPawns & forward_file_bb(Us, s); opposed = theirPawns & forward_file_bb(Us, s);
stoppers = theirPawns & passed_pawn_mask(Us, s); blocked = theirPawns & (s + Up);
stoppers = theirPawns & passed_pawn_span(Us, s);
lever = theirPawns & PawnAttacks[Us][s]; lever = theirPawns & PawnAttacks[Us][s];
leverPush = theirPawns & PawnAttacks[Us][s + Up]; leverPush = theirPawns & PawnAttacks[Us][s + Up];
doubled = ourPawns & (s - Up); doubled = ourPawns & (s - Up);
neighbours = ourPawns & adjacent_files_bb(f); neighbours = ourPawns & adjacent_files_bb(s);
phalanx = neighbours & rank_bb(s); phalanx = neighbours & rank_bb(s);
supported = neighbours & rank_bb(s - Up); support = neighbours & rank_bb(s - Up);
// A pawn is backward when it is behind all pawns of the same color // A pawn is backward when it is behind all pawns of the same color on
// on the adjacent files and cannot be safely advanced. // the adjacent files and cannot safely advance.
backward = !(ourPawns & pawn_attack_span(Them, s + Up)) backward = !(neighbours & forward_ranks_bb(Them, s + Up))
&& (stoppers & (leverPush | (s + Up))); && (leverPush | blocked);
// Passed pawns will be properly scored in evaluation because we need // Compute additional span if pawn is not backward nor blocked
// full attack info to evaluate them. Include also not passed pawns if (!backward && !blocked)
// which could become passed after one or two pawn pushes when are e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// not attacked more times than defended.
if ( !(stoppers ^ lever ^ leverPush) // A pawn is passed if one of the three following conditions is true:
&& popcount(supported) >= popcount(lever) - 1 // (a) there is no stoppers except some levers
// (b) the only stoppers are the leverPush, but we outnumber them
// (c) there is only one front stopper which can be levered.
passed = !(stoppers ^ lever)
|| ( !(stoppers ^ leverPush)
&& popcount(phalanx) >= popcount(leverPush)) && popcount(phalanx) >= popcount(leverPush))
e->passedPawns[Us] |= s; || ( stoppers == blocked && r >= RANK_5
&& (shift<Up>(support) & ~(theirPawns | doubleAttackThem)));
else if ( stoppers == SquareBB[s + Up] // Passed pawns will be properly scored later in evaluation when we have
&& relative_rank(Us, s) >= RANK_5) // full attack info.
{ if (passed)
b = shift<Up>(supported) & ~theirPawns;
while (b)
if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
e->passedPawns[Us] |= s; e->passedPawns[Us] |= s;
}
// Score this pawn // Score this pawn
if (supported | phalanx) if (support | phalanx)
score += Connected[opposed][bool(phalanx)][popcount(supported)][relative_rank(Us, s)]; {
int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
+ 21 * popcount(support);
score += make_score(v, v * (r - 2) / 4);
}
else if (!neighbours) else if (!neighbours)
score -= Isolated, e->weakUnopposed[Us] += !opposed; score -= Isolated
+ WeakUnopposed * !opposed;
else if (backward) else if (backward)
score -= Backward, e->weakUnopposed[Us] += !opposed; score -= Backward
+ WeakUnopposed * !opposed;
if (doubled && !supported) if (!support)
score -= Doubled; score -= Doubled * doubled
+ WeakLever * more_than_one(lever);
} }
return score; return score;
@@ -148,27 +158,6 @@ namespace {
namespace Pawns { namespace Pawns {
/// Pawns::init() initializes some tables needed by evaluation. Instead of using
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
/// to reduce independent parameters and to allow easier tuning and better insight.
void init() {
static constexpr int Seed[RANK_NB] = { 0, 13, 24, 18, 65, 100, 175, 330 };
for (int opposed = 0; opposed <= 1; ++opposed)
for (int phalanx = 0; phalanx <= 1; ++phalanx)
for (int support = 0; support <= 2; ++support)
for (Rank r = RANK_2; r < RANK_8; ++r)
{
int v = 17 * support;
v += (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed;
Connected[opposed][phalanx][support][r] = make_score(v, v * (r - 2) / 4);
}
}
/// Pawns::probe() looks up the current position's pawns configuration in /// Pawns::probe() looks up the current position's pawns configuration in
/// the pawns hash table. It returns a pointer to the Entry if the position /// the pawns hash table. It returns a pointer to the Entry if the position
/// is found. Otherwise a new Entry is computed and stored there, so we don't /// is found. Otherwise a new Entry is computed and stored there, so we don't
@@ -185,9 +174,6 @@ Entry* probe(const Position& pos) {
e->key = key; e->key = key;
e->scores[WHITE] = evaluate<WHITE>(pos, e); e->scores[WHITE] = evaluate<WHITE>(pos, e);
e->scores[BLACK] = evaluate<BLACK>(pos, e); e->scores[BLACK] = evaluate<BLACK>(pos, e);
e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]);
e->asymmetry = popcount( (e->passedPawns[WHITE] | e->passedPawns[BLACK])
| (e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]));
return e; return e;
} }
@@ -197,35 +183,35 @@ Entry* probe(const Position& pos) {
/// penalty for a king, looking at the king file and the two closest files. /// penalty for a king, looking at the king file and the two closest files.
template<Color Us> template<Color Us>
Value Entry::evaluate_shelter(const Position& pos, Square ksq) { Score Entry::evaluate_shelter(const Position& pos, Square ksq) {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
constexpr Bitboard BlockRanks = (Us == WHITE ? Rank1BB | Rank2BB : Rank8BB | Rank7BB);
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
Bitboard ourPawns = b & pos.pieces(Us); Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them); Bitboard theirPawns = b & pos.pieces(Them);
Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ? Score bonus = make_score(5, 5);
Value(374) : Value(5);
File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq))); File center = clamp(file_of(ksq), FILE_B, FILE_G);
for (File f = File(center - 1); f <= File(center + 1); ++f) for (File f = File(center - 1); f <= File(center + 1); ++f)
{ {
b = ourPawns & file_bb(f); b = ourPawns & file_bb(f);
int ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : 0; int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
b = theirPawns & file_bb(f); b = theirPawns & file_bb(f);
int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
int d = std::min(f, ~f); File d = map_to_queenside(f);
safety += ShelterStrength[d][ourRank]; bonus += make_score(ShelterStrength[d][ourRank], 0);
safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3)
: UnblockedStorm[d][theirRank]; if (ourRank && (ourRank == theirRank - 1))
bonus -= BlockedStorm * int(theirRank == RANK_3);
else
bonus -= make_score(UnblockedStorm[d][theirRank], 0);
} }
return safety; return bonus;
} }
@@ -237,23 +223,29 @@ Score Entry::do_king_safety(const Position& pos) {
Square ksq = pos.square<KING>(Us); Square ksq = pos.square<KING>(Us);
kingSquares[Us] = ksq; kingSquares[Us] = ksq;
castlingRights[Us] = pos.can_castle(Us); castlingRights[Us] = pos.castling_rights(Us);
int minKingPawnDistance = 0; auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); };
Score shelter = evaluate_shelter<Us>(pos, ksq);
// If we can castle use the bonus after castling if it is bigger
if (pos.can_castle(Us & KING_SIDE))
shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)), compare);
if (pos.can_castle(Us & QUEEN_SIDE))
shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)), compare);
// In endgame we like to bring our king near our closest pawn
Bitboard pawns = pos.pieces(Us, PAWN); Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns) int minPawnDist = pawns ? 8 : 0;
while (!(DistanceRingBB[ksq][++minKingPawnDistance] & pawns)) {}
Value bonus = evaluate_shelter<Us>(pos, ksq); if (pawns & PseudoAttacks[KING][ksq])
minPawnDist = 1;
else while (pawns)
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
// If we can castle use the bonus after the castling if it is bigger return shelter - make_score(0, 16 * minPawnDist);
if (pos.can_castle(Us | KING_SIDE))
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)));
if (pos.can_castle(Us | QUEEN_SIDE))
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)));
return make_score(bonus, -16 * minKingPawnDistance);
} }
// Explicit template instantiation // Explicit template instantiation

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -37,21 +37,11 @@ struct Entry {
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
int weak_unopposed(Color c) const { return weakUnopposed[c]; } int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }
int pawn_asymmetry() const { return asymmetry; }
int open_files() const { return openFiles; }
int semiopen_file(Color c, File f) const {
return semiopenFiles[c] & (1 << f);
}
int pawns_on_same_color_squares(Color c, Square s) const {
return pawnsOnSquares[c][bool(DarkSquares & s)];
}
template<Color Us> template<Color Us>
Score king_safety(const Position& pos) { Score king_safety(const Position& pos) {
return kingSquares[Us] == pos.square<KING>(Us) && castlingRights[Us] == pos.can_castle(Us) return kingSquares[Us] == pos.square<KING>(Us) && castlingRights[Us] == pos.castling_rights(Us)
? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos)); ? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos));
} }
@@ -59,7 +49,7 @@ struct Entry {
Score do_king_safety(const Position& pos); Score do_king_safety(const Position& pos);
template<Color Us> template<Color Us>
Value evaluate_shelter(const Position& pos, Square ksq); Score evaluate_shelter(const Position& pos, Square ksq);
Key key; Key key;
Score scores[COLOR_NB]; Score scores[COLOR_NB];
@@ -68,17 +58,11 @@ struct Entry {
Bitboard pawnAttacksSpan[COLOR_NB]; Bitboard pawnAttacksSpan[COLOR_NB];
Square kingSquares[COLOR_NB]; Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int weakUnopposed[COLOR_NB];
int castlingRights[COLOR_NB]; int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
int asymmetry;
int openFiles;
}; };
typedef HashTable<Entry, 16384> Table; typedef HashTable<Entry, 131072> Table;
void init();
Entry* probe(const Position& pos); Entry* probe(const Position& pos);
} // namespace Pawns } // namespace Pawns

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -50,41 +50,6 @@ const string PieceToChar(" PNBRQK pnbrqk");
constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
// min_attacker() is a helper function used by see_ge() to locate the least
// valuable attacker for the side to move, remove the attacker we just found
// from the bitboards and scan for new X-ray attacks behind it.
template<int Pt>
PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers,
Bitboard& occupied, Bitboard& attackers) {
Bitboard b = stmAttackers & byTypeBB[Pt];
if (!b)
return min_attacker<Pt + 1>(byTypeBB, to, stmAttackers, occupied, attackers);
occupied ^= lsb(b); // Remove the attacker from occupied
// Add any X-ray attack behind the just removed piece. For instance with
// rooks in a8 and a7 attacking a1, after removing a7 we add rook in a8.
// Note that new added attackers can be of any color.
if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
attackers |= attacks_bb<BISHOP>(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]);
if (Pt == ROOK || Pt == QUEEN)
attackers |= attacks_bb<ROOK>(to, occupied) & (byTypeBB[ROOK] | byTypeBB[QUEEN]);
// X-ray may add already processed pieces because byTypeBB[] is constant: in
// the rook example, now attackers contains _again_ rook in a7, so remove it.
attackers &= occupied;
return (PieceType)Pt;
}
template<>
PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) {
return KING; // No need to update bitboards: it is the last cycle
}
} // namespace } // namespace
@@ -183,7 +148,7 @@ void Position::init() {
{ {
std::swap(cuckoo[i], key); std::swap(cuckoo[i], key);
std::swap(cuckooMove[i], move); std::swap(cuckooMove[i], move);
if (move == 0) // Arrived at empty slot ? if (move == MOVE_NONE) // Arrived at empty slot?
break; break;
i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot
} }
@@ -330,24 +295,18 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
void Position::set_castling_right(Color c, Square rfrom) { void Position::set_castling_right(Color c, Square rfrom) {
Square kfrom = square<KING>(c); Square kfrom = square<KING>(c);
CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE; CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
CastlingRight cr = (c | cs);
st->castlingRights |= cr; st->castlingRights |= cr;
castlingRightsMask[kfrom] |= cr; castlingRightsMask[kfrom] |= cr;
castlingRightsMask[rfrom] |= cr; castlingRightsMask[rfrom] |= cr;
castlingRookSquare[cr] = rfrom; castlingRookSquare[cr] = rfrom;
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1); Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1); Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s) castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
if (s != kfrom && s != rfrom) & ~(square_bb(kfrom) | rfrom);
castlingPath[cr] |= s;
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s)
if (s != kfrom && s != rfrom)
castlingPath[cr] |= s;
} }
@@ -388,6 +347,12 @@ void Position::set_state(StateInfo* si) const {
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
Piece pc = piece_on(s); Piece pc = piece_on(s);
si->key ^= Zobrist::psq[pc][s]; si->key ^= Zobrist::psq[pc][s];
if (type_of(pc) == PAWN)
si->pawnKey ^= Zobrist::psq[pc][s];
else if (type_of(pc) != KING)
si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
} }
if (si->epSquare != SQ_NONE) if (si->epSquare != SQ_NONE)
@@ -398,21 +363,10 @@ void Position::set_state(StateInfo* si) const {
si->key ^= Zobrist::castling[si->castlingRights]; si->key ^= Zobrist::castling[si->castlingRights];
for (Bitboard b = pieces(PAWN); b; )
{
Square s = pop_lsb(&b);
si->pawnKey ^= Zobrist::psq[piece_on(s)][s];
}
for (Piece pc : Pieces) for (Piece pc : Pieces)
{
if (type_of(pc) != PAWN && type_of(pc) != KING)
si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc];
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
si->materialKey ^= Zobrist::psq[pc][cnt]; si->materialKey ^= Zobrist::psq[pc][cnt];
} }
}
/// Position::set() is an overload to initialize the position object with /// Position::set() is an overload to initialize the position object with
@@ -465,18 +419,18 @@ const string Position::fen() const {
ss << (sideToMove == WHITE ? " w " : " b "); ss << (sideToMove == WHITE ? " w " : " b ");
if (can_castle(WHITE_OO)) if (can_castle(WHITE_OO))
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K'); ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO ))) : 'K');
if (can_castle(WHITE_OOO)) if (can_castle(WHITE_OOO))
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q'); ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q');
if (can_castle(BLACK_OO)) if (can_castle(BLACK_OO))
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | KING_SIDE))) : 'k'); ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO ))) : 'k');
if (can_castle(BLACK_OOO)) if (can_castle(BLACK_OOO))
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q'); ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q');
if (!can_castle(WHITE) && !can_castle(BLACK)) if (!can_castle(ANY_CASTLING))
ss << '-'; ss << '-';
ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
@@ -498,14 +452,15 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
Bitboard blockers = 0; Bitboard blockers = 0;
pinners = 0; pinners = 0;
// Snipers are sliders that attack 's' when a piece is removed // Snipers are sliders that attack 's' when a piece and other snipers are removed
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK)) Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
Bitboard occupancy = pieces() ^ snipers;
while (snipers) while (snipers)
{ {
Square sniperSq = pop_lsb(&snipers); Square sniperSq = pop_lsb(&snipers);
Bitboard b = between_bb(s, sniperSq) & pieces(); Bitboard b = between_bb(s, sniperSq) & occupancy;
if (b && !more_than_one(b)) if (b && !more_than_one(b))
{ {
@@ -540,6 +495,7 @@ bool Position::legal(Move m) const {
Color us = sideToMove; Color us = sideToMove;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m);
assert(color_of(moved_piece(m)) == us); assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING)); assert(piece_on(square<KING>(us)) == make_piece(us, KING));
@@ -550,7 +506,6 @@ bool Position::legal(Move m) const {
if (type_of(m) == ENPASSANT) if (type_of(m) == ENPASSANT)
{ {
Square ksq = square<KING>(us); Square ksq = square<KING>(us);
Square to = to_sq(m);
Square capsq = to - pawn_push(us); Square capsq = to - pawn_push(us);
Bitboard occupied = (pieces() ^ from ^ capsq) | to; Bitboard occupied = (pieces() ^ from ^ capsq) | to;
@@ -563,16 +518,35 @@ bool Position::legal(Move m) const {
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); && !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
} }
// If the moving piece is a king, check whether the destination // Castling moves generation does not check if the castling path is clear of
// square is attacked by the opponent. Castling moves are checked // enemy attacks, it is delayed at a later time: now!
// for legality during move generation. if (type_of(m) == CASTLING)
{
// After castling, the rook and king final positions are the same in
// Chess960 as they would be in standard chess.
to = relative_square(us, to > from ? SQ_G1 : SQ_C1);
Direction step = to > from ? WEST : EAST;
for (Square s = to; s != from; s += step)
if (attackers_to(s) & pieces(~us))
return false;
// In case of Chess960, verify that when moving the castling rook we do
// not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
return !chess960
|| !(attacks_bb<ROOK>(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN));
}
// If the moving piece is a king, check whether the destination square is
// attacked by the opponent.
if (type_of(piece_on(from)) == KING) if (type_of(piece_on(from)) == KING)
return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us)); return !(attackers_to(to) & pieces(~us));
// A non-king move is legal if and only if it is not pinned or it // A non-king move is legal if and only if it is not pinned or it
// is moving along the ray towards or away from the king. // is moving along the ray towards or away from the king.
return !(blockers_for_king(us) & from) return !(blockers_for_king(us) & from)
|| aligned(from, to_sq(m), square<KING>(us)); || aligned(from, to, square<KING>(us));
} }
@@ -609,7 +583,7 @@ bool Position::pseudo_legal(const Move m) const {
{ {
// We have already handled promotion moves, so destination // We have already handled promotion moves, so destination
// cannot be on the 8th/1st rank. // cannot be on the 8th/1st rank.
if (rank_of(to) == relative_rank(us, RANK_8)) if ((Rank8BB | Rank1BB) & to)
return false; return false;
if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
@@ -843,9 +817,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
st->nonPawnMaterial[us] += PieceValue[MG][promotion]; st->nonPawnMaterial[us] += PieceValue[MG][promotion];
} }
// Update pawn hash key and prefetch access to pawnsTable // Update pawn hash key
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
prefetch2(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter // Reset rule 50 draw counter
st->rule50 = 0; st->rule50 = 0;
@@ -865,6 +838,25 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// Update king attacks used for fast check detection // Update king attacks used for fast check detection
set_check_info(st); set_check_info(st);
// Calculate the repetition info. It is the ply distance from the previous
// occurrence of the same position, negative in the 3-fold case, or zero
// if the position was not repeated.
st->repetition = 0;
int end = std::min(st->rule50, st->pliesFromNull);
if (end >= 4)
{
StateInfo* stp = st->previous->previous;
for (int i = 4; i <= end; i += 2)
{
stp = stp->previous->previous;
if (stp->key == st->key)
{
st->repetition = stp->repetition ? -i : i;
break;
}
}
}
assert(pos_is_ok()); assert(pos_is_ok());
} }
@@ -952,7 +944,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
} }
/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips /// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips
/// the side to move without executing any move on the board. /// the side to move without executing any move on the board.
void Position::do_null_move(StateInfo& newSt) { void Position::do_null_move(StateInfo& newSt) {
@@ -980,6 +972,8 @@ void Position::do_null_move(StateInfo& newSt) {
set_check_info(st); set_check_info(st);
st->repetition = 0;
assert(pos_is_ok()); assert(pos_is_ok());
} }
@@ -1023,77 +1017,96 @@ bool Position::see_ge(Move m, Value threshold) const {
if (type_of(m) != NORMAL) if (type_of(m) != NORMAL)
return VALUE_ZERO >= threshold; return VALUE_ZERO >= threshold;
Bitboard stmAttackers;
Square from = from_sq(m), to = to_sq(m); Square from = from_sq(m), to = to_sq(m);
PieceType nextVictim = type_of(piece_on(from));
Color us = color_of(piece_on(from));
Color stm = ~us; // First consider opponent's move
Value balance; // Values of the pieces taken by us minus opponent's ones
// The opponent may be able to recapture so this is the best result int swap = PieceValue[MG][piece_on(to)] - threshold;
// we can hope for. if (swap < 0)
balance = PieceValue[MG][piece_on(to)] - threshold;
if (balance < VALUE_ZERO)
return false; return false;
// Now assume the worst possible result: that the opponent can swap = PieceValue[MG][piece_on(from)] - swap;
// capture our piece for free. if (swap <= 0)
balance -= PieceValue[MG][nextVictim];
// If it is enough (like in PxQ) then return immediately. Note that
// in case nextVictim == KING we always return here, this is ok
// if the given move is legal.
if (balance >= VALUE_ZERO)
return true; return true;
// Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it.
Bitboard occupied = pieces() ^ from ^ to; Bitboard occupied = pieces() ^ from ^ to;
Bitboard attackers = attackers_to(to, occupied) & occupied; Color stm = color_of(piece_on(from));
Bitboard attackers = attackers_to(to, occupied);
Bitboard stmAttackers, bb;
int res = 1;
while (true) while (true)
{ {
stmAttackers = attackers & pieces(stm); stm = ~stm;
attackers &= occupied;
// Don't allow pinned pieces to attack (except the king) as long as
// all pinners are on their original square.
if (!(st->pinners[~stm] & ~occupied))
stmAttackers &= ~st->blockersForKing[stm];
// If stm has no more attackers then give up: stm loses // If stm has no more attackers then give up: stm loses
if (!(stmAttackers = attackers & pieces(stm)))
break;
// Don't allow pinned pieces to attack (except the king) as long as
// there are pinners on their original square.
if (st->pinners[~stm] & occupied)
stmAttackers &= ~st->blockersForKing[stm];
if (!stmAttackers) if (!stmAttackers)
break; break;
res ^= 1;
// Locate and remove the next least valuable attacker, and add to // Locate and remove the next least valuable attacker, and add to
// the bitboard 'attackers' the possibly X-ray attackers behind it. // the bitboard 'attackers' any X-ray attackers behind it.
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers); if ((bb = stmAttackers & pieces(PAWN)))
stm = ~stm; // Switch side to move
// Negamax the balance with alpha = balance, beta = balance+1 and
// add nextVictim's value.
//
// (balance, balance+1) -> (-balance-1, -balance)
//
assert(balance < VALUE_ZERO);
balance = -balance - 1 - PieceValue[MG][nextVictim];
// If balance is still non-negative after giving away nextVictim then we
// win. The only thing to be careful about it is that we should revert
// stm if we captured with the king when the opponent still has attackers.
if (balance >= VALUE_ZERO)
{ {
if (nextVictim == KING && (attackers & pieces(stm))) if ((swap = PawnValueMg - swap) < res)
stm = ~stm;
break; break;
}
assert(nextVictim != KING); occupied ^= lsb(bb);
} attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
return us != stm; // We break the above loop when stm loses
} }
else if ((bb = stmAttackers & pieces(KNIGHT)))
{
if ((swap = KnightValueMg - swap) < res)
break;
occupied ^= lsb(bb);
}
else if ((bb = stmAttackers & pieces(BISHOP)))
{
if ((swap = BishopValueMg - swap) < res)
break;
occupied ^= lsb(bb);
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
else if ((bb = stmAttackers & pieces(ROOK)))
{
if ((swap = RookValueMg - swap) < res)
break;
occupied ^= lsb(bb);
attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
}
else if ((bb = stmAttackers & pieces(QUEEN)))
{
if ((swap = QueenValueMg - swap) < res)
break;
occupied ^= lsb(bb);
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
| (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
}
else // KING
// If we "capture" with the king but opponent still has attackers,
// reverse the result.
return (attackers & ~pieces(stm)) ? res ^ 1 : res;
}
return bool(res);
}
/// Position::is_draw() tests whether the position is drawn by 50-move rule /// Position::is_draw() tests whether the position is drawn by 50-move rule
/// or by repetition. It does not detect stalemates. /// or by repetition. It does not detect stalemates.
@@ -1103,24 +1116,10 @@ bool Position::is_draw(int ply) const {
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size())) if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true; return true;
int end = std::min(st->rule50, st->pliesFromNull);
if (end < 4)
return false;
StateInfo* stp = st->previous->previous;
int cnt = 0;
for (int i = 4; i <= end; i += 2)
{
stp = stp->previous->previous;
// Return a draw score if a position repeats once earlier but strictly // Return a draw score if a position repeats once earlier but strictly
// after the root, or repeats twice before or at the root. // after the root, or repeats twice before or at the root.
if ( stp->key == st->key if (st->repetition && st->repetition < ply)
&& ++cnt + (ply > i) == 2)
return true; return true;
}
return false; return false;
} }
@@ -1132,26 +1131,15 @@ bool Position::is_draw(int ply) const {
bool Position::has_repeated() const { bool Position::has_repeated() const {
StateInfo* stc = st; StateInfo* stc = st;
while (true) int end = std::min(st->rule50, st->pliesFromNull);
while (end-- >= 4)
{ {
int i = 4, end = std::min(stc->rule50, stc->pliesFromNull); if (stc->repetition)
if (end < i)
return false;
StateInfo* stp = stc->previous->previous;
do {
stp = stp->previous->previous;
if (stp->key == stc->key)
return true; return true;
i += 2;
} while (i <= end);
stc = stc->previous; stc = stc->previous;
} }
return false;
} }
@@ -1184,25 +1172,22 @@ bool Position::has_game_cycle(int ply) const {
if (!(between_bb(s1, s2) & pieces())) if (!(between_bb(s1, s2) & pieces()))
{ {
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same
// location. We select the legal one by reversing the move variable if necessary.
if (empty(s1))
move = make_move(s2, s1);
if (ply > i) if (ply > i)
return true; return true;
// For nodes before or at the root, check that the move is a
// repetition rather than a move to the current position.
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
// the same location, so we have to select which square to check.
if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
continue;
// For repetitions before or at the root, require one more // For repetitions before or at the root, require one more
StateInfo* next_stp = stp; if (stp->repetition)
for (int k = i + 2; k <= end; k += 2)
{
next_stp = next_stp->previous->previous;
if (next_stp->key == stp->key)
return true; return true;
} }
} }
} }
}
return false; return false;
} }
@@ -1297,15 +1282,15 @@ bool Position::pos_is_ok() const {
assert(0 && "pos_is_ok: Index"); assert(0 && "pos_is_ok: Index");
} }
for (Color c = WHITE; c <= BLACK; ++c) for (Color c : { WHITE, BLACK })
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
{ {
if (!can_castle(c | s)) if (!can_castle(cr))
continue; continue;
if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK) if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
|| castlingRightsMask[castlingRookSquare[c | s]] != (c | s) || castlingRightsMask[castlingRookSquare[cr]] != cr
|| (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s)) || (castlingRightsMask[square<KING>(c)] & cr) != cr)
assert(0 && "pos_is_ok: Castling"); assert(0 && "pos_is_ok: Castling");
} }

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -53,6 +53,7 @@ struct StateInfo {
Bitboard blockersForKing[COLOR_NB]; Bitboard blockersForKing[COLOR_NB];
Bitboard pinners[COLOR_NB]; Bitboard pinners[COLOR_NB];
Bitboard checkSquares[PIECE_TYPE_NB]; Bitboard checkSquares[PIECE_TYPE_NB];
int repetition;
}; };
/// A list to keep track of the position states along the setup moves (from the /// A list to keep track of the position states along the setup moves (from the
@@ -95,17 +96,19 @@ public:
template<PieceType Pt> int count() const; template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(Color c) const; template<PieceType Pt> const Square* squares(Color c) const;
template<PieceType Pt> Square square(Color c) const; template<PieceType Pt> Square square(Color c) const;
bool is_on_semiopen_file(Color c, Square s) const;
// Castling // Castling
int can_castle(Color c) const; int castling_rights(Color c) const;
int can_castle(CastlingRight cr) const; bool can_castle(CastlingRights cr) const;
bool castling_impeded(CastlingRight cr) const; bool castling_impeded(CastlingRights cr) const;
Square castling_rook_square(CastlingRight cr) const; Square castling_rook_square(CastlingRights cr) const;
// Checking // Checking
Bitboard checkers() const; Bitboard checkers() const;
Bitboard blockers_for_king(Color c) const; Bitboard blockers_for_king(Color c) const;
Bitboard check_squares(PieceType pt) const; Bitboard check_squares(PieceType pt) const;
bool is_discovery_check_on_king(Color c, Move m) const;
// Attacks to/from a given square // Attacks to/from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
@@ -128,6 +131,7 @@ public:
// Piece specific // Piece specific
bool pawn_passed(Color c, Square s) const; bool pawn_passed(Color c, Square s) const;
bool opposite_bishops() const; bool opposite_bishops() const;
int pawns_on_same_color_squares(Color c, Square s) const;
// Doing and undoing moves // Doing and undoing moves
void do_move(Move m, StateInfo& newSt); void do_move(Move m, StateInfo& newSt);
@@ -260,25 +264,34 @@ inline Square Position::ep_square() const {
return st->epSquare; return st->epSquare;
} }
inline int Position::can_castle(CastlingRight cr) const { inline bool Position::is_on_semiopen_file(Color c, Square s) const {
return !(pieces(c, PAWN) & file_bb(s));
}
inline bool Position::can_castle(CastlingRights cr) const {
return st->castlingRights & cr; return st->castlingRights & cr;
} }
inline int Position::can_castle(Color c) const { inline int Position::castling_rights(Color c) const {
return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING);
} }
inline bool Position::castling_impeded(CastlingRight cr) const { inline bool Position::castling_impeded(CastlingRights cr) const {
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
return byTypeBB[ALL_PIECES] & castlingPath[cr]; return byTypeBB[ALL_PIECES] & castlingPath[cr];
} }
inline Square Position::castling_rook_square(CastlingRight cr) const { inline Square Position::castling_rook_square(CastlingRights cr) const {
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
return castlingRookSquare[cr]; return castlingRookSquare[cr];
} }
template<PieceType Pt> template<PieceType Pt>
inline Bitboard Position::attacks_from(Square s) const { inline Bitboard Position::attacks_from(Square s) const {
assert(Pt != PAWN); static_assert(Pt != PAWN, "Pawn attacks need color");
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES]) return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s) : Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
: PseudoAttacks[Pt][s]; : PseudoAttacks[Pt][s];
@@ -309,13 +322,21 @@ inline Bitboard Position::check_squares(PieceType pt) const {
return st->checkSquares[pt]; return st->checkSquares[pt];
} }
inline bool Position::is_discovery_check_on_king(Color c, Move m) const {
return st->blockersForKing[c] & from_sq(m);
}
inline bool Position::pawn_passed(Color c, Square s) const { inline bool Position::pawn_passed(Color c, Square s) const {
return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); return !(pieces(~c, PAWN) & passed_pawn_span(c, s));
} }
inline bool Position::advanced_pawn_push(Move m) const { inline bool Position::advanced_pawn_push(Move m) const {
return type_of(moved_piece(m)) == PAWN return type_of(moved_piece(m)) == PAWN
&& relative_rank(sideToMove, from_sq(m)) > RANK_4; && relative_rank(sideToMove, to_sq(m)) > RANK_5;
}
inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares));
} }
inline Key Position::key() const { inline Key Position::key() const {
@@ -413,7 +434,7 @@ inline void Position::move_piece(Piece pc, Square from, Square to) {
// index[from] is not updated and becomes stale. This works as long as index[] // index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares. // is accessed just by known occupied squares.
Bitboard fromTo = SquareBB[from] ^ SquareBB[to]; Bitboard fromTo = from | to;
byTypeBB[ALL_PIECES] ^= fromTo; byTypeBB[ALL_PIECES] ^= fromTo;
byTypeBB[type_of(pc)] ^= fromTo; byTypeBB[type_of(pc)] ^= fromTo;
byColorBB[color_of(pc)] ^= fromTo; byColorBB[color_of(pc)] ^= fromTo;

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -37,44 +37,36 @@ namespace PSQT {
// second half of the files. // second half of the files.
constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ }, { },
{ // Pawn { },
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
{ S(-11,-3), S( 7, -1), S( 7, 7), S(17, 2) },
{ S(-16,-2), S( -3, 2), S( 23, 6), S(23,-1) },
{ S(-14, 7), S( -7, -4), S( 20,-8), S(24, 2) },
{ S( -5,13), S( -2, 10), S( -1,-1), S(12,-8) },
{ S(-11,16), S(-12, 6), S( -2, 1), S( 4,16) },
{ S( -2, 1), S( 20,-12), S(-10, 6), S(-2,25) }
},
{ // Knight { // Knight
{ S(-169,-105), S(-96,-74), S(-80,-46), S(-79,-18) }, { S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) },
{ S( -79, -70), S(-39,-56), S(-24,-15), S( -9, 6) }, { S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) },
{ S( -64, -38), S(-20,-33), S( 4, -5), S( 19, 27) }, { S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) },
{ S( -28, -36), S( 5, 0), S( 41, 13), S( 47, 34) }, { S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) },
{ S( -29, -41), S( 13,-20), S( 42, 4), S( 52, 35) }, { S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) },
{ S( -11, -51), S( 28,-38), S( 63,-17), S( 55, 19) }, { S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) },
{ S( -67, -64), S(-21,-45), S( 6,-37), S( 37, 16) }, { S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) },
{ S(-200, -98), S(-80,-89), S(-53,-53), S(-32,-16) } { S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
}, },
{ // Bishop { // Bishop
{ S(-49,-58), S(- 7,-31), S(-10,-37), S(-34,-19) }, { S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) },
{ S(-24,-34), S( 9, -9), S( 15,-14), S( 1, 4) }, { S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) },
{ S( -9,-23), S( 22, 0), S( -3, -3), S( 12, 16) }, { S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) },
{ S( 4,-26), S( 9, -3), S( 18, -5), S( 40, 16) }, { S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) },
{ S( -8,-26), S( 27, -4), S( 13, -7), S( 30, 14) }, { S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) },
{ S(-17,-24), S( 14, -2), S( -6, 0), S( 6, 13) }, { S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) },
{ S(-19,-34), S(-13,-10), S( 7,-12), S(-11, 6) }, { S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) },
{ S(-47,-55), S( -7,-32), S(-17,-36), S(-29,-17) } { S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) }
}, },
{ // Rook { // Rook
{ S(-24, 0), S(-15, 3), S( -8, 0), S( 0, 3) }, { S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
{ S(-18,-7), S( -5,-5), S( -1,-5), S( 1,-1) }, { S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) },
{ S(-19, 6), S(-10,-7), S( 1, 3), S( 0, 3) }, { S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) },
{ S(-21, 0), S( -7, 4), S( -4,-2), S(-4, 1) }, { S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) },
{ S(-21,-7), S(-12, 5), S( -1,-5), S( 4,-7) }, { S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) },
{ S(-23, 3), S(-10, 2), S( 1,-1), S( 6, 3) }, { S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) },
{ S(-11,-1), S( 8, 7), S( 9,11), S(12,-1) }, { S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) },
{ S(-25, 6), S(-18, 4), S(-11, 6), S( 2, 2) } { S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) }
}, },
{ // Queen { // Queen
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
@@ -87,17 +79,28 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) } { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
}, },
{ // King { // King
{ S(272, 0), S(325, 41), S(273, 80), S(190, 93) }, { S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
{ S(277, 57), S(305, 98), S(241,138), S(183,131) }, { S(278, 53), S(303,100), S(234,133), S(179,135) },
{ S(198, 86), S(253,138), S(168,165), S(120,173) }, { S(195, 88), S(258,130), S(169,169), S(120,175) },
{ S(169,103), S(191,152), S(136,168), S(108,169) }, { S(164,103), S(190,156), S(138,172), S( 98,172) },
{ S(145, 98), S(176,166), S(112,197), S(69, 194) }, { S(154, 96), S(179,166), S(105,199), S( 70,199) },
{ S(122, 87), S(159,164), S(85, 174), S(36, 189) }, { S(123, 92), S(145,172), S( 81,184), S( 31,191) },
{ S(87, 40), S(120, 99), S(64, 128), S(25, 141) }, { S( 88, 47), S(120,121), S( 65,116), S( 33,131) },
{ S(64, 5), S(87, 60), S(49, 75), S(0, 75) } { S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) }
} }
}; };
constexpr Score PBonus[RANK_NB][FILE_NB] =
{ // Pawn (asymmetric distribution)
{ },
{ S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) },
{ S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) },
{ S( -8, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S(-12, -9) },
{ S( 13, 9), S( 0, 4), S(-13, 3), S( 1,-12), S( 11,-12), S( -2, -6), S(-13, 13), S( 5, 8) },
{ S( -5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S(-18, 13) },
{ S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) }
};
#undef S #undef S
Score psq[PIECE_NB][SQUARE_NB]; Score psq[PIECE_NB][SQUARE_NB];
@@ -116,8 +119,9 @@ void init() {
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
{ {
File f = std::min(file_of(s), ~file_of(s)); File f = map_to_queenside(file_of(s));
psq[ pc][ s] = score + Bonus[pc][rank_of(s)][f]; psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
: Bonus[pc][rank_of(s)][f]);
psq[~pc][~s] = -psq[pc][s]; psq[~pc][~s] = -psq[pc][s];
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -69,7 +69,8 @@ struct RootMove {
Value score = -VALUE_INFINITE; Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE;
int selDepth = 0; int selDepth = 0;
int tbRank; int tbRank = 0;
int bestMoveCount = 0;
Value tbScore; Value tbScore;
std::vector<Move> pv; std::vector<Move> pv;
}; };

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (c) 2013 Ronald de Man Copyright (c) 2013 Ronald de Man
Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,12 +27,12 @@
#include <list> #include <list>
#include <sstream> #include <sstream>
#include <type_traits> #include <type_traits>
#include <mutex>
#include "../bitboard.h" #include "../bitboard.h"
#include "../movegen.h" #include "../movegen.h"
#include "../position.h" #include "../position.h"
#include "../search.h" #include "../search.h"
#include "../thread_win32.h"
#include "../types.h" #include "../types.h"
#include "../uci.h" #include "../uci.h"
@@ -45,7 +45,9 @@
#include <sys/stat.h> #include <sys/stat.h>
#else #else
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define NOMINMAX #ifndef NOMINMAX
# define NOMINMAX // Disable macros min() and max()
#endif
#include <windows.h> #include <windows.h>
#endif #endif
@@ -214,39 +216,57 @@ public:
return *baseAddress = nullptr, nullptr; return *baseAddress = nullptr, nullptr;
fstat(fd, &statbuf); fstat(fd, &statbuf);
if (statbuf.st_size % 64 != 16)
{
std::cerr << "Corrupt tablebase file " << fname << std::endl;
exit(EXIT_FAILURE);
}
*mapping = statbuf.st_size; *mapping = statbuf.st_size;
*baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
madvise(*baseAddress, statbuf.st_size, MADV_RANDOM); madvise(*baseAddress, statbuf.st_size, MADV_RANDOM);
::close(fd); ::close(fd);
if (*baseAddress == MAP_FAILED) { if (*baseAddress == MAP_FAILED)
{
std::cerr << "Could not mmap() " << fname << std::endl; std::cerr << "Could not mmap() " << fname << std::endl;
exit(1); exit(EXIT_FAILURE);
} }
#else #else
// Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored.
HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
if (fd == INVALID_HANDLE_VALUE) if (fd == INVALID_HANDLE_VALUE)
return *baseAddress = nullptr, nullptr; return *baseAddress = nullptr, nullptr;
DWORD size_high; DWORD size_high;
DWORD size_low = GetFileSize(fd, &size_high); DWORD size_low = GetFileSize(fd, &size_high);
if (size_low % 64 != 16)
{
std::cerr << "Corrupt tablebase file " << fname << std::endl;
exit(EXIT_FAILURE);
}
HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr); HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr);
CloseHandle(fd); CloseHandle(fd);
if (!mmap) { if (!mmap)
{
std::cerr << "CreateFileMapping() failed" << std::endl; std::cerr << "CreateFileMapping() failed" << std::endl;
exit(1); exit(EXIT_FAILURE);
} }
*mapping = (uint64_t)mmap; *mapping = (uint64_t)mmap;
*baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0); *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
if (!*baseAddress) { if (!*baseAddress)
{
std::cerr << "MapViewOfFile() failed, name = " << fname std::cerr << "MapViewOfFile() failed, name = " << fname
<< ", error = " << GetLastError() << std::endl; << ", error = " << GetLastError() << std::endl;
exit(1); exit(EXIT_FAILURE);
} }
#endif #endif
uint8_t* data = (uint8_t*)*baseAddress; uint8_t* data = (uint8_t*)*baseAddress;
@@ -254,7 +274,8 @@ public:
constexpr uint8_t Magics[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 }, constexpr uint8_t Magics[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 },
{ 0x71, 0xE8, 0x23, 0x5D } }; { 0x71, 0xE8, 0x23, 0x5D } };
if (memcmp(data, Magics[type == WDL], 4)) { if (memcmp(data, Magics[type == WDL], 4))
{
std::cerr << "Corrupted table in file " << fname << std::endl; std::cerr << "Corrupted table in file " << fname << std::endl;
unmap(*baseAddress, *mapping); unmap(*baseAddress, *mapping);
return *baseAddress = nullptr, nullptr; return *baseAddress = nullptr, nullptr;
@@ -348,7 +369,7 @@ TBTable<WDL>::TBTable(const std::string& code) : TBTable() {
hasPawns = pos.pieces(PAWN); hasPawns = pos.pieces(PAWN);
hasUniquePieces = false; hasUniquePieces = false;
for (Color c = WHITE; c <= BLACK; ++c) for (Color c : { WHITE, BLACK })
for (PieceType pt = PAWN; pt < KING; ++pt) for (PieceType pt = PAWN; pt < KING; ++pt)
if (popcount(pos.pieces(c, pt)) == 1) if (popcount(pos.pieces(c, pt)) == 1)
hasUniquePieces = true; hasUniquePieces = true;
@@ -415,7 +436,7 @@ class TBTables {
} }
} }
std::cerr << "TB hash table size too low!" << std::endl; std::cerr << "TB hash table size too low!" << std::endl;
exit(1); exit(EXIT_FAILURE);
} }
public: public:
@@ -664,7 +685,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
bool blackStronger = (pos.material_key() != entry->key); bool blackStronger = (pos.material_key() != entry->key);
int flipColor = (symmetricBlackToMove || blackStronger) * 8; int flipColor = (symmetricBlackToMove || blackStronger) * 8;
int flipSquares = (symmetricBlackToMove || blackStronger) * 070; int flipSquares = (symmetricBlackToMove || blackStronger) * 56;
int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move(); int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move();
// For pawns, TB files store 4 separate tables according if leading pawn is on // For pawns, TB files store 4 separate tables according if leading pawn is on
@@ -687,9 +708,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp)); std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp));
tbFile = file_of(squares[0]); tbFile = map_to_queenside(file_of(squares[0]));
if (tbFile > FILE_D)
tbFile = file_of(squares[0] ^ 7); // Horizontal flip: SQ_H1 -> SQ_A1
} }
// DTZ tables are one-sided, i.e. they store positions only for white to // DTZ tables are one-sided, i.e. they store positions only for white to
@@ -713,8 +732,8 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
// Then we reorder the pieces to have the same sequence as the one stored // Then we reorder the pieces to have the same sequence as the one stored
// in pieces[i]: the sequence that ensures the best compression. // in pieces[i]: the sequence that ensures the best compression.
for (int i = leadPawnsCnt; i < size; ++i) for (int i = leadPawnsCnt; i < size - 1; ++i)
for (int j = i; j < size; ++j) for (int j = i + 1; j < size; ++j)
if (d->pieces[i] == pieces[j]) if (d->pieces[i] == pieces[j])
{ {
std::swap(pieces[i], pieces[j]); std::swap(pieces[i], pieces[j]);
@@ -745,7 +764,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
// piece is below RANK_5. // piece is below RANK_5.
if (rank_of(squares[0]) > RANK_4) if (rank_of(squares[0]) > RANK_4)
for (int i = 0; i < size; ++i) for (int i = 0; i < size; ++i)
squares[i] ^= 070; // Vertical flip: SQ_A8 -> SQ_A1 squares[i] ^= SQ_A8; // Vertical flip: SQ_A8 -> SQ_A1
// Look for the first piece of the leading group not on the A1-D4 diagonal // Look for the first piece of the leading group not on the A1-D4 diagonal
// and ensure it is mapped below the diagonal. // and ensure it is mapped below the diagonal.
@@ -1043,8 +1062,8 @@ void set(T& e, uint8_t* data) {
enum { Split = 1, HasPawns = 2 }; enum { Split = 1, HasPawns = 2 };
assert(e.hasPawns == !!(*data & HasPawns)); assert(e.hasPawns == bool(*data & HasPawns));
assert((e.key != e.key2) == !!(*data & Split)); assert((e.key != e.key2) == bool(*data & Split));
data++; // First byte stores flags data++; // First byte stores flags
@@ -1107,14 +1126,14 @@ void set(T& e, uint8_t* data) {
template<TBType Type> template<TBType Type>
void* mapped(TBTable<Type>& e, const Position& pos) { void* mapped(TBTable<Type>& e, const Position& pos) {
static Mutex mutex; static std::mutex mutex;
// Use 'aquire' to avoid a thread reads 'ready' == true while another is // Use 'acquire' to avoid a thread reading 'ready' == true while
// still working, this could happen due to compiler reordering. // another is still working. (compiler reordering may cause this).
if (e.ready.load(std::memory_order_acquire)) if (e.ready.load(std::memory_order_acquire))
return e.baseAddress; // Could be nullptr if file does not exsist return e.baseAddress; // Could be nullptr if file does not exist
std::unique_lock<Mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock
return e.baseAddress; return e.baseAddress;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (c) 2013 Ronald de Man Copyright (c) 2013 Ronald de Man
Copyright (C) 2016-2018 Marco Costalba, Lucas Braesch Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by 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 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,9 +18,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm> // For std::count
#include <cassert> #include <cassert>
#include <algorithm> // For std::count
#include "movegen.h" #include "movegen.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
@@ -32,7 +32,7 @@ ThreadPool Threads; // Global object
/// Thread constructor launches the thread and waits until it goes to sleep /// Thread constructor launches the thread and waits until it goes to sleep
/// in idle_loop(). Note that 'searching' and 'exit' should be alredy set. /// in idle_loop(). Note that 'searching' and 'exit' should be already set.
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
@@ -52,6 +52,15 @@ Thread::~Thread() {
stdThread.join(); stdThread.join();
} }
/// Thread::bestMoveCount(Move move) return best move counter for the given root move
int Thread::best_move_count(Move move) {
auto rm = std::find(rootMoves.begin() + pvIdx,
rootMoves.begin() + pvLast, move);
return rm != rootMoves.begin() + pvLast ? rm->bestMoveCount : 0;
}
/// Thread::clear() reset histories, usually before a new game /// Thread::clear() reset histories, usually before a new game
@@ -61,18 +70,22 @@ void Thread::clear() {
mainHistory.fill(0); mainHistory.fill(0);
captureHistory.fill(0); captureHistory.fill(0);
for (auto& to : continuationHistory) for (bool inCheck : { false, true })
for (StatsType c : { NoCaptures, Captures })
for (auto& to : continuationHistory[inCheck][c])
for (auto& h : to) for (auto& h : to)
h->fill(0); h->fill(0);
continuationHistory[NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); for (bool inCheck : { false, true })
for (StatsType c : { NoCaptures, Captures })
continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1);
} }
/// Thread::start_searching() wakes up the thread that will start the search /// Thread::start_searching() wakes up the thread that will start the search
void Thread::start_searching() { void Thread::start_searching() {
std::lock_guard<Mutex> lk(mutex); std::lock_guard<std::mutex> lk(mutex);
searching = true; searching = true;
cv.notify_one(); // Wake up the thread in idle_loop() cv.notify_one(); // Wake up the thread in idle_loop()
} }
@@ -83,7 +96,7 @@ void Thread::start_searching() {
void Thread::wait_for_search_finished() { void Thread::wait_for_search_finished() {
std::unique_lock<Mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
cv.wait(lk, [&]{ return !searching; }); cv.wait(lk, [&]{ return !searching; });
} }
@@ -103,7 +116,7 @@ void Thread::idle_loop() {
while (true) while (true)
{ {
std::unique_lock<Mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
searching = false; searching = false;
cv.notify_one(); // Wake up anyone waiting for search finished cv.notify_one(); // Wake up anyone waiting for search finished
cv.wait(lk, [&]{ return searching; }); cv.wait(lk, [&]{ return searching; });
@@ -118,7 +131,7 @@ void Thread::idle_loop() {
} }
/// ThreadPool::set() creates/destroys threads to match the requested number. /// ThreadPool::set() creates/destroys threads to match the requested number.
/// Created and launched threads will go immediately to sleep in idle_loop. /// Created and launched threads will immediately go to sleep in idle_loop.
/// Upon resizing, threads are recreated to allow for binding if necessary. /// Upon resizing, threads are recreated to allow for binding if necessary.
void ThreadPool::set(size_t requested) { void ThreadPool::set(size_t requested) {
@@ -136,10 +149,13 @@ void ThreadPool::set(size_t requested) {
while (size() < requested) while (size() < requested)
push_back(new Thread(size())); push_back(new Thread(size()));
clear(); clear();
}
// Reallocate the hash with the new threadpool size // Reallocate the hash with the new threadpool size
TT.resize(Options["Hash"]); TT.resize(Options["Hash"]);
// Init thread number dependent search params.
Search::init();
}
} }
/// ThreadPool::clear() sets threadPool data to initial values. /// ThreadPool::clear() sets threadPool data to initial values.
@@ -162,8 +178,9 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
main()->wait_for_search_finished(); main()->wait_for_search_finished();
stopOnPonderhit = stop = false; main()->stopOnPonderhit = stop = false;
ponder = ponderMode; increaseDepth = true;
main()->ponder = ponderMode;
Search::Limits = limits; Search::Limits = limits;
Search::RootMoves rootMoves; Search::RootMoves rootMoves;
@@ -192,7 +209,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
for (Thread* th : *this) for (Thread* th : *this)
{ {
th->nodes = th->tbHits = th->nmpMinPly = 0; th->nodes = th->tbHits = th->nmpMinPly = 0;
th->rootDepth = th->completedDepth = DEPTH_ZERO; th->rootDepth = th->completedDepth = 0;
th->rootMoves = rootMoves; th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); 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 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -32,7 +32,7 @@
#include "pawns.h" #include "pawns.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread_win32.h" #include "thread_win32_osx.h"
/// Thread class keeps together all the thread-related stuff. We use /// Thread class keeps together all the thread-related stuff. We use
@@ -42,11 +42,11 @@
class Thread { class Thread {
Mutex mutex; std::mutex mutex;
ConditionVariable cv; std::condition_variable cv;
size_t idx; size_t idx;
bool exit = false, searching = true; // Set before starting std::thread bool exit = false, searching = true; // Set before starting std::thread
std::thread stdThread; NativeThread stdThread;
public: public:
explicit Thread(size_t); explicit Thread(size_t);
@@ -56,14 +56,15 @@ public:
void idle_loop(); void idle_loop();
void start_searching(); void start_searching();
void wait_for_search_finished(); void wait_for_search_finished();
int best_move_count(Move move);
Pawns::Table pawnsTable; Pawns::Table pawnsTable;
Material::Table materialTable; Material::Table materialTable;
Endgames endgames;
size_t pvIdx, pvLast; size_t pvIdx, pvLast;
uint64_t ttHitAverage;
int selDepth, nmpMinPly; int selDepth, nmpMinPly;
Color nmpColor; Color nmpColor;
std::atomic<uint64_t> nodes, tbHits; std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
Position rootPos; Position rootPos;
Search::RootMoves rootMoves; Search::RootMoves rootMoves;
@@ -71,7 +72,7 @@ public:
CounterMoveHistory counterMoves; CounterMoveHistory counterMoves;
ButterflyHistory mainHistory; ButterflyHistory mainHistory;
CapturePieceToHistory captureHistory; CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory; ContinuationHistory continuationHistory[2][2];
Score contempt; Score contempt;
}; };
@@ -85,9 +86,12 @@ struct MainThread : public Thread {
void search() override; void search() override;
void check_time(); void check_time();
double bestMoveChanges, previousTimeReduction; double previousTimeReduction;
Value previousScore; Value previousScore;
Value iterValue[4];
int callsCnt; int callsCnt;
bool stopOnPonderhit;
std::atomic_bool ponder;
}; };
@@ -105,7 +109,7 @@ struct ThreadPool : public std::vector<Thread*> {
uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
std::atomic_bool stop, ponder, stopOnPonderhit; std::atomic_bool stop, increaseDepth;
private: private:
StateListPtr setupStates; StateListPtr setupStates;

View File

@@ -1,70 +0,0 @@
/*
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-2019 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef THREAD_WIN32_H_INCLUDED
#define THREAD_WIN32_H_INCLUDED
/// STL thread library used by mingw and gcc when cross compiling for Windows
/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
/// mode transition in order to lock or unlock, which is very slow compared to
/// interlocked operations (about 30% slower on bench test). To work around this
/// issue, we define our wrappers to the low level Win32 calls. We use critical
/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
/// is racy between unlock() and WaitForSingleObject() but they have the same
/// speed performance as the SRW locks.
#include <condition_variable>
#include <mutex>
#if defined(_WIN32) && !defined(_MSC_VER)
#ifndef NOMINMAX
# define NOMINMAX // Disable macros min() and max()
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
/// Mutex and ConditionVariable struct are wrappers of the low level locking
/// machinery and are modeled after the corresponding C++11 classes.
struct Mutex {
Mutex() { InitializeCriticalSection(&cs); }
~Mutex() { DeleteCriticalSection(&cs); }
void lock() { EnterCriticalSection(&cs); }
void unlock() { LeaveCriticalSection(&cs); }
private:
CRITICAL_SECTION cs;
};
typedef std::condition_variable_any ConditionVariable;
#else // Default case: use STL classes
typedef std::mutex Mutex;
typedef std::condition_variable ConditionVariable;
#endif
#endif // #ifndef THREAD_WIN32_H_INCLUDED

View File

@@ -0,0 +1,68 @@
/*
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-2020 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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef THREAD_WIN32_OSX_H_INCLUDED
#define THREAD_WIN32_OSX_H_INCLUDED
#include <thread>
/// On OSX threads other than the main thread are created with a reduced stack
/// size of 512KB by default, this is too low for deep searches, which require
/// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
/// The implementation calls pthread_create() with the stack size parameter
/// equal to the linux 8MB default, on platforms that support it.
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__)
#include <pthread.h>
static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
template <class T, class P = std::pair<T*, void(T::*)()>>
void* start_routine(void* ptr)
{
P* p = reinterpret_cast<P*>(ptr);
(p->first->*(p->second))(); // Call member function pointer
delete p;
return NULL;
}
class NativeThread {
pthread_t thread;
public:
template<class T, class P = std::pair<T*, void(T::*)()>>
explicit NativeThread(void(T::*fun)(), T* obj) {
pthread_attr_t attr_storage, *attr = &attr_storage;
pthread_attr_init(attr);
pthread_attr_setstacksize(attr, TH_STACK_SIZE);
pthread_create(&thread, attr, start_routine<T>, new P(obj, fun));
}
void join() { pthread_join(thread, NULL); }
};
#else // Default case: use STL classes
typedef std::thread NativeThread;
#endif
#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by 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 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by 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 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -24,15 +24,16 @@
#include "bitboard.h" #include "bitboard.h"
#include "misc.h" #include "misc.h"
#include "thread.h"
#include "tt.h" #include "tt.h"
#include "uci.h" #include "uci.h"
TranspositionTable TT; // Our global transposition table TranspositionTable TT; // Our global transposition table
/// TTEntry::save saves a TTEntry /// TTEntry::save populates the TTEntry with a new node's data, possibly
void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) { /// overwriting an old position. Update is not atomic and can be racy.
assert(d / ONE_PLY * ONE_PLY == d); void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
// Preserve any existing move for the same position // Preserve any existing move for the same position
if (m || (k >> 48) != key16) if (m || (k >> 48) != key16)
@@ -40,14 +41,16 @@ void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) {
// Overwrite less valuable entries // Overwrite less valuable entries
if ( (k >> 48) != key16 if ( (k >> 48) != key16
|| d / ONE_PLY > depth8 - 4 || d - DEPTH_OFFSET > depth8 - 4
|| b == BOUND_EXACT) || b == BOUND_EXACT)
{ {
assert(d >= DEPTH_OFFSET);
key16 = (uint16_t)(k >> 48); key16 = (uint16_t)(k >> 48);
value16 = (int16_t)v; value16 = (int16_t)v;
eval16 = (int16_t)ev; eval16 = (int16_t)ev;
genBound8 = (uint8_t)(TT.generation8 | b); genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
depth8 = (int8_t)(d / ONE_PLY); depth8 = (uint8_t)(d - DEPTH_OFFSET);
} }
} }
@@ -58,6 +61,8 @@ void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) {
void TranspositionTable::resize(size_t mbSize) { void TranspositionTable::resize(size_t mbSize) {
Threads.main()->wait_for_search_finished();
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
free(mem); free(mem);
@@ -82,7 +87,7 @@ void TranspositionTable::clear() {
std::vector<std::thread> threads; std::vector<std::thread> threads;
for (size_t idx = 0; idx < Options["Threads"]; idx++) for (size_t idx = 0; idx < Options["Threads"]; ++idx)
{ {
threads.emplace_back([this, idx]() { threads.emplace_back([this, idx]() {
@@ -119,7 +124,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
for (int i = 0; i < ClusterSize; ++i) for (int i = 0; i < ClusterSize; ++i)
if (!tte[i].key16 || tte[i].key16 == key16) if (!tte[i].key16 || tte[i].key16 == key16)
{ {
tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh
return found = (bool)tte[i].key16, &tte[i]; return found = (bool)tte[i].key16, &tte[i];
} }
@@ -128,11 +133,11 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
TTEntry* replace = tte; TTEntry* replace = tte;
for (int i = 1; i < ClusterSize; ++i) for (int i = 1; i < ClusterSize; ++i)
// Due to our packed storage format for generation and its cyclic // Due to our packed storage format for generation and its cyclic
// nature we add 259 (256 is the modulus plus 3 to keep the lowest // nature we add 263 (256 is the modulus plus 7 to keep the unrelated
// two bound bits from affecting the result) to calculate the entry // lowest three bits from affecting the result) to calculate the entry
// age correctly even after generation8 overflows into the next cycle. // age correctly even after generation8 overflows into the next cycle.
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8)
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2) > tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8))
replace = &tte[i]; replace = &tte[i];
return found = false, replace; return found = false, replace;
@@ -145,12 +150,9 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
int TranspositionTable::hashfull() const { int TranspositionTable::hashfull() const {
int cnt = 0; int cnt = 0;
for (int i = 0; i < 1000 / ClusterSize; i++) for (int i = 0; i < 1000 / ClusterSize; ++i)
{ for (int j = 0; j < ClusterSize; ++j)
const TTEntry* tte = &table[i].entry[0]; cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8;
for (int j = 0; j < ClusterSize; j++)
if ((tte[j].genBound8 & 0xFC) == generation8) return cnt * 1000 / (ClusterSize * (1000 / ClusterSize));
cnt++;
}
return cnt;
} }

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -30,7 +30,8 @@
/// move 16 bit /// move 16 bit
/// value 16 bit /// value 16 bit
/// eval value 16 bit /// eval value 16 bit
/// generation 6 bit /// generation 5 bit
/// pv node 1 bit
/// bound type 2 bit /// bound type 2 bit
/// depth 8 bit /// depth 8 bit
@@ -39,9 +40,10 @@ struct TTEntry {
Move move() const { return (Move )move16; } Move move() const { return (Move )move16; }
Value value() const { return (Value)value16; } Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; } Value eval() const { return (Value)eval16; }
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); } Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; }
bool is_pv() const { return (bool)(genBound8 & 0x4); }
Bound bound() const { return (Bound)(genBound8 & 0x3); } Bound bound() const { return (Bound)(genBound8 & 0x3); }
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev); void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev);
private: private:
friend class TranspositionTable; friend class TranspositionTable;
@@ -51,7 +53,7 @@ private:
int16_t value16; int16_t value16;
int16_t eval16; int16_t eval16;
uint8_t genBound8; uint8_t genBound8;
int8_t depth8; uint8_t depth8;
}; };
@@ -76,7 +78,7 @@ class TranspositionTable {
public: public:
~TranspositionTable() { free(mem); } ~TranspositionTable() { free(mem); }
void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound
TTEntry* probe(const Key key, bool& found) const; TTEntry* probe(const Key key, bool& found) const;
int hashfull() const; int hashfull() const;
void resize(size_t mbSize); void resize(size_t mbSize);

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -43,6 +43,7 @@
#include <climits> #include <climits>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <algorithm>
#if defined(_MSC_VER) #if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler // Disable some silly and noisy warning from MSVC compiler
@@ -101,7 +102,7 @@ typedef uint64_t Key;
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
constexpr int MAX_MOVES = 256; constexpr int MAX_MOVES = 256;
constexpr int MAX_PLY = 128; constexpr int MAX_PLY = 246;
/// A move needs 16 bits to be stored /// A move needs 16 bits to be stored
/// ///
@@ -131,17 +132,19 @@ enum Color {
WHITE, BLACK, COLOR_NB = 2 WHITE, BLACK, COLOR_NB = 2
}; };
enum CastlingSide { enum CastlingRights {
KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2
};
enum CastlingRight {
NO_CASTLING, NO_CASTLING,
WHITE_OO, WHITE_OO,
WHITE_OOO = WHITE_OO << 1, WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2, BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3, BLACK_OOO = WHITE_OO << 3,
ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
KING_SIDE = WHITE_OO | BLACK_OO,
QUEEN_SIDE = WHITE_OOO | BLACK_OOO,
WHITE_CASTLING = WHITE_OO | WHITE_OOO,
BLACK_CASTLING = BLACK_OO | BLACK_OOO,
ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING,
CASTLING_RIGHT_NB = 16 CASTLING_RIGHT_NB = 16
}; };
@@ -176,11 +179,11 @@ enum Value : int {
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
PawnValueMg = 136, PawnValueEg = 208, PawnValueMg = 128, PawnValueEg = 213,
KnightValueMg = 782, KnightValueEg = 865, KnightValueMg = 781, KnightValueEg = 854,
BishopValueMg = 830, BishopValueEg = 918, BishopValueMg = 825, BishopValueEg = 915,
RookValueMg = 1289, RookValueEg = 1378, RookValueMg = 1276, RookValueEg = 1380,
QueenValueMg = 2529, QueenValueEg = 2687, QueenValueMg = 2538, QueenValueEg = 2682,
MidgameLimit = 15258, EndgameLimit = 3915 MidgameLimit = 15258, EndgameLimit = 3915
}; };
@@ -200,21 +203,18 @@ enum Piece {
extern Value PieceValue[PHASE_NB][PIECE_NB]; extern Value PieceValue[PHASE_NB][PIECE_NB];
enum Depth : int { typedef int Depth;
ONE_PLY = 1, enum : int {
DEPTH_ZERO = 0 * ONE_PLY, DEPTH_QS_CHECKS = 0,
DEPTH_QS_CHECKS = 0 * ONE_PLY, DEPTH_QS_NO_CHECKS = -1,
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY, DEPTH_QS_RECAPTURES = -5,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_NONE = -6 * ONE_PLY, DEPTH_NONE = -6,
DEPTH_MAX = MAX_PLY * ONE_PLY DEPTH_OFFSET = DEPTH_NONE,
}; };
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
enum Square : int { enum Square : int {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
@@ -286,7 +286,6 @@ inline T& operator--(T& d) { return d = T(int(d) - 1); }
#define ENABLE_FULL_OPERATORS_ON(T) \ #define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_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*(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 T operator/(T d, int i) { return T(int(d) / i); } \ constexpr T operator/(T d, int i) { return T(int(d) / i); } \
@@ -295,12 +294,10 @@ 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); } inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Value)
ENABLE_FULL_OPERATORS_ON(Depth)
ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_FULL_OPERATORS_ON(Direction)
ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(PieceType)
ENABLE_INCR_OPERATORS_ON(Piece) ENABLE_INCR_OPERATORS_ON(Piece)
ENABLE_INCR_OPERATORS_ON(Color)
ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(Square)
ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(File)
ENABLE_INCR_OPERATORS_ON(Rank) ENABLE_INCR_OPERATORS_ON(Rank)
@@ -344,6 +341,11 @@ inline Score operator*(Score s, int i) {
return result; return result;
} }
/// Multiplication of a Score by a boolean
inline Score operator*(Score s, bool b) {
return Score(int(s) * int(b));
}
constexpr Color operator~(Color c) { constexpr Color operator~(Color c) {
return Color(c ^ BLACK); // Toggle color return Color(c ^ BLACK); // Toggle color
} }
@@ -352,16 +354,16 @@ constexpr Square operator~(Square s) {
return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
} }
constexpr File operator~(File f) {
return File(f ^ FILE_H); // Horizontal flip FILE_A -> FILE_H
}
constexpr Piece operator~(Piece pc) { constexpr Piece operator~(Piece pc) {
return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
} }
constexpr CastlingRight operator|(Color c, CastlingSide s) { inline File map_to_queenside(File f) {
return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c)); return std::min(f, File(FILE_H - f)); // Map files ABCDEFGH to files ABCDDCBA
}
constexpr CastlingRights operator&(Color c, CastlingRights cr) {
return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr);
} }
constexpr Value mate_in(int ply) { constexpr Value mate_in(int ply) {
@@ -413,11 +415,6 @@ constexpr Rank relative_rank(Color c, Square s) {
return relative_rank(c, rank_of(s)); return relative_rank(c, rank_of(s));
} }
inline bool opposite_colors(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return ((s >> 3) ^ s) & 1;
}
constexpr Direction pawn_push(Color c) { constexpr Direction pawn_push(Color c) {
return c == WHITE ? NORTH : SOUTH; return c == WHITE ? NORTH : SOUTH;
} }
@@ -446,6 +443,10 @@ constexpr Move make_move(Square from, Square to) {
return Move((from << 6) + to); return Move((from << 6) + to);
} }
constexpr Move reverse_move(Move m) {
return make_move(to_sq(m), from_sq(m));
}
template<MoveType T> template<MoveType T>
constexpr 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); return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -146,7 +146,7 @@ namespace {
uint64_t num, nodes = 0, cnt = 1; uint64_t num, nodes = 0, cnt = 1;
vector<string> list = setup_bench(pos, args); vector<string> list = setup_bench(pos, args);
num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0; }); num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; });
TimePoint elapsed = now(); TimePoint elapsed = now();
@@ -155,16 +155,21 @@ namespace {
istringstream is(cmd); istringstream is(cmd);
is >> skipws >> token; is >> skipws >> token;
if (token == "go") if (token == "go" || token == "eval")
{ {
cerr << "\nPosition: " << cnt++ << '/' << num << endl; cerr << "\nPosition: " << cnt++ << '/' << num << endl;
if (token == "go")
{
go(pos, is, states); go(pos, is, states);
Threads.main()->wait_for_search_finished(); Threads.main()->wait_for_search_finished();
nodes += Threads.nodes_searched(); nodes += Threads.nodes_searched();
} }
else
sync_cout << "\n" << Eval::trace(pos) << sync_endl;
}
else if (token == "setoption") setoption(is); else if (token == "setoption") setoption(is);
else if (token == "position") position(pos, is, states); else if (token == "position") position(pos, is, states);
else if (token == "ucinewgame") Search::clear(); else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while
} }
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
@@ -191,9 +196,8 @@ void UCI::loop(int argc, char* argv[]) {
Position pos; Position pos;
string token, cmd; string token, cmd;
StateListPtr states(new std::deque<StateInfo>(1)); StateListPtr states(new std::deque<StateInfo>(1));
auto uiThread = std::make_shared<Thread>(0);
pos.set(StartFEN, false, &states->back(), uiThread.get()); pos.set(StartFEN, false, &states->back(), Threads.main());
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " "; cmd += std::string(argv[i]) + " ";
@@ -207,18 +211,16 @@ void UCI::loop(int argc, char* argv[]) {
token.clear(); // Avoid a stale if getline() returns empty or blank line token.clear(); // Avoid a stale if getline() returns empty or blank line
is >> skipws >> token; is >> skipws >> token;
if ( token == "quit"
|| token == "stop")
Threads.stop = true;
// The GUI sends 'ponderhit' to tell us the user has played the expected move. // The GUI sends 'ponderhit' to tell us the user has played the expected move.
// So 'ponderhit' will be sent if we were told to ponder on the same move the // So 'ponderhit' will be sent if we were told to ponder on the same move the
// user has played. We should continue searching but switch from pondering to // user has played. We should continue searching but switch from pondering to
// normal search. In case Threads.stopOnPonderhit is set we are waiting for // normal search.
// 'ponderhit' to stop the search, for instance if max search depth is reached.
if ( token == "quit"
|| token == "stop"
|| (token == "ponderhit" && Threads.stopOnPonderhit))
Threads.stop = true;
else if (token == "ponderhit") else if (token == "ponderhit")
Threads.ponder = false; // Switch to normal search Threads.main()->ponder = false; // Switch to normal search
else if (token == "uci") else if (token == "uci")
sync_cout << "id name " << engine_info(true) sync_cout << "id name " << engine_info(true)
@@ -231,11 +233,13 @@ void UCI::loop(int argc, char* argv[]) {
else if (token == "ucinewgame") Search::clear(); else if (token == "ucinewgame") Search::clear();
else if (token == "isready") sync_cout << "readyok" << sync_endl; else if (token == "isready") sync_cout << "readyok" << sync_endl;
// Additional custom non-UCI commands, mainly for debugging // Additional custom non-UCI commands, mainly for debugging.
// Do not use these commands during a search!
else if (token == "flip") pos.flip(); else if (token == "flip") pos.flip();
else if (token == "bench") bench(pos, is, states); else if (token == "bench") bench(pos, is, states);
else if (token == "d") sync_cout << pos << sync_endl; else if (token == "d") sync_cout << pos << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else if (token == "compiler") sync_cout << compiler_info() << sync_endl;
else else
sync_cout << "Unknown command: " << cmd << sync_endl; sync_cout << "Unknown command: " << cmd << sync_endl;

View File

@@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by 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 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <ostream> #include <ostream>
#include <sstream>
#include "misc.h" #include "misc.h"
#include "search.h" #include "search.h"
@@ -73,6 +74,8 @@ void init(OptionsMap& o) {
o["nodestime"] << Option(0, 0, 10000); o["nodestime"] << Option(0, 0, 10000);
o["UCI_Chess960"] << Option(false); o["UCI_Chess960"] << Option(false);
o["UCI_AnalyseMode"] << Option(false); o["UCI_AnalyseMode"] << Option(false);
o["UCI_LimitStrength"] << Option(false);
o["UCI_Elo"] << Option(1350, 1350, 2850);
o["SyzygyPath"] << Option("<empty>", on_tb_path); o["SyzygyPath"] << Option("<empty>", on_tb_path);
o["SyzygyProbeDepth"] << Option(1, 1, 100); o["SyzygyProbeDepth"] << Option(1, 1, 100);
o["Syzygy50MoveRule"] << Option(true); o["Syzygy50MoveRule"] << Option(true);
@@ -153,8 +156,8 @@ void Option::operator<<(const Option& o) {
/// operator=() updates currentValue and triggers on_change() action. It's up to /// operator=() updates currentValue and triggers on_change() action. It's up to
/// the GUI to check for option's limits, but we could receive the new value from /// the GUI to check for option's limits, but we could receive the new value
/// the user by console window, so let's check the bounds anyway. /// from the user by console window, so let's check the bounds anyway.
Option& Option::operator=(const string& v) { Option& Option::operator=(const string& v) {
@@ -165,6 +168,17 @@ Option& Option::operator=(const string& v) {
|| (type == "spin" && (stof(v) < min || stof(v) > max))) || (type == "spin" && (stof(v) < min || stof(v) > max)))
return *this; return *this;
if (type == "combo")
{
OptionsMap comboMap; // To have case insensitive compare
string token;
std::istringstream ss(defaultValue);
while (ss >> token)
comboMap[token] << Option();
if (!comboMap.count(v) || v == "var")
return *this;
}
if (type != "button") if (type != "button")
currentValue = v; currentValue = v;