mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-08 15:12:40 +01:00
Update to Stockfish 11
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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",
|
||||
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/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
|
||||
"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 limitType = (is >> token) ? token : "depth";
|
||||
|
||||
go = "go " + limitType + " " + limit;
|
||||
go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
|
||||
|
||||
if (fenFile == "default")
|
||||
fens = Defaults;
|
||||
@@ -139,9 +143,9 @@ vector<string> setup_bench(const Position& current, istream& is) {
|
||||
file.close();
|
||||
}
|
||||
|
||||
list.emplace_back("ucinewgame");
|
||||
list.emplace_back("setoption name Threads value " + threads);
|
||||
list.emplace_back("setoption name Hash value " + ttSize);
|
||||
list.emplace_back("ucinewgame");
|
||||
|
||||
for (const string& fen : fens)
|
||||
if (fen.find("setoption") != string::npos)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
@@ -28,7 +27,8 @@
|
||||
|
||||
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
|
||||
|
||||
// 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 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) {
|
||||
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 {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,24 +19,16 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "misc.h"
|
||||
|
||||
uint8_t PopCnt16[1 << 16];
|
||||
int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
uint8_t SquareDistance[SQUARE_NB][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 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 PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
|
||||
@@ -49,15 +41,6 @@ namespace {
|
||||
Bitboard BishopTable[0x1480]; // To store bishop attacks
|
||||
|
||||
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,56 +69,36 @@ const std::string Bitboards::pretty(Bitboard b) {
|
||||
void Bitboards::init() {
|
||||
|
||||
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)
|
||||
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 s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
if (s1 != s2)
|
||||
{
|
||||
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
||||
DistanceRingBB[s1][SquareDistance[s1][s2]] |= s2;
|
||||
}
|
||||
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
||||
|
||||
int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
PawnAttacks[WHITE][s] = pawn_attacks_bb<WHITE>(square_bb(s));
|
||||
PawnAttacks[BLACK][s] = pawn_attacks_bb<BLACK>(square_bb(s));
|
||||
}
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt : { PAWN, KNIGHT, KING })
|
||||
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]);
|
||||
// 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);
|
||||
};
|
||||
|
||||
if (is_ok(to) && distance(s, to) < 3)
|
||||
{
|
||||
if (pt == PAWN)
|
||||
PawnAttacks[c][s] |= to;
|
||||
else
|
||||
PseudoAttacks[pt][s] |= to;
|
||||
}
|
||||
}
|
||||
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 BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
|
||||
@@ -150,13 +113,8 @@ void Bitboards::init() {
|
||||
|
||||
for (PieceType pt : { BISHOP, ROOK })
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
{
|
||||
if (!(PseudoAttacks[pt][s1] & s2))
|
||||
continue;
|
||||
|
||||
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]);
|
||||
}
|
||||
if (PseudoAttacks[pt][s1] & s2)
|
||||
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,8 +142,8 @@ namespace {
|
||||
|
||||
// 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
|
||||
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
|
||||
// use the so called "fancy" approach.
|
||||
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
|
||||
// called "fancy" approach.
|
||||
|
||||
void init_magics(Bitboard table[], Magic magics[], Direction directions[]) {
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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 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 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 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 PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
|
||||
@@ -102,64 +105,61 @@ struct Magic {
|
||||
extern Magic RookMagics[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
|
||||
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
|
||||
|
||||
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) { 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) { return b |= square_bb(s); }
|
||||
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
|
||||
|
||||
inline Bitboard operator|(Bitboard b, Square s) {
|
||||
assert(s >= SQ_A1 && s <= SQ_H8);
|
||||
return b | SquareBB[s];
|
||||
}
|
||||
inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
|
||||
inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
|
||||
inline Bitboard operator^(Square s, Bitboard b) { return b ^ 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];
|
||||
}
|
||||
|
||||
inline Bitboard& operator^=(Bitboard& b, Square s) {
|
||||
assert(s >= SQ_A1 && s <= SQ_H8);
|
||||
return b ^= SquareBB[s];
|
||||
}
|
||||
inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | square_bb(s2); }
|
||||
|
||||
constexpr bool more_than_one(Bitboard b) {
|
||||
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
|
||||
/// the given file or rank.
|
||||
|
||||
inline Bitboard rank_bb(Rank r) {
|
||||
return RankBB[r];
|
||||
return Rank1BB << (8 * r);
|
||||
}
|
||||
|
||||
inline Bitboard rank_bb(Square s) {
|
||||
return RankBB[rank_of(s)];
|
||||
return rank_bb(rank_of(s));
|
||||
}
|
||||
|
||||
inline Bitboard file_bb(File f) {
|
||||
return FileBB[f];
|
||||
return FileABB << f;
|
||||
}
|
||||
|
||||
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>
|
||||
constexpr Bitboard shift(Bitboard b) {
|
||||
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 == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7
|
||||
: 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
|
||||
/// squares in the given bitboard.
|
||||
/// pawn_attacks_bb() returns the squares attacked by pawns of the given color
|
||||
/// from the squares in the given bitboard.
|
||||
|
||||
template<Color C>
|
||||
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 of the given one.
|
||||
|
||||
inline Bitboard adjacent_files_bb(File f) {
|
||||
return AdjacentFilesBB[f];
|
||||
inline Bitboard adjacent_files_bb(Square s) {
|
||||
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
|
||||
}
|
||||
|
||||
|
||||
/// between_bb() returns a bitboard representing all the squares between the two
|
||||
/// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with
|
||||
/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, file
|
||||
/// or diagonal, 0 is returned.
|
||||
/// between_bb() returns squares that are linearly between the given squares
|
||||
/// If the given squares are not on a same file/rank/diagonal, return 0.
|
||||
|
||||
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,
|
||||
/// 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) {
|
||||
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
|
||||
/// 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)
|
||||
/// forward_file_bb() returns a bitboard representing all the squares along the
|
||||
/// line in front of the given one, from the point of view of the given color.
|
||||
|
||||
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
|
||||
/// attacked by a pawn of the given color when it moves along its file, starting
|
||||
/// from the given square:
|
||||
/// PawnAttackSpan[c][s] = forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s));
|
||||
/// pawn_attack_span() returns a bitboard representing all the squares that can
|
||||
/// be attacked by a pawn of the given color when it moves along its file,
|
||||
/// starting from the given square.
|
||||
|
||||
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
|
||||
/// pawn of 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)
|
||||
/// passed_pawn_span() returns a bitboard which can be used to test if a pawn of
|
||||
/// the given color and on the given square is a passed pawn.
|
||||
|
||||
inline Bitboard passed_pawn_mask(Color c, Square s) {
|
||||
return PassedPawnMask[c][s];
|
||||
inline Bitboard passed_pawn_span(Color c, Square 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
|
||||
/// 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<typename T1, typename T2> inline int distance(T2 x, T2 y);
|
||||
template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); }
|
||||
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); }
|
||||
|
||||
template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
|
||||
return v < lo ? lo : v > hi ? hi : v;
|
||||
}
|
||||
|
||||
/// attacks_bb() returns a bitboard representing all the squares attacked by a
|
||||
/// piece of type Pt (bishop or rook) placed on 's'.
|
||||
@@ -281,7 +289,6 @@ inline int popcount(Bitboard b) {
|
||||
|
||||
#ifndef USE_POPCNT
|
||||
|
||||
extern uint8_t PopCnt16[1 << 16];
|
||||
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]];
|
||||
|
||||
@@ -375,10 +382,9 @@ inline Square pop_lsb(Bitboard* b) {
|
||||
}
|
||||
|
||||
|
||||
/// frontmost_sq() and backmost_sq() return the square corresponding to the
|
||||
/// 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 backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
|
||||
/// frontmost_sq() returns the most advanced square for the given color
|
||||
inline Square frontmost_sq(Color c, Bitboard b) {
|
||||
return c == WHITE ? msb(b) : lsb(b);
|
||||
}
|
||||
|
||||
#endif // #ifndef BITBOARD_H_INCLUDED
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "bitboard.h"
|
||||
@@ -45,14 +44,14 @@ namespace {
|
||||
// Table used to drive the king towards a corner square of the
|
||||
// right color in KBN vs K endgames.
|
||||
constexpr int PushToCorners[SQUARE_NB] = {
|
||||
200, 190, 180, 170, 160, 150, 140, 130,
|
||||
190, 180, 170, 160, 150, 140, 130, 140,
|
||||
180, 170, 155, 140, 140, 125, 140, 150,
|
||||
170, 160, 140, 120, 110, 140, 150, 160,
|
||||
160, 150, 140, 110, 120, 140, 160, 170,
|
||||
150, 140, 125, 140, 140, 155, 170, 180,
|
||||
140, 130, 140, 150, 160, 170, 180, 190,
|
||||
130, 140, 150, 160, 170, 180, 190, 200
|
||||
6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160,
|
||||
6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480,
|
||||
5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800,
|
||||
5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120,
|
||||
5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440,
|
||||
4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760,
|
||||
4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080,
|
||||
4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400
|
||||
};
|
||||
|
||||
// Tables used to drive a piece towards or away from another piece
|
||||
@@ -75,17 +74,42 @@ namespace {
|
||||
assert(pos.count<PAWN>(strongSide) == 1);
|
||||
|
||||
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)
|
||||
sq = ~sq;
|
||||
|
||||
return sq;
|
||||
return strongSide == WHITE ? sq : ~sq;
|
||||
}
|
||||
|
||||
} // 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
|
||||
/// 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
|
||||
@@ -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
|
||||
/// defending king towards a corner square of the right color.
|
||||
/// defending king towards a corner square that our bishop attacks.
|
||||
template<>
|
||||
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 bishopSq = pos.square<BISHOP>(strongSide);
|
||||
|
||||
// kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a
|
||||
// bishop that cannot reach the above squares, we flip the kings in order
|
||||
// to drive the enemy toward corners A8 or H1.
|
||||
if (opposite_colors(bishopSq, SQ_A1))
|
||||
{
|
||||
winnerKSq = ~winnerKSq;
|
||||
loserKSq = ~loserKSq;
|
||||
}
|
||||
// If our bishop does not attack A1/H8, we flip the enemy king square
|
||||
// to drive to opposite corners (A8/H1).
|
||||
|
||||
Value result = VALUE_KNOWN_WIN
|
||||
+ 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;
|
||||
}
|
||||
|
||||
|
||||
/// 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<>
|
||||
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
|
||||
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)
|
||||
{
|
||||
// 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 weakKingSq = pos.square<KING>(weakSide);
|
||||
@@ -629,8 +663,6 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
||||
Square ksq = pos.square<KING>(weakSide);
|
||||
Square psq1 = pos.squares<PAWN>(strongSide)[0];
|
||||
Square psq2 = pos.squares<PAWN>(strongSide)[1];
|
||||
Rank r1 = rank_of(psq1);
|
||||
Rank r2 = rank_of(psq2);
|
||||
Square blockSq1, blockSq2;
|
||||
|
||||
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)
|
||||
&& ( bbsq == blockSq2
|
||||
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|
||||
|| distance(r1, r2) >= 2))
|
||||
|| distance<Rank>(psq1, psq2) >= 2))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
else if ( ksq == blockSq2
|
||||
@@ -729,6 +761,9 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
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 bishopSq = pos.square<BISHOP>(weakSide);
|
||||
Square weakKingSq = pos.square<KING>(weakSide);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +21,7 @@
|
||||
#ifndef ENDGAME_H_INCLUDED
|
||||
#define ENDGAME_H_INCLUDED
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
@@ -37,6 +37,7 @@ enum EndgameCode {
|
||||
|
||||
EVALUATION_FUNCTIONS,
|
||||
KNNK, // KNN vs K
|
||||
KNNKP, // KNN vs KP
|
||||
KXK, // Generic "mate lone king" eval
|
||||
KBNK, // KBN 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
|
||||
/// 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 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>
|
||||
Map<T>& map() {
|
||||
@@ -112,34 +117,11 @@ class Endgames {
|
||||
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>
|
||||
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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -73,28 +73,17 @@ using namespace Trace;
|
||||
|
||||
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
|
||||
constexpr Value LazyThreshold = Value(1500);
|
||||
constexpr Value LazyThreshold = Value(1400);
|
||||
constexpr Value SpaceThreshold = Value(12222);
|
||||
|
||||
// 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
|
||||
constexpr int QueenSafeCheck = 780;
|
||||
constexpr int RookSafeCheck = 880;
|
||||
constexpr int BishopSafeCheck = 435;
|
||||
constexpr int RookSafeCheck = 1080;
|
||||
constexpr int BishopSafeCheck = 635;
|
||||
constexpr int KnightSafeCheck = 790;
|
||||
|
||||
#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) }
|
||||
};
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// which piece type attacks which one. Attacks on lesser pieces which are
|
||||
// pawn-defended are not considered.
|
||||
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] = {
|
||||
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
|
||||
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)
|
||||
};
|
||||
|
||||
// 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)
|
||||
S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
|
||||
};
|
||||
|
||||
// Assorted bonuses and penalties
|
||||
constexpr Score BishopPawns = S( 3, 8);
|
||||
constexpr Score CloseEnemies = S( 7, 0);
|
||||
constexpr Score BishopPawns = S( 3, 7);
|
||||
constexpr Score CorneredBishop = S( 50, 50);
|
||||
constexpr Score Hanging = S( 62, 34);
|
||||
constexpr Score KingProtector = S( 6, 7);
|
||||
constexpr Score KnightOnQueen = S( 20, 12);
|
||||
constexpr Score LongDiagonalBishop = S( 44, 0);
|
||||
constexpr Score MinorBehindPawn = S( 16, 0);
|
||||
constexpr Score Overload = S( 12, 6);
|
||||
constexpr Score PawnlessFlank = S( 18, 94);
|
||||
constexpr Score RestrictedPiece = S( 7, 6);
|
||||
constexpr Score RookOnPawn = S( 10, 28);
|
||||
constexpr Score SliderOnQueen = S( 49, 21);
|
||||
constexpr Score ThreatByKing = S( 21, 84);
|
||||
constexpr Score ThreatByPawnPush = S( 48, 42);
|
||||
constexpr Score ThreatByRank = S( 14, 3);
|
||||
constexpr Score ThreatBySafePawn = S(169, 99);
|
||||
constexpr Score TrappedRook = S( 98, 5);
|
||||
constexpr Score WeakQueen = S( 51, 10);
|
||||
constexpr Score WeakUnopposedPawn = S( 14, 20);
|
||||
constexpr Score FlankAttacks = S( 8, 0);
|
||||
constexpr Score Hanging = S( 69, 36);
|
||||
constexpr Score KingProtector = S( 7, 8);
|
||||
constexpr Score KnightOnQueen = S( 16, 12);
|
||||
constexpr Score LongDiagonalBishop = S( 45, 0);
|
||||
constexpr Score MinorBehindPawn = S( 18, 3);
|
||||
constexpr Score Outpost = S( 30, 21);
|
||||
constexpr Score PassedFile = S( 11, 8);
|
||||
constexpr Score PawnlessFlank = S( 17, 95);
|
||||
constexpr Score RestrictedPiece = S( 7, 7);
|
||||
constexpr Score ReachableOutpost = S( 32, 10);
|
||||
constexpr Score RookOnQueenFile = S( 7, 6);
|
||||
constexpr Score SliderOnQueen = S( 59, 18);
|
||||
constexpr Score ThreatByKing = S( 24, 89);
|
||||
constexpr Score ThreatByPawnPush = S( 48, 39);
|
||||
constexpr Score ThreatBySafePawn = S(173, 94);
|
||||
constexpr Score TrappedRook = S( 52, 10);
|
||||
constexpr Score WeakQueen = S( 49, 15);
|
||||
|
||||
#undef S
|
||||
|
||||
@@ -193,7 +168,7 @@ namespace {
|
||||
template<Color Us> Score passed() const;
|
||||
template<Color Us> Score space() const;
|
||||
ScaleFactor scale_factor(Value eg) const;
|
||||
Score initiative(Value eg) const;
|
||||
Score initiative(Score score) const;
|
||||
|
||||
const Position& pos;
|
||||
Material::Entry* me;
|
||||
@@ -206,15 +181,12 @@ namespace {
|
||||
// is also calculated is ALL_PIECES.
|
||||
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
|
||||
|
||||
// attackedBy2[color] are the squares attacked by 2 pieces of a given color,
|
||||
// possibly via x-ray or by one pawn and one piece. Diagonal x-ray through
|
||||
// pawn or squares attacked by 2 pawns are not explicitly added.
|
||||
// attackedBy2[color] are the squares attacked by at least 2 units of a given
|
||||
// color, including x-rays. But diagonal x-rays through pawns are not computed.
|
||||
Bitboard attackedBy2[COLOR_NB];
|
||||
|
||||
// kingRing[color] are the squares adjacent to the king, plus (only for a
|
||||
// king on its first rank) the squares two ranks in front. For instance,
|
||||
// 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.
|
||||
// kingRing[color] are the squares adjacent to the king plus some other
|
||||
// very near squares, depending on king position.
|
||||
Bitboard kingRing[COLOR_NB];
|
||||
|
||||
// kingAttackersCount[color] is the number of pieces of the given color
|
||||
@@ -242,41 +214,37 @@ namespace {
|
||||
void Evaluation<T>::initialize() {
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
|
||||
constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB: Rank7BB | Rank6BB);
|
||||
constexpr Direction Up = pawn_push(Us);
|
||||
constexpr Direction Down = -Up;
|
||||
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
|
||||
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
|
||||
// are excluded from the mobility area.
|
||||
mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them));
|
||||
// Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king
|
||||
// or controlled by enemy pawns are excluded from the mobility area.
|
||||
mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them));
|
||||
|
||||
// Initialise attackedBy bitboards for kings and pawns
|
||||
attackedBy[Us][KING] = pos.attacks_from<KING>(pos.square<KING>(Us));
|
||||
// Initialize attackedBy[] for king and pawns
|
||||
attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
|
||||
attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
|
||||
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),
|
||||
clamp(rank_of(ksq), RANK_2, RANK_7));
|
||||
kingRing[Us] = PseudoAttacks[KING][s] | s;
|
||||
|
||||
// Init our king safety tables only if we are going to use them
|
||||
if (pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg)
|
||||
{
|
||||
kingRing[Us] = attackedBy[Us][KING];
|
||||
if (relative_rank(Us, pos.square<KING>(Us)) == RANK_1)
|
||||
kingRing[Us] |= shift<Up>(kingRing[Us]);
|
||||
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
|
||||
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
|
||||
|
||||
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));
|
||||
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() {
|
||||
|
||||
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
|
||||
: Rank5BB | Rank4BB | Rank3BB);
|
||||
const Square* pl = pos.squares<Pt>(Us);
|
||||
|
||||
Bitboard b, bb;
|
||||
Square s;
|
||||
Score score = SCORE_ZERO;
|
||||
|
||||
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
|
||||
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
|
||||
@@ -324,12 +291,12 @@ namespace {
|
||||
if (Pt == BISHOP || Pt == KNIGHT)
|
||||
{
|
||||
// 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)
|
||||
score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & s)] * 2;
|
||||
score += Outpost * (Pt == KNIGHT ? 2 : 1);
|
||||
|
||||
else if (bb &= b & ~pos.pieces(Us))
|
||||
score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & bb)];
|
||||
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
|
||||
score += ReachableOutpost;
|
||||
|
||||
// Knight and Bishop bonus for being right behind a pawn
|
||||
if (shift<Down>(pos.pieces(PAWN)) & s)
|
||||
@@ -344,45 +311,44 @@ namespace {
|
||||
// bishop, bigger when the center files are blocked with pawns.
|
||||
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));
|
||||
|
||||
// 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))
|
||||
score += LongDiagonalBishop;
|
||||
}
|
||||
|
||||
// An important Chess960 pattern: A cornered bishop blocked by a friendly
|
||||
// pawn diagonally in front of it is a very serious problem, especially
|
||||
// when that pawn is also blocked.
|
||||
if ( Pt == BISHOP
|
||||
&& pos.is_chess960()
|
||||
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
|
||||
{
|
||||
Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
|
||||
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4
|
||||
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2
|
||||
: CorneredBishop;
|
||||
// An important Chess960 pattern: a cornered bishop blocked by a friendly
|
||||
// pawn diagonally in front of it is a very serious problem, especially
|
||||
// when that pawn is also blocked.
|
||||
if ( pos.is_chess960()
|
||||
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
|
||||
{
|
||||
Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
|
||||
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4
|
||||
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2
|
||||
: CorneredBishop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Pt == ROOK)
|
||||
{
|
||||
// Bonus for aligning rook with enemy pawns on the same rank/file
|
||||
if (relative_rank(Us, s) >= RANK_5)
|
||||
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
|
||||
// Bonus for rook on the same file as a queen
|
||||
if (file_bb(s) & pos.pieces(QUEEN))
|
||||
score += RookOnQueenFile;
|
||||
|
||||
// Bonus for rook on an open or semi-open file
|
||||
if (pe->semiopen_file(Us, file_of(s)))
|
||||
score += RookOnFile[bool(pe->semiopen_file(Them, file_of(s)))];
|
||||
if (pos.is_on_semiopen_file(Us, s))
|
||||
score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
|
||||
|
||||
// Penalty when trapped by the king, even more if the king cannot castle
|
||||
else if (mob <= 3)
|
||||
{
|
||||
File kf = file_of(pos.square<KING>(Us));
|
||||
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,89 +375,97 @@ namespace {
|
||||
constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
|
||||
: AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
|
||||
|
||||
Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0;
|
||||
Bitboard rookChecks, queenChecks, bishopChecks, knightChecks;
|
||||
int kingDanger = 0;
|
||||
const Square ksq = pos.square<KING>(Us);
|
||||
Bitboard kingFlank, weak, b, b1, b2, safe, unsafeChecks;
|
||||
|
||||
// King shelter and enemy pawns storm
|
||||
// Init the score with 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;
|
||||
// Attacked squares defended at most once by our queen or king
|
||||
weak = attackedBy[Them][ALL_PIECES]
|
||||
& ~attackedBy2[Us]
|
||||
& (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);
|
||||
|
||||
// Analyse the safe enemy's checks which are possible on next move
|
||||
safe = ~pos.pieces(Them);
|
||||
safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
|
||||
|
||||
b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
|
||||
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
|
||||
|
||||
// Enemy rooks checks
|
||||
rookChecks = b1 & safe & attackedBy[Them][ROOK];
|
||||
|
||||
if (rookChecks)
|
||||
kingDanger += RookSafeCheck;
|
||||
else
|
||||
unsafeChecks |= b1 & attackedBy[Them][ROOK];
|
||||
|
||||
// Enemy queen safe checks: we count them only if they are from squares from
|
||||
// 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;
|
||||
else
|
||||
unsafeChecks |= b2 & attackedBy[Them][BISHOP];
|
||||
|
||||
// Enemy knights checks
|
||||
knightChecks = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
|
||||
|
||||
if (knightChecks & safe)
|
||||
kingDanger += KnightSafeCheck;
|
||||
else
|
||||
unsafeChecks |= knightChecks;
|
||||
|
||||
// Find the squares that opponent attacks in our king flank, the squares
|
||||
// which they attack twice in that flank, and the squares that we defend.
|
||||
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 tropism = popcount(b1) + popcount(b2);
|
||||
int kingFlankAttack = popcount(b1) + popcount(b2);
|
||||
int kingFlankDefense = popcount(b3);
|
||||
|
||||
// Main king safety evaluation
|
||||
if (kingAttackersCount[Them] > 1 - pos.count<QUEEN>(Them))
|
||||
{
|
||||
int kingDanger = 0;
|
||||
unsafeChecks = 0;
|
||||
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
|
||||
+ 185 * popcount(kingRing[Us] & weak)
|
||||
+ 148 * popcount(unsafeChecks)
|
||||
+ 98 * popcount(pos.blockers_for_king(Us))
|
||||
+ 69 * kingAttacksCount[Them]
|
||||
+ 3 * kingFlankAttack * kingFlankAttack / 8
|
||||
+ mg_value(mobility[Them] - mobility[Us])
|
||||
- 873 * !pos.count<QUEEN>(Them)
|
||||
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
|
||||
- 6 * mg_value(score) / 8
|
||||
- 4 * kingFlankDefense
|
||||
+ 37;
|
||||
|
||||
// Attacked squares defended at most once by our queen or king
|
||||
weak = attackedBy[Them][ALL_PIECES]
|
||||
& ~attackedBy2[Us]
|
||||
& (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);
|
||||
|
||||
// Analyse the safe enemy's checks which are possible on next move
|
||||
safe = ~pos.pieces(Them);
|
||||
safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
|
||||
|
||||
b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
|
||||
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
|
||||
|
||||
// Enemy queen safe checks
|
||||
if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN])
|
||||
kingDanger += QueenSafeCheck;
|
||||
|
||||
b1 &= attackedBy[Them][ROOK];
|
||||
b2 &= attackedBy[Them][BISHOP];
|
||||
|
||||
// Enemy rooks checks
|
||||
if (b1 & safe)
|
||||
kingDanger += RookSafeCheck;
|
||||
else
|
||||
unsafeChecks |= b1;
|
||||
|
||||
// Enemy bishops checks
|
||||
if (b2 & safe)
|
||||
kingDanger += BishopSafeCheck;
|
||||
else
|
||||
unsafeChecks |= b2;
|
||||
|
||||
// Enemy knights checks
|
||||
b = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
|
||||
if (b & safe)
|
||||
kingDanger += KnightSafeCheck;
|
||||
else
|
||||
unsafeChecks |= b;
|
||||
|
||||
// Unsafe or occupied checking squares will also be considered, as long as
|
||||
// the square is in the attacker's mobility area.
|
||||
unsafeChecks &= mobilityArea[Them];
|
||||
|
||||
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
|
||||
+ 69 * kingAttacksCount[Them]
|
||||
+ 185 * popcount(kingRing[Us] & weak)
|
||||
+ 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
|
||||
+ tropism * tropism / 4
|
||||
- 873 * !pos.count<QUEEN>(Them)
|
||||
- 6 * mg_value(score) / 8
|
||||
+ mg_value(mobility[Them] - mobility[Us])
|
||||
- 30;
|
||||
|
||||
// Transform the kingDanger units into a Score, and subtract it from the evaluation
|
||||
if (kingDanger > 0)
|
||||
score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
|
||||
}
|
||||
// Transform the kingDanger units into a Score, and subtract it from the evaluation
|
||||
if (kingDanger > 100)
|
||||
score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
|
||||
|
||||
// Penalty when our king is on a pawnless flank
|
||||
if (!(pos.pieces(PAWN) & kingFlank))
|
||||
if (!(pos.pieces(PAWN) & KingFlank[file_of(ksq)]))
|
||||
score -= PawnlessFlank;
|
||||
|
||||
// King tropism bonus, to anticipate slow motion attacks on our king
|
||||
score -= CloseEnemies * tropism;
|
||||
// Penalty if king flank is under attack, potentially moving toward the king
|
||||
score -= FlankAttacks * kingFlankAttack;
|
||||
|
||||
if (T)
|
||||
Trace::add(KING, Us, score);
|
||||
@@ -506,14 +480,14 @@ namespace {
|
||||
Score Evaluation<T>::threats() const {
|
||||
|
||||
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);
|
||||
|
||||
Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe, restricted;
|
||||
Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe;
|
||||
Score score = SCORE_ZERO;
|
||||
|
||||
// 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
|
||||
// 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
|
||||
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
|
||||
if (defended | weak)
|
||||
{
|
||||
b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
|
||||
while (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);
|
||||
}
|
||||
score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
b = weak & attackedBy[Us][ROOK];
|
||||
while (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);
|
||||
}
|
||||
score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
if (weak & attackedBy[Us][KING])
|
||||
score += ThreatByKing;
|
||||
|
||||
score += Hanging * popcount(weak & ~attackedBy[Them][ALL_PIECES]);
|
||||
|
||||
b = weak & nonPawnEnemies & attackedBy[Them][ALL_PIECES];
|
||||
score += Overload * popcount(b);
|
||||
b = ~attackedBy[Them][ALL_PIECES]
|
||||
| (nonPawnEnemies & attackedBy2[Us]);
|
||||
score += Hanging * popcount(weak & b);
|
||||
}
|
||||
|
||||
// Bonus for restricting their piece moves
|
||||
restricted = attackedBy[Them][ALL_PIECES]
|
||||
& ~attackedBy[Them][PAWN]
|
||||
& ~attackedBy2[Them]
|
||||
& attackedBy[Us][ALL_PIECES];
|
||||
score += RestrictedPiece * popcount(restricted);
|
||||
b = attackedBy[Them][ALL_PIECES]
|
||||
& ~stronglyProtected
|
||||
& attackedBy[Us][ALL_PIECES];
|
||||
|
||||
// Bonus for enemy unopposed weak pawns
|
||||
if (pos.pieces(Us, ROOK, QUEEN))
|
||||
score += WeakUnopposedPawn * pe->weak_unopposed(Them);
|
||||
score += RestrictedPiece * popcount(b);
|
||||
|
||||
// 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
|
||||
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
|
||||
@@ -578,14 +542,8 @@ namespace {
|
||||
b &= ~attackedBy[Them][PAWN] & safe;
|
||||
|
||||
// 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;
|
||||
score += ThreatBySafePawn * popcount(b);
|
||||
score += ThreatByPawnPush * popcount(b);
|
||||
|
||||
// Bonus for threats on the next moves against enemy queen
|
||||
if (pos.count<QUEEN>(Them) == 1)
|
||||
@@ -616,13 +574,13 @@ namespace {
|
||||
Score Evaluation<T>::passed() const {
|
||||
|
||||
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) {
|
||||
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;
|
||||
|
||||
b = pe->passed_pawns(Us);
|
||||
@@ -639,12 +597,12 @@ namespace {
|
||||
|
||||
if (r > RANK_3)
|
||||
{
|
||||
int w = (r-2) * (r-2) + 2;
|
||||
int w = 5 * r - 13;
|
||||
Square blockSq = s + Up;
|
||||
|
||||
// Adjust bonus based on the king's proximity
|
||||
bonus += make_score(0, ( king_proximity(Them, blockSq) * 5
|
||||
- king_proximity(Us, blockSq) * 2) * w);
|
||||
bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4
|
||||
- king_proximity(Us, blockSq) * 2) * w);
|
||||
|
||||
// If blockSq is not the queening square then consider also a second push
|
||||
if (r != RANK_7)
|
||||
@@ -653,42 +611,37 @@ namespace {
|
||||
// If the pawn is free to advance, then increase the bonus
|
||||
if (pos.empty(blockSq))
|
||||
{
|
||||
// If there is a rook or queen attacking/defending the pawn from behind,
|
||||
// consider all the squaresToQueen. Otherwise consider only the squares
|
||||
// in the pawn's path attacked or occupied by the enemy.
|
||||
defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
|
||||
squaresToQueen = forward_file_bb(Us, s);
|
||||
unsafeSquares = passed_pawn_span(Us, s);
|
||||
|
||||
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
|
||||
|
||||
if (!(pos.pieces(Us) & bb))
|
||||
defendedSquares &= attackedBy[Us][ALL_PIECES];
|
||||
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
|
||||
|
||||
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
|
||||
// assign a smaller bonus if the block square isn't attacked.
|
||||
int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0;
|
||||
// If there are no enemy attacks on passed pawn span, assign a big bonus.
|
||||
// Otherwise assign a smaller bonus if the path to queen is not attacked
|
||||
// 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.
|
||||
// Otherwise assign a smaller bonus if the block square is defended.
|
||||
if (defendedSquares == squaresToQueen)
|
||||
k += 6;
|
||||
|
||||
else if (defendedSquares & blockSq)
|
||||
k += 4;
|
||||
// Assign a larger bonus if the block square is defended
|
||||
if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq))
|
||||
k += 5;
|
||||
|
||||
bonus += make_score(k * w, k * w);
|
||||
}
|
||||
} // rank > RANK_3
|
||||
} // r > RANK_3
|
||||
|
||||
// Scale down bonus for candidate passers which need more than one
|
||||
// pawn push to become passed, or have a pawn in front of them.
|
||||
if ( !pos.pawn_passed(Us, s + Up)
|
||||
|| (pos.pieces(PAWN) & forward_file_bb(Us, s)))
|
||||
|| (pos.pieces(PAWN) & (s + Up)))
|
||||
bonus = bonus / 2;
|
||||
|
||||
score += bonus + PassedFile[file_of(s)];
|
||||
score += bonus - PassedFile * map_to_queenside(file_of(s));
|
||||
}
|
||||
|
||||
if (T)
|
||||
@@ -711,7 +664,8 @@ namespace {
|
||||
if (pos.non_pawn_material() < SpaceThreshold)
|
||||
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 =
|
||||
Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
|
||||
: CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
|
||||
@@ -723,12 +677,11 @@ namespace {
|
||||
|
||||
// Find all squares which are at most three squares behind some friendly pawn
|
||||
Bitboard behind = pos.pieces(Us, PAWN);
|
||||
behind |= (Us == WHITE ? behind >> 8 : behind << 8);
|
||||
behind |= (Us == WHITE ? behind >> 16 : behind << 16);
|
||||
|
||||
int bonus = popcount(safe) + popcount(behind & safe);
|
||||
int weight = pos.count<ALL_PIECES>(Us) - 2 * pe->open_files();
|
||||
behind |= shift<Down>(behind);
|
||||
behind |= shift<Down+Down>(behind);
|
||||
|
||||
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);
|
||||
|
||||
if (T)
|
||||
@@ -743,31 +696,44 @@ namespace {
|
||||
// known attacking/defending status of the players.
|
||||
|
||||
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))
|
||||
- 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)
|
||||
&& (pos.pieces(PAWN) & KingSide);
|
||||
|
||||
// Compute the initiative bonus for the attacking side
|
||||
int complexity = 8 * pe->pawn_asymmetry()
|
||||
+ 12 * pos.count<PAWN>()
|
||||
+ 12 * outflanking
|
||||
+ 16 * pawnsOnBothFlanks
|
||||
+ 48 * !pos.non_pawn_material()
|
||||
-118 ;
|
||||
bool almostUnwinnable = !pe->passed_count()
|
||||
&& outflanking < 0
|
||||
&& !pawnsOnBothFlanks;
|
||||
|
||||
// Now apply the bonus: note that we find the attacking side by extracting
|
||||
// the sign of the endgame value, and that we carefully cap the bonus so
|
||||
// that the endgame score will never change sign after the bonus.
|
||||
// Compute the initiative bonus for the attacking side
|
||||
int complexity = 9 * pe->passed_count()
|
||||
+ 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));
|
||||
|
||||
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 ( pos.opposite_bishops()
|
||||
&& pos.non_pawn_material(WHITE) == BishopValueMg
|
||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||
sf = 8 + 4 * pe->pawn_asymmetry();
|
||||
&& pos.non_pawn_material() == 2 * BishopValueMg)
|
||||
sf = 22 ;
|
||||
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);
|
||||
@@ -823,7 +789,7 @@ namespace {
|
||||
|
||||
// Early exit if score is high
|
||||
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;
|
||||
|
||||
// Main evaluation begins here
|
||||
@@ -844,14 +810,14 @@ namespace {
|
||||
+ passed< WHITE>() - passed< BLACK>()
|
||||
+ space< WHITE>() - space< BLACK>();
|
||||
|
||||
score += initiative(eg_value(score));
|
||||
score += initiative(score);
|
||||
|
||||
// Interpolate between a middlegame and a (scaled by 'sf') endgame score
|
||||
ScaleFactor sf = scale_factor(eg_value(score));
|
||||
v = mg_value(score) * int(me->game_phase())
|
||||
+ 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
|
||||
if (T)
|
||||
@@ -884,6 +850,9 @@ Value Eval::evaluate(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));
|
||||
|
||||
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
|
||||
@@ -899,7 +868,6 @@ std::string Eval::trace(const Position& pos) {
|
||||
<< " ------------+-------------+-------------+------------\n"
|
||||
<< " Material | " << Term(MATERIAL)
|
||||
<< " Imbalance | " << Term(IMBALANCE)
|
||||
<< " Initiative | " << Term(INITIATIVE)
|
||||
<< " Pawns | " << Term(PAWN)
|
||||
<< " Knights | " << Term(KNIGHT)
|
||||
<< " Bishops | " << Term(BISHOP)
|
||||
@@ -910,6 +878,7 @@ std::string Eval::trace(const Position& pos) {
|
||||
<< " Threats | " << Term(THREAT)
|
||||
<< " Passed | " << Term(PASSED)
|
||||
<< " Space | " << Term(SPACE)
|
||||
<< " Initiative | " << Term(INITIATIVE)
|
||||
<< " ------------+-------------+-------------+------------\n"
|
||||
<< " Total | " << Term(TOTAL);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -29,7 +29,7 @@ class Position;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
#include "endgame.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
|
||||
namespace PSQT {
|
||||
@@ -41,8 +42,7 @@ int main(int argc, char* argv[]) {
|
||||
Bitboards::init();
|
||||
Position::init();
|
||||
Bitbases::init();
|
||||
Search::init();
|
||||
Pawns::init();
|
||||
Endgames::init();
|
||||
Threads.set(Options["Threads"]);
|
||||
Search::clear(); // After threads are up
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <algorithm> // For std::min
|
||||
#include <cassert>
|
||||
#include <cstring> // For std::memset
|
||||
|
||||
@@ -70,14 +69,12 @@ namespace {
|
||||
|
||||
bool is_KBPsK(const Position& pos, Color us) {
|
||||
return pos.non_pawn_material(us) == BishopValueMg
|
||||
&& pos.count<BISHOP>(us) == 1
|
||||
&& pos.count<PAWN >(us) >= 1;
|
||||
}
|
||||
|
||||
bool is_KQKRPs(const Position& pos, Color us) {
|
||||
return !pos.count<PAWN>(us)
|
||||
&& pos.non_pawn_material(us) == QueenValueMg
|
||||
&& pos.count<QUEEN>(us) == 1
|
||||
&& pos.count<ROOK>(~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_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]
|
||||
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
|
||||
// material configuration. Firstly we look for a fixed configuration one, then
|
||||
// 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;
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (Color c : { WHITE, BLACK })
|
||||
if (is_KXK(pos, 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
|
||||
// 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
|
||||
return e;
|
||||
@@ -163,7 +160,7 @@ Entry* probe(const Position& pos) {
|
||||
// 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
|
||||
// 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))
|
||||
e->scalingFunction[c] = &ScaleKBPsK[c];
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -58,7 +58,7 @@ struct Entry {
|
||||
Key key;
|
||||
const EndgameBase<Value>* evaluationFunction;
|
||||
const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
|
||||
// side (e.g. KPKP, KBPsKs)
|
||||
// side (e.g. KPKP, KBPsK)
|
||||
int16_t value;
|
||||
uint8_t factor[COLOR_NB];
|
||||
Phase gamePhase;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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
|
||||
/// 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
|
||||
/// 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())
|
||||
{
|
||||
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);
|
||||
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
|
||||
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 c, bool b) { if (c) dbg_hit_on(b); }
|
||||
@@ -168,7 +246,7 @@ void dbg_print() {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
|
||||
|
||||
static Mutex m;
|
||||
static std::mutex m;
|
||||
|
||||
if (sc == IO_LOCK)
|
||||
m.lock();
|
||||
@@ -210,12 +288,6 @@ void prefetch(void* addr) {
|
||||
|
||||
#endif
|
||||
|
||||
void prefetch2(void* addr) {
|
||||
|
||||
prefetch(addr);
|
||||
prefetch((uint8_t*)addr + 64);
|
||||
}
|
||||
|
||||
namespace WinProcGroup {
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -257,7 +329,7 @@ int best_group(size_t idx) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength)
|
||||
while (byteOffset < returnLength)
|
||||
{
|
||||
if (ptr->Relationship == RelationNumaNode)
|
||||
nodes++;
|
||||
@@ -268,6 +340,7 @@ int best_group(size_t idx) {
|
||||
threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
|
||||
}
|
||||
|
||||
assert(ptr->Size);
|
||||
byteOffset += ptr->Size;
|
||||
ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -30,8 +30,8 @@
|
||||
#include "types.h"
|
||||
|
||||
const std::string engine_info(bool to_uci = false);
|
||||
const std::string compiler_info();
|
||||
void prefetch(void* addr);
|
||||
void prefetch2(void* addr);
|
||||
void start_logger(const std::string& fname);
|
||||
|
||||
void dbg_hit_on(bool b);
|
||||
@@ -53,7 +53,7 @@ struct HashTable {
|
||||
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
|
||||
|
||||
private:
|
||||
std::vector<Entry> table = std::vector<Entry>(Size);
|
||||
std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -25,47 +25,6 @@
|
||||
|
||||
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>
|
||||
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
|
||||
|
||||
@@ -93,16 +52,15 @@ namespace {
|
||||
template<Color Us, GenType Type>
|
||||
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
|
||||
|
||||
// Compute our parametrized parameters at compile time, named according to
|
||||
// the point of view of white side.
|
||||
// Compute some compile time parameters relative to the white side
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
||||
constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
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 UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
|
||||
|
||||
const Square ksq = pos.square<KING>(Them);
|
||||
Bitboard emptySquares;
|
||||
|
||||
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
|
||||
@@ -127,8 +85,6 @@ namespace {
|
||||
|
||||
if (Type == QUIET_CHECKS)
|
||||
{
|
||||
Square ksq = pos.square<KING>(Them);
|
||||
|
||||
b1 &= 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
|
||||
// don't generate captures. Note that a possible discovery check
|
||||
// promotion has been already generated amongst the captures.
|
||||
Bitboard dcCandidates = pos.blockers_for_king(Them);
|
||||
if (pawnsNotOn7 & dcCandidates)
|
||||
Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7;
|
||||
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;
|
||||
|
||||
b1 |= dc1;
|
||||
@@ -161,7 +117,7 @@ namespace {
|
||||
}
|
||||
|
||||
// Promotions and underpromotions
|
||||
if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB)))
|
||||
if (pawnsOn7)
|
||||
{
|
||||
if (Type == CAPTURES)
|
||||
emptySquares = ~pos.pieces();
|
||||
@@ -173,8 +129,6 @@ namespace {
|
||||
Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
|
||||
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
|
||||
|
||||
Square ksq = pos.square<KING>(Them);
|
||||
|
||||
while (b1)
|
||||
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,
|
||||
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);
|
||||
|
||||
@@ -262,7 +216,9 @@ namespace {
|
||||
template<Color Us, GenType Type>
|
||||
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_moves<KNIGHT, Checks>(pos, moveList, Us, target);
|
||||
@@ -276,19 +232,14 @@ namespace {
|
||||
Bitboard b = pos.attacks_from<KING>(ksq) & target;
|
||||
while (b)
|
||||
*moveList++ = make_move(ksq, pop_lsb(&b));
|
||||
}
|
||||
|
||||
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
|
||||
{
|
||||
if (pos.is_chess960())
|
||||
if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
|
||||
{
|
||||
moveList = generate_castling<Us, KING_SIDE, Checks, true>(pos, moveList);
|
||||
moveList = generate_castling<Us, QUEEN_SIDE, Checks, true>(pos, moveList);
|
||||
}
|
||||
else
|
||||
{
|
||||
moveList = generate_castling<Us, KING_SIDE, Checks, false>(pos, moveList);
|
||||
moveList = generate_castling<Us, QUEEN_SIDE, Checks, false>(pos, moveList);
|
||||
if (!pos.castling_impeded(OO) && pos.can_castle(OO))
|
||||
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OO));
|
||||
|
||||
if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
|
||||
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OOO));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,19 +249,16 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
|
||||
/// generate<CAPTURES> generates all pseudo-legal captures and queen
|
||||
/// promotions. Returns a pointer to the end of the move list.
|
||||
/// <CAPTURES> Generates all pseudo-legal captures and queen promotions
|
||||
/// <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
|
||||
/// 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.
|
||||
/// Returns a pointer to the end of the move list.
|
||||
|
||||
template<GenType Type>
|
||||
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());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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
|
||||
};
|
||||
|
||||
// Helper filter used with select()
|
||||
const auto Any = [](){ return true; };
|
||||
|
||||
// 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.
|
||||
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),
|
||||
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;
|
||||
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)
|
||||
: 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;
|
||||
ttMove = ttm
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) ? ttm : MOVE_NONE;
|
||||
ttMove = ttm
|
||||
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
|
||||
&& pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
|
||||
stage += (ttMove == MOVE_NONE);
|
||||
}
|
||||
|
||||
@@ -94,8 +91,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece
|
||||
|
||||
stage = PROBCUT_TT;
|
||||
ttMove = ttm
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& pos.capture(ttm)
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE;
|
||||
stage += (ttMove == MOVE_NONE);
|
||||
}
|
||||
@@ -110,14 +107,15 @@ void MovePicker::score() {
|
||||
|
||||
for (auto& m : *this)
|
||||
if (Type == CAPTURES)
|
||||
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))] / 8;
|
||||
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)))];
|
||||
|
||||
else if (Type == QUIETS)
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)];
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ 2 * (*continuationHistory[1])[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
|
||||
{
|
||||
@@ -141,12 +139,12 @@ Move MovePicker::select(Pred filter) {
|
||||
if (T == Best)
|
||||
std::swap(*cur, *std::max_element(cur, endMoves));
|
||||
|
||||
move = *cur++;
|
||||
if (*cur != ttMove && filter())
|
||||
return *cur++;
|
||||
|
||||
if (move != ttMove && filter())
|
||||
return move;
|
||||
cur++;
|
||||
}
|
||||
return move = MOVE_NONE;
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
/// MovePicker::next_move() is the most important method of the MovePicker class. It
|
||||
@@ -176,10 +174,10 @@ top:
|
||||
|
||||
case GOOD_CAPTURE:
|
||||
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
|
||||
true : (*endBadCaptures++ = move, false); }))
|
||||
return move;
|
||||
true : (*endBadCaptures++ = *cur, false); }))
|
||||
return *(cur - 1);
|
||||
|
||||
// Prepare the pointers to loop over the refutations array
|
||||
cur = std::begin(refutations);
|
||||
@@ -194,28 +192,32 @@ top:
|
||||
/* fallthrough */
|
||||
|
||||
case REFUTATION:
|
||||
if (select<Next>([&](){ return move != MOVE_NONE
|
||||
&& !pos.capture(move)
|
||||
&& pos.pseudo_legal(move); }))
|
||||
return move;
|
||||
if (select<Next>([&](){ return *cur != MOVE_NONE
|
||||
&& !pos.capture(*cur)
|
||||
&& pos.pseudo_legal(*cur); }))
|
||||
return *(cur - 1);
|
||||
++stage;
|
||||
/* fallthrough */
|
||||
|
||||
case QUIET_INIT:
|
||||
cur = endBadCaptures;
|
||||
endMoves = generate<QUIETS>(pos, cur);
|
||||
if (!skipQuiets)
|
||||
{
|
||||
cur = endBadCaptures;
|
||||
endMoves = generate<QUIETS>(pos, cur);
|
||||
|
||||
score<QUIETS>();
|
||||
partial_insertion_sort(cur, endMoves, -3000 * depth);
|
||||
}
|
||||
|
||||
score<QUIETS>();
|
||||
partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY);
|
||||
++stage;
|
||||
/* fallthrough */
|
||||
|
||||
case QUIET:
|
||||
if ( !skipQuiets
|
||||
&& select<Next>([&](){return move != refutations[0]
|
||||
&& move != refutations[1]
|
||||
&& move != refutations[2];}))
|
||||
return move;
|
||||
&& select<Next>([&](){return *cur != refutations[0].move
|
||||
&& *cur != refutations[1].move
|
||||
&& *cur != refutations[2].move;}))
|
||||
return *(cur - 1);
|
||||
|
||||
// Prepare the pointers to loop over the bad captures
|
||||
cur = moves;
|
||||
@@ -225,7 +227,7 @@ top:
|
||||
/* fallthrough */
|
||||
|
||||
case BAD_CAPTURE:
|
||||
return select<Next>(Any);
|
||||
return select<Next>([](){ return true; });
|
||||
|
||||
case EVASION_INIT:
|
||||
cur = moves;
|
||||
@@ -236,15 +238,15 @@ top:
|
||||
/* fallthrough */
|
||||
|
||||
case EVASION:
|
||||
return select<Best>(Any);
|
||||
return select<Best>([](){ return true; });
|
||||
|
||||
case PROBCUT:
|
||||
return select<Best>([&](){ return pos.see_ge(move, threshold); });
|
||||
return select<Best>([&](){ return pos.see_ge(*cur, threshold); });
|
||||
|
||||
case QCAPTURE:
|
||||
if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES
|
||||
|| to_sq(move) == recaptureSquare; }))
|
||||
return move;
|
||||
|| to_sq(*cur) == recaptureSquare; }))
|
||||
return *(cur - 1);
|
||||
|
||||
// If we did not find any move and we do not try checks, we have finished
|
||||
if (depth != DEPTH_QS_CHECKS)
|
||||
@@ -261,7 +263,7 @@ top:
|
||||
/* fallthrough */
|
||||
|
||||
case QCHECK:
|
||||
return select<Next>(Any);
|
||||
return select<Next>([](){ return true; });
|
||||
}
|
||||
|
||||
assert(false);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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
|
||||
enum StatsParams { NOT_USED = 0 };
|
||||
|
||||
enum StatsType { NoCaptures, Captures };
|
||||
|
||||
/// ButterflyHistory records how often quiet moves have been successful or
|
||||
/// unsuccessful during the current search, and is used for reduction and move
|
||||
/// ordering decisions. It uses 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
|
||||
@@ -142,7 +142,6 @@ private:
|
||||
Move ttMove;
|
||||
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
|
||||
int stage;
|
||||
Move move;
|
||||
Square recaptureSquare;
|
||||
Value threshold;
|
||||
Depth depth;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -32,12 +32,15 @@ namespace {
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Pawn penalties
|
||||
constexpr Score Backward = S( 9, 24);
|
||||
constexpr Score Doubled = S(11, 56);
|
||||
constexpr Score Isolated = S( 5, 15);
|
||||
constexpr Score Backward = S( 9, 24);
|
||||
constexpr Score BlockedStorm = S(82, 82);
|
||||
constexpr Score Doubled = S(11, 56);
|
||||
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
|
||||
Score Connected[2][2][3][RANK_NB];
|
||||
// Connected pawn bonus
|
||||
constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };
|
||||
|
||||
// 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.
|
||||
@@ -50,12 +53,13 @@ namespace {
|
||||
|
||||
// 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
|
||||
// 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] = {
|
||||
{ V( 89), V(107), V(123), V(93), V(57), V( 45), V( 51) },
|
||||
{ V( 44), V(-18), V(123), V(46), V(39), V( -7), V( 23) },
|
||||
{ V( 4), V( 52), V(162), V(37), V( 7), V(-14), V( -2) },
|
||||
{ V(-10), V(-14), V( 90), V(15), V( 2), V( -7), V(-16) }
|
||||
{ V( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) },
|
||||
{ V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) },
|
||||
{ V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) },
|
||||
{ V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) }
|
||||
};
|
||||
|
||||
#undef S
|
||||
@@ -65,80 +69,86 @@ namespace {
|
||||
Score evaluate(const Position& pos, Pawns::Entry* e) {
|
||||
|
||||
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 lever, leverPush;
|
||||
Bitboard neighbours, stoppers, support, phalanx, opposed;
|
||||
Bitboard lever, leverPush, blocked;
|
||||
Square s;
|
||||
bool opposed, backward;
|
||||
bool backward, passed, doubled;
|
||||
Score score = SCORE_ZERO;
|
||||
const Square* pl = pos.squares<PAWN>(Us);
|
||||
|
||||
Bitboard ourPawns = pos.pieces( Us, PAWN);
|
||||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
||||
|
||||
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
|
||||
e->semiopenFiles[Us] = 0xFF;
|
||||
e->kingSquares[Us] = SQ_NONE;
|
||||
e->pawnAttacks[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];
|
||||
Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns);
|
||||
|
||||
e->passedPawns[Us] = 0;
|
||||
e->kingSquares[Us] = SQ_NONE;
|
||||
e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb<Us>(ourPawns);
|
||||
|
||||
// Loop through all pawns of the current color and score each pawn
|
||||
while ((s = *pl++) != SQ_NONE)
|
||||
{
|
||||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
||||
|
||||
File f = file_of(s);
|
||||
|
||||
e->semiopenFiles[Us] &= ~(1 << f);
|
||||
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
||||
Rank r = relative_rank(Us, s);
|
||||
|
||||
// Flag the pawn
|
||||
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];
|
||||
leverPush = theirPawns & PawnAttacks[Us][s + Up];
|
||||
doubled = ourPawns & (s - Up);
|
||||
neighbours = ourPawns & adjacent_files_bb(f);
|
||||
neighbours = ourPawns & adjacent_files_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
|
||||
// on the adjacent files and cannot be safely advanced.
|
||||
backward = !(ourPawns & pawn_attack_span(Them, s + Up))
|
||||
&& (stoppers & (leverPush | (s + Up)));
|
||||
// A pawn is backward when it is behind all pawns of the same color on
|
||||
// the adjacent files and cannot safely advance.
|
||||
backward = !(neighbours & forward_ranks_bb(Them, s + Up))
|
||||
&& (leverPush | blocked);
|
||||
|
||||
// Passed pawns will be properly scored in evaluation because we need
|
||||
// full attack info to evaluate them. Include also not passed pawns
|
||||
// which could become passed after one or two pawn pushes when are
|
||||
// not attacked more times than defended.
|
||||
if ( !(stoppers ^ lever ^ leverPush)
|
||||
&& popcount(supported) >= popcount(lever) - 1
|
||||
&& popcount(phalanx) >= popcount(leverPush))
|
||||
// Compute additional span if pawn is not backward nor blocked
|
||||
if (!backward && !blocked)
|
||||
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
||||
|
||||
// A pawn is passed if one of the three following conditions is true:
|
||||
// (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))
|
||||
|| ( stoppers == blocked && r >= RANK_5
|
||||
&& (shift<Up>(support) & ~(theirPawns | doubleAttackThem)));
|
||||
|
||||
// Passed pawns will be properly scored later in evaluation when we have
|
||||
// full attack info.
|
||||
if (passed)
|
||||
e->passedPawns[Us] |= s;
|
||||
|
||||
else if ( stoppers == SquareBB[s + Up]
|
||||
&& relative_rank(Us, s) >= RANK_5)
|
||||
// Score this pawn
|
||||
if (support | phalanx)
|
||||
{
|
||||
b = shift<Up>(supported) & ~theirPawns;
|
||||
while (b)
|
||||
if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
|
||||
e->passedPawns[Us] |= s;
|
||||
int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
|
||||
+ 21 * popcount(support);
|
||||
|
||||
score += make_score(v, v * (r - 2) / 4);
|
||||
}
|
||||
|
||||
// Score this pawn
|
||||
if (supported | phalanx)
|
||||
score += Connected[opposed][bool(phalanx)][popcount(supported)][relative_rank(Us, s)];
|
||||
|
||||
else if (!neighbours)
|
||||
score -= Isolated, e->weakUnopposed[Us] += !opposed;
|
||||
score -= Isolated
|
||||
+ WeakUnopposed * !opposed;
|
||||
|
||||
else if (backward)
|
||||
score -= Backward, e->weakUnopposed[Us] += !opposed;
|
||||
score -= Backward
|
||||
+ WeakUnopposed * !opposed;
|
||||
|
||||
if (doubled && !supported)
|
||||
score -= Doubled;
|
||||
if (!support)
|
||||
score -= Doubled * doubled
|
||||
+ WeakLever * more_than_one(lever);
|
||||
}
|
||||
|
||||
return score;
|
||||
@@ -148,27 +158,6 @@ namespace {
|
||||
|
||||
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
|
||||
/// 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
|
||||
@@ -185,9 +174,6 @@ Entry* probe(const Position& pos) {
|
||||
e->key = key;
|
||||
e->scores[WHITE] = evaluate<WHITE>(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;
|
||||
}
|
||||
@@ -197,35 +183,35 @@ Entry* probe(const Position& pos) {
|
||||
/// penalty for a king, looking at the king file and the two closest files.
|
||||
|
||||
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 Direction Down = (Us == WHITE ? SOUTH : NORTH);
|
||||
constexpr Bitboard BlockRanks = (Us == WHITE ? Rank1BB | Rank2BB : Rank8BB | Rank7BB);
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
|
||||
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
|
||||
Bitboard ourPawns = b & pos.pieces(Us);
|
||||
Bitboard theirPawns = b & pos.pieces(Them);
|
||||
|
||||
Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ?
|
||||
Value(374) : Value(5);
|
||||
Score bonus = make_score(5, 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)
|
||||
{
|
||||
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);
|
||||
int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
|
||||
|
||||
int d = std::min(f, ~f);
|
||||
safety += ShelterStrength[d][ourRank];
|
||||
safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3)
|
||||
: UnblockedStorm[d][theirRank];
|
||||
File d = map_to_queenside(f);
|
||||
bonus += make_score(ShelterStrength[d][ourRank], 0);
|
||||
|
||||
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);
|
||||
kingSquares[Us] = ksq;
|
||||
castlingRights[Us] = pos.can_castle(Us);
|
||||
int minKingPawnDistance = 0;
|
||||
castlingRights[Us] = pos.castling_rights(Us);
|
||||
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);
|
||||
if (pawns)
|
||||
while (!(DistanceRingBB[ksq][++minKingPawnDistance] & pawns)) {}
|
||||
int minPawnDist = pawns ? 8 : 0;
|
||||
|
||||
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
|
||||
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);
|
||||
return shelter - make_score(0, 16 * minPawnDist);
|
||||
}
|
||||
|
||||
// Explicit template instantiation
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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 passed_pawns(Color c) const { return passedPawns[c]; }
|
||||
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
|
||||
int weak_unopposed(Color c) const { return weakUnopposed[c]; }
|
||||
int pawn_asymmetry() const { return asymmetry; }
|
||||
int open_files() const { return openFiles; }
|
||||
|
||||
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)];
|
||||
}
|
||||
int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }
|
||||
|
||||
template<Color Us>
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -59,7 +49,7 @@ struct Entry {
|
||||
Score do_king_safety(const Position& pos);
|
||||
|
||||
template<Color Us>
|
||||
Value evaluate_shelter(const Position& pos, Square ksq);
|
||||
Score evaluate_shelter(const Position& pos, Square ksq);
|
||||
|
||||
Key key;
|
||||
Score scores[COLOR_NB];
|
||||
@@ -68,17 +58,11 @@ struct Entry {
|
||||
Bitboard pawnAttacksSpan[COLOR_NB];
|
||||
Square kingSquares[COLOR_NB];
|
||||
Score kingSafety[COLOR_NB];
|
||||
int weakUnopposed[COLOR_NB];
|
||||
int castlingRights[COLOR_NB];
|
||||
int semiopenFiles[COLOR_NB];
|
||||
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
|
||||
int asymmetry;
|
||||
int openFiles;
|
||||
};
|
||||
|
||||
typedef HashTable<Entry, 16384> Table;
|
||||
typedef HashTable<Entry, 131072> Table;
|
||||
|
||||
void init();
|
||||
Entry* probe(const Position& pos);
|
||||
|
||||
} // namespace Pawns
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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,
|
||||
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
|
||||
|
||||
|
||||
@@ -183,7 +148,7 @@ void Position::init() {
|
||||
{
|
||||
std::swap(cuckoo[i], key);
|
||||
std::swap(cuckooMove[i], move);
|
||||
if (move == 0) // Arrived at empty slot ?
|
||||
if (move == MOVE_NONE) // Arrived at empty slot?
|
||||
break;
|
||||
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) {
|
||||
|
||||
Square kfrom = square<KING>(c);
|
||||
CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE;
|
||||
CastlingRight cr = (c | cs);
|
||||
CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
|
||||
|
||||
st->castlingRights |= cr;
|
||||
castlingRightsMask[kfrom] |= cr;
|
||||
castlingRightsMask[rfrom] |= cr;
|
||||
castlingRookSquare[cr] = rfrom;
|
||||
|
||||
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
|
||||
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
|
||||
Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
|
||||
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)
|
||||
if (s != kfrom && s != 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;
|
||||
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
|
||||
& ~(square_bb(kfrom) | rfrom);
|
||||
}
|
||||
|
||||
|
||||
@@ -388,6 +347,12 @@ void Position::set_state(StateInfo* si) const {
|
||||
Square s = pop_lsb(&b);
|
||||
Piece pc = piece_on(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)
|
||||
@@ -398,20 +363,9 @@ void Position::set_state(StateInfo* si) const {
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
si->materialKey ^= Zobrist::psq[pc][cnt];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -465,18 +419,18 @@ const string Position::fen() const {
|
||||
ss << (sideToMove == WHITE ? " w " : " b ");
|
||||
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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 << (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;
|
||||
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))
|
||||
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
|
||||
Bitboard occupancy = pieces() ^ snipers;
|
||||
|
||||
while (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))
|
||||
{
|
||||
@@ -540,6 +495,7 @@ bool Position::legal(Move m) const {
|
||||
|
||||
Color us = sideToMove;
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
|
||||
assert(color_of(moved_piece(m)) == us);
|
||||
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)
|
||||
{
|
||||
Square ksq = square<KING>(us);
|
||||
Square to = to_sq(m);
|
||||
Square capsq = to - pawn_push(us);
|
||||
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));
|
||||
}
|
||||
|
||||
// If the moving piece is a king, check whether the destination
|
||||
// square is attacked by the opponent. Castling moves are checked
|
||||
// for legality during move generation.
|
||||
// Castling moves generation does not check if the castling path is clear of
|
||||
// enemy attacks, it is delayed at a later time: now!
|
||||
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)
|
||||
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
|
||||
// is moving along the ray towards or away from the king.
|
||||
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
|
||||
// cannot be on the 8th/1st rank.
|
||||
if (rank_of(to) == relative_rank(us, RANK_8))
|
||||
if ((Rank8BB | Rank1BB) & to)
|
||||
return false;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
// Update pawn hash key and prefetch access to pawnsTable
|
||||
// Update pawn hash key
|
||||
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
|
||||
prefetch2(thisThread->pawnsTable[st->pawnKey]);
|
||||
|
||||
// Reset rule 50 draw counter
|
||||
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
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
void Position::do_null_move(StateInfo& newSt) {
|
||||
@@ -980,6 +972,8 @@ void Position::do_null_move(StateInfo& newSt) {
|
||||
|
||||
set_check_info(st);
|
||||
|
||||
st->repetition = 0;
|
||||
|
||||
assert(pos_is_ok());
|
||||
}
|
||||
|
||||
@@ -1023,77 +1017,96 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||
if (type_of(m) != NORMAL)
|
||||
return VALUE_ZERO >= threshold;
|
||||
|
||||
Bitboard stmAttackers;
|
||||
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
|
||||
// we can hope for.
|
||||
balance = PieceValue[MG][piece_on(to)] - threshold;
|
||||
|
||||
if (balance < VALUE_ZERO)
|
||||
int swap = PieceValue[MG][piece_on(to)] - threshold;
|
||||
if (swap < 0)
|
||||
return false;
|
||||
|
||||
// Now assume the worst possible result: that the opponent can
|
||||
// capture our piece for free.
|
||||
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)
|
||||
swap = PieceValue[MG][piece_on(from)] - swap;
|
||||
if (swap <= 0)
|
||||
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 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)
|
||||
{
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
|
||||
// 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];
|
||||
stm = ~stm;
|
||||
attackers &= occupied;
|
||||
|
||||
// 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)
|
||||
break;
|
||||
|
||||
res ^= 1;
|
||||
|
||||
// Locate and remove the next least valuable attacker, and add to
|
||||
// the bitboard 'attackers' the possibly X-ray attackers behind it.
|
||||
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
|
||||
|
||||
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)
|
||||
// the bitboard 'attackers' any X-ray attackers behind it.
|
||||
if ((bb = stmAttackers & pieces(PAWN)))
|
||||
{
|
||||
if (nextVictim == KING && (attackers & pieces(stm)))
|
||||
stm = ~stm;
|
||||
break;
|
||||
}
|
||||
assert(nextVictim != KING);
|
||||
}
|
||||
return us != stm; // We break the above loop when stm loses
|
||||
}
|
||||
if ((swap = PawnValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= lsb(bb);
|
||||
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
|
||||
}
|
||||
|
||||
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
|
||||
/// 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()))
|
||||
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
|
||||
// after the root, or repeats twice before or at the root.
|
||||
if ( stp->key == st->key
|
||||
&& ++cnt + (ply > i) == 2)
|
||||
return true;
|
||||
}
|
||||
// Return a draw score if a position repeats once earlier but strictly
|
||||
// after the root, or repeats twice before or at the root.
|
||||
if (st->repetition && st->repetition < ply)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1132,26 +1131,15 @@ bool Position::is_draw(int ply) const {
|
||||
bool Position::has_repeated() const {
|
||||
|
||||
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 (end < i)
|
||||
return false;
|
||||
|
||||
StateInfo* stp = stc->previous->previous;
|
||||
|
||||
do {
|
||||
stp = stp->previous->previous;
|
||||
|
||||
if (stp->key == stc->key)
|
||||
return true;
|
||||
|
||||
i += 2;
|
||||
} while (i <= end);
|
||||
if (stc->repetition)
|
||||
return true;
|
||||
|
||||
stc = stc->previous;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1184,22 +1172,19 @@ bool Position::has_game_cycle(int ply) const {
|
||||
|
||||
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)
|
||||
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
|
||||
StateInfo* next_stp = stp;
|
||||
for (int k = i + 2; k <= end; k += 2)
|
||||
{
|
||||
next_stp = next_stp->previous->previous;
|
||||
if (next_stp->key == stp->key)
|
||||
return true;
|
||||
}
|
||||
if (stp->repetition)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1297,15 +1282,15 @@ bool Position::pos_is_ok() const {
|
||||
assert(0 && "pos_is_ok: Index");
|
||||
}
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
|
||||
for (Color c : { WHITE, BLACK })
|
||||
for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
|
||||
{
|
||||
if (!can_castle(c | s))
|
||||
if (!can_castle(cr))
|
||||
continue;
|
||||
|
||||
if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK)
|
||||
|| castlingRightsMask[castlingRookSquare[c | s]] != (c | s)
|
||||
|| (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s))
|
||||
if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
|
||||
|| castlingRightsMask[castlingRookSquare[cr]] != cr
|
||||
|| (castlingRightsMask[square<KING>(c)] & cr) != cr)
|
||||
assert(0 && "pos_is_ok: Castling");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -53,6 +53,7 @@ struct StateInfo {
|
||||
Bitboard blockersForKing[COLOR_NB];
|
||||
Bitboard pinners[COLOR_NB];
|
||||
Bitboard checkSquares[PIECE_TYPE_NB];
|
||||
int repetition;
|
||||
};
|
||||
|
||||
/// 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> const Square* squares(Color c) const;
|
||||
template<PieceType Pt> Square square(Color c) const;
|
||||
bool is_on_semiopen_file(Color c, Square s) const;
|
||||
|
||||
// Castling
|
||||
int can_castle(Color c) const;
|
||||
int can_castle(CastlingRight cr) const;
|
||||
bool castling_impeded(CastlingRight cr) const;
|
||||
Square castling_rook_square(CastlingRight cr) const;
|
||||
int castling_rights(Color c) const;
|
||||
bool can_castle(CastlingRights cr) const;
|
||||
bool castling_impeded(CastlingRights cr) const;
|
||||
Square castling_rook_square(CastlingRights cr) const;
|
||||
|
||||
// Checking
|
||||
Bitboard checkers() const;
|
||||
Bitboard blockers_for_king(Color c) const;
|
||||
Bitboard check_squares(PieceType pt) const;
|
||||
bool is_discovery_check_on_king(Color c, Move m) const;
|
||||
|
||||
// Attacks to/from a given square
|
||||
Bitboard attackers_to(Square s) const;
|
||||
@@ -128,6 +131,7 @@ public:
|
||||
// Piece specific
|
||||
bool pawn_passed(Color c, Square s) const;
|
||||
bool opposite_bishops() const;
|
||||
int pawns_on_same_color_squares(Color c, Square s) const;
|
||||
|
||||
// Doing and undoing moves
|
||||
void do_move(Move m, StateInfo& newSt);
|
||||
@@ -260,25 +264,34 @@ inline Square Position::ep_square() const {
|
||||
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;
|
||||
}
|
||||
|
||||
inline int Position::can_castle(Color c) const {
|
||||
return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c));
|
||||
inline int Position::castling_rights(Color c) const {
|
||||
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];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
template<PieceType Pt>
|
||||
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])
|
||||
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
|
||||
: PseudoAttacks[Pt][s];
|
||||
@@ -309,13 +322,21 @@ inline Bitboard Position::check_squares(PieceType pt) const {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
@@ -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[]
|
||||
// is accessed just by known occupied squares.
|
||||
Bitboard fromTo = SquareBB[from] ^ SquareBB[to];
|
||||
Bitboard fromTo = from | to;
|
||||
byTypeBB[ALL_PIECES] ^= fromTo;
|
||||
byTypeBB[type_of(pc)] ^= fromTo;
|
||||
byColorBB[color_of(pc)] ^= fromTo;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -37,67 +37,70 @@ namespace PSQT {
|
||||
// second half of the files.
|
||||
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
|
||||
{ S(-169,-105), S(-96,-74), S(-80,-46), S(-79,-18) },
|
||||
{ S( -79, -70), S(-39,-56), S(-24,-15), S( -9, 6) },
|
||||
{ S( -64, -38), S(-20,-33), S( 4, -5), S( 19, 27) },
|
||||
{ S( -28, -36), S( 5, 0), S( 41, 13), S( 47, 34) },
|
||||
{ S( -29, -41), S( 13,-20), S( 42, 4), S( 52, 35) },
|
||||
{ S( -11, -51), S( 28,-38), S( 63,-17), S( 55, 19) },
|
||||
{ S( -67, -64), S(-21,-45), S( 6,-37), S( 37, 16) },
|
||||
{ S(-200, -98), S(-80,-89), S(-53,-53), S(-32,-16) }
|
||||
{ S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) },
|
||||
{ S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) },
|
||||
{ S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) },
|
||||
{ S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) },
|
||||
{ S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) },
|
||||
{ S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) },
|
||||
{ S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) },
|
||||
{ S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
|
||||
},
|
||||
{ // Bishop
|
||||
{ S(-49,-58), S(- 7,-31), S(-10,-37), S(-34,-19) },
|
||||
{ S(-24,-34), S( 9, -9), S( 15,-14), S( 1, 4) },
|
||||
{ S( -9,-23), S( 22, 0), S( -3, -3), S( 12, 16) },
|
||||
{ S( 4,-26), S( 9, -3), S( 18, -5), S( 40, 16) },
|
||||
{ S( -8,-26), S( 27, -4), S( 13, -7), S( 30, 14) },
|
||||
{ S(-17,-24), S( 14, -2), S( -6, 0), S( 6, 13) },
|
||||
{ S(-19,-34), S(-13,-10), S( 7,-12), S(-11, 6) },
|
||||
{ S(-47,-55), S( -7,-32), S(-17,-36), S(-29,-17) }
|
||||
{ S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) },
|
||||
{ S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) },
|
||||
{ S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) },
|
||||
{ S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) },
|
||||
{ S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) },
|
||||
{ S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) },
|
||||
{ S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) },
|
||||
{ S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) }
|
||||
},
|
||||
{ // Rook
|
||||
{ S(-24, 0), S(-15, 3), S( -8, 0), S( 0, 3) },
|
||||
{ S(-18,-7), S( -5,-5), S( -1,-5), S( 1,-1) },
|
||||
{ S(-19, 6), S(-10,-7), S( 1, 3), S( 0, 3) },
|
||||
{ S(-21, 0), S( -7, 4), S( -4,-2), S(-4, 1) },
|
||||
{ S(-21,-7), S(-12, 5), S( -1,-5), S( 4,-7) },
|
||||
{ S(-23, 3), S(-10, 2), S( 1,-1), S( 6, 3) },
|
||||
{ S(-11,-1), S( 8, 7), S( 9,11), S(12,-1) },
|
||||
{ S(-25, 6), S(-18, 4), S(-11, 6), S( 2, 2) }
|
||||
{ S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
|
||||
{ S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) },
|
||||
{ S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) },
|
||||
{ S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) },
|
||||
{ S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) },
|
||||
{ S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) },
|
||||
{ S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) },
|
||||
{ S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) }
|
||||
},
|
||||
{ // Queen
|
||||
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
|
||||
{ S( -3,-55), S( 5,-31), S( 8,-22), S(12, -4) },
|
||||
{ S( -3,-39), S( 6,-18), S(13, -9), S( 7, 3) },
|
||||
{ S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) },
|
||||
{ S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) },
|
||||
{ S( -4,-38), S(10,-18), S( 6,-12), S( 8, 1) },
|
||||
{ S( -5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
|
||||
{ S( -2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
|
||||
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
|
||||
{ S(-3,-55), S( 5,-31), S( 8,-22), S(12, -4) },
|
||||
{ S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) },
|
||||
{ S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) },
|
||||
{ S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) },
|
||||
{ S(-4,-38), S(10,-18), S( 6,-12), S( 8, 1) },
|
||||
{ S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
|
||||
{ S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
|
||||
},
|
||||
{ // King
|
||||
{ S(272, 0), S(325, 41), S(273, 80), S(190, 93) },
|
||||
{ S(277, 57), S(305, 98), S(241,138), S(183,131) },
|
||||
{ S(198, 86), S(253,138), S(168,165), S(120,173) },
|
||||
{ S(169,103), S(191,152), S(136,168), S(108,169) },
|
||||
{ S(145, 98), S(176,166), S(112,197), S(69, 194) },
|
||||
{ S(122, 87), S(159,164), S(85, 174), S(36, 189) },
|
||||
{ S(87, 40), S(120, 99), S(64, 128), S(25, 141) },
|
||||
{ S(64, 5), S(87, 60), S(49, 75), S(0, 75) }
|
||||
{ S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
|
||||
{ S(278, 53), S(303,100), S(234,133), S(179,135) },
|
||||
{ S(195, 88), S(258,130), S(169,169), S(120,175) },
|
||||
{ S(164,103), S(190,156), S(138,172), S( 98,172) },
|
||||
{ S(154, 96), S(179,166), S(105,199), S( 70,199) },
|
||||
{ S(123, 92), S(145,172), S( 81,184), S( 31,191) },
|
||||
{ S( 88, 47), S(120,121), S( 65,116), S( 33,131) },
|
||||
{ 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
|
||||
|
||||
Score psq[PIECE_NB][SQUARE_NB];
|
||||
@@ -116,8 +119,9 @@ void init() {
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
File f = std::min(file_of(s), ~file_of(s));
|
||||
psq[ pc][ s] = score + Bonus[pc][rank_of(s)][f];
|
||||
File f = map_to_queenside(file_of(s));
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -69,7 +69,8 @@ struct RootMove {
|
||||
Value score = -VALUE_INFINITE;
|
||||
Value previousScore = -VALUE_INFINITE;
|
||||
int selDepth = 0;
|
||||
int tbRank;
|
||||
int tbRank = 0;
|
||||
int bestMoveCount = 0;
|
||||
Value tbScore;
|
||||
std::vector<Move> pv;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -27,12 +27,12 @@
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <mutex>
|
||||
|
||||
#include "../bitboard.h"
|
||||
#include "../movegen.h"
|
||||
#include "../position.h"
|
||||
#include "../search.h"
|
||||
#include "../thread_win32.h"
|
||||
#include "../types.h"
|
||||
#include "../uci.h"
|
||||
|
||||
@@ -45,7 +45,9 @@
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX // Disable macros min() and max()
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
@@ -214,39 +216,57 @@ public:
|
||||
return *baseAddress = nullptr, nullptr;
|
||||
|
||||
fstat(fd, &statbuf);
|
||||
|
||||
if (statbuf.st_size % 64 != 16)
|
||||
{
|
||||
std::cerr << "Corrupt tablebase file " << fname << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
*mapping = statbuf.st_size;
|
||||
*baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
madvise(*baseAddress, statbuf.st_size, MADV_RANDOM);
|
||||
::close(fd);
|
||||
|
||||
if (*baseAddress == MAP_FAILED) {
|
||||
if (*baseAddress == MAP_FAILED)
|
||||
{
|
||||
std::cerr << "Could not mmap() " << fname << std::endl;
|
||||
exit(1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#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,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
|
||||
|
||||
if (fd == INVALID_HANDLE_VALUE)
|
||||
return *baseAddress = nullptr, nullptr;
|
||||
|
||||
DWORD 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);
|
||||
CloseHandle(fd);
|
||||
|
||||
if (!mmap) {
|
||||
if (!mmap)
|
||||
{
|
||||
std::cerr << "CreateFileMapping() failed" << std::endl;
|
||||
exit(1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
*mapping = (uint64_t)mmap;
|
||||
*baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
|
||||
|
||||
if (!*baseAddress) {
|
||||
if (!*baseAddress)
|
||||
{
|
||||
std::cerr << "MapViewOfFile() failed, name = " << fname
|
||||
<< ", error = " << GetLastError() << std::endl;
|
||||
exit(1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
uint8_t* data = (uint8_t*)*baseAddress;
|
||||
@@ -254,7 +274,8 @@ public:
|
||||
constexpr uint8_t Magics[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 },
|
||||
{ 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;
|
||||
unmap(*baseAddress, *mapping);
|
||||
return *baseAddress = nullptr, nullptr;
|
||||
@@ -348,7 +369,7 @@ TBTable<WDL>::TBTable(const std::string& code) : TBTable() {
|
||||
hasPawns = pos.pieces(PAWN);
|
||||
|
||||
hasUniquePieces = false;
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (Color c : { WHITE, BLACK })
|
||||
for (PieceType pt = PAWN; pt < KING; ++pt)
|
||||
if (popcount(pos.pieces(c, pt)) == 1)
|
||||
hasUniquePieces = true;
|
||||
@@ -415,7 +436,7 @@ class TBTables {
|
||||
}
|
||||
}
|
||||
std::cerr << "TB hash table size too low!" << std::endl;
|
||||
exit(1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int flipColor = (symmetricBlackToMove || blackStronger) * 8;
|
||||
int flipSquares = (symmetricBlackToMove || blackStronger) * 070;
|
||||
int flipSquares = (symmetricBlackToMove || blackStronger) * 56;
|
||||
int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move();
|
||||
|
||||
// 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));
|
||||
|
||||
tbFile = file_of(squares[0]);
|
||||
if (tbFile > FILE_D)
|
||||
tbFile = file_of(squares[0] ^ 7); // Horizontal flip: SQ_H1 -> SQ_A1
|
||||
tbFile = map_to_queenside(file_of(squares[0]));
|
||||
}
|
||||
|
||||
// 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
|
||||
// in pieces[i]: the sequence that ensures the best compression.
|
||||
for (int i = leadPawnsCnt; i < size; ++i)
|
||||
for (int j = i; j < size; ++j)
|
||||
for (int i = leadPawnsCnt; i < size - 1; ++i)
|
||||
for (int j = i + 1; j < size; ++j)
|
||||
if (d->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.
|
||||
if (rank_of(squares[0]) > RANK_4)
|
||||
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
|
||||
// and ensure it is mapped below the diagonal.
|
||||
@@ -1043,8 +1062,8 @@ void set(T& e, uint8_t* data) {
|
||||
|
||||
enum { Split = 1, HasPawns = 2 };
|
||||
|
||||
assert(e.hasPawns == !!(*data & HasPawns));
|
||||
assert((e.key != e.key2) == !!(*data & Split));
|
||||
assert(e.hasPawns == bool(*data & HasPawns));
|
||||
assert((e.key != e.key2) == bool(*data & Split));
|
||||
|
||||
data++; // First byte stores flags
|
||||
|
||||
@@ -1107,14 +1126,14 @@ void set(T& e, uint8_t* data) {
|
||||
template<TBType Type>
|
||||
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
|
||||
// still working, this could happen due to compiler reordering.
|
||||
// Use 'acquire' to avoid a thread reading 'ready' == true while
|
||||
// another is still working. (compiler reordering may cause this).
|
||||
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
|
||||
return e.baseAddress;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <algorithm> // For std::count
|
||||
#include <cassert>
|
||||
|
||||
#include <algorithm> // For std::count
|
||||
#include "movegen.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
@@ -32,7 +32,7 @@ ThreadPool Threads; // Global object
|
||||
|
||||
|
||||
/// 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) {
|
||||
|
||||
@@ -52,6 +52,15 @@ Thread::~Thread() {
|
||||
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
|
||||
|
||||
@@ -61,18 +70,22 @@ void Thread::clear() {
|
||||
mainHistory.fill(0);
|
||||
captureHistory.fill(0);
|
||||
|
||||
for (auto& to : continuationHistory)
|
||||
for (auto& h : to)
|
||||
for (bool inCheck : { false, true })
|
||||
for (StatsType c : { NoCaptures, Captures })
|
||||
for (auto& to : continuationHistory[inCheck][c])
|
||||
for (auto& h : to)
|
||||
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
|
||||
|
||||
void Thread::start_searching() {
|
||||
|
||||
std::lock_guard<Mutex> lk(mutex);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
searching = true;
|
||||
cv.notify_one(); // Wake up the thread in idle_loop()
|
||||
}
|
||||
@@ -83,7 +96,7 @@ void Thread::start_searching() {
|
||||
|
||||
void Thread::wait_for_search_finished() {
|
||||
|
||||
std::unique_lock<Mutex> lk(mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
cv.wait(lk, [&]{ return !searching; });
|
||||
}
|
||||
|
||||
@@ -103,7 +116,7 @@ void Thread::idle_loop() {
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::unique_lock<Mutex> lk(mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
searching = false;
|
||||
cv.notify_one(); // Wake up anyone waiting for search finished
|
||||
cv.wait(lk, [&]{ return searching; });
|
||||
@@ -118,7 +131,7 @@ void Thread::idle_loop() {
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
||||
void ThreadPool::set(size_t requested) {
|
||||
@@ -136,10 +149,13 @@ void ThreadPool::set(size_t requested) {
|
||||
while (size() < requested)
|
||||
push_back(new Thread(size()));
|
||||
clear();
|
||||
}
|
||||
|
||||
// Reallocate the hash with the new threadpool size
|
||||
TT.resize(Options["Hash"]);
|
||||
// Reallocate the hash with the new threadpool size
|
||||
TT.resize(Options["Hash"]);
|
||||
|
||||
// Init thread number dependent search params.
|
||||
Search::init();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
stopOnPonderhit = stop = false;
|
||||
ponder = ponderMode;
|
||||
main()->stopOnPonderhit = stop = false;
|
||||
increaseDepth = true;
|
||||
main()->ponder = ponderMode;
|
||||
Search::Limits = limits;
|
||||
Search::RootMoves rootMoves;
|
||||
|
||||
@@ -192,7 +209,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
||||
for (Thread* th : *this)
|
||||
{
|
||||
th->nodes = th->tbHits = th->nmpMinPly = 0;
|
||||
th->rootDepth = th->completedDepth = DEPTH_ZERO;
|
||||
th->rootDepth = th->completedDepth = 0;
|
||||
th->rootMoves = rootMoves;
|
||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "pawns.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread_win32.h"
|
||||
#include "thread_win32_osx.h"
|
||||
|
||||
|
||||
/// Thread class keeps together all the thread-related stuff. We use
|
||||
@@ -42,11 +42,11 @@
|
||||
|
||||
class Thread {
|
||||
|
||||
Mutex mutex;
|
||||
ConditionVariable cv;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
size_t idx;
|
||||
bool exit = false, searching = true; // Set before starting std::thread
|
||||
std::thread stdThread;
|
||||
NativeThread stdThread;
|
||||
|
||||
public:
|
||||
explicit Thread(size_t);
|
||||
@@ -56,14 +56,15 @@ public:
|
||||
void idle_loop();
|
||||
void start_searching();
|
||||
void wait_for_search_finished();
|
||||
int best_move_count(Move move);
|
||||
|
||||
Pawns::Table pawnsTable;
|
||||
Material::Table materialTable;
|
||||
Endgames endgames;
|
||||
size_t pvIdx, pvLast;
|
||||
uint64_t ttHitAverage;
|
||||
int selDepth, nmpMinPly;
|
||||
Color nmpColor;
|
||||
std::atomic<uint64_t> nodes, tbHits;
|
||||
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
|
||||
|
||||
Position rootPos;
|
||||
Search::RootMoves rootMoves;
|
||||
@@ -71,7 +72,7 @@ public:
|
||||
CounterMoveHistory counterMoves;
|
||||
ButterflyHistory mainHistory;
|
||||
CapturePieceToHistory captureHistory;
|
||||
ContinuationHistory continuationHistory;
|
||||
ContinuationHistory continuationHistory[2][2];
|
||||
Score contempt;
|
||||
};
|
||||
|
||||
@@ -85,9 +86,12 @@ struct MainThread : public Thread {
|
||||
void search() override;
|
||||
void check_time();
|
||||
|
||||
double bestMoveChanges, previousTimeReduction;
|
||||
double previousTimeReduction;
|
||||
Value previousScore;
|
||||
Value iterValue[4];
|
||||
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 tb_hits() const { return accumulate(&Thread::tbHits); }
|
||||
|
||||
std::atomic_bool stop, ponder, stopOnPonderhit;
|
||||
std::atomic_bool stop, increaseDepth;
|
||||
|
||||
private:
|
||||
StateListPtr setupStates;
|
||||
|
||||
@@ -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
|
||||
68
DroidFishApp/src/main/cpp/stockfish/thread_win32_osx.h
Normal file
68
DroidFishApp/src/main/cpp/stockfish/thread_win32_osx.h
Normal 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
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,15 +24,16 @@
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "misc.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
|
||||
TranspositionTable TT; // Our global transposition table
|
||||
|
||||
/// TTEntry::save saves a TTEntry
|
||||
void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) {
|
||||
/// TTEntry::save populates the TTEntry with a new node's data, possibly
|
||||
/// 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
|
||||
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
|
||||
if ( (k >> 48) != key16
|
||||
|| d / ONE_PLY > depth8 - 4
|
||||
|| d - DEPTH_OFFSET > depth8 - 4
|
||||
|| b == BOUND_EXACT)
|
||||
{
|
||||
assert(d >= DEPTH_OFFSET);
|
||||
|
||||
key16 = (uint16_t)(k >> 48);
|
||||
value16 = (int16_t)v;
|
||||
eval16 = (int16_t)ev;
|
||||
genBound8 = (uint8_t)(TT.generation8 | b);
|
||||
depth8 = (int8_t)(d / ONE_PLY);
|
||||
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
|
||||
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) {
|
||||
|
||||
Threads.main()->wait_for_search_finished();
|
||||
|
||||
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
|
||||
|
||||
free(mem);
|
||||
@@ -82,7 +87,7 @@ void TranspositionTable::clear() {
|
||||
|
||||
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]() {
|
||||
|
||||
@@ -119,7 +124,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
||||
for (int i = 0; i < ClusterSize; ++i)
|
||||
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];
|
||||
}
|
||||
@@ -128,11 +133,11 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
||||
TTEntry* replace = tte;
|
||||
for (int i = 1; i < ClusterSize; ++i)
|
||||
// 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
|
||||
// two bound bits from affecting the result) to calculate the entry
|
||||
// nature we add 263 (256 is the modulus plus 7 to keep the unrelated
|
||||
// lowest three bits from affecting the result) to calculate the entry
|
||||
// age correctly even after generation8 overflows into the next cycle.
|
||||
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2
|
||||
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2)
|
||||
if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8)
|
||||
> tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8))
|
||||
replace = &tte[i];
|
||||
|
||||
return found = false, replace;
|
||||
@@ -145,12 +150,9 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
||||
int TranspositionTable::hashfull() const {
|
||||
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < 1000 / ClusterSize; i++)
|
||||
{
|
||||
const TTEntry* tte = &table[i].entry[0];
|
||||
for (int j = 0; j < ClusterSize; j++)
|
||||
if ((tte[j].genBound8 & 0xFC) == generation8)
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
for (int i = 0; i < 1000 / ClusterSize; ++i)
|
||||
for (int j = 0; j < ClusterSize; ++j)
|
||||
cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8;
|
||||
|
||||
return cnt * 1000 / (ClusterSize * (1000 / ClusterSize));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -30,7 +30,8 @@
|
||||
/// move 16 bit
|
||||
/// value 16 bit
|
||||
/// eval value 16 bit
|
||||
/// generation 6 bit
|
||||
/// generation 5 bit
|
||||
/// pv node 1 bit
|
||||
/// bound type 2 bit
|
||||
/// depth 8 bit
|
||||
|
||||
@@ -39,9 +40,10 @@ struct TTEntry {
|
||||
Move move() const { return (Move )move16; }
|
||||
Value value() const { return (Value)value16; }
|
||||
Value eval() const { return (Value)eval16; }
|
||||
Depth depth() const { return (Depth)(depth8 * 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); }
|
||||
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:
|
||||
friend class TranspositionTable;
|
||||
@@ -51,7 +53,7 @@ private:
|
||||
int16_t value16;
|
||||
int16_t eval16;
|
||||
uint8_t genBound8;
|
||||
int8_t depth8;
|
||||
uint8_t depth8;
|
||||
};
|
||||
|
||||
|
||||
@@ -76,7 +78,7 @@ class TranspositionTable {
|
||||
|
||||
public:
|
||||
~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;
|
||||
int hashfull() const;
|
||||
void resize(size_t mbSize);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Disable some silly and noisy warning from MSVC compiler
|
||||
@@ -101,7 +102,7 @@ typedef uint64_t Key;
|
||||
typedef uint64_t Bitboard;
|
||||
|
||||
constexpr int MAX_MOVES = 256;
|
||||
constexpr int MAX_PLY = 128;
|
||||
constexpr int MAX_PLY = 246;
|
||||
|
||||
/// A move needs 16 bits to be stored
|
||||
///
|
||||
@@ -131,17 +132,19 @@ enum Color {
|
||||
WHITE, BLACK, COLOR_NB = 2
|
||||
};
|
||||
|
||||
enum CastlingSide {
|
||||
KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2
|
||||
};
|
||||
|
||||
enum CastlingRight {
|
||||
enum CastlingRights {
|
||||
NO_CASTLING,
|
||||
WHITE_OO,
|
||||
WHITE_OOO = WHITE_OO << 1,
|
||||
BLACK_OO = WHITE_OO << 2,
|
||||
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
|
||||
};
|
||||
|
||||
@@ -176,11 +179,11 @@ enum Value : int {
|
||||
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
|
||||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
|
||||
|
||||
PawnValueMg = 136, PawnValueEg = 208,
|
||||
KnightValueMg = 782, KnightValueEg = 865,
|
||||
BishopValueMg = 830, BishopValueEg = 918,
|
||||
RookValueMg = 1289, RookValueEg = 1378,
|
||||
QueenValueMg = 2529, QueenValueEg = 2687,
|
||||
PawnValueMg = 128, PawnValueEg = 213,
|
||||
KnightValueMg = 781, KnightValueEg = 854,
|
||||
BishopValueMg = 825, BishopValueEg = 915,
|
||||
RookValueMg = 1276, RookValueEg = 1380,
|
||||
QueenValueMg = 2538, QueenValueEg = 2682,
|
||||
|
||||
MidgameLimit = 15258, EndgameLimit = 3915
|
||||
};
|
||||
@@ -200,21 +203,18 @@ enum Piece {
|
||||
|
||||
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 * ONE_PLY,
|
||||
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
|
||||
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
|
||||
DEPTH_QS_CHECKS = 0,
|
||||
DEPTH_QS_NO_CHECKS = -1,
|
||||
DEPTH_QS_RECAPTURES = -5,
|
||||
|
||||
DEPTH_NONE = -6 * ONE_PLY,
|
||||
DEPTH_MAX = MAX_PLY * ONE_PLY
|
||||
DEPTH_NONE = -6,
|
||||
DEPTH_OFFSET = DEPTH_NONE,
|
||||
};
|
||||
|
||||
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
|
||||
|
||||
enum Square : int {
|
||||
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
|
||||
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
|
||||
@@ -286,7 +286,6 @@ inline T& operator--(T& d) { return d = T(int(d) - 1); }
|
||||
|
||||
#define ENABLE_FULL_OPERATORS_ON(T) \
|
||||
ENABLE_BASE_OPERATORS_ON(T) \
|
||||
ENABLE_INCR_OPERATORS_ON(T) \
|
||||
constexpr T operator*(int i, T d) { return T(i * int(d)); } \
|
||||
constexpr T operator*(T d, int i) { return T(int(d) * i); } \
|
||||
constexpr T operator/(T d, int i) { return T(int(d) / i); } \
|
||||
@@ -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); }
|
||||
|
||||
ENABLE_FULL_OPERATORS_ON(Value)
|
||||
ENABLE_FULL_OPERATORS_ON(Depth)
|
||||
ENABLE_FULL_OPERATORS_ON(Direction)
|
||||
|
||||
ENABLE_INCR_OPERATORS_ON(PieceType)
|
||||
ENABLE_INCR_OPERATORS_ON(Piece)
|
||||
ENABLE_INCR_OPERATORS_ON(Color)
|
||||
ENABLE_INCR_OPERATORS_ON(Square)
|
||||
ENABLE_INCR_OPERATORS_ON(File)
|
||||
ENABLE_INCR_OPERATORS_ON(Rank)
|
||||
@@ -344,6 +341,11 @@ inline Score operator*(Score s, int i) {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
constexpr File operator~(File f) {
|
||||
return File(f ^ FILE_H); // Horizontal flip FILE_A -> FILE_H
|
||||
}
|
||||
|
||||
constexpr Piece operator~(Piece pc) {
|
||||
return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
|
||||
}
|
||||
|
||||
constexpr CastlingRight operator|(Color c, CastlingSide s) {
|
||||
return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
|
||||
inline File map_to_queenside(File f) {
|
||||
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) {
|
||||
@@ -413,11 +415,6 @@ constexpr Rank relative_rank(Color c, Square 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) {
|
||||
return c == WHITE ? NORTH : SOUTH;
|
||||
}
|
||||
@@ -446,6 +443,10 @@ constexpr Move make_move(Square from, Square to) {
|
||||
return Move((from << 6) + to);
|
||||
}
|
||||
|
||||
constexpr Move reverse_move(Move m) {
|
||||
return make_move(to_sq(m), from_sq(m));
|
||||
}
|
||||
|
||||
template<MoveType T>
|
||||
constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
|
||||
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
@@ -155,16 +155,21 @@ namespace {
|
||||
istringstream is(cmd);
|
||||
is >> skipws >> token;
|
||||
|
||||
if (token == "go")
|
||||
if (token == "go" || token == "eval")
|
||||
{
|
||||
cerr << "\nPosition: " << cnt++ << '/' << num << endl;
|
||||
go(pos, is, states);
|
||||
Threads.main()->wait_for_search_finished();
|
||||
nodes += Threads.nodes_searched();
|
||||
if (token == "go")
|
||||
{
|
||||
go(pos, is, states);
|
||||
Threads.main()->wait_for_search_finished();
|
||||
nodes += Threads.nodes_searched();
|
||||
}
|
||||
else
|
||||
sync_cout << "\n" << Eval::trace(pos) << sync_endl;
|
||||
}
|
||||
else if (token == "setoption") setoption(is);
|
||||
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'
|
||||
@@ -191,9 +196,8 @@ void UCI::loop(int argc, char* argv[]) {
|
||||
Position pos;
|
||||
string token, cmd;
|
||||
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)
|
||||
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
|
||||
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.
|
||||
// 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
|
||||
// normal search. In case Threads.stopOnPonderhit is set we are waiting for
|
||||
// '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;
|
||||
|
||||
// normal search.
|
||||
else if (token == "ponderhit")
|
||||
Threads.ponder = false; // Switch to normal search
|
||||
Threads.main()->ponder = false; // Switch to normal search
|
||||
|
||||
else if (token == "uci")
|
||||
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 == "isready") sync_cout << "readyok" << sync_endl;
|
||||
|
||||
// Additional custom non-UCI commands, mainly for debugging
|
||||
else if (token == "flip") pos.flip();
|
||||
else if (token == "bench") bench(pos, is, states);
|
||||
else if (token == "d") sync_cout << pos << sync_endl;
|
||||
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
|
||||
// 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 == "bench") bench(pos, is, states);
|
||||
else if (token == "d") sync_cout << 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
|
||||
sync_cout << "Unknown command: " << cmd << sync_endl;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "misc.h"
|
||||
#include "search.h"
|
||||
@@ -73,6 +74,8 @@ void init(OptionsMap& o) {
|
||||
o["nodestime"] << Option(0, 0, 10000);
|
||||
o["UCI_Chess960"] << 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["SyzygyProbeDepth"] << Option(1, 1, 100);
|
||||
o["Syzygy50MoveRule"] << Option(true);
|
||||
@@ -136,8 +139,8 @@ Option::operator std::string() const {
|
||||
|
||||
bool Option::operator==(const char* s) const {
|
||||
assert(type == "combo");
|
||||
return !CaseInsensitiveLess()(currentValue, s)
|
||||
&& !CaseInsensitiveLess()(s, currentValue);
|
||||
return !CaseInsensitiveLess()(currentValue, s)
|
||||
&& !CaseInsensitiveLess()(s, currentValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -153,8 +156,8 @@ void Option::operator<<(const Option& o) {
|
||||
|
||||
|
||||
/// 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 user by console window, so let's check the bounds anyway.
|
||||
/// the GUI to check for option's limits, but we could receive the new value
|
||||
/// from the user by console window, so let's check the bounds anyway.
|
||||
|
||||
Option& Option::operator=(const string& v) {
|
||||
|
||||
@@ -165,6 +168,17 @@ Option& Option::operator=(const string& v) {
|
||||
|| (type == "spin" && (stof(v) < min || stof(v) > max)))
|
||||
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")
|
||||
currentValue = v;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user