1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-05-02 09:39:36 +00:00

Merge pull request #14 from joergoster/sf-nnue-nodchip

Update to SF master
This commit is contained in:
nodchip 2020-06-25 10:38:16 +09:00 committed by GitHub
commit ff31d92b94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 446 additions and 392 deletions

View file

@ -115,6 +115,7 @@ Nick Pelling (nickpelling)
Nicklas Persson (NicklasPersson) Nicklas Persson (NicklasPersson)
Niklas Fiekas (niklasf) Niklas Fiekas (niklasf)
Nikolay Kostov (NikolayIT) Nikolay Kostov (NikolayIT)
Nguyen Pham
Ondrej Mosnáček (WOnder93) Ondrej Mosnáček (WOnder93)
Oskar Werkelin Ahlin Oskar Werkelin Ahlin
Pablo Vazquez Pablo Vazquez

View file

@ -165,17 +165,23 @@ are in use, see the engine log.
## Compiling Stockfish yourself from the sources ## Compiling Stockfish yourself from the sources
On Unix-like systems, it should be possible to compile Stockfish Stockfish has support for 32 or 64-bit CPUs, certain hardware
directly from the source code with the included Makefile. instructions, big-endian machines such as Power PC, and other platforms.
Stockfish has support for 32 or 64-bit CPUs, the hardware POPCNT On Unix-like systems, it should be easy to compile Stockfish
instruction, big-endian machines such as Power PC, and other platforms. directly from the source code with the included Makefile in the folder
`src`. In general it is recommended to run `make help` to see a list of make
targets with corresponding descriptions.
In general it is recommended to run `make help` to see a list of make ```
targets with corresponding descriptions. When not using the Makefile to cd src
compile (for instance with Microsoft MSVC) you need to manually make help
set/unset some switches in the compiler command line; see file *types.h* make build ARCH=x86-64-modern
for a quick reference. ```
When not using the Makefile to compile (for instance with Microsoft MSVC) you
need to manually set/unset some switches in the compiler command line; see
file *types.h* for a quick reference.
When reporting an issue or a bug, please tell us which version and When reporting an issue or a bug, please tell us which version and
compiler you used to create your executable. These informations can compiler you used to create your executable. These informations can

View file

@ -391,7 +391,7 @@ ifeq ($(pext),yes)
endif endif
endif endif
### 3.8 Link Time Optimization, it works since gcc 4.5 but not on mingw under Windows. ### 3.8 Link Time Optimization
### This is a mix of compile and link time options because the lto link phase ### This is a mix of compile and link time options because the lto link phase
### needs access to the optimization flags. ### needs access to the optimization flags.
ifeq ($(optimize),yes) ifeq ($(optimize),yes)

View file

@ -108,25 +108,25 @@ namespace {
stm = Color ((idx >> 12) & 0x01); stm = Color ((idx >> 12) & 0x01);
psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7))); psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7)));
// Check if two pieces are on the same square or if a king can be captured // Invalid if two pieces are on the same square or if a king can be captured
if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
|| ksq[WHITE] == psq || ksq[WHITE] == psq
|| ksq[BLACK] == psq || ksq[BLACK] == psq
|| (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK]))) || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK])))
result = INVALID; result = INVALID;
// Immediate win if a pawn can be promoted without getting captured // Win if the pawn can be promoted without getting captured
else if ( stm == WHITE else if ( stm == WHITE
&& rank_of(psq) == RANK_7 && rank_of(psq) == RANK_7
&& ksq[stm] != psq + NORTH && ksq[WHITE] != psq + NORTH
&& ( distance(ksq[~stm], psq + NORTH) > 1 && ( distance(ksq[BLACK], psq + NORTH) > 1
|| (attacks_bb<KING>(ksq[stm]) & (psq + NORTH)))) || (distance(ksq[WHITE], psq + NORTH) == 1)))
result = WIN; result = WIN;
// Immediate draw if it is a stalemate or a king captures undefended pawn // Draw if it is stalemate or the black king can capture the pawn
else if ( stm == BLACK else if ( stm == BLACK
&& ( !(attacks_bb<KING>(ksq[stm]) & ~(attacks_bb<KING>(ksq[~stm]) | pawn_attacks_bb(~stm, psq))) && ( !(attacks_bb<KING>(ksq[BLACK]) & ~(attacks_bb<KING>(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq)))
|| (attacks_bb<KING>(ksq[stm]) & psq & ~attacks_bb<KING>(ksq[~stm])))) || (attacks_bb<KING>(ksq[BLACK]) & ~attacks_bb<KING>(ksq[WHITE]) & psq)))
result = DRAW; result = DRAW;
// Position will be classified later // Position will be classified later

View file

