1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 16:53:09 +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)
Niklas Fiekas (niklasf)
Nikolay Kostov (NikolayIT)
Nguyen Pham
Ondrej Mosnáček (WOnder93)
Oskar Werkelin Ahlin
Pablo Vazquez

View file

@ -165,17 +165,23 @@ are in use, see the engine log.
## Compiling Stockfish yourself from the sources
On Unix-like systems, it should be possible to compile Stockfish
directly from the source code with the included Makefile.
Stockfish has support for 32 or 64-bit CPUs, certain hardware
instructions, big-endian machines such as Power PC, and other platforms.
Stockfish has support for 32 or 64-bit CPUs, the hardware POPCNT
instruction, big-endian machines such as Power PC, and other platforms.
On Unix-like systems, it should be easy to compile Stockfish
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
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.
```
cd src
make help
make build ARCH=x86-64-modern
```
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
compiler you used to create your executable. These informations can

View file

@ -391,7 +391,7 @@ ifeq ($(pext),yes)
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
### needs access to the optimization flags.
ifeq ($(optimize),yes)

View file

@ -108,25 +108,25 @@ namespace {
stm = Color ((idx >> 12) & 0x01);
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
|| ksq[WHITE] == psq
|| ksq[BLACK] == psq
|| (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK])))
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
&& rank_of(psq) == RANK_7
&& ksq[stm] != psq + NORTH
&& ( distance(ksq[~stm], psq + NORTH) > 1
|| (attacks_bb<KING>(ksq[stm]) & (psq + NORTH))))
&& ksq[WHITE] != psq + NORTH
&& ( distance(ksq[BLACK], psq + NORTH) > 1
|| (distance(ksq[WHITE], psq + NORTH) == 1)))
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
&& ( !(attacks_bb<KING>(ksq[stm]) & ~(attacks_bb<KING>(ksq[~stm]) | pawn_attacks_bb(~stm, psq)))
|| (attacks_bb<KING>(ksq[stm]) & psq & ~attacks_bb<KING>(ksq[~stm]))))
&& ( !(attacks_bb<KING>(ksq[BLACK]) & ~(attacks_bb<KING>(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq)))
|| (attacks_bb<KING>(ksq[BLACK]) & ~attacks_bb<KING>(ksq[WHITE]) & psq)))
result = DRAW;
// Position will be classified later

View file

