1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 00:33:09 +00:00

Merge branch 'master' into clusterMergeMaster6

This commit is contained in:
Joost VandeVondele 2019-07-01 16:36:58 +02:00
commit 0fd0e4e849
27 changed files with 344 additions and 293 deletions

View file

@ -1,7 +1,7 @@
## Overview ## Overview
[![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish) [![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish)
[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish) [![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
derived from Glaurung 2.1. It is not a complete chess program and requires a derived from Glaurung 2.1. It is not a complete chess program and requires a
@ -34,11 +34,11 @@ Currently, Stockfish has the following UCI options:
A positive value for contempt favors middle game positions and avoids draws. A positive value for contempt favors middle game positions and avoids draws.
* #### Analysis Contempt * #### Analysis Contempt
By default, contempt is set to prefer the side to move. Set this option to "White" By default, contempt is set to prefer the side to move. Set this option to "White"
or "Black" to analyse with contempt for that side, or "Off" to disable contempt. or "Black" to analyse with contempt for that side, or "Off" to disable contempt.
* #### Threads * #### Threads
The number of CPU threads used for searching a position. For best performance, set The number of CPU threads used for searching a position. For best performance, set
this equal to the number of CPU cores available. this equal to the number of CPU cores available.
* #### Hash * #### Hash
@ -58,18 +58,18 @@ Currently, Stockfish has the following UCI options:
Lower the Skill Level in order to make Stockfish play weaker. Lower the Skill Level in order to make Stockfish play weaker.
* #### Move Overhead * #### Move Overhead
Assume a time delay of x ms due to network and GUI overheads. This is useful to Assume a time delay of x ms due to network and GUI overheads. This is useful to
avoid losses on time in those cases. avoid losses on time in those cases.
* #### Minimum Thinking Time * #### Minimum Thinking Time
Search for at least x ms per move. Search for at least x ms per move.
* #### Slow Mover * #### Slow Mover
Lower values will make Stockfish take less time in games, higher values will Lower values will make Stockfish take less time in games, higher values will
make it think longer. make it think longer.
* #### nodestime * #### nodestime
Tells the engine to use nodes searched instead of wall time to account for Tells the engine to use nodes searched instead of wall time to account for
elapsed time. Useful for engine testing. elapsed time. Useful for engine testing.
* #### UCI_Chess960 * #### UCI_Chess960
@ -79,13 +79,13 @@ Currently, Stockfish has the following UCI options:
An option handled by your GUI. An option handled by your GUI.
* #### SyzygyPath * #### SyzygyPath
Path to the folders/directories storing the Syzygy tablebase files. Multiple Path to the folders/directories storing the Syzygy tablebase files. Multiple
directories are to be separated by ";" on Windows and by ":" on Unix-based directories are to be separated by ";" on Windows and by ":" on Unix-based
operating systems. Do not use spaces around the ";" or ":". operating systems. Do not use spaces around the ";" or ":".
Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6` Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
It is recommended to store .rtbw files on an SSD. There is no loss in storing It is recommended to store .rtbw files on an SSD. There is no loss in storing
the .rtbz files on a regular HD. It is recommended to verify all md5 checksums the .rtbz files on a regular HD. It is recommended to verify all md5 checksums
of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will
lead to engine crashes. lead to engine crashes.
@ -177,7 +177,7 @@ community effort. There are a few ways to help contribute to its growth.
### Donating hardware ### Donating hardware
Improving Stockfish requires a massive amount of testing. You can donate Improving Stockfish requires a massive amount of testing. You can donate
your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker) your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker)
and view the current tests on [Fishtest](http://tests.stockfishchess.org/tests). and view the current tests on [Fishtest](http://tests.stockfishchess.org/tests).
### Improving the code ### Improving the code
@ -193,7 +193,7 @@ generic rather than being focused on Stockfish's precise implementation.
Nevertheless, a helpful resource. Nevertheless, a helpful resource.
* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish). * The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish).
Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
group and engine testing is done on [Fishtest](http://tests.stockfishchess.org/tests). group and engine testing is done on [Fishtest](http://tests.stockfishchess.org/tests).
If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test) If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test)
first, where the basics of Stockfish development are explained. first, where the basics of Stockfish development are explained.

View file

@ -139,9 +139,9 @@ vector<string> setup_bench(const Position& current, istream& is) {
file.close(); file.close();
} }
list.emplace_back("ucinewgame");
list.emplace_back("setoption name Threads value " + threads); list.emplace_back("setoption name Threads value " + threads);
list.emplace_back("setoption name Hash value " + ttSize); list.emplace_back("setoption name Hash value " + ttSize);
list.emplace_back("ucinewgame");
for (const string& fen : fens) for (const string& fen : fens)
if (fen.find("setoption") != string::npos) if (fen.find("setoption") != string::npos)

View file

@ -27,7 +27,8 @@
namespace { namespace {
// There are 24 possible pawn squares: the first 4 files and ranks from 2 to 7 // There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
// Positions with the pawn on files E to H will be mirrored before probing.
constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608 constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
// Each uint32_t stores results of 32 positions, one per bit // Each uint32_t stores results of 32 positions, one per bit

View file

@ -18,8 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <bitset>
#include <algorithm> #include <algorithm>
#include <bitset>
#include "bitboard.h" #include "bitboard.h"
#include "misc.h" #include "misc.h"
@ -27,16 +27,10 @@
uint8_t PopCnt16[1 << 16]; uint8_t PopCnt16[1 << 16];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard KingFlank[FILE_NB] = {
QueenSide ^ FileDBB, QueenSide, QueenSide,
CenterFiles, CenterFiles,
KingSide, KingSide, KingSide ^ FileEBB
};
Magic RookMagics[SQUARE_NB]; Magic RookMagics[SQUARE_NB];
Magic BishopMagics[SQUARE_NB]; Magic BishopMagics[SQUARE_NB];

View file

@ -65,14 +65,19 @@ constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB); constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
constexpr Bitboard KingFlank[FILE_NB] = {
QueenSide ^ FileDBB, QueenSide, QueenSide,
CenterFiles, CenterFiles,
KingSide, KingSide, KingSide ^ FileEBB
};
extern uint8_t PopCnt16[1 << 16]; extern uint8_t PopCnt16[1 << 16];
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
extern Bitboard KingFlank[FILE_NB];
extern Bitboard SquareBB[SQUARE_NB];
/// Magic holds all magic bitboards relevant data for a single square /// Magic holds all magic bitboards relevant data for a single square
@ -148,6 +153,7 @@ inline Bitboard file_bb(Square s) {
template<Direction D> template<Direction D>
constexpr Bitboard shift(Bitboard b) { constexpr Bitboard shift(Bitboard b) {
return D == NORTH ? b << 8 : D == SOUTH ? b >> 8 return D == NORTH ? b << 8 : D == SOUTH ? b >> 8
: D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16
: D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1 : D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1
: D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7 : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7
: D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9
@ -178,8 +184,8 @@ constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
/// adjacent_files_bb() returns a bitboard representing all the squares on the /// adjacent_files_bb() returns a bitboard representing all the squares on the
/// adjacent files of the given one. /// adjacent files of the given one.
inline Bitboard adjacent_files_bb(File f) { inline Bitboard adjacent_files_bb(Square s) {
return shift<EAST>(file_bb(f)) | shift<WEST>(file_bb(f)); return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
} }
@ -215,7 +221,7 @@ inline Bitboard forward_file_bb(Color c, Square s) {
/// starting from the given square. /// starting from the given square.
inline Bitboard pawn_attack_span(Color c, Square s) { inline Bitboard pawn_attack_span(Color c, Square s) {
return forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s)); return forward_ranks_bb(c, s) & adjacent_files_bb(s);
} }
@ -223,7 +229,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(file_of(s)) | file_bb(s)); return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s));
} }
@ -370,10 +376,7 @@ inline Square pop_lsb(Bitboard* b) {
} }
/// frontmost_sq() and backmost_sq() return the square corresponding to the /// frontmost_sq() returns the most advanced square for the given color
/// most/least advanced bit relative to the given color.
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
#endif // #ifndef BITBOARD_H_INCLUDED #endif // #ifndef BITBOARD_H_INCLUDED

View file

@ -82,6 +82,34 @@ namespace {
} // namespace } // namespace
namespace Endgames {
std::pair<Map<Value>, Map<ScaleFactor>> maps;
void init() {
add<KPK>("KPK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKP>("KQKP");
add<KQKR>("KQKR");
add<KNNKP>("KNNKP");
add<KNPK>("KNPK");
add<KNPKB>("KNPKB");
add<KRPKR>("KRPKR");
add<KRPKB>("KRPKB");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
}
}
/// Mate with KX vs K. This function is used to evaluate positions with /// Mate with KX vs K. This function is used to evaluate positions with
/// king and plenty of material vs a lone king. It simply gives the /// king and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge /// attacking side a bonus for driving the defending king towards the edge
@ -337,7 +365,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
&& pos.count<PAWN>(weakSide) >= 1) && pos.count<PAWN>(weakSide) >= 1)
{ {
// Get weakSide pawn that is closest to the home rank // Get weakSide pawn that is closest to the home rank
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN)); Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
Square strongKingSq = pos.square<KING>(strongSide); Square strongKingSq = pos.square<KING>(strongSide);
Square weakKingSq = pos.square<KING>(weakSide); Square weakKingSq = pos.square<KING>(weakSide);

View file

@ -91,15 +91,19 @@ struct Endgame : public EndgameBase<T> {
}; };
/// The Endgames class stores the pointers to endgame evaluation and scaling /// The Endgames namespace handles the pointers to endgame evaluation and scaling
/// base objects in two std::map. We use polymorphism to invoke the actual /// base objects in two std::map. We use polymorphism to invoke the actual
/// endgame function by calling its virtual operator(). /// endgame function by calling its virtual operator().
class Endgames { namespace Endgames {
template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>; template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
template<typename T> using Map = std::map<Key, Ptr<T>>; template<typename T> using Map = std::map<Key, Ptr<T>>;
extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
void init();
template<typename T> template<typename T>
Map<T>& map() { Map<T>& map() {
return std::get<std::is_same<T, ScaleFactor>::value>(maps); return std::get<std::is_same<T, ScaleFactor>::value>(maps);
@ -113,35 +117,10 @@ class Endgames {
map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK)); map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK));
} }
std::pair<Map<Value>, Map<ScaleFactor>> maps;
public:
Endgames() {
add<KPK>("KPK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKP>("KQKP");
add<KQKR>("KQKR");
add<KNNKP>("KNNKP");
add<KNPK>("KNPK");
add<KNPKB>("KNPKB");
add<KRPKR>("KRPKR");
add<KRPKB>("KRPKB");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
}
template<typename T> template<typename T>
const EndgameBase<T>* probe(Key key) { const EndgameBase<T>* probe(Key key) {
return map<T>().count(key) ? map<T>()[key].get() : nullptr; return map<T>().count(key) ? map<T>()[key].get() : nullptr;
} }
}; }
#endif // #ifndef ENDGAME_H_INCLUDED #endif // #ifndef ENDGAME_H_INCLUDED

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstring> // For std::memset #include <cstring> // For std::memset
#include <iomanip> #include <iomanip>
@ -73,7 +74,7 @@ using namespace Trace;
namespace { namespace {
// Threshold for lazy and space evaluation // Threshold for lazy and space evaluation
constexpr Value LazyThreshold = Value(1500); constexpr Value LazyThreshold = Value(1400);
constexpr Value SpaceThreshold = Value(12222); constexpr Value SpaceThreshold = Value(12222);
// KingAttackWeights[PieceType] contains king attack weights by piece type // KingAttackWeights[PieceType] contains king attack weights by piece type
@ -132,6 +133,7 @@ namespace {
}; };
// Assorted bonuses and penalties // Assorted bonuses and penalties
constexpr Score AttacksOnSpaceArea = S( 4, 0);
constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopPawns = S( 3, 7);
constexpr Score CorneredBishop = S( 50, 50); constexpr Score CorneredBishop = S( 50, 50);
constexpr Score FlankAttacks = S( 8, 0); constexpr Score FlankAttacks = S( 8, 0);
@ -140,7 +142,7 @@ namespace {
constexpr Score KnightOnQueen = S( 16, 12); constexpr Score KnightOnQueen = S( 16, 12);
constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score LongDiagonalBishop = S( 45, 0);
constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score MinorBehindPawn = S( 18, 3);
constexpr Score Outpost = S( 9, 3); constexpr Score Outpost = S( 36, 12);
constexpr Score PawnlessFlank = S( 17, 95); constexpr Score PawnlessFlank = S( 17, 95);
constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RestrictedPiece = S( 7, 7);
constexpr Score RookOnPawn = S( 10, 32); constexpr Score RookOnPawn = S( 10, 32);
@ -151,7 +153,6 @@ namespace {
constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score ThreatBySafePawn = S(173, 94);
constexpr Score TrappedRook = S( 47, 4); constexpr Score TrappedRook = S( 47, 4);
constexpr Score WeakQueen = S( 49, 15); constexpr Score WeakQueen = S( 49, 15);
constexpr Score WeakUnopposedPawn = S( 12, 23);
#undef S #undef S
@ -186,15 +187,12 @@ namespace {
// is also calculated is ALL_PIECES. // is also calculated is ALL_PIECES.
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB]; Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
// attackedBy2[color] are the squares attacked by 2 pieces of a given color, // attackedBy2[color] are the squares attacked by at least 2 units of a given
// possibly via x-ray or by one pawn and one piece. Diagonal x-ray through // color, including x-rays. But diagonal x-rays through pawns are not computed.
// pawn or squares attacked by 2 pawns are not explicitly added.
Bitboard attackedBy2[COLOR_NB]; Bitboard attackedBy2[COLOR_NB];
// kingRing[color] are the squares adjacent to the king, plus (only for a // kingRing[color] are the squares adjacent to the king plus some other
// king on its first rank) the squares two ranks in front. For instance, // very near squares, depending on king position.
// if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6
// and h6.
Bitboard kingRing[COLOR_NB]; Bitboard kingRing[COLOR_NB];
// kingAttackersCount[color] is the number of pieces of the given color // kingAttackersCount[color] is the number of pieces of the given color
@ -241,8 +239,7 @@ namespace {
attackedBy[Us][KING] = pos.attacks_from<KING>(ksq); attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
attackedBy[Us][PAWN] = pe->pawn_attacks(Us); attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
attackedBy2[Us] = (attackedBy[Us][KING] & attackedBy[Us][PAWN]) attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
| dblAttackByPawn;
// Init our king safety tables // Init our king safety tables
kingRing[Us] = attackedBy[Us][KING]; kingRing[Us] = attackedBy[Us][KING];
@ -306,14 +303,12 @@ namespace {
if (Pt == BISHOP || Pt == KNIGHT) if (Pt == BISHOP || Pt == KNIGHT)
{ {
// Bonus if piece is on an outpost square or can reach one // Bonus if piece is on an outpost square or can reach one
bb = OutpostRanks & ~pe->pawn_attacks_span(Them); bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
if (bb & s) if (bb & s)
score += Outpost * (Pt == KNIGHT ? 4 : 2) score += Outpost * (Pt == KNIGHT ? 2 : 1);
* (1 + bool(attackedBy[Us][PAWN] & s));
else if (bb &= b & ~pos.pieces(Us)) else if (bb & b & ~pos.pieces(Us))
score += Outpost * (Pt == KNIGHT ? 2 : 1) score += Outpost / (Pt == KNIGHT ? 1 : 2);
* (1 + bool(attackedBy[Us][PAWN] & bb));
// Knight and Bishop bonus for being right behind a pawn // Knight and Bishop bonus for being right behind a pawn
if (shift<Down>(pos.pieces(PAWN)) & s) if (shift<Down>(pos.pieces(PAWN)) & s)
@ -358,8 +353,8 @@ namespace {
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
// Bonus for rook on an open or semi-open file // Bonus for rook on an open or semi-open file
if (pos.semiopen_file(Us, file_of(s))) if (pos.is_on_semiopen_file(Us, s))
score += RookOnFile[bool(pos.semiopen_file(Them, file_of(s)))]; score += RookOnFile[bool(pos.is_on_semiopen_file(Them, s))];
// Penalty when trapped by the king, even more if the king cannot castle // Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3) else if (mob <= 3)
@ -558,10 +553,6 @@ namespace {
score += RestrictedPiece * popcount(b); score += RestrictedPiece * popcount(b);
// Bonus for enemy unopposed weak pawns
if (pos.pieces(Us, ROOK, QUEEN))
score += WeakUnopposedPawn * pe->weak_unopposed(Them);
// Find squares where our pawns can push on the next move // Find squares where our pawns can push on the next move
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces(); b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
b |= shift<Up>(b & TRank3BB) & ~pos.pieces(); b |= shift<Up>(b & TRank3BB) & ~pos.pieces();
@ -645,12 +636,10 @@ namespace {
// If the pawn is free to advance, then increase the bonus // If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq)) if (pos.empty(blockSq))
{ {
// If there is a rook or queen attacking/defending the pawn from behind, defendedSquares = squaresToQueen = forward_file_bb(Us, s);
// consider all the squaresToQueen. Otherwise consider only the squares unsafeSquares = passed_pawn_span(Us, s);
// in the pawn's path attacked or occupied by the enemy.
defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s); bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
if (!(pos.pieces(Us) & bb)) if (!(pos.pieces(Us) & bb))
defendedSquares &= attackedBy[Us][ALL_PIECES]; defendedSquares &= attackedBy[Us][ALL_PIECES];
@ -658,21 +647,21 @@ namespace {
if (!(pos.pieces(Them) & bb)) if (!(pos.pieces(Them) & bb))
unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
// If there aren't any enemy attacks, assign a big bonus. Otherwise // If there are no enemy attacks on passed pawn span, assign a big bonus.
// assign a smaller bonus if the block square isn't attacked. // Otherwise assign a smaller bonus if the path to queen is not attacked
int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0; // and even smaller bonus if it is attacked but block square is not.
int k = !unsafeSquares ? 35 :
!(unsafeSquares & squaresToQueen) ? 20 :
!(unsafeSquares & blockSq) ? 9 :
0 ;
// If the path to the queen is fully defended, assign a big bonus. // Assign a larger bonus if the block square is defended.
// Otherwise assign a smaller bonus if the block square is defended. if (defendedSquares & blockSq)
if (defendedSquares == squaresToQueen) k += 5;
k += 6;
else if (defendedSquares & blockSq)
k += 4;
bonus += make_score(k * w, k * w); bonus += make_score(k * w, k * w);
} }
} // rank > RANK_3 } // r > RANK_3
// Scale down bonus for candidate passers which need more than one // Scale down bonus for candidate passers which need more than one
// pawn push to become passed, or have a pawn in front of them. // pawn push to become passed, or have a pawn in front of them.
@ -717,14 +706,14 @@ namespace {
// Find all squares which are at most three squares behind some friendly pawn // Find all squares which are at most three squares behind some friendly pawn
Bitboard behind = pos.pieces(Us, PAWN); Bitboard behind = pos.pieces(Us, PAWN);
behind |= shift<Down>(behind); behind |= shift<Down>(behind);
behind |= shift<Down>(shift<Down>(behind)); behind |= shift<Down+Down>(behind);
int bonus = popcount(safe) + popcount(behind & safe); int bonus = popcount(safe) + popcount(behind & safe);
int weight = pos.count<ALL_PIECES>(Us) int weight = pos.count<ALL_PIECES>(Us) - 1;
- (16 - pos.count<PAWN>()) / 4;
Score score = make_score(bonus * weight * weight / 16, 0); Score score = make_score(bonus * weight * weight / 16, 0);
score -= AttacksOnSpaceArea * popcount(attackedBy[Them][ALL_PIECES] & behind & safe);
if (T) if (T)
Trace::add(SPACE, Us, score); Trace::add(SPACE, Us, score);
@ -777,8 +766,7 @@ namespace {
if (sf == SCALE_FACTOR_NORMAL) if (sf == SCALE_FACTOR_NORMAL)
{ {
if ( pos.opposite_bishops() if ( pos.opposite_bishops()
&& pos.non_pawn_material(WHITE) == BishopValueMg && pos.non_pawn_material() == 2 * BishopValueMg)
&& pos.non_pawn_material(BLACK) == BishopValueMg)
sf = 16 + 4 * pe->passed_count(); sf = 16 + 4 * pe->passed_count();
else else
sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf); sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf);
@ -817,7 +805,7 @@ namespace {
// Early exit if score is high // Early exit if score is high
Value v = (mg_value(score) + eg_value(score)) / 2; Value v = (mg_value(score) + eg_value(score)) / 2;
if (abs(v) > LazyThreshold) if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
return pos.side_to_move() == WHITE ? v : -v; return pos.side_to_move() == WHITE ? v : -v;
// Main evaluation begins here // Main evaluation begins here

View file

@ -26,6 +26,7 @@
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
#include "uci.h" #include "uci.h"
#include "endgame.h"
#include "syzygy/tbprobe.h" #include "syzygy/tbprobe.h"
namespace PSQT { namespace PSQT {
@ -43,6 +44,7 @@ int main(int argc, char* argv[]) {
Bitboards::init(); Bitboards::init();
Position::init(); Position::init();
Bitbases::init(); Bitbases::init();
Endgames::init();
Search::init(); Search::init();
Threads.set(Options["Threads"]); Threads.set(Options["Threads"]);
Search::clear(); // After threads are up Search::clear(); // After threads are up

View file

@ -137,7 +137,7 @@ Entry* probe(const Position& pos) {
// Let's look if we have a specialized evaluation function for this particular // Let's look if we have a specialized evaluation function for this particular
// material configuration. Firstly we look for a fixed configuration one, then // material configuration. Firstly we look for a fixed configuration one, then
// for a generic one if the previous search failed. // for a generic one if the previous search failed.
if ((e->evaluationFunction = pos.this_thread()->endgames.probe<Value>(key)) != nullptr) if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
return e; return e;
for (Color c = WHITE; c <= BLACK; ++c) for (Color c = WHITE; c <= BLACK; ++c)
@ -149,7 +149,7 @@ Entry* probe(const Position& pos) {
// OK, we didn't find any special evaluation function for the current material // OK, we didn't find any special evaluation function for the current material
// configuration. Is there a suitable specialized scaling function? // configuration. Is there a suitable specialized scaling function?
const auto* sf = pos.this_thread()->endgames.probe<ScaleFactor>(key); const auto* sf = Endgames::probe<ScaleFactor>(key);
if (sf) if (sf)
{ {

View file

@ -145,7 +145,7 @@ const string engine_info(bool to_uci) {
/// Debug functions used mainly to collect run-time statistics /// Debug functions used mainly to collect run-time statistics
static int64_t hits[2], means[2]; static std::atomic<int64_t> hits[2], means[2];
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); } void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
@ -210,12 +210,6 @@ void prefetch(void* addr) {
#endif #endif
void prefetch2(void* addr) {
prefetch(addr);
prefetch((uint8_t*)addr + 64);
}
namespace WinProcGroup { namespace WinProcGroup {
#ifndef _WIN32 #ifndef _WIN32

View file

@ -31,7 +31,6 @@
const std::string engine_info(bool to_uci = false); const std::string engine_info(bool to_uci = false);
void prefetch(void* addr); void prefetch(void* addr);
void prefetch2(void* addr);
void start_logger(const std::string& fname); void start_logger(const std::string& fname);
void dbg_hit_on(bool b); void dbg_hit_on(bool b);

View file

@ -200,11 +200,15 @@ top:
/* fallthrough */ /* fallthrough */
case QUIET_INIT: case QUIET_INIT:
cur = endBadCaptures; if (!skipQuiets)
endMoves = generate<QUIETS>(pos, cur); {
cur = endBadCaptures;
endMoves = generate<QUIETS>(pos, cur);
score<QUIETS>();
partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY);
}
score<QUIETS>();
partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY);
++stage; ++stage;
/* fallthrough */ /* fallthrough */

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "bitboard.h" #include "bitboard.h"
@ -34,9 +35,11 @@ namespace {
constexpr Score Backward = S( 9, 24); constexpr Score Backward = S( 9, 24);
constexpr Score Doubled = S(11, 56); constexpr Score Doubled = S(11, 56);
constexpr Score Isolated = S( 5, 15); constexpr Score Isolated = S( 5, 15);
constexpr Score WeakUnopposed = S( 13, 27);
constexpr Score Attacked2Unsupported = S( 0, 20);
// Connected pawn bonus // Connected pawn bonus
constexpr int Connected[RANK_NB] = { 0, 13, 17, 24, 59, 96, 171 }; constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };
// Strength of pawn shelter for our king by [distance from edge][rank]. // Strength of pawn shelter for our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
@ -50,11 +53,12 @@ namespace {
// Danger of enemy pawns moving toward our king by [distance from edge][rank]. // Danger of enemy pawns moving toward our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn // RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
// is behind our king. // is behind our king.
// [0][1-2] accommodate opponent pawn on edge (likely blocked by our king)
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = { constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
{ V( 89), V(107), V(123), V(93), V(57), V( 45), V( 51) }, { V( 89), V(-285), V(-185), V(93), V(57), V( 45), V( 51) },
{ V( 44), V(-18), V(123), V(46), V(39), V( -7), V( 23) }, { V( 44), V( -18), V( 123), V(46), V(39), V( -7), V( 23) },
{ V( 4), V( 52), V(162), V(37), V( 7), V(-14), V( -2) }, { V( 4), V( 52), V( 162), V(37), V( 7), V(-14), V( -2) },
{ V(-10), V(-14), V( 90), V(15), V( 2), V( -7), V(-16) } { V(-10), V( -14), V( 90), V(15), V( 2), V( -7), V(-16) }
}; };
#undef S #undef S
@ -76,16 +80,20 @@ namespace {
Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0; e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
e->kingSquares[Us] = SQ_NONE; e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns); e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns);
// Unsupported enemy pawns attacked twice by us
score += Attacked2Unsupported * popcount( theirPawns
& pawn_double_attacks_bb<Us>(ourPawns)
& ~pawn_attacks_bb<Them>(theirPawns));
// Loop through all pawns of the current color and score each pawn // Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE) while ((s = *pl++) != SQ_NONE)
{ {
assert(pos.piece_on(s) == make_piece(Us, PAWN)); assert(pos.piece_on(s) == make_piece(Us, PAWN));
File f = file_of(s);
Rank r = relative_rank(Us, s); Rank r = relative_rank(Us, s);
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
@ -96,7 +104,7 @@ namespace {
lever = theirPawns & PawnAttacks[Us][s]; lever = theirPawns & PawnAttacks[Us][s];
leverPush = theirPawns & PawnAttacks[Us][s + Up]; leverPush = theirPawns & PawnAttacks[Us][s + Up];
doubled = ourPawns & (s - Up); doubled = ourPawns & (s - Up);
neighbours = ourPawns & adjacent_files_bb(f); neighbours = ourPawns & adjacent_files_bb(s);
phalanx = neighbours & rank_bb(s); phalanx = neighbours & rank_bb(s);
support = neighbours & rank_bb(s - Up); support = neighbours & rank_bb(s - Up);
@ -109,9 +117,8 @@ namespace {
// full attack info to evaluate them. Include also not passed pawns // full attack info to evaluate them. Include also not passed pawns
// which could become passed after one or two pawn pushes when are // which could become passed after one or two pawn pushes when are
// not attacked more times than defended. // not attacked more times than defended.
if ( !(stoppers ^ lever ^ leverPush) if ( !(stoppers ^ lever) ||
&& (support || !more_than_one(lever)) (!(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush)))
&& popcount(phalanx) >= popcount(leverPush))
e->passedPawns[Us] |= s; e->passedPawns[Us] |= s;
else if (stoppers == square_bb(s + Up) && r >= RANK_5) else if (stoppers == square_bb(s + Up) && r >= RANK_5)
@ -125,15 +132,16 @@ namespace {
// Score this pawn // Score this pawn
if (support | phalanx) if (support | phalanx)
{ {
int v = (phalanx ? 3 : 2) * Connected[r]; int v = Connected[r] * (phalanx ? 3 : 2) / (opposed ? 2 : 1)
v = 17 * popcount(support) + (v >> (opposed + 1)); + 17 * popcount(support);
score += make_score(v, v * (r - 2) / 4); score += make_score(v, v * (r - 2) / 4);
} }
else if (!neighbours) else if (!neighbours)
score -= Isolated, e->weakUnopposed[Us] += !opposed; score -= Isolated + WeakUnopposed * int(!opposed);
else if (backward) else if (backward)
score -= Backward, e->weakUnopposed[Us] += !opposed; score -= Backward + WeakUnopposed * int(!opposed);
if (doubled && !support) if (doubled && !support)
score -= Doubled; score -= Doubled;
@ -171,35 +179,36 @@ Entry* probe(const Position& pos) {
/// penalty for a king, looking at the king file and the two closest files. /// penalty for a king, looking at the king file and the two closest files.
template<Color Us> template<Color Us>
Value Entry::evaluate_shelter(const Position& pos, Square ksq) { void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
constexpr Bitboard BlockRanks = (Us == WHITE ? Rank1BB | Rank2BB : Rank8BB | Rank7BB);
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
Bitboard ourPawns = b & pos.pieces(Us); Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them); Bitboard theirPawns = b & pos.pieces(Them);
Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ? Value bonus[] = { Value(5), Value(5) };
Value(374) : Value(5);
File center = clamp(file_of(ksq), FILE_B, FILE_G); File center = clamp(file_of(ksq), FILE_B, FILE_G);
for (File f = File(center - 1); f <= File(center + 1); ++f) for (File f = File(center - 1); f <= File(center + 1); ++f)
{ {
b = ourPawns & file_bb(f); b = ourPawns & file_bb(f);
Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; Rank ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
b = theirPawns & file_bb(f); b = theirPawns & file_bb(f);
Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
int d = std::min(f, ~f); int d = std::min(f, ~f);
safety += ShelterStrength[d][ourRank]; bonus[MG] += ShelterStrength[d][ourRank];
safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3)
: UnblockedStorm[d][theirRank]; if (ourRank && (ourRank == theirRank - 1))
bonus[MG] -= 82 * (theirRank == RANK_3), bonus[EG] -= 82 * (theirRank == RANK_3);
else
bonus[MG] -= UnblockedStorm[d][theirRank];
} }
return safety; if (bonus[MG] > mg_value(shelter))
shelter = make_score(bonus[MG], bonus[EG]);
} }
@ -222,16 +231,17 @@ Score Entry::do_king_safety(const Position& pos) {
else while (pawns) else while (pawns)
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
Value bonus = evaluate_shelter<Us>(pos, ksq); Score shelter = make_score(-VALUE_INFINITE, VALUE_ZERO);
evaluate_shelter<Us>(pos, ksq, shelter);
// If we can castle use the bonus after the castling if it is bigger // If we can castle use the bonus after the castling if it is bigger
if (pos.can_castle(Us | KING_SIDE)) if (pos.can_castle(Us | KING_SIDE))
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1))); evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1), shelter);
if (pos.can_castle(Us | QUEEN_SIDE)) if (pos.can_castle(Us | QUEEN_SIDE))
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1))); evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1), shelter);
return make_score(bonus, -16 * minPawnDist); return shelter - make_score(VALUE_ZERO, 16 * minPawnDist);
} }
// Explicit template instantiation // Explicit template instantiation

View file

@ -37,8 +37,7 @@ struct Entry {
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
int weak_unopposed(Color c) const { return weakUnopposed[c]; } int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }
int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); };
template<Color Us> template<Color Us>
Score king_safety(const Position& pos) { Score king_safety(const Position& pos) {
@ -50,7 +49,7 @@ struct Entry {
Score do_king_safety(const Position& pos); Score do_king_safety(const Position& pos);
template<Color Us> template<Color Us>
Value evaluate_shelter(const Position& pos, Square ksq); void evaluate_shelter(const Position& pos, Square ksq, Score& shelter);
Key key; Key key;
Score scores[COLOR_NB]; Score scores[COLOR_NB];
@ -59,12 +58,10 @@ struct Entry {
Bitboard pawnAttacksSpan[COLOR_NB]; Bitboard pawnAttacksSpan[COLOR_NB];
Square kingSquares[COLOR_NB]; Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int weakUnopposed[COLOR_NB];
int castlingRights[COLOR_NB]; int castlingRights[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
}; };
typedef HashTable<Entry, 16384> Table; typedef HashTable<Entry, 131072> Table;
Entry* probe(const Position& pos); Entry* probe(const Position& pos);

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstddef> // For offsetof() #include <cstddef> // For offsetof()
#include <cstring> // For std::memset, std::memcmp #include <cstring> // For std::memset, std::memcmp
@ -340,8 +341,8 @@ void Position::set_castling_right(Color c, Square rfrom) {
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1); Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1); Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto) castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
& ~(square_bb(kfrom) | rfrom); & ~(square_bb(kfrom) | rfrom);
} }
@ -382,6 +383,12 @@ void Position::set_state(StateInfo* si) const {
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
Piece pc = piece_on(s); Piece pc = piece_on(s);
si->key ^= Zobrist::psq[pc][s]; si->key ^= Zobrist::psq[pc][s];
if (type_of(pc) == PAWN)
si->pawnKey ^= Zobrist::psq[pc][s];
else if (type_of(pc) != KING)
si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
} }
if (si->epSquare != SQ_NONE) if (si->epSquare != SQ_NONE)
@ -392,20 +399,9 @@ void Position::set_state(StateInfo* si) const {
si->key ^= Zobrist::castling[si->castlingRights]; si->key ^= Zobrist::castling[si->castlingRights];
for (Bitboard b = pieces(PAWN); b; )
{
Square s = pop_lsb(&b);
si->pawnKey ^= Zobrist::psq[piece_on(s)][s];
}
for (Piece pc : Pieces) for (Piece pc : Pieces)
{
if (type_of(pc) != PAWN && type_of(pc) != KING)
si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc];
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
si->materialKey ^= Zobrist::psq[pc][cnt]; si->materialKey ^= Zobrist::psq[pc][cnt];
}
} }
@ -495,7 +491,7 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
// Snipers are sliders that attack 's' when a piece and other snipers are removed // Snipers are sliders that attack 's' when a piece and other snipers are removed
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK)) Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
Bitboard occupancy = pieces() & ~snipers; Bitboard occupancy = pieces() ^ snipers;
while (snipers) while (snipers)
{ {
@ -859,7 +855,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// Update pawn hash key and prefetch access to pawnsTable // Update pawn hash key and prefetch access to pawnsTable
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
prefetch2(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter // Reset rule 50 draw counter
st->rule50 = 0; st->rule50 = 0;
@ -879,6 +874,25 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// Update king attacks used for fast check detection // Update king attacks used for fast check detection
set_check_info(st); set_check_info(st);
// Calculate the repetition info. It is the ply distance from the previous
// occurrence of the same position, negative in the 3-fold case, or zero
// if the position was not repeated.
st->repetition = 0;
int end = std::min(st->rule50, st->pliesFromNull);
if (end >= 4)
{
StateInfo* stp = st->previous->previous;
for (int i=4; i <= end; i += 2)
{
stp = stp->previous->previous;
if (stp->key == st->key)
{
st->repetition = stp->repetition ? -i : i;
break;
}
}
}
assert(pos_is_ok()); assert(pos_is_ok());
} }
@ -994,6 +1008,8 @@ void Position::do_null_move(StateInfo& newSt) {
set_check_info(st); set_check_info(st);
st->repetition = 0;
assert(pos_is_ok()); assert(pos_is_ok());
} }
@ -1117,24 +1133,10 @@ bool Position::is_draw(int ply) const {
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size())) if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true; return true;
int end = std::min(st->rule50, st->pliesFromNull); // Return a draw score if a position repeats once earlier but strictly
// after the root, or repeats twice before or at the root.
if (end < 4) if (st->repetition && st->repetition < ply)
return false; return true;
StateInfo* stp = st->previous->previous;
int cnt = 0;
for (int i = 4; i <= end; i += 2)
{
stp = stp->previous->previous;
// Return a draw score if a position repeats once earlier but strictly
// after the root, or repeats twice before or at the root.
if ( stp->key == st->key
&& ++cnt + (ply > i) == 2)
return true;
}
return false; return false;
} }
@ -1146,26 +1148,15 @@ bool Position::is_draw(int ply) const {
bool Position::has_repeated() const { bool Position::has_repeated() const {
StateInfo* stc = st; StateInfo* stc = st;
while (true) int end = std::min(st->rule50, st->pliesFromNull);
while (end-- >= 4)
{ {
int i = 4, end = std::min(stc->rule50, stc->pliesFromNull); if (stc->repetition)
return true;
if (end < i)
return false;
StateInfo* stp = stc->previous->previous;
do {
stp = stp->previous->previous;
if (stp->key == stc->key)
return true;
i += 2;
} while (i <= end);
stc = stc->previous; stc = stc->previous;
} }
return false;
} }
@ -1198,22 +1189,19 @@ bool Position::has_game_cycle(int ply) const {
if (!(between_bb(s1, s2) & pieces())) if (!(between_bb(s1, s2) & pieces()))
{ {
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same
// location. We select the legal one by reversing the move variable if necessary.
if (empty(s1))
move = make_move(s2, s1);
if (ply > i) if (ply > i)
return true; return true;
// For nodes before or at the root, check that the move is a
// repetition rather than a move to the current position.
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
// the same location, so we have to select which square to check.
if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
continue;
// For repetitions before or at the root, require one more // For repetitions before or at the root, require one more
StateInfo* next_stp = stp; if (stp->repetition)
for (int k = i + 2; k <= end; k += 2) return true;
{
next_stp = next_stp->previous->previous;
if (next_stp->key == stp->key)
return true;
}
} }
} }
} }

View file

@ -46,6 +46,7 @@ struct StateInfo {
Square epSquare; Square epSquare;
// Not copied when making a move (will be recomputed anyhow) // Not copied when making a move (will be recomputed anyhow)
int repetition;
Key key; Key key;
Bitboard checkersBB; Bitboard checkersBB;
Piece capturedPiece; Piece capturedPiece;
@ -95,7 +96,7 @@ public:
template<PieceType Pt> int count() const; template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(Color c) const; template<PieceType Pt> const Square* squares(Color c) const;
template<PieceType Pt> Square square(Color c) const; template<PieceType Pt> Square square(Color c) const;
int semiopen_file(Color c, File f) const; bool is_on_semiopen_file(Color c, Square s) const;
// Castling // Castling
int castling_rights(Color c) const; int castling_rights(Color c) const;
@ -262,8 +263,8 @@ inline Square Position::ep_square() const {
return st->epSquare; return st->epSquare;
} }
inline int Position::semiopen_file(Color c, File f) const { inline bool Position::is_on_semiopen_file(Color c, Square s) const {
return !(pieces(c, PAWN) & file_bb(f)); return !(pieces(c, PAWN) & file_bb(s));
} }
inline bool Position::can_castle(CastlingRight cr) const { inline bool Position::can_castle(CastlingRight cr) const {
@ -321,7 +322,7 @@ inline bool Position::pawn_passed(Color c, Square s) const {
inline bool Position::advanced_pawn_push(Move m) const { inline bool Position::advanced_pawn_push(Move m) const {
return type_of(moved_piece(m)) == PAWN return type_of(moved_piece(m)) == PAWN
&& relative_rank(sideToMove, from_sq(m)) > RANK_4; && relative_rank(sideToMove, to_sq(m)) > RANK_5;
} }
inline int Position::pawns_on_same_color_squares(Color c, Square s) const { inline int Position::pawns_on_same_color_squares(Color c, Square s) const {

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <cstring> // For std::memset #include <cstring> // For std::memset
@ -70,9 +71,9 @@ namespace {
// Reductions lookup table, initialized at startup // Reductions lookup table, initialized at startup
int Reductions[MAX_MOVES]; // [depth or moveNumber] int Reductions[MAX_MOVES]; // [depth or moveNumber]
template <bool PvNode> Depth reduction(bool i, Depth d, int mn) { Depth reduction(bool i, Depth d, int mn) {
int r = Reductions[d / ONE_PLY] * Reductions[mn] / 1024; int r = Reductions[d / ONE_PLY] * Reductions[mn];
return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY; return ((r + 512) / 1024 + (!i && r > 1024)) * ONE_PLY;
} }
constexpr int futility_move_count(bool improving, int depth) { constexpr int futility_move_count(bool improving, int depth) {
@ -102,6 +103,48 @@ namespace {
Move best = MOVE_NONE; Move best = MOVE_NONE;
}; };
// Breadcrumbs are used to mark nodes as being searched by a given thread.
struct Breadcrumb {
std::atomic<Thread*> thread;
std::atomic<Key> key;
};
std::array<Breadcrumb, 1024> breadcrumbs;
// ThreadHolding keeps track of which thread left breadcrumbs at the given node for potential reductions.
// A free node will be marked upon entering the moves loop, and unmarked upon leaving that loop, by the ctor/dtor of this struct.
struct ThreadHolding {
explicit ThreadHolding(Thread* thisThread, Key posKey, int ply) {
location = ply < 8 ? &breadcrumbs[posKey & (breadcrumbs.size() - 1)] : nullptr;
otherThread = false;
owning = false;
if (location)
{
// see if another already marked this location, if not, mark it ourselves.
Thread* tmp = (*location).thread.load(std::memory_order_relaxed);
if (tmp == nullptr)
{
(*location).thread.store(thisThread, std::memory_order_relaxed);
(*location).key.store(posKey, std::memory_order_relaxed);
owning = true;
}
else if ( tmp != thisThread
&& (*location).key.load(std::memory_order_relaxed) == posKey)
otherThread = true;
}
}
~ThreadHolding() {
if (owning) // free the marked location.
(*location).thread.store(nullptr, std::memory_order_relaxed);
}
bool marked() { return otherThread; }
private:
Breadcrumb* location;
bool otherThread, owning;
};
template <NodeType NT> template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
@ -149,7 +192,7 @@ namespace {
void Search::init() { void Search::init() {
for (int i = 1; i < MAX_MOVES; ++i) for (int i = 1; i < MAX_MOVES; ++i)
Reductions[i] = int(1024 * std::log(i) / std::sqrt(1.95)); Reductions[i] = int(22.9 * std::log(i));
} }
@ -244,21 +287,15 @@ void MainThread::search() {
for (Thread* th: Threads) for (Thread* th: Threads)
minScore = std::min(minScore, th->rootMoves[0].score); minScore = std::min(minScore, th->rootMoves[0].score);
// Vote according to score and depth // Vote according to score and depth, and select the best thread
for (Thread* th : Threads) for (Thread* th : Threads)
{ {
int64_t s = th->rootMoves[0].score - minScore + 1; votes[th->rootMoves[0].pv[0]] +=
votes[th->rootMoves[0].pv[0]] += 200 + s * s * int(th->completedDepth); (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
}
// Select best thread if (votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])
auto bestVote = votes[this->rootMoves[0].pv[0]];
for (Thread* th : Threads)
if (votes[th->rootMoves[0].pv[0]] > bestVote)
{
bestVote = votes[th->rootMoves[0].pv[0]];
bestThread = th; bestThread = th;
} }
} }
@ -326,8 +363,13 @@ void Thread::search() {
bestValue = delta = alpha = -VALUE_INFINITE; bestValue = delta = alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE; beta = VALUE_INFINITE;
size_t multiPV = Options["MultiPV"]; multiPV = Options["MultiPV"];
Skill skill(Options["Skill Level"]); // Pick integer skill levels, but non-deterministically round up or down
// such that the average integer skill corresponds to the input floating point one.
PRNG rng(now());
int intLevel = int(Options["Skill Level"]) +
((Options["Skill Level"] - int(Options["Skill Level"])) * 1024 > rng.rand<unsigned>() % 1024 ? 1 : 0);
Skill skill(intLevel);
// When playing with strength handicap enable MultiPV search that we will // When playing with strength handicap enable MultiPV search that we will
// use behind the scenes to retrieve a set of possible moves. // use behind the scenes to retrieve a set of possible moves.
@ -439,17 +481,14 @@ void Thread::search() {
beta = (alpha + beta) / 2; beta = (alpha + beta) / 2;
alpha = std::max(bestValue - delta, -VALUE_INFINITE); alpha = std::max(bestValue - delta, -VALUE_INFINITE);
failedHighCnt = 0;
if (mainThread) if (mainThread)
{
failedHighCnt = 0;
mainThread->stopOnPonderhit = false; mainThread->stopOnPonderhit = false;
}
} }
else if (bestValue >= beta) else if (bestValue >= beta)
{ {
beta = std::min(bestValue + delta, VALUE_INFINITE); beta = std::min(bestValue + delta, VALUE_INFINITE);
if (mainThread) ++failedHighCnt;
++failedHighCnt;
} }
else else
break; break;
@ -579,13 +618,13 @@ namespace {
bool ttHit, ttPv, inCheck, givesCheck, improving; bool ttHit, ttPv, inCheck, givesCheck, improving;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture;
Piece movedPiece; Piece movedPiece;
int moveCount, captureCount, quietCount; int moveCount, captureCount, quietCount, singularLMR;
// Step 1. Initialize node // Step 1. Initialize node
Thread* thisThread = pos.this_thread(); Thread* thisThread = pos.this_thread();
inCheck = pos.checkers(); inCheck = pos.checkers();
Color us = pos.side_to_move(); Color us = pos.side_to_move();
moveCount = captureCount = quietCount = ss->moveCount = 0; moveCount = captureCount = quietCount = singularLMR = ss->moveCount = 0;
bestValue = -VALUE_INFINITE; bestValue = -VALUE_INFINITE;
maxValue = VALUE_INFINITE; maxValue = VALUE_INFINITE;
@ -630,7 +669,10 @@ namespace {
// starts with statScore = 0. Later grandchildren start with the last calculated // starts with statScore = 0. Later grandchildren start with the last calculated
// statScore of the previous grandchild. This influences the reduction rules in // statScore of the previous grandchild. This influences the reduction rules in
// LMR which are based on the statScore of parent position. // LMR which are based on the statScore of parent position.
(ss+2)->statScore = 0; if (rootNode)
(ss + 4)->statScore = 0;
else
(ss + 2)->statScore = 0;
// Step 4. Transposition table lookup. We don't want the score of a partial // Step 4. Transposition table lookup. We don't want the score of a partial
// 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
@ -641,16 +683,7 @@ namespace {
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
: ttHit ? tte->move() : MOVE_NONE; : ttHit ? tte->move() : MOVE_NONE;
ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY); ttPv = PvNode || (ttHit && tte->is_pv());
// If position has been searched at higher depths and we are shuffling,
// return value_draw.
if ( pos.rule50_count() > 36 - 6 * (pos.count<ALL_PIECES>() > 14)
&& ss->ply > 36 - 6 * (pos.count<ALL_PIECES>() > 14)
&& ttHit
&& tte->depth() > depth
&& pos.count<PAWN>() > 0)
return VALUE_DRAW;
// At non-PV nodes we check for an early TT cutoff // At non-PV nodes we check for an early TT cutoff
if ( !PvNode if ( !PvNode
@ -901,6 +934,9 @@ moves_loop: // When in check, search starts from here
moveCountPruning = false; moveCountPruning = false;
ttCapture = ttMove && pos.capture_or_promotion(ttMove); ttCapture = ttMove && pos.capture_or_promotion(ttMove);
// Mark this node as being searched.
ThreadHolding th(thisThread, posKey, ss->ply);
// Step 12. Loop through all pseudo-legal moves until no moves remain // Step 12. Loop through all pseudo-legal moves until no moves remain
// or a beta cutoff occurs. // or a beta cutoff occurs.
while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE)
@ -924,6 +960,12 @@ moves_loop: // When in check, search starts from here
sync_cout << "info depth " << depth / ONE_PLY sync_cout << "info depth " << depth / ONE_PLY
<< " currmove " << UCI::move(move, pos.is_chess960()) << " currmove " << UCI::move(move, pos.is_chess960())
<< " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl; << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl;
// In MultiPV mode also skip moves which will be searched later as PV moves
if (rootNode && std::count(thisThread->rootMoves.begin() + thisThread->pvIdx + 1,
thisThread->rootMoves.begin() + thisThread->multiPV, move))
continue;
if (PvNode) if (PvNode)
(ss+1)->pv = nullptr; (ss+1)->pv = nullptr;
@ -943,7 +985,7 @@ moves_loop: // When in check, search starts from here
&& move == ttMove && move == ttMove
&& !rootNode && !rootNode
&& !excludedMove // Avoid recursive singular search && !excludedMove // Avoid recursive singular search
/* && ttValue != VALUE_NONE Already implicit in the next condition */ /* && ttValue != VALUE_NONE Already implicit in the next condition */
&& abs(ttValue) < VALUE_KNOWN_WIN && abs(ttValue) < VALUE_KNOWN_WIN
&& (tte->bound() & BOUND_LOWER) && (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3 * ONE_PLY && tte->depth() >= depth - 3 * ONE_PLY
@ -956,15 +998,22 @@ moves_loop: // When in check, search starts from here
ss->excludedMove = MOVE_NONE; ss->excludedMove = MOVE_NONE;
if (value < singularBeta) if (value < singularBeta)
{
extension = ONE_PLY; extension = ONE_PLY;
singularLMR++;
if (value < singularBeta - std::min(3 * depth / ONE_PLY, 39))
singularLMR++;
}
// Multi-cut pruning // Multi-cut pruning
// Our ttMove is assumed to fail high, and now we failed high also on a reduced // Our ttMove is assumed to fail high, and now we failed high also on a reduced
// search without the ttMove. So we assume this expected Cut-node is not singular, // search without the ttMove. So we assume this expected Cut-node is not singular,
// that is multiple moves fail high, and we can prune the whole subtree by returning // that multiple moves fail high, and we can prune the whole subtree by returning
// the hard beta bound. // a soft bound.
else if (cutNode && singularBeta > beta) else if ( eval >= beta
return beta; && singularBeta >= beta)
return singularBeta;
} }
// Check extension (~2 Elo) // Check extension (~2 Elo)
@ -979,9 +1028,8 @@ moves_loop: // When in check, search starts from here
// Shuffle extension // Shuffle extension
else if ( PvNode else if ( PvNode
&& pos.rule50_count() > 18 && pos.rule50_count() > 18
&& ss->ply > 18
&& depth < 3 * ONE_PLY && depth < 3 * ONE_PLY
&& ss->ply < 3 * thisThread->rootDepth / ONE_PLY) // To avoid infinite loops && ++thisThread->shuffleExts < thisThread->nodes.load(std::memory_order_relaxed) / 4) // To avoid too many extensions
extension = ONE_PLY; extension = ONE_PLY;
// Passed pawn extension // Passed pawn extension
@ -1003,14 +1051,14 @@ moves_loop: // When in check, search starts from here
if ( !captureOrPromotion if ( !captureOrPromotion
&& !givesCheck && !givesCheck
&& !pos.advanced_pawn_push(move)) && (!pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg))
{ {
// Move count based pruning (~30 Elo) // Move count based pruning (~30 Elo)
if (moveCountPruning) if (moveCountPruning)
continue; continue;
// Reduced depth of the next LMR search // Reduced depth of the next LMR search
int lmrDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO); int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO);
lmrDepth /= ONE_PLY; lmrDepth /= ONE_PLY;
// Countermoves based pruning (~20 Elo) // Countermoves based pruning (~20 Elo)
@ -1029,7 +1077,8 @@ moves_loop: // When in check, search starts from here
if (!pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth))) if (!pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth)))
continue; continue;
} }
else if (!pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo) else if ((!givesCheck || !extension)
&& !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo)
continue; continue;
} }
@ -1053,19 +1102,28 @@ moves_loop: // When in check, search starts from here
// Step 16. Reduced depth search (LMR). If the move fails high it will be // Step 16. Reduced depth search (LMR). If the move fails high it will be
// re-searched at full depth. // re-searched at full depth.
if ( depth >= 3 * ONE_PLY if ( depth >= 3 * ONE_PLY
&& moveCount > 1 && moveCount > 1 + 3 * rootNode
&& (!captureOrPromotion || moveCountPruning)) && ( !captureOrPromotion
|| moveCountPruning
|| ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha))
{ {
Depth r = reduction<PvNode>(improving, depth, moveCount); Depth r = reduction(improving, depth, moveCount);
// Reduction if other threads are searching this position.
if (th.marked())
r += ONE_PLY;
// Decrease reduction if position is or has been on the PV // Decrease reduction if position is or has been on the PV
if (ttPv) if (ttPv)
r -= ONE_PLY; r -= 2 * ONE_PLY;
// Decrease reduction if opponent's move count is high (~10 Elo) // Decrease reduction if opponent's move count is high (~10 Elo)
if ((ss-1)->moveCount > 15) if ((ss-1)->moveCount > 15)
r -= ONE_PLY; r -= ONE_PLY;
// Decrease reduction if move has been singularly extended
r -= singularLMR * ONE_PLY;
if (!captureOrPromotion) if (!captureOrPromotion)
{ {
// Increase reduction if ttMove is a capture (~0 Elo) // Increase reduction if ttMove is a capture (~0 Elo)
@ -1100,7 +1158,7 @@ moves_loop: // When in check, search starts from here
r -= ss->statScore / 20000 * ONE_PLY; r -= ss->statScore / 20000 * ONE_PLY;
} }
Depth d = std::max(newDepth - std::max(r, DEPTH_ZERO), ONE_PLY); Depth d = clamp(newDepth - r, ONE_PLY, newDepth);
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true); value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
@ -1252,8 +1310,8 @@ moves_loop: // When in check, search starts from here
} }
// qsearch() is the quiescence search function, which is called by the main // qsearch() is the quiescence search function, which is called by the main search
// search function with depth zero, or recursively with depth less than ONE_PLY. // function with zero depth, or recursively with further decreasing depth per call.
template <NodeType NT> template <NodeType NT>
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
@ -1408,6 +1466,7 @@ moves_loop: // When in check, search starts from here
// Don't search moves with negative SEE values // Don't search moves with negative SEE values
if ( (!inCheck || evasionPrunable) if ( (!inCheck || evasionPrunable)
&& (!givesCheck || !(pos.blockers_for_king(~pos.side_to_move()) & from_sq(move)))
&& !pos.see_ge(move)) && !pos.see_ge(move))
continue; continue;
@ -1519,7 +1578,7 @@ moves_loop: // When in check, search starts from here
void update_capture_stats(const Position& pos, Move move, void update_capture_stats(const Position& pos, Move move,
Move* captures, int captureCount, int bonus) { Move* captures, int captureCount, int bonus) {
CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory; CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
Piece moved_piece = pos.moved_piece(move); Piece moved_piece = pos.moved_piece(move);
PieceType captured = type_of(pos.piece_on(to_sq(move))); PieceType captured = type_of(pos.piece_on(to_sq(move)));
@ -1763,7 +1822,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) {
} }
else else
{ {
// Assign the same rank to all moves // Clean up if root_probe() and root_probe_wdl() have failed
for (auto& m : rootMoves) for (auto& m : rootMoves)
m.tbRank = 0; m.tbRank = 0;
} }

View file

@ -70,7 +70,7 @@ struct RootMove {
Value score = -VALUE_INFINITE; Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE;
int selDepth = 0; int selDepth = 0;
int tbRank; int tbRank = 0;
Value tbScore; Value tbScore;
std::vector<Move> pv; std::vector<Move> pv;
}; };

View file

@ -20,6 +20,7 @@
#include <cassert> #include <cassert>
#include <algorithm> // For std::count
#include "movegen.h" #include "movegen.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
@ -193,7 +194,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
for (Thread* th : *this) for (Thread* th : *this)
{ {
th->nodes = th->tbHits = th->TTsaves = th->nmpMinPly = 0; th->shuffleExts = th->nodes = th->tbHits = th->TTsaves = th->nmpMinPly = 0;
th->rootDepth = th->completedDepth = DEPTH_ZERO; th->rootDepth = th->completedDepth = DEPTH_ZERO;
th->rootMoves = rootMoves; th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);

View file

@ -60,8 +60,7 @@ public:
Pawns::Table pawnsTable; Pawns::Table pawnsTable;
Material::Table materialTable; Material::Table materialTable;
Endgames endgames; size_t pvIdx, multiPV, pvLast, shuffleExts;
size_t pvIdx, pvLast;
int selDepth, nmpMinPly; int selDepth, nmpMinPly;
Color nmpColor; Color nmpColor;
std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges; std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cfloat> #include <cfloat>
#include <cmath> #include <cmath>

View file

@ -43,14 +43,16 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev)
// Overwrite less valuable entries // Overwrite less valuable entries
if ( (k >> 48) != key16 if ( (k >> 48) != key16
|| d / ONE_PLY > depth8 - 4 ||(d - DEPTH_OFFSET) / ONE_PLY > depth8 - 4
|| b == BOUND_EXACT) || b == BOUND_EXACT)
{ {
assert((d - DEPTH_OFFSET) / ONE_PLY >= 0);
key16 = (uint16_t)(k >> 48); key16 = (uint16_t)(k >> 48);
value16 = (int16_t)v; value16 = (int16_t)v;
eval16 = (int16_t)ev; eval16 = (int16_t)ev;
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
depth8 = (int8_t)(d / ONE_PLY); depth8 = (uint8_t)((d - DEPTH_OFFSET) / ONE_PLY);
} }
} }

View file

@ -45,7 +45,7 @@ struct TTEntry {
Move move() const { return (Move )move16; } Move move() const { return (Move )move16; }
Value value() const { return (Value)value16; } Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; } Value eval() const { return (Value)eval16; }
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); } Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_OFFSET; }
bool is_pv() const { return (bool)(genBound8 & 0x4); } bool is_pv() const { return (bool)(genBound8 & 0x4); }
Bound bound() const { return (Bound)(genBound8 & 0x3); } Bound bound() const { return (Bound)(genBound8 & 0x3); }
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev); void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev);
@ -59,7 +59,7 @@ private:
int16_t value16; int16_t value16;
int16_t eval16; int16_t eval16;
uint8_t genBound8; uint8_t genBound8;
int8_t depth8; uint8_t depth8;
}; };

View file

@ -101,7 +101,7 @@ typedef uint64_t Key;
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
constexpr int MAX_MOVES = 256; constexpr int MAX_MOVES = 256;
constexpr int MAX_PLY = 128; constexpr int MAX_PLY = 246;
/// A move needs 16 bits to be stored /// A move needs 16 bits to be stored
/// ///
@ -213,8 +213,9 @@ enum Depth : int {
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY, DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY, DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_NONE = -6 * ONE_PLY, DEPTH_NONE = -6 * ONE_PLY,
DEPTH_MAX = MAX_PLY * ONE_PLY DEPTH_OFFSET = DEPTH_NONE,
DEPTH_MAX = MAX_PLY * ONE_PLY
}; };
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2"); static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
@ -290,7 +291,6 @@ inline T& operator--(T& d) { return d = T(int(d) - 1); }
#define ENABLE_FULL_OPERATORS_ON(T) \ #define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_OPERATORS_ON(T) \ ENABLE_BASE_OPERATORS_ON(T) \
ENABLE_INCR_OPERATORS_ON(T) \
constexpr T operator*(int i, T d) { return T(i * int(d)); } \ constexpr T operator*(int i, T d) { return T(i * int(d)); } \
constexpr T operator*(T d, int i) { return T(int(d) * i); } \ constexpr T operator*(T d, int i) { return T(int(d) * i); } \
constexpr T operator/(T d, int i) { return T(int(d) / i); } \ constexpr T operator/(T d, int i) { return T(int(d) / i); } \

View file

@ -166,7 +166,7 @@ namespace {
} }
else if (token == "setoption") setoption(is); else if (token == "setoption") setoption(is);
else if (token == "position") position(pos, is, states); else if (token == "position") position(pos, is, states);
else if (token == "ucinewgame") Search::clear(); else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while
} }
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>