@ -40,7 +40,7 @@ namespace {
Bitboard RookTable[0x19000]; // To store rook attacks Bitboard RookTable[0x19000]; // To store rook attacks
Bitboard BishopTable[0x1480]; // To store bishop attacks Bitboard BishopTable[0x1480]; // To store bishop attacks
void init_magics(Bitboard table[], Magic magics[], Direction directions[]); void init_magics(PieceType pt, Bitboard table[], Magic magics[]);
} }
@ -56,8 +56,9 @@ const std::string Bitboards::pretty(Bitboard b) {
for (File f = FILE_A; f <= FILE_H; ++f) for (File f = FILE_A; f <= FILE_H; ++f)
s += b & make_square(f, r) ? "| X " : "| "; s += b & make_square(f, r) ? "| X " : "| ";
s += "|\n+---+---+---+---+---+---+---+---+\n"; s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n";
} }
s += " a b c d e f g h\n";
return s; return s;
} }
@ -78,11 +79,8 @@ void Bitboards::init() {
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2)); SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST }; init_magics(ROOK, RookTable, RookMagics);
Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; init_magics(BISHOP, BishopTable, BishopMagics);
init_magics(RookTable, RookMagics, RookDirections);
init_magics(BishopTable, BishopMagics, BishopDirections);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{ {
@ -108,15 +106,17 @@ void Bitboards::init() {
namespace { namespace {
Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) { Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
Bitboard attacks = 0; Bitboard attacks = 0;
Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
for (int i = 0; i < 4; ++i) for(Direction d : (pt == ROOK ? RookDirections : BishopDirections))
{ {
Square s = sq; Square s = sq;
while(safe_destination(s, directions[i]) && !(occupied & s)) while(safe_destination(s, d) && !(occupied & s))
attacks |= (s += directions[i]); attacks |= (s += d);
} }
return attacks; return attacks;
@ -128,7 +128,7 @@ namespace {
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
// called "fancy" approach. // called "fancy" approach.
void init_magics(Bitboard table[], Magic magics[], Direction directions[]) { void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {
// Optimal PRNG seeds to pick the correct magics in the shortest time // Optimal PRNG seeds to pick the correct magics in the shortest time
int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
@ -148,7 +148,7 @@ namespace {
// the number of 1s of the mask. Hence we deduce the size of the shift to // the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index. // apply to the 64 or 32 bits word to get the index.
Magic& m = magics[s]; Magic& m = magics[s];
m.mask = sliding_attack(directions, s, 0) & ~edges; m.mask = sliding_attack(pt, s, 0) & ~edges;
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
// Set the offset for the attacks table of the square. We have individual // Set the offset for the attacks table of the square. We have individual
@ -160,7 +160,7 @@ namespace {
b = size = 0; b = size = 0;
do { do {
occupancy[size] = b; occupancy[size] = b;
reference[size] = sliding_attack(directions, s, b); reference[size] = sliding_attack(pt, s, b);
if (HasPext) if (HasPext)
m.attacks[pext(b, m.mask)] = reference[size]; m.attacks[pext(b, m.mask)] = reference[size];

View file

@ -200,12 +200,24 @@ inline Bitboard adjacent_files_bb(Square s) {
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s)); return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
} }
/// line_bb(Square, Square) returns a Bitboard representing an entire line
/// between_bb() returns squares that are linearly between the given squares /// (from board edge to board edge) that intersects the given squares.
/// If the given squares are not on a same file/rank/diagonal, return 0. /// If the given squares are not on a same file/rank/diagonal, return 0.
/// Ex. line_bb(SQ_C4, SQ_F7) returns a bitboard with the A2-G8 diagonal.
inline Bitboard line_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2));
return LineBB[s1][s2];
}
/// between_bb() returns a Bitboard representing squares that are linearly
/// between the given squares (excluding the given squares).
/// If the given squares are not on a same file/rank/diagonal, return 0.
/// Ex. between_bb(SQ_C4, SQ_F7) returns a bitboard with squares D5 and E6.
inline Bitboard between_bb(Square s1, Square s2) { inline Bitboard between_bb(Square s1, Square s2) {
Bitboard b = LineBB[s1][s2] & ((AllSquares << s1) ^ (AllSquares << s2)); Bitboard b = line_bb(s1, s2) & ((AllSquares << s1) ^ (AllSquares << s2));
return b & (b - 1); //exclude lsb return b & (b - 1); //exclude lsb
} }
@ -241,7 +253,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) {
/// the given color and on the given square is a passed pawn. /// the given color and on the given square is a passed pawn.
inline Bitboard passed_pawn_span(Color c, Square s) { inline Bitboard passed_pawn_span(Color c, Square s) {
return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s)); return pawn_attack_span(c, s) | forward_file_bb(c, s);
} }
@ -249,7 +261,7 @@ inline Bitboard passed_pawn_span(Color c, Square s) {
/// straight or on a diagonal line. /// straight or on a diagonal line.
inline bool aligned(Square s1, Square s2, Square s3) { inline bool aligned(Square s1, Square s2, Square s3) {
return LineBB[s1][s2] & s3; return line_bb(s1, s2) & s3;
} }

View file

@ -28,12 +28,14 @@ namespace {
// Used to drive the king towards the edge of the board // Used to drive the king towards the edge of the board
// in KX vs K and KQ vs KR endgames. // in KX vs K and KQ vs KR endgames.
// Values range from 27 (center squares) to 90 (in the corners)
inline int push_to_edge(Square s) { inline int push_to_edge(Square s) {
int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s)); int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s));
return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2); return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2);
} }
// Used to drive the king towards A1H8 corners in KBN vs K endgames. // Used to drive the king towards A1H8 corners in KBN vs K endgames.
// Values range from 0 on A8H1 diagonal to 7 in A1H8 corners
inline int push_to_corner(Square s) { inline int push_to_corner(Square s) {
return abs(7 - rank_of(s) - file_of(s)); return abs(7 - rank_of(s) - file_of(s));
} }
@ -103,13 +105,13 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
if (pos.side_to_move() == weakSide && !MoveList<LEGAL>(pos).size()) if (pos.side_to_move() == weakSide && !MoveList<LEGAL>(pos).size())
return VALUE_DRAW; return VALUE_DRAW;
Square winnerKSq = pos.square<KING>(strongSide); Square strongKing = pos.square<KING>(strongSide);
Square loserKSq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
Value result = pos.non_pawn_material(strongSide) Value result = pos.non_pawn_material(strongSide)
+ pos.count<PAWN>(strongSide) * PawnValueEg + pos.count<PAWN>(strongSide) * PawnValueEg
+ push_to_edge(loserKSq) + push_to_edge(weakKing)
+ push_close(winnerKSq, loserKSq); + push_close(strongKing, weakKing);
if ( pos.count<QUEEN>(strongSide) if ( pos.count<QUEEN>(strongSide)
|| pos.count<ROOK>(strongSide) || pos.count<ROOK>(strongSide)
@ -130,16 +132,16 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0)); assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
Square winnerKSq = pos.square<KING>(strongSide); Square strongKing = pos.square<KING>(strongSide);
Square loserKSq = pos.square<KING>(weakSide); Square strongBishop = pos.square<BISHOP>(strongSide);
Square bishopSq = pos.square<BISHOP>(strongSide); Square weakKing = pos.square<KING>(weakSide);
// If our bishop does not attack A1/H8, we flip the enemy king square // If our bishop does not attack A1/H8, we flip the enemy king square
// to drive to opposite corners (A8/H1). // to drive to opposite corners (A8/H1).
Value result = (VALUE_KNOWN_WIN + 3520) Value result = (VALUE_KNOWN_WIN + 3520)
+ push_close(winnerKSq, loserKSq) + push_close(strongKing, weakKing)
+ 420 * push_to_corner(opposite_colors(bishopSq, SQ_A1) ? flip_file(loserKSq) : loserKSq); + 420 * push_to_corner(opposite_colors(strongBishop, SQ_A1) ? flip_file(weakKing) : weakKing);
assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY); assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY);
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
@ -154,16 +156,16 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
// Assume strongSide is white and the pawn is on files A-D // Assume strongSide is white and the pawn is on files A-D
Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide)); Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide)); Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide)); Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
if (!Bitbases::probe(wksq, psq, bksq, us)) if (!Bitbases::probe(strongKing, strongPawn, weakKing, us))
return VALUE_DRAW; return VALUE_DRAW;
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(strongPawn));
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
@ -179,36 +181,35 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square wksq = relative_square(strongSide, pos.square<KING>(strongSide)); Square strongKing = relative_square(strongSide, pos.square<KING>(strongSide));
Square bksq = relative_square(strongSide, pos.square<KING>(weakSide)); Square weakKing = relative_square(strongSide, pos.square<KING>(weakSide));
Square rsq = relative_square(strongSide, pos.square<ROOK>(strongSide)); Square strongRook = relative_square(strongSide, pos.square<ROOK>(strongSide));
Square psq = relative_square(strongSide, pos.square<PAWN>(weakSide)); Square weakPawn = relative_square(strongSide, pos.square<PAWN>(weakSide));
Square queeningSquare = make_square(file_of(weakPawn), RANK_1);
Square queeningSq = make_square(file_of(psq), RANK_1);
Value result; Value result;
// If the stronger side's king is in front of the pawn, it's a win // If the stronger side's king is in front of the pawn, it's a win
if (forward_file_bb(WHITE, wksq) & psq) if (forward_file_bb(WHITE, strongKing) & weakPawn)
result = RookValueEg - distance(wksq, psq); result = RookValueEg - distance(strongKing, weakPawn);
// If the weaker side's king is too far from the pawn and the rook, // If the weaker side's king is too far from the pawn and the rook,
// it's a win. // it's a win.
else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) else if ( distance(weakKing, weakPawn) >= 3 + (pos.side_to_move() == weakSide)
&& distance(bksq, rsq) >= 3) && distance(weakKing, strongRook) >= 3)
result = RookValueEg - distance(wksq, psq); result = RookValueEg - distance(strongKing, weakPawn);
// If the pawn is far advanced and supported by the defending king, // If the pawn is far advanced and supported by the defending king,
// the position is drawish // the position is drawish
else if ( rank_of(bksq) <= RANK_3 else if ( rank_of(weakKing) <= RANK_3
&& distance(bksq, psq) == 1 && distance(weakKing, weakPawn) == 1
&& rank_of(wksq) >= RANK_4 && rank_of(strongKing) >= RANK_4
&& distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) && distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide))
result = Value(80) - 8 * distance(wksq, psq); result = Value(80) - 8 * distance(strongKing, weakPawn);
else else
result = Value(200) - 8 * ( distance(wksq, psq + SOUTH) result = Value(200) - 8 * ( distance(strongKing, weakPawn + SOUTH)
- distance(bksq, psq + SOUTH) - distance(weakKing, weakPawn + SOUTH)
- distance(psq, queeningSq)); - distance(weakPawn, queeningSquare));
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
@ -235,9 +236,9 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, KnightValueMg, 0)); assert(verify_material(pos, weakSide, KnightValueMg, 0));
Square bksq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
Square bnsq = pos.square<KNIGHT>(weakSide); Square weakKnight = pos.square<KNIGHT>(weakSide);
Value result = Value(push_to_edge(bksq) + push_away(bksq, bnsq)); Value result = Value(push_to_edge(weakKing) + push_away(weakKing, weakKnight));
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
@ -252,15 +253,15 @@ Value Endgame<KQKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, strongSide, QueenValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square winnerKSq = pos.square<KING>(strongSide); Square strongKing = pos.square<KING>(strongSide);
Square loserKSq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
Square pawnSq = pos.square<PAWN>(weakSide); Square weakPawn = pos.square<PAWN>(weakSide);
Value result = Value(push_close(winnerKSq, loserKSq)); Value result = Value(push_close(strongKing, weakKing));
if ( relative_rank(weakSide, pawnSq) != RANK_7 if ( relative_rank(weakSide, weakPawn) != RANK_7
|| distance(loserKSq, pawnSq) != 1 || distance(weakKing, weakPawn) != 1
|| ((FileBBB | FileDBB | FileEBB | FileGBB) & pawnSq)) || ((FileBBB | FileDBB | FileEBB | FileGBB) & weakPawn))
result += QueenValueEg - PawnValueEg; result += QueenValueEg - PawnValueEg;
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
@ -277,13 +278,13 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, strongSide, QueenValueMg, 0));
assert(verify_material(pos, weakSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, RookValueMg, 0));
Square winnerKSq = pos.square<KING>(strongSide); Square strongKing = pos.square<KING>(strongSide);
Square loserKSq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
Value result = QueenValueEg Value result = QueenValueEg
- RookValueEg - RookValueEg
+ push_to_edge(loserKSq) + push_to_edge(weakKing)
+ push_close(winnerKSq, loserKSq); + push_close(strongKing, weakKing);
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
@ -297,9 +298,12 @@ Value Endgame<KNNKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square weakKing = pos.square<KING>(weakSide);
Square weakPawn = pos.square<PAWN>(weakSide);
Value result = PawnValueEg Value result = PawnValueEg
+ 2 * push_to_edge(pos.square<KING>(weakSide)) + 2 * push_to_edge(weakKing)
- 10 * relative_rank(weakSide, pos.square<PAWN>(weakSide)); - 10 * relative_rank(weakSide, weakPawn);
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
@ -325,15 +329,17 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
Bitboard strongPawns = pos.pieces(strongSide, PAWN); Bitboard strongPawns = pos.pieces(strongSide, PAWN);
Bitboard allPawns = pos.pieces(PAWN); Bitboard allPawns = pos.pieces(PAWN);
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Square strongKing = pos.square<KING>(strongSide);
// All strongSide pawns are on a single rook file? // All strongSide pawns are on a single rook file?
if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB)) if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB))
{ {
Square bishopSq = pos.square<BISHOP>(strongSide); Square queeningSquare = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
Square weakKingSq = pos.square<KING>(weakSide);
if ( opposite_colors(queeningSq, bishopSq) if ( opposite_colors(queeningSquare, strongBishop)
&& distance(queeningSq, weakKingSq) <= 1) && distance(queeningSquare, weakKing) <= 1)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
@ -343,20 +349,16 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
&& pos.count<PAWN>(weakSide) >= 1) && pos.count<PAWN>(weakSide) >= 1)
{ {
// Get the least advanced weakSide pawn // Get the least advanced weakSide pawn
Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
Square strongKingSq = pos.square<KING>(strongSide);
Square weakKingSq = pos.square<KING>(weakSide);
Square bishopSq = pos.square<BISHOP>(strongSide);
// There's potential for a draw if our pawn is blocked on the 7th rank, // There's potential for a draw if our pawn is blocked on the 7th rank,
// the bishop cannot attack it or they only have one pawn left // the bishop cannot attack it or they only have one pawn left
if ( relative_rank(strongSide, weakPawnSq) == RANK_7 if ( relative_rank(strongSide, weakPawn) == RANK_7
&& (strongPawns & (weakPawnSq + pawn_push(weakSide))) && (strongPawns & (weakPawn + pawn_push(weakSide)))
&& (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongPawns))) && (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns)))
{ {
int strongKingDist = distance(weakPawnSq, strongKingSq); int strongKingDist = distance(weakPawn, strongKing);
int weakKingDist = distance(weakPawnSq, weakKingSq); int weakKingDist = distance(weakPawn, weakKing);
// It's a draw if the weak king is on its back two ranks, within 2 // It's a draw if the weak king is on its back two ranks, within 2
// squares of the blocking pawn and the strong king is not // squares of the blocking pawn and the strong king is not
@ -364,7 +366,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
// unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
// and positions where qsearch will immediately correct the // and positions where qsearch will immediately correct the
// problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w)
if ( relative_rank(strongSide, weakKingSq) >= RANK_7 if ( relative_rank(strongSide, weakKing) >= RANK_7
&& weakKingDist <= 2 && weakKingDist <= 2
&& weakKingDist <= strongKingDist) && weakKingDist <= strongKingDist)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
@ -384,15 +386,16 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
assert(pos.count<ROOK>(weakSide) == 1); assert(pos.count<ROOK>(weakSide) == 1);
assert(pos.count<PAWN>(weakSide) >= 1); assert(pos.count<PAWN>(weakSide) >= 1);
Square kingSq = pos.square<KING>(weakSide); Square strongKing = pos.square<KING>(strongSide);
Square rsq = pos.square<ROOK>(weakSide); Square weakKing = pos.square<KING>(weakSide);
Square weakRook = pos.square<ROOK>(weakSide);
if ( relative_rank(weakSide, kingSq) <= RANK_2 if ( relative_rank(weakSide, weakKing) <= RANK_2
&& relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4 && relative_rank(weakSide, strongKing) >= RANK_4
&& relative_rank(weakSide, rsq) == RANK_3 && relative_rank(weakSide, weakRook) == RANK_3
&& ( pos.pieces(weakSide, PAWN) && ( pos.pieces(weakSide, PAWN)
& attacks_bb<KING>(kingSq) & attacks_bb<KING>(weakKing)
& pawn_attacks_bb(strongSide, rsq))) & pawn_attacks_bb(strongSide, weakRook)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@ -412,89 +415,89 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, RookValueMg, 0));
// Assume strongSide is white and the pawn is on files A-D // Assume strongSide is white and the pawn is on files A-D
Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide)); Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide)); Square strongRook = normalize(pos, strongSide, pos.square<ROOK>(strongSide));
Square wrsq = normalize(pos, strongSide, pos.square<ROOK>(strongSide)); Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square wpsq = normalize(pos, strongSide, pos.square<PAWN>(strongSide)); Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square brsq = normalize(pos, strongSide, pos.square<ROOK>(weakSide)); Square weakRook = normalize(pos, strongSide, pos.square<ROOK>(weakSide));
File f = file_of(wpsq); File pawnFile = file_of(strongPawn);
Rank r = rank_of(wpsq); Rank pawnRank = rank_of(strongPawn);
Square queeningSq = make_square(f, RANK_8); Square queeningSquare = make_square(pawnFile, RANK_8);
int tempo = (pos.side_to_move() == strongSide); int tempo = (pos.side_to_move() == strongSide);
// If the pawn is not too far advanced and the defending king defends the // If the pawn is not too far advanced and the defending king defends the
// queening square, use the third-rank defence. // queening square, use the third-rank defence.
if ( r <= RANK_5 if ( pawnRank <= RANK_5
&& distance(bksq, queeningSq) <= 1 && distance(weakKing, queeningSquare) <= 1
&& wksq <= SQ_H5 && strongKing <= SQ_H5
&& (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6))) && (rank_of(weakRook) == RANK_6 || (pawnRank <= RANK_3 && rank_of(strongRook) != RANK_6)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
// The defending side saves a draw by checking from behind in case the pawn // The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind. // has advanced to the 6th rank with the king behind.
if ( r == RANK_6 if ( pawnRank == RANK_6
&& distance(bksq, queeningSq) <= 1 && distance(weakKing, queeningSquare) <= 1
&& rank_of(wksq) + tempo <= RANK_6 && rank_of(strongKing) + tempo <= RANK_6
&& (rank_of(brsq) == RANK_1 || (!tempo && distance<File>(brsq, wpsq) >= 3))) && (rank_of(weakRook) == RANK_1 || (!tempo && distance<File>(weakRook, strongPawn) >= 3)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
if ( r >= RANK_6 if ( pawnRank >= RANK_6
&& bksq == queeningSq && weakKing == queeningSquare
&& rank_of(brsq) == RANK_1 && rank_of(weakRook) == RANK_1
&& (!tempo || distance(wksq, wpsq) >= 2)) && (!tempo || distance(strongKing, strongPawn) >= 2))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
// White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
// and the black rook is behind the pawn. // and the black rook is behind the pawn.
if ( wpsq == SQ_A7 if ( strongPawn == SQ_A7
&& wrsq == SQ_A8 && strongRook == SQ_A8
&& (bksq == SQ_H7 || bksq == SQ_G7) && (weakKing == SQ_H7 || weakKing == SQ_G7)
&& file_of(brsq) == FILE_A && file_of(weakRook) == FILE_A
&& (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5)) && (rank_of(weakRook) <= RANK_3 || file_of(strongKing) >= FILE_D || rank_of(strongKing) <= RANK_5))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
// If the defending king blocks the pawn and the attacking king is too far // If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw. // away, it's a draw.
if ( r <= RANK_5 if ( pawnRank <= RANK_5
&& bksq == wpsq + NORTH && weakKing == strongPawn + NORTH
&& distance(wksq, wpsq) - tempo >= 2 && distance(strongKing, strongPawn) - tempo >= 2
&& distance(wksq, brsq) - tempo >= 2) && distance(strongKing, weakRook) - tempo >= 2)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
// Pawn on the 7th rank supported by the rook from behind usually wins if the // Pawn on the 7th rank supported by the rook from behind usually wins if the
// attacking king is closer to the queening square than the defending king, // attacking king is closer to the queening square than the defending king,
// and the defending king cannot gain tempi by threatening the attacking rook. // and the defending king cannot gain tempi by threatening the attacking rook.
if ( r == RANK_7 if ( pawnRank == RANK_7
&& f != FILE_A && pawnFile != FILE_A
&& file_of(wrsq) == f && file_of(strongRook) == pawnFile
&& wrsq != queeningSq && strongRook != queeningSquare
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
&& (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo)) && (distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo))
return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq)); return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(strongKing, queeningSquare));
// Similar to the above, but with the pawn further back // Similar to the above, but with the pawn further back
if ( f != FILE_A if ( pawnFile != FILE_A
&& file_of(wrsq) == f && file_of(strongRook) == pawnFile
&& wrsq < wpsq && strongRook < strongPawn
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
&& (distance(wksq, wpsq + NORTH) < distance(bksq, wpsq + NORTH) - 2 + tempo) && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn + NORTH) - 2 + tempo)
&& ( distance(bksq, wrsq) + tempo >= 3 && ( distance(weakKing, strongRook) + tempo >= 3
|| ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo || ( distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo
&& (distance(wksq, wpsq + NORTH) < distance(bksq, wrsq) + tempo)))) && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn) + tempo))))
return ScaleFactor( SCALE_FACTOR_MAX return ScaleFactor( SCALE_FACTOR_MAX
- 8 * distance(wpsq, queeningSq) - 8 * distance(strongPawn, queeningSquare)
- 2 * distance(wksq, queeningSq)); - 2 * distance(strongKing, queeningSquare));
// If the pawn is not far advanced and the defending king is somewhere in // If the pawn is not far advanced and the defending king is somewhere in
// the pawn's path, it's probably a draw. // the pawn's path, it's probably a draw.
if (r <= RANK_4 && bksq > wpsq) if (pawnRank <= RANK_4 && weakKing > strongPawn)
{ {
if (file_of(bksq) == file_of(wpsq)) if (file_of(weakKing) == file_of(strongPawn))
return ScaleFactor(10); return ScaleFactor(10);
if ( distance<File>(bksq, wpsq) == 1 if ( distance<File>(weakKing, strongPawn) == 1
&& distance(wksq, bksq) > 2) && distance(strongKing, weakKing) > 2)
return ScaleFactor(24 - 2 * distance(wksq, bksq)); return ScaleFactor(24 - 2 * distance(strongKing, weakKing));
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@ -508,10 +511,11 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
// Test for a rook pawn // Test for a rook pawn
if (pos.pieces(PAWN) & (FileABB | FileHBB)) if (pos.pieces(PAWN) & (FileABB | FileHBB))
{ {
Square ksq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
Square bsq = pos.square<BISHOP>(weakSide); Square weakBishop = pos.square<BISHOP>(weakSide);
Square psq = pos.square<PAWN>(strongSide); Square strongKing = pos.square<KING>(strongSide);
Rank rk = relative_rank(strongSide, psq); Square strongPawn = pos.square<PAWN>(strongSide);
Rank pawnRank = relative_rank(strongSide, strongPawn);
Direction push = pawn_push(strongSide); Direction push = pawn_push(strongSide);
// If the pawn is on the 5th rank and the pawn (currently) is on // If the pawn is on the 5th rank and the pawn (currently) is on
@ -519,11 +523,11 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
// a fortress. Depending on the king position give a moderate // a fortress. Depending on the king position give a moderate
// reduction or a stronger one if the defending king is near the // reduction or a stronger one if the defending king is near the
// corner but not trapped there. // corner but not trapped there.
if (rk == RANK_5 && !opposite_colors(bsq, psq)) if (pawnRank == RANK_5 && !opposite_colors(weakBishop, strongPawn))
{ {
int d = distance(psq + 3 * push, ksq); int d = distance(strongPawn + 3 * push, weakKing);
if (d <= 2 && !(d == 0 && ksq == pos.square<KING>(strongSide) + 2 * push)) if (d <= 2 && !(d == 0 && weakKing == strongKing + 2 * push))
return ScaleFactor(24); return ScaleFactor(24);
else else
return ScaleFactor(48); return ScaleFactor(48);
@ -533,10 +537,10 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
// it's drawn if the bishop attacks the square in front of the // it's drawn if the bishop attacks the square in front of the
// pawn from a reasonable distance and the defending king is near // pawn from a reasonable distance and the defending king is near
// the corner // the corner
if ( rk == RANK_6 if ( pawnRank == RANK_6
&& distance(psq + 2 * push, ksq) <= 1 && distance(strongPawn + 2 * push, weakKing) <= 1
&& (attacks_bb<BISHOP>(bsq) & (psq + push)) && (attacks_bb<BISHOP>(weakBishop) & (strongPawn + push))
&& distance<File>(bsq, psq) >= 2) && distance<File>(weakBishop, strongPawn) >= 2)
return ScaleFactor(8); return ScaleFactor(8);
} }
@ -551,22 +555,22 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 2)); assert(verify_material(pos, strongSide, RookValueMg, 2));
assert(verify_material(pos, weakSide, RookValueMg, 1)); assert(verify_material(pos, weakSide, RookValueMg, 1));
Square wpsq1 = pos.squares<PAWN>(strongSide)[0]; Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
Square wpsq2 = pos.squares<PAWN>(strongSide)[1]; Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
Square bksq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
// Does the stronger side have a passed pawn? // Does the stronger side have a passed pawn?
if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) if (pos.pawn_passed(strongSide, strongPawn1) || pos.pawn_passed(strongSide, strongPawn2))
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); Rank pawnRank = std::max(relative_rank(strongSide, strongPawn1), relative_rank(strongSide, strongPawn2));
if ( distance<File>(bksq, wpsq1) <= 1 if ( distance<File>(weakKing, strongPawn1) <= 1
&& distance<File>(bksq, wpsq2) <= 1 && distance<File>(weakKing, strongPawn2) <= 1
&& relative_rank(strongSide, bksq) > r) && relative_rank(strongSide, weakKing) > pawnRank)
{ {
assert(r > RANK_1 && r < RANK_7); assert(pawnRank > RANK_1 && pawnRank < RANK_7);
return ScaleFactor(7 * r); return ScaleFactor(7 * pawnRank);
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@ -581,12 +585,12 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.count<PAWN>(strongSide) >= 2); assert(pos.count<PAWN>(strongSide) >= 2);
assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
Square ksq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
Bitboard pawns = pos.pieces(strongSide, PAWN); Bitboard strongPawns = pos.pieces(strongSide, PAWN);
// If all pawns are ahead of the king on a single rook file, it's a draw. // If all pawns are ahead of the king on a single rook file, it's a draw.
if (!((pawns & ~FileABB) || (pawns & ~FileHBB)) && if (!((strongPawns & ~FileABB) || (strongPawns & ~FileHBB)) &&
!(pawns & ~passed_pawn_span(weakSide, ksq))) !(strongPawns & ~passed_pawn_span(weakSide, weakKing)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@ -603,19 +607,19 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, strongSide, BishopValueMg, 1));
assert(verify_material(pos, weakSide, BishopValueMg, 0)); assert(verify_material(pos, weakSide, BishopValueMg, 0));
Square pawnSq = pos.square<PAWN>(strongSide); Square strongPawn = pos.square<PAWN>(strongSide);
Square strongBishopSq = pos.square<BISHOP>(strongSide); Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakBishopSq = pos.square<BISHOP>(weakSide); Square weakBishop = pos.square<BISHOP>(weakSide);
Square weakKingSq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away // Case 1: Defending king blocks the pawn, and cannot be driven away
if ( (forward_file_bb(strongSide, pawnSq) & weakKingSq) if ( (forward_file_bb(strongSide, strongPawn) & weakKing)
&& ( opposite_colors(weakKingSq, strongBishopSq) && ( opposite_colors(weakKing, strongBishop)
|| relative_rank(strongSide, weakKingSq) <= RANK_6)) || relative_rank(strongSide, weakKing) <= RANK_6))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
// Case 2: Opposite colored bishops // Case 2: Opposite colored bishops
if (opposite_colors(strongBishopSq, weakBishopSq)) if (opposite_colors(strongBishop, weakBishop))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@ -629,36 +633,36 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 2)); assert(verify_material(pos, strongSide, BishopValueMg, 2));
assert(verify_material(pos, weakSide, BishopValueMg, 0)); assert(verify_material(pos, weakSide, BishopValueMg, 0));
Square wbsq = pos.square<BISHOP>(strongSide); Square strongBishop = pos.square<BISHOP>(strongSide);
Square bbsq = pos.square<BISHOP>(weakSide); Square weakBishop = pos.square<BISHOP>(weakSide);
if (!opposite_colors(wbsq, bbsq)) if (!opposite_colors(strongBishop, weakBishop))
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
Square ksq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
Square psq1 = pos.squares<PAWN>(strongSide)[0]; Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
Square psq2 = pos.squares<PAWN>(strongSide)[1]; Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
Square blockSq1, blockSq2; Square blockSq1, blockSq2;
if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2))
{ {
blockSq1 = psq1 + pawn_push(strongSide); blockSq1 = strongPawn1 + pawn_push(strongSide);
blockSq2 = make_square(file_of(psq2), rank_of(psq1)); blockSq2 = make_square(file_of(strongPawn2), rank_of(strongPawn1));
} }
else else
{ {
blockSq1 = psq2 + pawn_push(strongSide); blockSq1 = strongPawn2 + pawn_push(strongSide);
blockSq2 = make_square(file_of(psq1), rank_of(psq2)); blockSq2 = make_square(file_of(strongPawn1), rank_of(strongPawn2));
} }
switch (distance<File>(psq1, psq2)) switch (distance<File>(strongPawn1, strongPawn2))
{ {
case 0: case 0:
// Both pawns are on the same file. It's an easy draw if the defender firmly // Both pawns are on the same file. It's an easy draw if the defender firmly
// controls some square in the frontmost pawn's path. // controls some square in the frontmost pawn's path.
if ( file_of(ksq) == file_of(blockSq1) if ( file_of(weakKing) == file_of(blockSq1)
&& relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1) && relative_rank(strongSide, weakKing) >= relative_rank(strongSide, blockSq1)
&& opposite_colors(ksq, wbsq)) && opposite_colors(weakKing, strongBishop))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
else else
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@ -667,16 +671,16 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
// Pawns on adjacent files. It's a draw if the defender firmly controls the // Pawns on adjacent files. It's a draw if the defender firmly controls the
// square in front of the frontmost pawn's path, and the square diagonally // square in front of the frontmost pawn's path, and the square diagonally
// behind this square on the file of the other pawn. // behind this square on the file of the other pawn.
if ( ksq == blockSq1 if ( weakKing == blockSq1
&& opposite_colors(ksq, wbsq) && opposite_colors(weakKing, strongBishop)
&& ( bbsq == blockSq2 && ( weakBishop == blockSq2
|| (attacks_bb<BISHOP>(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP)) || (attacks_bb<BISHOP>(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP))
|| distance<Rank>(psq1, psq2) >= 2)) || distance<Rank>(strongPawn1, strongPawn2) >= 2))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2 else if ( weakKing == blockSq2
&& opposite_colors(ksq, wbsq) && opposite_colors(weakKing, strongBishop)
&& ( bbsq == blockSq1 && ( weakBishop == blockSq1
|| (attacks_bb<BISHOP>(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP)))) || (attacks_bb<BISHOP>(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP))))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
else else
@ -698,14 +702,14 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, strongSide, BishopValueMg, 1));
assert(verify_material(pos, weakSide, KnightValueMg, 0)); assert(verify_material(pos, weakSide, KnightValueMg, 0));
Square pawnSq = pos.square<PAWN>(strongSide); Square strongPawn = pos.square<PAWN>(strongSide);
Square strongBishopSq = pos.square<BISHOP>(strongSide); Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakKingSq = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
if ( file_of(weakKingSq) == file_of(pawnSq) if ( file_of(weakKing) == file_of(strongPawn)
&& relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) && relative_rank(strongSide, strongPawn) < relative_rank(strongSide, weakKing)
&& ( opposite_colors(weakKingSq, strongBishopSq) && ( opposite_colors(weakKing, strongBishop)
|| relative_rank(strongSide, weakKingSq) <= RANK_6)) || relative_rank(strongSide, weakKing) <= RANK_6))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@ -724,18 +728,18 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
// Assume strongSide is white and the pawn is on files A-D // Assume strongSide is white and the pawn is on files A-D
Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide)); Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide)); Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide)); Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
// If the pawn has advanced to the fifth rank or further, and is not a // If the pawn has advanced to the fifth rank or further, and is not a
// rook pawn, it's too dangerous to assume that it's at least a draw. // rook pawn, it's too dangerous to assume that it's at least a draw.
if (rank_of(psq) >= RANK_5 && file_of(psq) != FILE_A) if (rank_of(strongPawn) >= RANK_5 && file_of(strongPawn) != FILE_A)
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn. // it's probably at least a draw even with the pawn.
return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
} }