@ -40,7 +40,7 @@ namespace {
Bitboard RookTable[0x19000]; // To store rook 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)
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;
}
@ -78,11 +79,8 @@ void Bitboards::init() {
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
init_magics(RookTable, RookMagics, RookDirections);
init_magics(BishopTable, BishopMagics, BishopDirections);
init_magics(ROOK, RookTable, RookMagics);
init_magics(BISHOP, BishopTable, BishopMagics);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{
@ -108,15 +106,17 @@ void Bitboards::init() {
namespace {
Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) {
Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
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;
while(safe_destination(s, directions[i]) && !(occupied & s))
attacks |= (s += directions[i]);
while(safe_destination(s, d) && !(occupied & s))
attacks |= (s += d);
}
return attacks;
@ -128,7 +128,7 @@ namespace {
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
// 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
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
// apply to the 64 or 32 bits word to get the index.
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);
// Set the offset for the attacks table of the square. We have individual
@ -160,7 +160,7 @@ namespace {
b = size = 0;
do {
occupancy[size] = b;
reference[size] = sliding_attack(directions, s, b);
reference[size] = sliding_attack(pt, s, b);
if (HasPext)
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));
}
/// between_bb() returns squares that are linearly between the given squares
/// line_bb(Square, Square) returns a Bitboard representing an entire line
/// (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.
/// 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) {
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
}
@ -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.
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.
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
// 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) {
int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s));
return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2);
}
// 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) {
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())
return VALUE_DRAW;
Square winnerKSq = pos.square<KING>(strongSide);
Square loserKSq = pos.square<KING>(weakSide);
Square strongKing = pos.square<KING>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Value result = pos.non_pawn_material(strongSide)
+ pos.count<PAWN>(strongSide) * PawnValueEg
+ push_to_edge(loserKSq)
+ push_close(winnerKSq, loserKSq);
+ push_to_edge(weakKing)
+ push_close(strongKing, weakKing);
if ( pos.count<QUEEN>(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, weakSide, VALUE_ZERO, 0));
Square winnerKSq = pos.square<KING>(strongSide);
Square loserKSq = pos.square<KING>(weakSide);
Square bishopSq = pos.square<BISHOP>(strongSide);
Square strongKing = pos.square<KING>(strongSide);
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
// 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 + 3520)
+ push_close(winnerKSq, loserKSq)
+ 420 * push_to_corner(opposite_colors(bishopSq, SQ_A1) ? flip_file(loserKSq) : loserKSq);
+ push_close(strongKing, weakKing)
+ 420 * push_to_corner(opposite_colors(strongBishop, SQ_A1) ? flip_file(weakKing) : weakKing);
assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY);
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));
// Assume strongSide is white and the pawn is on files A-D
Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square strongPawn = 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;
if (!Bitbases::probe(wksq, psq, bksq, us))
if (!Bitbases::probe(strongKing, strongPawn, weakKing, us))
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;
}
@ -179,36 +181,35 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square wksq = relative_square(strongSide, pos.square<KING>(strongSide));
Square bksq = relative_square(strongSide, pos.square<KING>(weakSide));
Square rsq = relative_square(strongSide, pos.square<ROOK>(strongSide));
Square psq = relative_square(strongSide, pos.square<PAWN>(weakSide));
Square queeningSq = make_square(file_of(psq), RANK_1);
Square strongKing = relative_square(strongSide, pos.square<KING>(strongSide));
Square weakKing = relative_square(strongSide, pos.square<KING>(weakSide));
Square strongRook = relative_square(strongSide, pos.square<ROOK>(strongSide));
Square weakPawn = relative_square(strongSide, pos.square<PAWN>(weakSide));
Square queeningSquare = make_square(file_of(weakPawn), RANK_1);
Value result;
// If the stronger side's king is in front of the pawn, it's a win
if (forward_file_bb(WHITE, wksq) & psq)
result = RookValueEg - distance(wksq, psq);
if (forward_file_bb(WHITE, strongKing) & weakPawn)
result = RookValueEg - distance(strongKing, weakPawn);
// If the weaker side's king is too far from the pawn and the rook,
// it's a win.
else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide)
&& distance(bksq, rsq) >= 3)
result = RookValueEg - distance(wksq, psq);
else if ( distance(weakKing, weakPawn) >= 3 + (pos.side_to_move() == weakSide)
&& distance(weakKing, strongRook) >= 3)
result = RookValueEg - distance(strongKing, weakPawn);
// If the pawn is far advanced and supported by the defending king,
// the position is drawish
else if ( rank_of(bksq) <= RANK_3
&& distance(bksq, psq) == 1
&& rank_of(wksq) >= RANK_4
&& distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide))
result = Value(80) - 8 * distance(wksq, psq);
else if ( rank_of(weakKing) <= RANK_3
&& distance(weakKing, weakPawn) == 1
&& rank_of(strongKing) >= RANK_4
&& distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide))
result = Value(80) - 8 * distance(strongKing, weakPawn);
else
result = Value(200) - 8 * ( distance(wksq, psq + SOUTH)
- distance(bksq, psq + SOUTH)
- distance(psq, queeningSq));
result = Value(200) - 8 * ( distance(strongKing, weakPawn + SOUTH)
- distance(weakKing, weakPawn + SOUTH)
- distance(weakPawn, queeningSquare));
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, weakSide, KnightValueMg, 0));
Square bksq = pos.square<KING>(weakSide);
Square bnsq = pos.square<KNIGHT>(weakSide);
Value result = Value(push_to_edge(bksq) + push_away(bksq, bnsq));
Square weakKing = pos.square<KING>(weakSide);
Square weakKnight = pos.square<KNIGHT>(weakSide);
Value result = Value(push_to_edge(weakKing) + push_away(weakKing, weakKnight));
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, weakSide, VALUE_ZERO, 1));
Square winnerKSq = pos.square<KING>(strongSide);
Square loserKSq = pos.square<KING>(weakSide);
Square pawnSq = pos.square<PAWN>(weakSide);
Square strongKing = pos.square<KING>(strongSide);
Square weakKing = pos.square<KING>(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
|| distance(loserKSq, pawnSq) != 1
|| ((FileBBB | FileDBB | FileEBB | FileGBB) & pawnSq))
if ( relative_rank(weakSide, weakPawn) != RANK_7
|| distance(weakKing, weakPawn) != 1
|| ((FileBBB | FileDBB | FileEBB | FileGBB) & weakPawn))
result += QueenValueEg - PawnValueEg;
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, weakSide, RookValueMg, 0));
Square winnerKSq = pos.square<KING>(strongSide);
Square loserKSq = pos.square<KING>(weakSide);
Square strongKing = pos.square<KING>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Value result = QueenValueEg
- RookValueEg
+ push_to_edge(loserKSq)
+ push_close(winnerKSq, loserKSq);
+ push_to_edge(weakKing)
+ push_close(strongKing, weakKing);
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, weakSide, VALUE_ZERO, 1));
Square weakKing = pos.square<KING>(weakSide);
Square weakPawn = pos.square<PAWN>(weakSide);
Value result = PawnValueEg
+ 2 * push_to_edge(pos.square<KING>(weakSide))
- 10 * relative_rank(weakSide, pos.square<PAWN>(weakSide));
+ 2 * push_to_edge(weakKing)
- 10 * relative_rank(weakSide, weakPawn);
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 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?
if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB))
{
Square bishopSq = pos.square<BISHOP>(strongSide);
Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
Square weakKingSq = pos.square<KING>(weakSide);
Square queeningSquare = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
if ( opposite_colors(queeningSq, bishopSq)
&& distance(queeningSq, weakKingSq) <= 1)
if ( opposite_colors(queeningSquare, strongBishop)
&& distance(queeningSquare, weakKing) <= 1)
return SCALE_FACTOR_DRAW;
}
@ -343,20 +349,16 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
&& pos.count<PAWN>(weakSide) >= 1)
{
// Get the least advanced weakSide pawn
Square weakPawnSq = 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);
Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
// 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
if ( relative_rank(strongSide, weakPawnSq) == RANK_7
&& (strongPawns & (weakPawnSq + pawn_push(weakSide)))
&& (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongPawns)))
if ( relative_rank(strongSide, weakPawn) == RANK_7
&& (strongPawns & (weakPawn + pawn_push(weakSide)))
&& (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns)))
{
int strongKingDist = distance(weakPawnSq, strongKingSq);
int weakKingDist = distance(weakPawnSq, weakKingSq);
int strongKingDist = distance(weakPawn, strongKing);
int weakKingDist = distance(weakPawn, weakKing);
// 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
@ -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
// and positions where qsearch will immediately correct the
// 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 <= strongKingDist)
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<PAWN>(weakSide) >= 1);
Square kingSq = pos.square<KING>(weakSide);
Square rsq = pos.square<ROOK>(weakSide);
Square strongKing = pos.square<KING>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Square weakRook = pos.square<ROOK>(weakSide);
if ( relative_rank(weakSide, kingSq) <= RANK_2
&& relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4
&& relative_rank(weakSide, rsq) == RANK_3
if ( relative_rank(weakSide, weakKing) <= RANK_2
&& relative_rank(weakSide, strongKing) >= RANK_4
&& relative_rank(weakSide, weakRook) == RANK_3
&& ( pos.pieces(weakSide, PAWN)
& attacks_bb<KING>(kingSq)
& pawn_attacks_bb(strongSide, rsq)))
& attacks_bb<KING>(weakKing)
& pawn_attacks_bb(strongSide, weakRook)))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
@ -412,89 +415,89 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, RookValueMg, 0));
// Assume strongSide is white and the pawn is on files A-D
Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square wrsq = normalize(pos, strongSide, pos.square<ROOK>(strongSide));
Square wpsq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square brsq = normalize(pos, strongSide, pos.square<ROOK>(weakSide));
Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square strongRook = normalize(pos, strongSide, pos.square<ROOK>(strongSide));
Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square weakRook = normalize(pos, strongSide, pos.square<ROOK>(weakSide));
File f = file_of(wpsq);
Rank r = rank_of(wpsq);
Square queeningSq = make_square(f, RANK_8);
File pawnFile = file_of(strongPawn);
Rank pawnRank = rank_of(strongPawn);
Square queeningSquare = make_square(pawnFile, RANK_8);
int tempo = (pos.side_to_move() == strongSide);
// If the pawn is not too far advanced and the defending king defends the
// queening square, use the third-rank defence.
if ( r <= RANK_5
&& distance(bksq, queeningSq) <= 1
&& wksq <= SQ_H5
&& (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6)))
if ( pawnRank <= RANK_5
&& distance(weakKing, queeningSquare) <= 1
&& strongKing <= SQ_H5
&& (rank_of(weakRook) == RANK_6 || (pawnRank <= RANK_3 && rank_of(strongRook) != RANK_6)))
return SCALE_FACTOR_DRAW;
// The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind.
if ( r == RANK_6
&& distance(bksq, queeningSq) <= 1
&& rank_of(wksq) + tempo <= RANK_6
&& (rank_of(brsq) == RANK_1 || (!tempo && distance<File>(brsq, wpsq) >= 3)))
if ( pawnRank == RANK_6
&& distance(weakKing, queeningSquare) <= 1
&& rank_of(strongKing) + tempo <= RANK_6
&& (rank_of(weakRook) == RANK_1 || (!tempo && distance<File>(weakRook, strongPawn) >= 3)))
return SCALE_FACTOR_DRAW;
if ( r >= RANK_6
&& bksq == queeningSq
&& rank_of(brsq) == RANK_1
&& (!tempo || distance(wksq, wpsq) >= 2))
if ( pawnRank >= RANK_6
&& weakKing == queeningSquare
&& rank_of(weakRook) == RANK_1
&& (!tempo || distance(strongKing, strongPawn) >= 2))
return SCALE_FACTOR_DRAW;
// 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.
if ( wpsq == SQ_A7
&& wrsq == SQ_A8
&& (bksq == SQ_H7 || bksq == SQ_G7)
&& file_of(brsq) == FILE_A
&& (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5))
if ( strongPawn == SQ_A7
&& strongRook == SQ_A8
&& (weakKing == SQ_H7 || weakKing == SQ_G7)
&& file_of(weakRook) == FILE_A
&& (rank_of(weakRook) <= RANK_3 || file_of(strongKing) >= FILE_D || rank_of(strongKing) <= RANK_5))
return SCALE_FACTOR_DRAW;
// If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw.
if ( r <= RANK_5
&& bksq == wpsq + NORTH
&& distance(wksq, wpsq) - tempo >= 2
&& distance(wksq, brsq) - tempo >= 2)
if ( pawnRank <= RANK_5
&& weakKing == strongPawn + NORTH
&& distance(strongKing, strongPawn) - tempo >= 2
&& distance(strongKing, weakRook) - tempo >= 2)
return SCALE_FACTOR_DRAW;
// 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,
// and the defending king cannot gain tempi by threatening the attacking rook.
if ( r == RANK_7
&& f != FILE_A
&& file_of(wrsq) == f
&& wrsq != queeningSq
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
&& (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo))
return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq));
if ( pawnRank == RANK_7
&& pawnFile != FILE_A
&& file_of(strongRook) == pawnFile
&& strongRook != queeningSquare
&& (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
&& (distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo))
return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(strongKing, queeningSquare));
// Similar to the above, but with the pawn further back
if ( f != FILE_A
&& file_of(wrsq) == f
&& wrsq < wpsq
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
&& (distance(wksq, wpsq + NORTH) < distance(bksq, wpsq + NORTH) - 2 + tempo)
&& ( distance(bksq, wrsq) + tempo >= 3
|| ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo
&& (distance(wksq, wpsq + NORTH) < distance(bksq, wrsq) + tempo))))
if ( pawnFile != FILE_A
&& file_of(strongRook) == pawnFile
&& strongRook < strongPawn
&& (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
&& (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn + NORTH) - 2 + tempo)
&& ( distance(weakKing, strongRook) + tempo >= 3
|| ( distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo
&& (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn) + tempo))))
return ScaleFactor( SCALE_FACTOR_MAX
- 8 * distance(wpsq, queeningSq)
- 2 * distance(wksq, queeningSq));
- 8 * distance(strongPawn, queeningSquare)
- 2 * distance(strongKing, queeningSquare));
// If the pawn is not far advanced and the defending king is somewhere in
// 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);
if ( distance<File>(bksq, wpsq) == 1
&& distance(wksq, bksq) > 2)
return ScaleFactor(24 - 2 * distance(wksq, bksq));
if ( distance<File>(weakKing, strongPawn) == 1
&& distance(strongKing, weakKing) > 2)
return ScaleFactor(24 - 2 * distance(strongKing, weakKing));
}
return SCALE_FACTOR_NONE;
}
@ -508,10 +511,11 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
// Test for a rook pawn
if (pos.pieces(PAWN) & (FileABB | FileHBB))
{
Square ksq = pos.square<KING>(weakSide);
Square bsq = pos.square<BISHOP>(weakSide);
Square psq = pos.square<PAWN>(strongSide);
Rank rk = relative_rank(strongSide, psq);
Square weakKing = pos.square<KING>(weakSide);
Square weakBishop = pos.square<BISHOP>(weakSide);
Square strongKing = pos.square<KING>(strongSide);
Square strongPawn = pos.square<PAWN>(strongSide);
Rank pawnRank = relative_rank(strongSide, strongPawn);
Direction push = pawn_push(strongSide);
// 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
// reduction or a stronger one if the defending king is near the
// 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);
else
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
// pawn from a reasonable distance and the defending king is near
// the corner
if ( rk == RANK_6
&& distance(psq + 2 * push, ksq) <= 1
&& (attacks_bb<BISHOP>(bsq) & (psq + push))
&& distance<File>(bsq, psq) >= 2)
if ( pawnRank == RANK_6
&& distance(strongPawn + 2 * push, weakKing) <= 1
&& (attacks_bb<BISHOP>(weakBishop) & (strongPawn + push))
&& distance<File>(weakBishop, strongPawn) >= 2)
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, weakSide, RookValueMg, 1));
Square wpsq1 = pos.squares<PAWN>(strongSide)[0];
Square wpsq2 = pos.squares<PAWN>(strongSide)[1];
Square bksq = pos.square<KING>(weakSide);
Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
Square weakKing = pos.square<KING>(weakSide);
// 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;
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
&& distance<File>(bksq, wpsq2) <= 1
&& relative_rank(strongSide, bksq) > r)
if ( distance<File>(weakKing, strongPawn1) <= 1
&& distance<File>(weakKing, strongPawn2) <= 1
&& relative_rank(strongSide, weakKing) > pawnRank)
{
assert(r > RANK_1 && r < RANK_7);
return ScaleFactor(7 * r);
assert(pawnRank > RANK_1 && pawnRank < RANK_7);
return ScaleFactor(7 * pawnRank);
}
return SCALE_FACTOR_NONE;
}
@ -581,12 +585,12 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.count<PAWN>(strongSide) >= 2);
assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
Square ksq = pos.square<KING>(weakSide);
Bitboard pawns = pos.pieces(strongSide, PAWN);
Square weakKing = pos.square<KING>(weakSide);
Bitboard strongPawns = pos.pieces(strongSide, PAWN);
// If all pawns are ahead of the king on a single rook file, it's a draw.
if (!((pawns & ~FileABB) || (pawns & ~FileHBB)) &&
!(pawns & ~passed_pawn_span(weakSide, ksq)))
if (!((strongPawns & ~FileABB) || (strongPawns & ~FileHBB)) &&
!(strongPawns & ~passed_pawn_span(weakSide, weakKing)))
return SCALE_FACTOR_DRAW;
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, weakSide, BishopValueMg, 0));
Square pawnSq = pos.square<PAWN>(strongSide);
Square strongBishopSq = pos.square<BISHOP>(strongSide);
Square weakBishopSq = pos.square<BISHOP>(weakSide);
Square weakKingSq = pos.square<KING>(weakSide);
Square strongPawn = pos.square<PAWN>(strongSide);
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakBishop = pos.square<BISHOP>(weakSide);
Square weakKing = pos.square<KING>(weakSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away
if ( (forward_file_bb(strongSide, pawnSq) & weakKingSq)
&& ( opposite_colors(weakKingSq, strongBishopSq)
|| relative_rank(strongSide, weakKingSq) <= RANK_6))
if ( (forward_file_bb(strongSide, strongPawn) & weakKing)
&& ( opposite_colors(weakKing, strongBishop)
|| relative_rank(strongSide, weakKing) <= RANK_6))
return SCALE_FACTOR_DRAW;
// Case 2: Opposite colored bishops
if (opposite_colors(strongBishopSq, weakBishopSq))
if (opposite_colors(strongBishop, weakBishop))
return SCALE_FACTOR_DRAW;
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, weakSide, BishopValueMg, 0));
Square wbsq = pos.square<BISHOP>(strongSide);
Square bbsq = pos.square<BISHOP>(weakSide);
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakBishop = pos.square<BISHOP>(weakSide);
if (!opposite_colors(wbsq, bbsq))
if (!opposite_colors(strongBishop, weakBishop))
return SCALE_FACTOR_NONE;
Square ksq = pos.square<KING>(weakSide);
Square psq1 = pos.squares<PAWN>(strongSide)[0];
Square psq2 = pos.squares<PAWN>(strongSide)[1];
Square weakKing = pos.square<KING>(weakSide);
Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
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);
blockSq2 = make_square(file_of(psq2), rank_of(psq1));
blockSq1 = strongPawn1 + pawn_push(strongSide);
blockSq2 = make_square(file_of(strongPawn2), rank_of(strongPawn1));
}
else
{
blockSq1 = psq2 + pawn_push(strongSide);
blockSq2 = make_square(file_of(psq1), rank_of(psq2));
blockSq1 = strongPawn2 + pawn_push(strongSide);
blockSq2 = make_square(file_of(strongPawn1), rank_of(strongPawn2));
}
switch (distance<File>(psq1, psq2))
switch (distance<File>(strongPawn1, strongPawn2))
{
case 0:
// 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.
if ( file_of(ksq) == file_of(blockSq1)
&& relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1)
&& opposite_colors(ksq, wbsq))
if ( file_of(weakKing) == file_of(blockSq1)
&& relative_rank(strongSide, weakKing) >= relative_rank(strongSide, blockSq1)
&& opposite_colors(weakKing, strongBishop))
return SCALE_FACTOR_DRAW;
else
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
// square in front of the frontmost pawn's path, and the square diagonally
// behind this square on the file of the other pawn.
if ( ksq == blockSq1
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
if ( weakKing == blockSq1
&& opposite_colors(weakKing, strongBishop)
&& ( weakBishop == blockSq2
|| (attacks_bb<BISHOP>(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP))
|| distance<Rank>(psq1, psq2) >= 2))
|| distance<Rank>(strongPawn1, strongPawn2) >= 2))
return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq1
else if ( weakKing == blockSq2
&& opposite_colors(weakKing, strongBishop)
&& ( weakBishop == blockSq1
|| (attacks_bb<BISHOP>(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP))))
return SCALE_FACTOR_DRAW;
else
@ -698,14 +702,14 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 1));
assert(verify_material(pos, weakSide, KnightValueMg, 0));
Square pawnSq = pos.square<PAWN>(strongSide);
Square strongBishopSq = pos.square<BISHOP>(strongSide);
Square weakKingSq = pos.square<KING>(weakSide);
Square strongPawn = pos.square<PAWN>(strongSide);
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
if ( file_of(weakKingSq) == file_of(pawnSq)
&& relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq)
&& ( opposite_colors(weakKingSq, strongBishopSq)
|| relative_rank(strongSide, weakKingSq) <= RANK_6))
if ( file_of(weakKing) == file_of(strongPawn)
&& relative_rank(strongSide, strongPawn) < relative_rank(strongSide, weakKing)
&& ( opposite_colors(weakKing, strongBishop)
|| relative_rank(strongSide, weakKing) <= RANK_6))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
@ -724,18 +728,18 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
// Assume strongSide is white and the pawn is on files A-D
Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
// 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.
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;
// 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.
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 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];
@ -61,7 +61,7 @@ namespace Trace {
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 << " ---- ----" << " | " << " ---- ----";
else
os << scores[t][WHITE] << " | " << scores[t][BLACK];
@ -145,16 +145,17 @@ namespace {
constexpr Score ReachableOutpost = S( 31, 22);
constexpr Score PassedFile = S( 11, 8);
constexpr Score PawnlessFlank = S( 17, 95);
constexpr Score QueenInfiltration = S( -2, 14);
constexpr Score RestrictedPiece = S( 7, 7);
constexpr Score RookOnKingRing = S( 16, 0);
constexpr Score RookOnQueenFile = S( 5, 9);
constexpr Score SliderOnQueen = S( 59, 18);
constexpr Score RookOnQueenFile = S( 6, 11);
constexpr Score SliderOnQueen = S( 60, 18);
constexpr Score ThreatByKing = S( 24, 89);
constexpr Score ThreatByPawnPush = S( 48, 39);
constexpr Score ThreatBySafePawn = S(173, 94);
constexpr Score TrappedRook = S( 55, 13);
constexpr Score WeakQueen = S( 51, 14);
constexpr Score WeakQueenProtection = S( 15, 0);
constexpr Score WeakQueen = S( 56, 15);
constexpr Score WeakQueenProtection = S( 14, 0);
#undef S
@ -175,8 +176,7 @@ namespace {
template<Color Us> Score threats() const;
template<Color Us> Score passed() const;
template<Color Us> Score space() const;
ScaleFactor scale_factor(Value eg) const;
Score initiative(Score score) const;
Value winnable(Score score) const;
const Position& pos;
Material::Entry* me;
@ -279,7 +279,7 @@ namespace {
: attacks_bb<Pt>(s, pos.pieces());
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;
attackedBy[Us][Pt] |= b;
@ -376,6 +376,10 @@ namespace {
Bitboard queenPinners;
if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners))
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)
@ -679,16 +683,15 @@ namespace {
}
// Evaluation::space() computes the space evaluation for a given side. The
// space evaluation is a simple bonus based on the number of safe squares
// available for minor pieces on the central four files on ranks 2--4. Safe
// squares one, two or three squares behind a friendly pawn are counted
// twice. Finally, the space bonus is multiplied by a weight. The aim is to
// improve play on game opening.
// Evaluation::space() computes a space evaluation for a given side, aiming to improve game
// play in the opening. It is based on the number of safe squares on the 4 central files
// on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice.
// Finally, the space bonus is multiplied by a weight which decreases according to occupancy.
template<Tracing T> template<Color Us>
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)
return SCORE_ZERO;
@ -719,12 +722,12 @@ namespace {
}
// Evaluation::initiative() computes the initiative correction value
// for the position. It is a second order bonus/malus based on the
// Evaluation::winnable() adjusts the mg and eg score components based on the
// known attacking/defending status of the players.
// A single value is derived from the mg and eg values and returned.
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))
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
@ -746,7 +749,6 @@ namespace {
+ 24 * infiltration
+ 51 * !pos.non_pawn_material()
- 43 * almostUnwinnable
- 2 * pos.rule50_count()
-110 ;
Value mg = mg_value(score);
@ -758,17 +760,10 @@ namespace {
int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0);
int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
if (T)
Trace::add(INITIATIVE, make_score(u, v));
mg += u;
eg += v;
return make_score(u, v);
}
// Evaluation::scale_factor() computes the scale factor for the winning side
template<Tracing T>
ScaleFactor Evaluation<T>::scale_factor(Value eg) const {
// Compute the scale factor for the winning side
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
int sf = me->scale_factor(pos, strongSide);
@ -788,7 +783,18 @@ namespace {
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>()
+ space< WHITE>() - space< BLACK>();
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 /= PHASE_MIDGAME;
// Derive single value from mg and eg parts of score
v = winnable(score);
// In case of tracing add all remaining individual evaluation terms
if (T)
@ -859,11 +859,18 @@ namespace {
Trace::add(IMBALANCE, me->imbalance());
Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK));
Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]);
Trace::add(TOTAL, score);
}
// Evaluation grain
v = (v / 16) * 16;
// 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
@ -913,11 +920,11 @@ std::string Eval::trace(const Position& pos) {
<< " Threats | " << Term(THREAT)
<< " Passed | " << Term(PASSED)
<< " Space | " << Term(SPACE)
<< " Initiative | " << Term(INITIATIVE)
<< " Winnable | " << Term(WINNABLE)
<< " ------------+-------------+-------------+------------\n"
<< " Total | " << Term(TOTAL);
ss << "\nTotal evaluation: " << to_cp(v) << " (white side)\n";
ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n";
return ss.str();
}
@ -976,7 +983,7 @@ bool EvalList::is_valid(const Position& pos)
for (Piece pc = NO_PIECE; pc < PIECE_NB; ++pc)
{
auto pt = type_of(pc);
if (pt == NO_PIECE || pt == 7) // <E28098>ݵȢî
if (pt == NO_PIECE_TYPE || pt == 7) // <E28098>ݵȢî
continue;
// îpcÌBonaPieceÌŠJŽn”Ô<E2809D>

View file

@ -130,6 +130,20 @@ inline std::ostream& operator<<(std::ostream& os, PRNG& prng)
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
/// logical processor group. This usually means to be limited to use max 64
/// 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
// useless legality checks later on.
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
Bitboard b = attacks_bb<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;

View file

@ -57,7 +57,7 @@ namespace {
/// MovePicker constructor for the main search
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),
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
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
/// and quiet moves which are/were in the PV (ttPv)
/// It get cleared with each new search and get filled during iterative deepening
/// At higher depths LowPlyHistory records successful quiet moves near the root and quiet
/// moves which are/were in the PV (ttPv)
/// It is cleared with each new search and filled during iterative deepening
constexpr int MAX_LPH = 4;
typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory;
@ -133,7 +133,7 @@ public:
const CapturePieceToHistory*,
const PieceToHistory**,
Move,
Move*,
const Move*,
int);
Move next_move(bool skipQuiets = false);