View file

@ -37,7 +37,7 @@ namespace Trace {
enum Tracing { NO_TRACE, TRACE }; enum Tracing { NO_TRACE, TRACE };
enum Term { // The first 8 entries are reserved for PieceType enum Term { // The first 8 entries are reserved for PieceType
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, WINNABLE, TOTAL, TERM_NB
}; };
Score scores[TERM_NB][COLOR_NB]; Score scores[TERM_NB][COLOR_NB];
@ -61,7 +61,7 @@ namespace Trace {
std::ostream& operator<<(std::ostream& os, Term t) { std::ostream& operator<<(std::ostream& os, Term t) {
if (t == MATERIAL || t == IMBALANCE || t == INITIATIVE || t == TOTAL) if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL)
os << " ---- ----" << " | " << " ---- ----"; os << " ---- ----" << " | " << " ---- ----";
else else
os << scores[t][WHITE] << " | " << scores[t][BLACK]; os << scores[t][WHITE] << " | " << scores[t][BLACK];
@ -145,16 +145,17 @@ namespace {
constexpr Score ReachableOutpost = S( 31, 22); constexpr Score ReachableOutpost = S( 31, 22);
constexpr Score PassedFile = S( 11, 8); constexpr Score PassedFile = S( 11, 8);
constexpr Score PawnlessFlank = S( 17, 95); constexpr Score PawnlessFlank = S( 17, 95);
constexpr Score QueenInfiltration = S( -2, 14);
constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RestrictedPiece = S( 7, 7);
constexpr Score RookOnKingRing = S( 16, 0); constexpr Score RookOnKingRing = S( 16, 0);
constexpr Score RookOnQueenFile = S( 5, 9); constexpr Score RookOnQueenFile = S( 6, 11);
constexpr Score SliderOnQueen = S( 59, 18); constexpr Score SliderOnQueen = S( 60, 18);
constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByKing = S( 24, 89);
constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatByPawnPush = S( 48, 39);
constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score ThreatBySafePawn = S(173, 94);
constexpr Score TrappedRook = S( 55, 13); constexpr Score TrappedRook = S( 55, 13);
constexpr Score WeakQueen = S( 51, 14); constexpr Score WeakQueen = S( 56, 15);
constexpr Score WeakQueenProtection = S( 15, 0); constexpr Score WeakQueenProtection = S( 14, 0);
#undef S #undef S
@ -175,8 +176,7 @@ namespace {
template<Color Us> Score threats() const; template<Color Us> Score threats() const;
template<Color Us> Score passed() const; template<Color Us> Score passed() const;
template<Color Us> Score space() const; template<Color Us> Score space() const;
ScaleFactor scale_factor(Value eg) const; Value winnable(Score score) const;
Score initiative(Score score) const;
const Position& pos; const Position& pos;
Material::Entry* me; Material::Entry* me;
@ -279,7 +279,7 @@ namespace {
: attacks_bb<Pt>(s, pos.pieces()); : attacks_bb<Pt>(s, pos.pieces());
if (pos.blockers_for_king(Us) & s) if (pos.blockers_for_king(Us) & s)
b &= LineBB[pos.square<KING>(Us)][s]; b &= line_bb(pos.square<KING>(Us), s);
attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b; attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b;
attackedBy[Us][Pt] |= b; attackedBy[Us][Pt] |= b;
@ -376,6 +376,10 @@ namespace {
Bitboard queenPinners; Bitboard queenPinners;
if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners)) if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners))
score -= WeakQueen; score -= WeakQueen;
// Bonus for queen on weak square in enemy camp
if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s))
score += QueenInfiltration;
} }
} }
if (T) if (T)
@ -679,16 +683,15 @@ namespace {
} }
// Evaluation::space() computes the space evaluation for a given side. The // Evaluation::space() computes a space evaluation for a given side, aiming to improve game
// space evaluation is a simple bonus based on the number of safe squares // play in the opening. It is based on the number of safe squares on the 4 central files
// available for minor pieces on the central four files on ranks 2--4. Safe // on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice.
// squares one, two or three squares behind a friendly pawn are counted // Finally, the space bonus is multiplied by a weight which decreases according to occupancy.
// twice. Finally, the space bonus is multiplied by a weight. The aim is to
// improve play on game opening.
template<Tracing T> template<Color Us> template<Tracing T> template<Color Us>
Score Evaluation<T>::space() const { Score Evaluation<T>::space() const {
// Early exit if, for example, both queens or 6 minor pieces have been exchanged
if (pos.non_pawn_material() < SpaceThreshold) if (pos.non_pawn_material() < SpaceThreshold)
return SCORE_ZERO; return SCORE_ZERO;
@ -719,12 +722,12 @@ namespace {
} }
// Evaluation::initiative() computes the initiative correction value // Evaluation::winnable() adjusts the mg and eg score components based on the
// for the position. It is a second order bonus/malus based on the
// known attacking/defending status of the players. // known attacking/defending status of the players.
// A single value is derived from the mg and eg values and returned.
template<Tracing T> template<Tracing T>
Score Evaluation<T>::initiative(Score score) const { Value Evaluation<T>::winnable(Score score) const {
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK)) int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK)); - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
@ -746,7 +749,6 @@ namespace {
+ 24 * infiltration + 24 * infiltration
+ 51 * !pos.non_pawn_material() + 51 * !pos.non_pawn_material()
- 43 * almostUnwinnable - 43 * almostUnwinnable
- 2 * pos.rule50_count()
-110 ; -110 ;
Value mg = mg_value(score); Value mg = mg_value(score);
@ -758,17 +760,10 @@ namespace {
int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0); int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0);
int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
if (T) mg += u;
Trace::add(INITIATIVE, make_score(u, v)); eg += v;
return make_score(u, v); // Compute the scale factor for the winning side
}
// Evaluation::scale_factor() computes the scale factor for the winning side
template<Tracing T>
ScaleFactor Evaluation<T>::scale_factor(Value eg) const {
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
int sf = me->scale_factor(pos, strongSide); int sf = me->scale_factor(pos, strongSide);
@ -788,7 +783,18 @@ namespace {
sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide)); sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
} }
return ScaleFactor(sf); // Interpolate between the middlegame and (scaled by 'sf') endgame score
v = mg * int(me->game_phase())
+ eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL;
v /= PHASE_MIDGAME;
if (T)
{
Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score)));
Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL));
}
return Value(v);
} }
@ -843,14 +849,8 @@ namespace {
+ passed< WHITE>() - passed< BLACK>() + passed< WHITE>() - passed< BLACK>()
+ space< WHITE>() - space< BLACK>(); + space< WHITE>() - space< BLACK>();
score += initiative(score); // Derive single value from mg and eg parts of score
v = winnable(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 /= PHASE_MIDGAME;
// In case of tracing add all remaining individual evaluation terms // In case of tracing add all remaining individual evaluation terms
if (T) if (T)
@ -859,11 +859,18 @@ namespace {
Trace::add(IMBALANCE, me->imbalance()); Trace::add(IMBALANCE, me->imbalance());
Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK)); Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK));
Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]); Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]);
Trace::add(TOTAL, score);
} }
// Evaluation grain
v = (v / 16) * 16;
// Side to move point of view // Side to move point of view
return (pos.side_to_move() == WHITE ? v : -v) + Tempo; v = (pos.side_to_move() == WHITE ? v : -v) + Tempo;
// Damp down the evaluation linearly when shuffling
v = v * (100 - pos.rule50_count()) / 100;
return v;
} }
} // namespace } // namespace
@ -913,11 +920,11 @@ std::string Eval::trace(const Position& pos) {
<< " Threats | " << Term(THREAT) << " Threats | " << Term(THREAT)
<< " Passed | " << Term(PASSED) << " Passed | " << Term(PASSED)
<< " Space | " << Term(SPACE) << " Space | " << Term(SPACE)
<< " Initiative | " << Term(INITIATIVE) << " Winnable | " << Term(WINNABLE)
<< " ------------+-------------+-------------+------------\n" << " ------------+-------------+-------------+------------\n"
<< " Total | " << Term(TOTAL); << " Total | " << Term(TOTAL);
ss << "\nTotal evaluation: " << to_cp(v) << " (white side)\n"; ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n";
return ss.str(); return ss.str();
} }
@ -976,7 +983,7 @@ bool EvalList::is_valid(const Position& pos)
for (Piece pc = NO_PIECE; pc < PIECE_NB; ++pc) for (Piece pc = NO_PIECE; pc < PIECE_NB; ++pc)
{ {
auto pt = type_of(pc); auto pt = type_of(pc);
if (pt == NO_PIECE || pt == 7) // <E28098>ݵȢî if (pt == NO_PIECE_TYPE || pt == 7) // <E28098>ݵȢî
continue; continue;
// îpcÌBonaPieceÌŠJŽn”Ô<E2809D> // îpcÌBonaPieceÌŠJŽn”Ô<E2809D>

View file

@ -130,6 +130,20 @@ inline std::ostream& operator<<(std::ostream& os, PRNG& prng)
return os; return os;
} }
inline uint64_t mul_hi64(uint64_t a, uint64_t b) {
#if defined(__GNUC__) && defined(IS_64BIT)
__extension__ typedef unsigned __int128 uint128;
return ((uint128)a * (uint128)b) >> 64;
#else
uint64_t aL = (uint32_t)a, aH = a >> 32;
uint64_t bL = (uint32_t)b, bH = b >> 32;
uint64_t c1 = (aL * bL) >> 32;
uint64_t c2 = aH * bL + c1;
uint64_t c3 = aL * bH + (uint32_t)c2;
return aH * bH + (c2 >> 32) + (c3 >> 32);
#endif
}
/// Under Windows it is not possible for a process to run on more than one /// Under Windows it is not possible for a process to run on more than one
/// logical processor group. This usually means to be limited to use max 64 /// logical processor group. This usually means to be limited to use max 64
/// cores. To overcome this, some special platform specific API should be /// cores. To overcome this, some special platform specific API should be