View file

@ -150,17 +150,17 @@ namespace {
&& !(theirPawns & adjacent_files_bb(s)))
score -= Doubled;
else
score -= Isolated
+ WeakUnopposed * !opposed;
score -= Isolated
+ WeakUnopposed * !opposed;
}
else if (backward)
score -= Backward
+ WeakUnopposed * !opposed;
score -= Backward
+ WeakUnopposed * !opposed;
if (!support)
score -= Doubled * doubled
+ WeakLever * more_than_one(lever);
score -= Doubled * doubled
+ WeakLever * more_than_one(lever);
}
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.
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;

View file

@ -50,7 +50,7 @@ struct Entry {
Score do_king_safety(const Position& pos);
template<Color Us>
Score evaluate_shelter(const Position& pos, Square ksq);
Score evaluate_shelter(const Position& pos, Square ksq) const;
Key key;
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)
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(' ') << 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( -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( -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) }
};
@ -106,7 +106,7 @@ Score psq[PIECE_NB][SQUARE_NB];
// tables are initialized by flipping and changing the sign of the white scores.
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]);

View file

@ -65,9 +65,9 @@ namespace {
constexpr uint64_t TtHitAverageResolution = 1024;
// Razor and futility margins
constexpr int RazorMargin = 531;
constexpr int RazorMargin = 527;
Value futility_margin(Depth d, bool improving) {
return Value(217 * (d - improving));
return Value(227 * (d - improving));
}
// Reductions lookup table, initialized at startup
@ -75,16 +75,16 @@ namespace {
Depth reduction(bool i, Depth d, int 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) {
return (4 + depth * depth) / (2 - improving);
return (3 + depth * depth) / (2 - improving);
}
// History and stats update bonus, based on depth
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
@ -236,14 +236,8 @@ void MainThread::search() {
}
else
{
for (Thread* th : Threads)
{
th->bestMoveChanges = 0;
if (th != this)
th->start_searching();
}
Thread::search(); // Let's start searching!
Threads.start_searching(); // start non-main threads
Thread::search(); // main thread start searching
}
// When we reach the maximum depth, we can arrive here without a raise of
@ -260,9 +254,7 @@ void MainThread::search() {
Threads.stop = true;
// Wait until all threads have finished
for (Thread* th : Threads)
if (th != this)
th->wait_for_search_finished();
Threads.wait_for_search_finished();
// When playing in 'nodes as time' mode, subtract the searched nodes from
// the available ones before exiting.
@ -271,37 +263,11 @@ void MainThread::search() {
Thread* bestThread = this;
// Check if there are threads with a better score than main thread
if ( int(Options["MultiPV"]) == 1
&& !Limits.depth
&& !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"]))
&& rootMoves[0].pv[0] != MOVE_NONE)
{
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;
}
}
if (int(Options["MultiPV"]) == 1 &&
!Limits.depth &&
!(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) &&
rootMoves[0].pv[0] != MOVE_NONE)
bestThread = Threads.get_best_thread();
bestPreviousScore = bestThread->rootMoves[0].score;
@ -437,12 +403,12 @@ void Thread::search() {
if (rootDepth >= 4)
{
Value prev = rootMoves[pvIdx].previousScore;
delta = Value(21);
delta = Value(19);
alpha = std::max(prev - delta,-VALUE_INFINITE);
beta = std::min(prev + delta, VALUE_INFINITE);
// 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)
: -make_score(dct, dct / 2));
@ -540,13 +506,13 @@ void Thread::search() {
&& !Threads.stop
&& !mainThread->stopOnPonderhit)
{
double fallingEval = (332 + 6 * (mainThread->bestPreviousScore - bestValue)
+ 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0;
double fallingEval = (296 + 6 * (mainThread->bestPreviousScore - bestValue)
+ 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 725.0;
fallingEval = Utility::clamp(fallingEval, 0.5, 1.5);
// If the bestMove is stable over several iterations, reduce time accordingly
timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.94 : 0.91;
double reduction = (1.41 + mainThread->previousTimeReduction) / (2.27 * timeReduction);
timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.92 : 0.95;
double reduction = (1.47 + mainThread->previousTimeReduction) / (2.22 * timeReduction);
// Use part of the gained time from a previous stable move for the current move
for (Thread* th : Threads)
@ -571,7 +537,7 @@ void Thread::search() {
}
else if ( Threads.increaseDepth
&& !mainThread->ponder
&& Time.elapsed() > totalTime * 0.6)
&& Time.elapsed() > totalTime * 0.56)
Threads.increaseDepth = false;
else
Threads.increaseDepth = true;
@ -696,7 +662,7 @@ namespace {
// search to overwrite a previous full search TT value, so we use a different
// position key in case of an excluded move.
excludedMove = ss->excludedMove;
posKey = pos.key() ^ Key(excludedMove << 16); // Isn't a very good hash
posKey = pos.key() ^ (Key(excludedMove) << 48); // Isn't a very good hash
tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
@ -704,7 +670,7 @@ namespace {
ttPv = PvNode || (ttHit && tte->is_pv());
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->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)
if ( !PvNode
&& (ss-1)->currentMove != MOVE_NULL
&& (ss-1)->statScore < 23397
&& (ss-1)->statScore < 23824
&& eval >= beta
&& eval >= ss->staticEval
&& ss->staticEval >= beta - 32 * depth - 30 * improving + 120 * ttPv + 292
&& ss->staticEval >= beta - 33 * depth - 33 * improving + 112 * ttPv + 311
&& !excludedMove
&& pos.non_pawn_material(us)
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
@ -864,7 +830,7 @@ namespace {
assert(eval - beta >= 0);
// 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->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
// much above beta, we can (almost) safely prune the previous move.
if ( !PvNode
&& depth >= 5
&& depth > 4
&& abs(beta) < VALUE_TB_WIN_IN_MAX_PLY)
{
Value raisedBeta = beta + 189 - 45 * improving;
Value raisedBeta = beta + 176 - 49 * improving;
assert(raisedBeta < VALUE_INFINITE);
MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory);
int probCutCount = 0;
@ -1037,14 +1003,15 @@ moves_loop: // When in check, search starts from here
// Futility pruning: parent node (~5 Elo)
if ( lmrDepth < 6
&& !ss->inCheck
&& ss->staticEval + 235 + 172 * lmrDepth <= alpha
&& ss->staticEval + 284 + 188 * lmrDepth <= alpha
&& (*contHist[0])[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;
// 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;
}
else
@ -1060,11 +1027,11 @@ moves_loop: // When in check, search starts from here
&& lmrDepth < 6
&& !(PvNode && abs(bestValue) < 2)
&& !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;
// See based pruning
if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo)
if (!pos.see_ge(move, Value(-202) * depth)) // (~25 Elo)
continue;
}
}
@ -1177,12 +1144,12 @@ moves_loop: // When in check, search starts from here
|| moveCountPruning
|| ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha
|| cutNode
|| thisThread->ttHitAverage < 375 * TtHitAverageResolution * TtHitAverageWindow / 1024))
|| thisThread->ttHitAverage < 415 * TtHitAverageResolution * TtHitAverageWindow / 1024))
{
Depth r = reduction(improving, depth, moveCount);
// Decrease reduction if the ttHit running average is large
if (thisThread->ttHitAverage > 500 * TtHitAverageResolution * TtHitAverageWindow / 1024)
if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024)
r--;
// Reduction if other threads are searching this position.
@ -1197,7 +1164,7 @@ moves_loop: // When in check, search starts from here
r++;
// Decrease reduction if opponent's move count is high (~5 Elo)
if ((ss-1)->moveCount > 14)
if ((ss-1)->moveCount > 13)
r--;
// 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)
else if ( type_of(move) == NORMAL
&& !pos.see_ge(reverse_move(move)))
r -= 2 + ttPv;
r -= 2 + ttPv - (type_of(movedPiece) == PAWN);
ss->statScore = thisThread->mainHistory[us][from_to(move)]
+ (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)]
- 4926;
- 4826;
// 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--;
else if ((ss-1)->statScore >= -116 && ss->statScore < -154)
else if ((ss-1)->statScore >= -125 && ss->statScore < -138)
r++;
// Decrease/increase reduction for moves with a good/bad history (~30 Elo)
r -= ss->statScore / 16434;
r -= ss->statScore / 14615;
}
else
{
@ -1245,7 +1212,7 @@ moves_loop: // When in check, search starts from here
// Unless giving check, this capture is likely bad
if ( !givesCheck
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 200 * depth <= alpha)
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 211 * depth <= alpha)
r++;
}
@ -1507,7 +1474,7 @@ moves_loop: // When in check, search starts from here
if (PvNode && bestValue > alpha)
alpha = bestValue;
futilityBase = bestValue + 154;
futilityBase = bestValue + 141;
}
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;
}
if (depth > 12 && ss->ply < MAX_LPH)
thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7);
if (depth > 11 && ss->ply < MAX_LPH)
thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 6);
}
// 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);
size_t totalCount = moveList.size(), moveCount = 0;
for (const Move& move : moveList)
for (const Move move : moveList)
{
if ( !pos.capture(move)
&& (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN))
@ -1362,7 +1362,7 @@ void Tablebases::init(const std::string& paths) {
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) {
TBTables.add({KING, p1, KING});
@ -1469,7 +1469,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
StateInfo st;
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;

View file

@ -208,7 +208,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
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->rootMoves = rootMoves;
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();
}
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()); }
uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
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;

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,
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)

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) {
// Preserve any existing move for the same position
if (m || (k >> 48) != key16)
if (m || (uint16_t)k != key16)
move16 = (uint16_t)m;
// Overwrite less valuable entries
if ( (k >> 48) != key16
if ((uint16_t)k != key16
|| d - DEPTH_OFFSET > depth8 - 4
|| b == BOUND_EXACT)
{
assert(d >= DEPTH_OFFSET);
key16 = (uint16_t)(k >> 48);
key16 = (uint16_t)k;
value16 = (int16_t)v;
eval16 = (int16_t)ev;
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
@ -120,7 +120,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
#else
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)
if (!tte[i].key16 || tte[i].key16 == key16)

View file

@ -82,9 +82,8 @@ public:
void resize(size_t mbSize);
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 {
return &table[(uint32_t(key) * uint64_t(clusterCount)) >> 32].entry[0];
return &table[mul_hi64(key, clusterCount)].entry[0];
}
private:

View file

@ -40,7 +40,6 @@
#include <cassert>
#include <cctype>
#include <climits>
#include <cstdint>
#include <cstdlib>
#include <algorithm>
@ -219,7 +218,6 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
typedef int Depth;
enum : int {
DEPTH_QS_CHECKS = 0,
DEPTH_QS_NO_CHECKS = -1,
DEPTH_QS_RECAPTURES = -5,
@ -288,11 +286,11 @@ inline Value mg_value(Score s) {
}
#define ENABLE_BASE_OPERATORS_ON(T) \
constexpr T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \
constexpr T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \
constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \
constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \
constexpr T operator-(T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; }
inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; }
#define ENABLE_INCR_OPERATORS_ON(T) \
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_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
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) {
// at most 2^32 clusters.
constexpr int MaxHashMB = Is64Bit ? 131072 : 2048;
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
o["Debug Log File"] << Option("", on_logger);
o["Contempt"] << Option(24, -100, 100);