View file

@ -332,7 +332,7 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
// the king evasions in order to skip known illegal moves, which avoids any // the king evasions in order to skip known illegal moves, which avoids any
// useless legality checks later on. // useless legality checks later on.
while (sliders) while (sliders)
sliderAttacks |= LineBB[ksq][pop_lsb(&sliders)] & ~pos.checkers(); sliderAttacks |= line_bb(ksq, pop_lsb(&sliders)) & ~pos.checkers();
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
Bitboard b = attacks_bb<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks; Bitboard b = attacks_bb<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;

View file

@ -57,7 +57,7 @@ namespace {
/// MovePicker constructor for the main search /// MovePicker constructor for the main search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp,
const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers, int pl) const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl)
: pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch),
ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) {

View file

@ -88,9 +88,9 @@ enum StatsType { NoCaptures, Captures };
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory; typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
/// LowPlyHistory at higher depths records successful quiet moves on plies 0 to 3 /// At higher depths LowPlyHistory records successful quiet moves near the root and quiet
/// and quiet moves which are/were in the PV (ttPv) /// moves which are/were in the PV (ttPv)
/// It get cleared with each new search and get filled during iterative deepening /// It is cleared with each new search and filled during iterative deepening
constexpr int MAX_LPH = 4; constexpr int MAX_LPH = 4;
typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory; typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory;
@ -133,7 +133,7 @@ public:
const CapturePieceToHistory*, const CapturePieceToHistory*,
const PieceToHistory**, const PieceToHistory**,
Move, Move,
Move*, const Move*,
int); int);
Move next_move(bool skipQuiets = false); Move next_move(bool skipQuiets = false);

View file

@ -150,17 +150,17 @@ namespace {
&& !(theirPawns & adjacent_files_bb(s))) && !(theirPawns & adjacent_files_bb(s)))
score -= Doubled; score -= Doubled;
else else
score -= Isolated score -= Isolated
+ WeakUnopposed * !opposed; + WeakUnopposed * !opposed;
} }
else if (backward) else if (backward)
score -= Backward score -= Backward
+ WeakUnopposed * !opposed; + WeakUnopposed * !opposed;
if (!support) if (!support)
score -= Doubled * doubled score -= Doubled * doubled
+ WeakLever * more_than_one(lever); + WeakLever * more_than_one(lever);
} }
return score; return score;
@ -196,7 +196,7 @@ Entry* probe(const Position& pos) {
/// penalty for a king, looking at the king file and the two closest files. /// penalty for a king, looking at the king file and the two closest files.
template<Color Us> template<Color Us>
Score Entry::evaluate_shelter(const Position& pos, Square ksq) { Score Entry::evaluate_shelter(const Position& pos, Square ksq) const {
constexpr Color Them = ~Us; constexpr Color Them = ~Us;

View file

@ -50,7 +50,7 @@ struct Entry {
Score do_king_safety(const Position& pos); Score do_king_safety(const Position& pos);
template<Color Us> template<Color Us>
Score evaluate_shelter(const Position& pos, Square ksq); Score evaluate_shelter(const Position& pos, Square ksq) const;
Key key; Key key;
Score scores[COLOR_NB]; Score scores[COLOR_NB];

View file

@ -64,10 +64,11 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
for (File f = FILE_A; f <= FILE_H; ++f) for (File f = FILE_A; f <= FILE_H; ++f)
os << " | " << PieceToChar[pos.piece_on(make_square(f, r))]; os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
os << " |\n +---+---+---+---+---+---+---+---+\n"; os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n";
} }
os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase os << " a b c d e f g h\n"
<< "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << pos.key() << std::setfill('0') << std::setw(16) << pos.key()
<< std::setfill(' ') << std::dec << "\nCheckers: "; << std::setfill(' ') << std::dec << "\nCheckers: ";

View file

@ -91,9 +91,9 @@ constexpr Score PBonus[RANK_NB][FILE_NB] =
{ }, { },
{ 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( 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( -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( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -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( 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( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 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) } { S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) }
}; };
@ -106,7 +106,7 @@ Score psq[PIECE_NB][SQUARE_NB];
// tables are initialized by flipping and changing the sign of the white scores. // tables are initialized by flipping and changing the sign of the white scores.
void init() { void init() {
for (Piece pc = W_PAWN; pc <= W_KING; ++pc) for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING})
{ {
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);

View file

@ -65,9 +65,9 @@ namespace {
constexpr uint64_t TtHitAverageResolution = 1024; constexpr uint64_t TtHitAverageResolution = 1024;
// Razor and futility margins // Razor and futility margins
constexpr int RazorMargin = 531; constexpr int RazorMargin = 527;
Value futility_margin(Depth d, bool improving) { Value futility_margin(Depth d, bool improving) {
return Value(217 * (d - improving)); return Value(227 * (d - improving));
} }
// Reductions lookup table, initialized at startup // Reductions lookup table, initialized at startup
@ -75,16 +75,16 @@ namespace {
Depth reduction(bool i, Depth d, int mn) { Depth reduction(bool i, Depth d, int mn) {
int r = Reductions[d] * Reductions[mn]; int r = Reductions[d] * Reductions[mn];
return (r + 511) / 1024 + (!i && r > 1007); return (r + 570) / 1024 + (!i && r > 1018);
} }
constexpr int futility_move_count(bool improving, Depth depth) { constexpr int futility_move_count(bool improving, Depth depth) {
return (4 + depth * depth) / (2 - improving); return (3 + depth * depth) / (2 - improving);
} }
// History and stats update bonus, based on depth // History and stats update bonus, based on depth
int stat_bonus(Depth d) { int stat_bonus(Depth d) {
return d > 15 ? -8 : 19 * d * d + 155 * d - 132; return d > 15 ? 27 : 17 * d * d + 133 * d - 134;
} }
// Add a small random component to draw evaluations to avoid 3fold-blindness // Add a small random component to draw evaluations to avoid 3fold-blindness
@ -236,14 +236,8 @@ void MainThread::search() {
} }
else else
{ {
for (Thread* th : Threads) Threads.start_searching(); // start non-main threads
{ Thread::search(); // main thread start searching
th->bestMoveChanges = 0;
if (th != this)
th->start_searching();
}
Thread::search(); // Let's start searching!
} }
// When we reach the maximum depth, we can arrive here without a raise of // When we reach the maximum depth, we can arrive here without a raise of
@ -260,9 +254,7 @@ void MainThread::search() {
Threads.stop = true; Threads.stop = true;
// Wait until all threads have finished // Wait until all threads have finished
for (Thread* th : Threads) Threads.wait_for_search_finished();
if (th != this)
th->wait_for_search_finished();
// When playing in 'nodes as time' mode, subtract the searched nodes from // When playing in 'nodes as time' mode, subtract the searched nodes from
// the available ones before exiting. // the available ones before exiting.
@ -271,37 +263,11 @@ void MainThread::search() {
Thread* bestThread = this; Thread* bestThread = this;
// Check if there are threads with a better score than main thread if (int(Options["MultiPV"]) == 1 &&
if ( int(Options["MultiPV"]) == 1 !Limits.depth &&
&& !Limits.depth !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) &&
&& !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) rootMoves[0].pv[0] != MOVE_NONE)
&& rootMoves[0].pv[0] != MOVE_NONE) bestThread = Threads.get_best_thread();
{
std::map<Move, int64_t> votes;
Value minScore = this->rootMoves[0].score;
// Find minimum score
for (Thread* th: Threads)
minScore = std::min(minScore, th->rootMoves[0].score);
// Vote according to score and depth, and select the best thread
for (Thread* th : Threads)
{
votes[th->rootMoves[0].pv[0]] +=
(th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
{
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest
if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
bestThread = th;
}
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]))
bestThread = th;
}
}
bestPreviousScore = bestThread->rootMoves[0].score; bestPreviousScore = bestThread->rootMoves[0].score;
@ -437,12 +403,12 @@ void Thread::search() {
if (rootDepth >= 4) if (rootDepth >= 4)
{ {
Value prev = rootMoves[pvIdx].previousScore; Value prev = rootMoves[pvIdx].previousScore;
delta = Value(21); delta = Value(19);
alpha = std::max(prev - delta,-VALUE_INFINITE); alpha = std::max(prev - delta,-VALUE_INFINITE);
beta = std::min(prev + delta, VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE);
// Adjust contempt based on root move's previousScore (dynamic contempt) // Adjust contempt based on root move's previousScore (dynamic contempt)
int dct = ct + (102 - ct / 2) * prev / (abs(prev) + 157); int dct = ct + (110 - ct / 2) * prev / (abs(prev) + 140);
contempt = (us == WHITE ? make_score(dct, dct / 2) contempt = (us == WHITE ? make_score(dct, dct / 2)
: -make_score(dct, dct / 2)); : -make_score(dct, dct / 2));
@ -540,13 +506,13 @@ void Thread::search() {
&& !Threads.stop && !Threads.stop
&& !mainThread->stopOnPonderhit) && !mainThread->stopOnPonderhit)
{ {
double fallingEval = (332 + 6 * (mainThread->bestPreviousScore - bestValue) double fallingEval = (296 + 6 * (mainThread->bestPreviousScore - bestValue)
+ 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 725.0;
fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); fallingEval = Utility::clamp(fallingEval, 0.5, 1.5);
// If the bestMove is stable over several iterations, reduce time accordingly // If the bestMove is stable over several iterations, reduce time accordingly
timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.94 : 0.91; timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.92 : 0.95;
double reduction = (1.41 + mainThread->previousTimeReduction) / (2.27 * timeReduction); double reduction = (1.47 + mainThread->previousTimeReduction) / (2.22 * timeReduction);
// Use part of the gained time from a previous stable move for the current move // Use part of the gained time from a previous stable move for the current move
for (Thread* th : Threads) for (Thread* th : Threads)
@ -571,7 +537,7 @@ void Thread::search() {
} }
else if ( Threads.increaseDepth else if ( Threads.increaseDepth
&& !mainThread->ponder && !mainThread->ponder
&& Time.elapsed() > totalTime * 0.6) && Time.elapsed() > totalTime * 0.56)
Threads.increaseDepth = false; Threads.increaseDepth = false;
else else
Threads.increaseDepth = true; Threads.increaseDepth = true;
@ -696,7 +662,7 @@ namespace {
// search to overwrite a previous full search TT value, so we use a different // search to overwrite a previous full search TT value, so we use a different
// position key in case of an excluded move. // position key in case of an excluded move.
excludedMove = ss->excludedMove; excludedMove = ss->excludedMove;
posKey = pos.key() ^ Key(excludedMove << 16); // Isn't a very good hash posKey = pos.key() ^ (Key(excludedMove) << 48); // Isn't a very good hash
tte = TT.probe(posKey, ttHit); tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
@ -704,7 +670,7 @@ namespace {
ttPv = PvNode || (ttHit && tte->is_pv()); ttPv = PvNode || (ttHit && tte->is_pv());
formerPv = ttPv && !PvNode; formerPv = ttPv && !PvNode;
if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !pos.captured_piece() && is_ok((ss-1)->currentMove)) if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !priorCapture && is_ok((ss-1)->currentMove))
thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5);
// thisThread->ttHitAverage can be used to approximate the running average of ttHit // thisThread->ttHitAverage can be used to approximate the running average of ttHit
@ -853,10 +819,10 @@ namespace {
// Step 9. Null move search with verification search (~40 Elo) // Step 9. Null move search with verification search (~40 Elo)
if ( !PvNode if ( !PvNode
&& (ss-1)->currentMove != MOVE_NULL && (ss-1)->currentMove != MOVE_NULL
&& (ss-1)->statScore < 23397 && (ss-1)->statScore < 23824
&& eval >= beta && eval >= beta
&& eval >= ss->staticEval && eval >= ss->staticEval
&& ss->staticEval >= beta - 32 * depth - 30 * improving + 120 * ttPv + 292 && ss->staticEval >= beta - 33 * depth - 33 * improving + 112 * ttPv + 311
&& !excludedMove && !excludedMove
&& pos.non_pawn_material(us) && pos.non_pawn_material(us)
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
@ -864,7 +830,7 @@ namespace {
assert(eval - beta >= 0); assert(eval - beta >= 0);
// Null move dynamic reduction based on depth and value // Null move dynamic reduction based on depth and value
Depth R = (854 + 68 * depth) / 258 + std::min(int(eval - beta) / 192, 3); Depth R = (737 + 77 * depth) / 246 + std::min(int(eval - beta) / 192, 3);
ss->currentMove = MOVE_NULL; ss->currentMove = MOVE_NULL;
ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
@ -904,10 +870,10 @@ namespace {
// If we have a good enough capture and a reduced search returns a value // If we have a good enough capture and a reduced search returns a value
// much above beta, we can (almost) safely prune the previous move. // much above beta, we can (almost) safely prune the previous move.
if ( !PvNode if ( !PvNode
&& depth >= 5 && depth > 4
&& abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY)
{ {
Value raisedBeta = beta + 189 - 45 * improving; Value raisedBeta = beta + 176 - 49 * improving;
assert(raisedBeta < VALUE_INFINITE); assert(raisedBeta < VALUE_INFINITE);
MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory);
int probCutCount = 0; int probCutCount = 0;
@ -1037,14 +1003,15 @@ moves_loop: // When in check, search starts from here
// Futility pruning: parent node (~5 Elo) // Futility pruning: parent node (~5 Elo)
if ( lmrDepth < 6 if ( lmrDepth < 6
&& !ss->inCheck && !ss->inCheck
&& ss->staticEval + 235 + 172 * lmrDepth <= alpha && ss->staticEval + 284 + 188 * lmrDepth <= alpha
&& (*contHist[0])[movedPiece][to_sq(move)] && (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)] < 27400) + (*contHist[3])[movedPiece][to_sq(move)]
+ (*contHist[5])[movedPiece][to_sq(move)] / 2 < 28388)
continue; continue;
// Prune moves with negative SEE (~20 Elo) // Prune moves with negative SEE (~20 Elo)
if (!pos.see_ge(move, Value(-(32 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) if (!pos.see_ge(move, Value(-(29 - std::min(lmrDepth, 17)) * lmrDepth * lmrDepth)))
continue; continue;
} }
else else
@ -1060,11 +1027,11 @@ moves_loop: // When in check, search starts from here
&& lmrDepth < 6 && lmrDepth < 6
&& !(PvNode && abs(bestValue) < 2) && !(PvNode && abs(bestValue) < 2)
&& !ss->inCheck && !ss->inCheck
&& ss->staticEval + 270 + 384 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) && ss->staticEval + 267 + 391 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha)
continue; continue;
// See based pruning // See based pruning
if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) if (!pos.see_ge(move, Value(-202) * depth)) // (~25 Elo)
continue; continue;
} }
} }
@ -1177,12 +1144,12 @@ moves_loop: // When in check, search starts from here
|| moveCountPruning || moveCountPruning
|| ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha
|| cutNode || cutNode
|| thisThread->ttHitAverage < 375 * TtHitAverageResolution * TtHitAverageWindow / 1024)) || thisThread->ttHitAverage < 415 * TtHitAverageResolution * TtHitAverageWindow / 1024))
{ {
Depth r = reduction(improving, depth, moveCount); Depth r = reduction(improving, depth, moveCount);
// Decrease reduction if the ttHit running average is large // Decrease reduction if the ttHit running average is large
if (thisThread->ttHitAverage > 500 * TtHitAverageResolution * TtHitAverageWindow / 1024) if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024)
r--; r--;
// Reduction if other threads are searching this position. // Reduction if other threads are searching this position.
@ -1197,7 +1164,7 @@ moves_loop: // When in check, search starts from here
r++; r++;
// Decrease reduction if opponent's move count is high (~5 Elo) // Decrease reduction if opponent's move count is high (~5 Elo)
if ((ss-1)->moveCount > 14) if ((ss-1)->moveCount > 13)
r--; r--;
// Decrease reduction if ttMove has been singularly extended (~3 Elo) // Decrease reduction if ttMove has been singularly extended (~3 Elo)
@ -1219,23 +1186,23 @@ moves_loop: // When in check, search starts from here
// hence break make_move(). (~2 Elo) // hence break make_move(). (~2 Elo)
else if ( type_of(move) == NORMAL else if ( type_of(move) == NORMAL
&& !pos.see_ge(reverse_move(move))) && !pos.see_ge(reverse_move(move)))
r -= 2 + ttPv; r -= 2 + ttPv - (type_of(movedPiece) == PAWN);
ss->statScore = thisThread->mainHistory[us][from_to(move)] ss->statScore = thisThread->mainHistory[us][from_to(move)]
+ (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)]
- 4926; - 4826;
// Decrease/increase reduction by comparing opponent's stat score (~10 Elo) // Decrease/increase reduction by comparing opponent's stat score (~10 Elo)
if (ss->statScore >= -102 && (ss-1)->statScore < -114) if (ss->statScore >= -100 && (ss-1)->statScore < -112)
r--; r--;
else if ((ss-1)->statScore >= -116 && ss->statScore < -154) else if ((ss-1)->statScore >= -125 && ss->statScore < -138)
r++; r++;
// Decrease/increase reduction for moves with a good/bad history (~30 Elo) // Decrease/increase reduction for moves with a good/bad history (~30 Elo)
r -= ss->statScore / 16434; r -= ss->statScore / 14615;
} }
else else
{ {
@ -1245,7 +1212,7 @@ moves_loop: // When in check, search starts from here
// Unless giving check, this capture is likely bad // Unless giving check, this capture is likely bad
if ( !givesCheck if ( !givesCheck
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 200 * depth <= alpha) && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 211 * depth <= alpha)
r++; r++;
} }
@ -1507,7 +1474,7 @@ moves_loop: // When in check, search starts from here
if (PvNode && bestValue > alpha) if (PvNode && bestValue > alpha)
alpha = bestValue; alpha = bestValue;
futilityBase = bestValue + 154; futilityBase = bestValue + 141;
} }
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
@ -1763,8 +1730,8 @@ moves_loop: // When in check, search starts from here
thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move;
} }
if (depth > 12 && ss->ply < MAX_LPH) if (depth > 11 && ss->ply < MAX_LPH)
thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7); thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 6);
} }
// When playing with strength handicap, choose best move among a set of RootMoves // When playing with strength handicap, choose best move among a set of RootMoves

View file

@ -1200,7 +1200,7 @@ WDLScore search(Position& pos, ProbeState* result) {
auto moveList = MoveList<LEGAL>(pos); auto moveList = MoveList<LEGAL>(pos);
size_t totalCount = moveList.size(), moveCount = 0; size_t totalCount = moveList.size(), moveCount = 0;
for (const Move& move : moveList) for (const Move move : moveList)
{ {
if ( !pos.capture(move) if ( !pos.capture(move)
&& (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN)) && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN))
@ -1362,7 +1362,7 @@ void Tablebases::init(const std::string& paths) {
LeadPawnsSize[leadPawnsCnt][f] = idx; LeadPawnsSize[leadPawnsCnt][f] = idx;
} }
// Add entries in TB tables if the corresponding ".rtbw" file exsists // Add entries in TB tables if the corresponding ".rtbw" file exists
for (PieceType p1 = PAWN; p1 < KING; ++p1) { for (PieceType p1 = PAWN; p1 < KING; ++p1) {
TBTables.add({KING, p1, KING}); TBTables.add({KING, p1, KING});
@ -1469,7 +1469,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
StateInfo st; StateInfo st;
int minDTZ = 0xFFFF; int minDTZ = 0xFFFF;
for (const Move& move : MoveList<LEGAL>(pos)) for (const Move move : MoveList<LEGAL>(pos))
{ {
bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN; bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN;

View file

@ -208,7 +208,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
for (Thread* th : *this) for (Thread* th : *this)
{ {
th->nodes = th->tbHits = th->nmpMinPly = 0; th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0;
th->rootDepth = th->completedDepth = 0; th->rootDepth = th->completedDepth = 0;
th->rootMoves = rootMoves; th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
@ -218,3 +218,52 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
main()->start_searching(); main()->start_searching();
} }
Thread* ThreadPool::get_best_thread() const {
Thread* bestThread = front();
std::map<Move, int64_t> votes;
Value minScore = VALUE_NONE;
// Find minimum score of all threads
for (Thread* th: *this)
minScore = std::min(minScore, th->rootMoves[0].score);
// Vote according to score and depth, and select the best thread
for (Thread* th : *this)
{
votes[th->rootMoves[0].pv[0]] +=
(th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
{
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest
if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
bestThread = th;
}
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]))
bestThread = th;
}
return bestThread;
}
/// Start non-main threads.
void ThreadPool::start_searching() {
for (Thread* th : *this)
if (th != front())
th->start_searching();
}
/// Wait for non-main threads.
void ThreadPool::wait_for_search_finished() const {
for (Thread* th : *this)
if (th != front())
th->wait_for_search_finished();
}

View file

@ -109,6 +109,9 @@ struct ThreadPool : public std::vector<Thread*> {
MainThread* main() const { return static_cast<MainThread*>(front()); } MainThread* main() const { return static_cast<MainThread*>(front()); }
uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
Thread* get_best_thread() const;
void start_searching();
void wait_for_search_finished() const;
std::atomic_bool stop, increaseDepth; std::atomic_bool stop, increaseDepth;

View file

@ -79,7 +79,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
{ {
opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0, opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0,
0.2 * limits.time[us] / double(timeLeft)); 0.2 * limits.time[us] / double(timeLeft));
max_scale = 4 + std::min(36, ply) / 12.0; max_scale = std::min(7.0, 4.0 + ply / 12.0);
} }
// x moves in y seconds (+ z increment) // x moves in y seconds (+ z increment)

View file

@ -36,17 +36,17 @@ TranspositionTable TT; // Our global transposition table
void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
// Preserve any existing move for the same position // Preserve any existing move for the same position
if (m || (k >> 48) != key16) if (m || (uint16_t)k != key16)
move16 = (uint16_t)m; move16 = (uint16_t)m;
// Overwrite less valuable entries // Overwrite less valuable entries
if ( (k >> 48) != key16 if ((uint16_t)k != key16
|| d - DEPTH_OFFSET > depth8 - 4 || d - DEPTH_OFFSET > depth8 - 4
|| b == BOUND_EXACT) || b == BOUND_EXACT)
{ {
assert(d >= DEPTH_OFFSET); assert(d >= DEPTH_OFFSET);
key16 = (uint16_t)(k >> 48); key16 = (uint16_t)k;
value16 = (int16_t)v; value16 = (int16_t)v;
eval16 = (int16_t)ev; eval16 = (int16_t)ev;
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
@ -120,7 +120,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
#else #else
TTEntry* const tte = first_entry(key); TTEntry* const tte = first_entry(key);
const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster
for (int i = 0; i < ClusterSize; ++i) for (int i = 0; i < ClusterSize; ++i)
if (!tte[i].key16 || tte[i].key16 == key16) if (!tte[i].key16 || tte[i].key16 == key16)

View file

@ -82,9 +82,8 @@ public:
void resize(size_t mbSize); void resize(size_t mbSize);
void clear(); void clear();
// The 32 lowest order bits of the key are used to get the index of the cluster
TTEntry* first_entry(const Key key) const { TTEntry* first_entry(const Key key) const {
return &table[(uint32_t(key) * uint64_t(clusterCount)) >> 32].entry[0]; return &table[mul_hi64(key, clusterCount)].entry[0];
} }
private: private:

View file

@ -40,7 +40,6 @@
#include <cassert> #include <cassert>
#include <cctype> #include <cctype>
#include <climits>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
@ -219,7 +218,6 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
typedef int Depth; typedef int Depth;
enum : int { enum : int {
DEPTH_QS_CHECKS = 0, DEPTH_QS_CHECKS = 0,
DEPTH_QS_NO_CHECKS = -1, DEPTH_QS_NO_CHECKS = -1,
DEPTH_QS_RECAPTURES = -5, DEPTH_QS_RECAPTURES = -5,
@ -288,11 +286,11 @@ inline Value mg_value(Score s) {
} }
#define ENABLE_BASE_OPERATORS_ON(T) \ #define ENABLE_BASE_OPERATORS_ON(T) \
constexpr T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \ constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \
constexpr T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \ constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \
constexpr T operator-(T d) { return T(-int(d)); } \ constexpr T operator-(T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \ inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; }
#define ENABLE_INCR_OPERATORS_ON(T) \ #define ENABLE_INCR_OPERATORS_ON(T) \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \
@ -322,12 +320,6 @@ ENABLE_BASE_OPERATORS_ON(Score)
#undef ENABLE_INCR_OPERATORS_ON #undef ENABLE_INCR_OPERATORS_ON
#undef ENABLE_BASE_OPERATORS_ON #undef ENABLE_BASE_OPERATORS_ON
/// Additional operators to add integers to a Value
constexpr Value operator+(Value v, int i) { return Value(int(v) + i); }
constexpr Value operator-(Value v, int i) { return Value(int(v) - i); }
inline Value& operator+=(Value& v, int i) { return v = v + i; }
inline Value& operator-=(Value& v, int i) { return v = v - i; }
/// Additional operators to add a Direction to a Square /// Additional operators to add a Direction to a Square
constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); } constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); } constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }

View file

@ -57,8 +57,7 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
void init(OptionsMap& o) { void init(OptionsMap& o) {
// at most 2^32 clusters. constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
constexpr int MaxHashMB = Is64Bit ? 131072 : 2048;
o["Debug Log File"] << Option("", on_logger); o["Debug Log File"] << Option("", on_logger);
o["Contempt"] << Option(24, -100, 100); o["Contempt"] << Option(24, -100, 100);