mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 08:43:09 +00:00
Standardize Comments
use double slashes (//) only for comments. closes #4820 No functional change.
This commit is contained in:
parent
fe53a18f7a
commit
edb4ab924f
28 changed files with 491 additions and 493 deletions
|
@ -95,17 +95,17 @@ const std::vector<std::string> Defaults = {
|
||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
/// setup_bench() builds a list of UCI commands to be run by bench. There
|
// setup_bench() builds a list of UCI commands to be run by bench. There
|
||||||
/// are five parameters: TT size in MB, number of search threads that
|
// are five parameters: TT size in MB, number of search threads that
|
||||||
/// should be used, the limit value spent for each position, a file name
|
// should be used, the limit value spent for each position, a file name
|
||||||
/// where to look for positions in FEN format, and the type of the limit:
|
// where to look for positions in FEN format, and the type of the limit:
|
||||||
/// depth, perft, nodes and movetime (in milliseconds). Examples:
|
// depth, perft, nodes and movetime (in milliseconds). Examples:
|
||||||
///
|
//
|
||||||
/// bench : search default positions up to depth 13
|
// bench : search default positions up to depth 13
|
||||||
/// bench 64 1 15 : search default positions up to depth 15 (TT = 64MB)
|
// bench 64 1 15 : search default positions up to depth 15 (TT = 64MB)
|
||||||
/// bench 64 1 100000 default nodes : search default positions for 100K nodes each
|
// bench 64 1 100000 default nodes : search default positions for 100K nodes each
|
||||||
/// bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec
|
// bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec
|
||||||
/// bench 16 1 5 blah perft : run a perft 5 on positions in file "blah"
|
// bench 16 1 5 blah perft : run a perft 5 on positions in file "blah"
|
||||||
|
|
||||||
std::vector<std::string> setup_bench(const Position& current, std::istream& is) {
|
std::vector<std::string> setup_bench(const Position& current, std::istream& is) {
|
||||||
|
|
||||||
|
|
|
@ -46,8 +46,8 @@ namespace {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// safe_destination() returns the bitboard of target square for the given step
|
// safe_destination() returns the bitboard of target square for the given step
|
||||||
/// from the given square. If the step is off the board, returns empty bitboard.
|
// from the given square. If the step is off the board, returns empty bitboard.
|
||||||
|
|
||||||
inline Bitboard safe_destination(Square s, int step) {
|
inline Bitboard safe_destination(Square s, int step) {
|
||||||
Square to = Square(s + step);
|
Square to = Square(s + step);
|
||||||
|
@ -55,8 +55,8 @@ inline Bitboard safe_destination(Square s, int step) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
|
// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
|
||||||
/// to be printed to standard output. Useful for debugging.
|
// to be printed to standard output. Useful for debugging.
|
||||||
|
|
||||||
std::string Bitboards::pretty(Bitboard b) {
|
std::string Bitboards::pretty(Bitboard b) {
|
||||||
|
|
||||||
|
@ -75,8 +75,8 @@ std::string Bitboards::pretty(Bitboard b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Bitboards::init() initializes various bitboard tables. It is called at
|
// Bitboards::init() initializes various bitboard tables. It is called at
|
||||||
/// startup and relies on global objects to be already zero-initialized.
|
// startup and relies on global objects to be already zero-initialized.
|
||||||
|
|
||||||
void Bitboards::init() {
|
void Bitboards::init() {
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||||
|
|
||||||
|
|
||||||
/// Magic holds all magic bitboards relevant data for a single square
|
// Magic holds all magic bitboards relevant data for a single square
|
||||||
struct Magic {
|
struct Magic {
|
||||||
Bitboard mask;
|
Bitboard mask;
|
||||||
Bitboard magic;
|
Bitboard magic;
|
||||||
|
@ -95,8 +95,8 @@ inline Bitboard square_bb(Square s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Overloads of bitwise operators between a Bitboard and a Square for testing
|
// Overloads of bitwise operators between a Bitboard and a Square for testing
|
||||||
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
|
// whether a given bit is set in a bitboard, and for setting and clearing bits.
|
||||||
|
|
||||||
inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); }
|
inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); }
|
||||||
inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); }
|
inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); }
|
||||||
|
@ -115,8 +115,8 @@ constexpr bool more_than_one(Bitboard b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// rank_bb() and file_bb() return a bitboard representing all the squares on
|
// rank_bb() and file_bb() return a bitboard representing all the squares on
|
||||||
/// the given file or rank.
|
// the given file or rank.
|
||||||
|
|
||||||
constexpr Bitboard rank_bb(Rank r) {
|
constexpr Bitboard rank_bb(Rank r) {
|
||||||
return Rank1BB << (8 * r);
|
return Rank1BB << (8 * r);
|
||||||
|
@ -135,7 +135,7 @@ constexpr Bitboard file_bb(Square s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// shift() moves a bitboard one or two steps as specified by the direction D
|
// shift() moves a bitboard one or two steps as specified by the direction D
|
||||||
|
|
||||||
template<Direction D>
|
template<Direction D>
|
||||||
constexpr Bitboard shift(Bitboard b) {
|
constexpr Bitboard shift(Bitboard b) {
|
||||||
|
@ -148,8 +148,8 @@ constexpr Bitboard shift(Bitboard b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// pawn_attacks_bb() returns the squares attacked by pawns of the given color
|
// pawn_attacks_bb() returns the squares attacked by pawns of the given color
|
||||||
/// from the squares in the given bitboard.
|
// from the squares in the given bitboard.
|
||||||
|
|
||||||
template<Color C>
|
template<Color C>
|
||||||
constexpr Bitboard pawn_attacks_bb(Bitboard b) {
|
constexpr Bitboard pawn_attacks_bb(Bitboard b) {
|
||||||
|
@ -163,10 +163,10 @@ inline Bitboard pawn_attacks_bb(Color c, Square s) {
|
||||||
return PawnAttacks[c][s];
|
return PawnAttacks[c][s];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// line_bb() returns a bitboard representing an entire line (from board edge
|
// line_bb() returns a bitboard representing an entire line (from board edge
|
||||||
/// to board edge) that intersects the two given squares. If the given squares
|
// to board edge) that intersects the two given squares. If the given squares
|
||||||
/// are not on a same file/rank/diagonal, the function returns 0. For instance,
|
// are not on a same file/rank/diagonal, the function returns 0. For instance,
|
||||||
/// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
|
// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
|
||||||
|
|
||||||
inline Bitboard line_bb(Square s1, Square s2) {
|
inline Bitboard line_bb(Square s1, Square s2) {
|
||||||
|
|
||||||
|
@ -176,13 +176,13 @@ inline Bitboard line_bb(Square s1, Square s2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open
|
// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open
|
||||||
/// segment between the squares s1 and s2 (excluding s1 but including s2). If the
|
// segment between the squares s1 and s2 (excluding s1 but including s2). If the
|
||||||
/// given squares are not on a same file/rank/diagonal, it returns s2. For instance,
|
// given squares are not on a same file/rank/diagonal, it returns s2. For instance,
|
||||||
/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but
|
// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but
|
||||||
/// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick
|
// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick
|
||||||
/// allows to generate non-king evasion moves faster: the defending piece must either
|
// allows to generate non-king evasion moves faster: the defending piece must either
|
||||||
/// interpose itself to cover the check or capture the checking piece.
|
// interpose itself to cover the check or capture the checking piece.
|
||||||
|
|
||||||
inline Bitboard between_bb(Square s1, Square s2) {
|
inline Bitboard between_bb(Square s1, Square s2) {
|
||||||
|
|
||||||
|
@ -191,16 +191,16 @@ inline Bitboard between_bb(Square s1, Square s2) {
|
||||||
return BetweenBB[s1][s2];
|
return BetweenBB[s1][s2];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a
|
// aligned() returns true if the squares s1, s2 and s3 are aligned either on a
|
||||||
/// straight or on a diagonal line.
|
// straight or on a diagonal line.
|
||||||
|
|
||||||
inline bool aligned(Square s1, Square s2, Square s3) {
|
inline bool aligned(Square s1, Square s2, Square s3) {
|
||||||
return line_bb(s1, s2) & s3;
|
return line_bb(s1, s2) & s3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// distance() functions return the distance between x and y, defined as the
|
// distance() functions return the distance between x and y, defined as the
|
||||||
/// number of steps for a king in x to reach y.
|
// number of steps for a king in x to reach y.
|
||||||
|
|
||||||
template<typename T1 = Square> inline int distance(Square x, Square y);
|
template<typename T1 = Square> inline int distance(Square x, Square y);
|
||||||
template<> inline int distance<File>(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); }
|
template<> inline int distance<File>(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); }
|
||||||
|
@ -209,8 +209,8 @@ template<> inline int distance<Square>(Square x, Square y) { return SquareDistan
|
||||||
|
|
||||||
inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
|
inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
|
||||||
|
|
||||||
/// attacks_bb(Square) returns the pseudo attacks of the given piece type
|
// attacks_bb(Square) returns the pseudo attacks of the given piece type
|
||||||
/// assuming an empty board.
|
// assuming an empty board.
|
||||||
|
|
||||||
template<PieceType Pt>
|
template<PieceType Pt>
|
||||||
inline Bitboard attacks_bb(Square s) {
|
inline Bitboard attacks_bb(Square s) {
|
||||||
|
@ -221,9 +221,9 @@ inline Bitboard attacks_bb(Square s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// attacks_bb(Square, Bitboard) returns the attacks by the given piece
|
// attacks_bb(Square, Bitboard) returns the attacks by the given piece
|
||||||
/// assuming the board is occupied according to the passed Bitboard.
|
// assuming the board is occupied according to the passed Bitboard.
|
||||||
/// Sliding piece attacks do not continue passed an occupied square.
|
// Sliding piece attacks do not continue passed an occupied square.
|
||||||
|
|
||||||
template<PieceType Pt>
|
template<PieceType Pt>
|
||||||
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
||||||
|
@ -253,7 +253,7 @@ inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// popcount() counts the number of non-zero bits in a bitboard
|
// popcount() counts the number of non-zero bits in a bitboard
|
||||||
|
|
||||||
inline int popcount(Bitboard b) {
|
inline int popcount(Bitboard b) {
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ inline int popcount(Bitboard b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// lsb() and msb() return the least/most significant bit in a non-zero bitboard
|
// lsb() and msb() return the least/most significant bit in a non-zero bitboard
|
||||||
|
|
||||||
#if defined(__GNUC__) // GCC, Clang, ICX
|
#if defined(__GNUC__) // GCC, Clang, ICX
|
||||||
|
|
||||||
|
@ -342,15 +342,15 @@ inline Square msb(Bitboard b) {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// least_significant_square_bb() returns the bitboard of the least significant
|
// least_significant_square_bb() returns the bitboard of the least significant
|
||||||
/// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)).
|
// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)).
|
||||||
|
|
||||||
inline Bitboard least_significant_square_bb(Bitboard b) {
|
inline Bitboard least_significant_square_bb(Bitboard b) {
|
||||||
assert(b);
|
assert(b);
|
||||||
return b & -b;
|
return b & -b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
|
// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
|
||||||
|
|
||||||
inline Square pop_lsb(Bitboard& b) {
|
inline Square pop_lsb(Bitboard& b) {
|
||||||
assert(b);
|
assert(b);
|
||||||
|
|
|
@ -57,13 +57,13 @@ namespace Eval {
|
||||||
|
|
||||||
std::string currentEvalFileName = "None";
|
std::string currentEvalFileName = "None";
|
||||||
|
|
||||||
/// NNUE::init() tries to load a NNUE network at startup time, or when the engine
|
// NNUE::init() tries to load a NNUE network at startup time, or when the engine
|
||||||
/// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
|
// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
|
||||||
/// The name of the NNUE network is always retrieved from the EvalFile option.
|
// The name of the NNUE network is always retrieved from the EvalFile option.
|
||||||
/// We search the given network in three locations: internally (the default
|
// We search the given network in three locations: internally (the default
|
||||||
/// network may be embedded in the binary), in the active working directory and
|
// network may be embedded in the binary), in the active working directory and
|
||||||
/// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
|
// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
|
||||||
/// variable to have the engine search in a special directory in their distro.
|
// variable to have the engine search in a special directory in their distro.
|
||||||
|
|
||||||
void NNUE::init() {
|
void NNUE::init() {
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ namespace Eval {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NNUE::verify() verifies that the last net used was loaded successfully
|
// NNUE::verify() verifies that the last net used was loaded successfully
|
||||||
void NNUE::verify() {
|
void NNUE::verify() {
|
||||||
|
|
||||||
std::string eval_file = std::string(Options["EvalFile"]);
|
std::string eval_file = std::string(Options["EvalFile"]);
|
||||||
|
@ -135,9 +135,9 @@ namespace Eval {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// simple_eval() returns a static, purely materialistic evaluation of the position
|
// simple_eval() returns a static, purely materialistic evaluation of the position
|
||||||
/// from the point of view of the given color. It can be divided by PawnValue to get
|
// from the point of view of the given color. It can be divided by PawnValue to get
|
||||||
/// an approximation of the material advantage on the board in terms of pawns.
|
// an approximation of the material advantage on the board in terms of pawns.
|
||||||
|
|
||||||
Value Eval::simple_eval(const Position& pos, Color c) {
|
Value Eval::simple_eval(const Position& pos, Color c) {
|
||||||
return PawnValue * (pos.count<PAWN>(c) - pos.count<PAWN>(~c))
|
return PawnValue * (pos.count<PAWN>(c) - pos.count<PAWN>(~c))
|
||||||
|
@ -145,8 +145,8 @@ Value Eval::simple_eval(const Position& pos, Color c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// evaluate() is the evaluator for the outer world. It returns a static evaluation
|
// evaluate() is the evaluator for the outer world. It returns a static evaluation
|
||||||
/// of the position from the point of view of the side to move.
|
// of the position from the point of view of the side to move.
|
||||||
|
|
||||||
Value Eval::evaluate(const Position& pos) {
|
Value Eval::evaluate(const Position& pos) {
|
||||||
|
|
||||||
|
@ -189,10 +189,10 @@ Value Eval::evaluate(const Position& pos) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// trace() is like evaluate(), but instead of returning a value, it returns
|
// trace() is like evaluate(), but instead of returning a value, it returns
|
||||||
/// a string (suitable for outputting to stdout) that contains the detailed
|
// a string (suitable for outputting to stdout) that contains the detailed
|
||||||
/// descriptions and values of each evaluation term. Useful for debugging.
|
// descriptions and values of each evaluation term. Useful for debugging.
|
||||||
/// Trace scores are from white's point of view
|
// Trace scores are from white's point of view
|
||||||
|
|
||||||
std::string Eval::trace(Position& pos) {
|
std::string Eval::trace(Position& pos) {
|
||||||
|
|
||||||
|
|
88
src/misc.cpp
88
src/misc.cpp
|
@ -71,14 +71,14 @@ namespace Stockfish {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// Version number or dev.
|
// Version number or dev.
|
||||||
constexpr std::string_view version = "dev";
|
constexpr std::string_view version = "dev";
|
||||||
|
|
||||||
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
||||||
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
||||||
/// can toggle the logging of std::cout and std:cin at runtime whilst preserving
|
// can toggle the logging of std::cout and std:cin at runtime whilst preserving
|
||||||
/// usual I/O functionality, all without changing a single line of code!
|
// usual I/O functionality, all without changing a single line of code!
|
||||||
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
|
// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
|
||||||
|
|
||||||
struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and cout
|
struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and cout
|
||||||
|
|
||||||
|
@ -141,15 +141,15 @@ public:
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// engine_info() returns the full name of the current Stockfish version.
|
// engine_info() returns the full name of the current Stockfish version.
|
||||||
/// For local dev compiles we try to append the commit sha and commit date
|
// For local dev compiles we try to append the commit sha and commit date
|
||||||
/// from git if that fails only the local compilation date is set and "nogit" is specified:
|
// from git if that fails only the local compilation date is set and "nogit" is specified:
|
||||||
/// Stockfish dev-YYYYMMDD-SHA
|
// Stockfish dev-YYYYMMDD-SHA
|
||||||
/// or
|
// or
|
||||||
/// Stockfish dev-YYYYMMDD-nogit
|
// Stockfish dev-YYYYMMDD-nogit
|
||||||
///
|
//
|
||||||
/// For releases (non dev builds) we only include the version number:
|
// For releases (non-dev builds) we only include the version number:
|
||||||
/// Stockfish version
|
// Stockfish version
|
||||||
|
|
||||||
std::string engine_info(bool to_uci) {
|
std::string engine_info(bool to_uci) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
@ -185,20 +185,20 @@ std::string engine_info(bool to_uci) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// compiler_info() returns a string trying to describe the compiler we use
|
// compiler_info() returns a string trying to describe the compiler we use
|
||||||
|
|
||||||
std::string compiler_info() {
|
std::string compiler_info() {
|
||||||
|
|
||||||
#define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch)
|
#define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch)
|
||||||
|
|
||||||
/// Predefined macros hell:
|
// Predefined macros hell:
|
||||||
///
|
//
|
||||||
/// __GNUC__ Compiler is GCC, Clang or ICX
|
// __GNUC__ Compiler is GCC, Clang or ICX
|
||||||
/// __clang__ Compiler is Clang or ICX
|
// __clang__ Compiler is Clang or ICX
|
||||||
/// __INTEL_LLVM_COMPILER Compiler is ICX
|
// __INTEL_LLVM_COMPILER Compiler is ICX
|
||||||
/// _MSC_VER Compiler is MSVC
|
// _MSC_VER Compiler is MSVC
|
||||||
/// _WIN32 Building on Windows (any)
|
// _WIN32 Building on Windows (any)
|
||||||
/// _WIN64 Building on Windows 64 bit
|
// _WIN64 Building on Windows 64 bit
|
||||||
|
|
||||||
std::string compiler = "\nCompiled by : ";
|
std::string compiler = "\nCompiled by : ";
|
||||||
|
|
||||||
|
@ -305,7 +305,7 @@ std::string compiler_info() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Debug functions used mainly to collect run-time statistics
|
// Debug functions used mainly to collect run-time statistics
|
||||||
constexpr int MaxDebugSlots = 32;
|
constexpr int MaxDebugSlots = 32;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -397,8 +397,8 @@ void dbg_print() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Used to serialize access to std::cout to avoid multiple threads writing at
|
// Used to serialize access to std::cout to avoid multiple threads writing at
|
||||||
/// the same time.
|
// the same time.
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
|
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
|
||||||
|
|
||||||
|
@ -414,13 +414,13 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Trampoline helper to avoid moving Logger to misc.h
|
// Trampoline helper to avoid moving Logger to misc.h
|
||||||
void start_logger(const std::string& fname) { Logger::start(fname); }
|
void start_logger(const std::string& fname) { Logger::start(fname); }
|
||||||
|
|
||||||
|
|
||||||
/// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
|
// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
|
||||||
/// function that doesn't stall the CPU waiting for data to be loaded from memory,
|
// function that doesn't stall the CPU waiting for data to be loaded from memory,
|
||||||
/// which can be quite slow.
|
// which can be quite slow.
|
||||||
#ifdef NO_PREFETCH
|
#ifdef NO_PREFETCH
|
||||||
|
|
||||||
void prefetch(void*) {}
|
void prefetch(void*) {}
|
||||||
|
@ -439,9 +439,9 @@ void prefetch(void* addr) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/// std_aligned_alloc() is our wrapper for systems where the c++17 implementation
|
// std_aligned_alloc() is our wrapper for systems where the c++17 implementation
|
||||||
/// does not guarantee the availability of aligned_alloc(). Memory allocated with
|
// does not guarantee the availability of aligned_alloc(). Memory allocated with
|
||||||
/// std_aligned_alloc() must be freed with std_aligned_free().
|
// std_aligned_alloc() must be freed with std_aligned_free().
|
||||||
|
|
||||||
void* std_aligned_alloc(size_t alignment, size_t size) {
|
void* std_aligned_alloc(size_t alignment, size_t size) {
|
||||||
|
|
||||||
|
@ -470,7 +470,7 @@ void std_aligned_free(void* ptr) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages.
|
// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages.
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
@ -550,7 +550,7 @@ void* aligned_large_pages_alloc(size_t allocSize) {
|
||||||
// Try to allocate large pages
|
// Try to allocate large pages
|
||||||
void* mem = aligned_large_pages_alloc_windows(allocSize);
|
void* mem = aligned_large_pages_alloc_windows(allocSize);
|
||||||
|
|
||||||
// Fall back to regular, page aligned, allocation if necessary
|
// Fall back to regular, page-aligned, allocation if necessary
|
||||||
if (!mem)
|
if (!mem)
|
||||||
mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
|
||||||
|
@ -579,7 +579,7 @@ void* aligned_large_pages_alloc(size_t allocSize) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/// aligned_large_pages_free() will free the previously allocated ttmem
|
// aligned_large_pages_free() will free the previously allocated ttmem
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
@ -612,9 +612,9 @@ void bindThisThread(size_t) {}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/// best_node() retrieves logical processor information using Windows specific
|
// best_node() retrieves logical processor information using Windows specific
|
||||||
/// API and returns the best node id for the thread with index idx. Original
|
// API and returns the best node id for the thread with index idx. Original
|
||||||
/// code from Texel by Peter Österlund.
|
// code from Texel by Peter Österlund.
|
||||||
|
|
||||||
static int best_node(size_t idx) {
|
static int best_node(size_t idx) {
|
||||||
|
|
||||||
|
@ -666,8 +666,8 @@ static int best_node(size_t idx) {
|
||||||
|
|
||||||
std::vector<int> groups;
|
std::vector<int> groups;
|
||||||
|
|
||||||
// Run as many threads as possible on the same node until core limit is
|
// Run as many threads as possible on the same node until the core limit is
|
||||||
// reached, then move on filling the next node.
|
// reached, then move on to filling the next node.
|
||||||
for (int n = 0; n < nodes; n++)
|
for (int n = 0; n < nodes; n++)
|
||||||
for (int i = 0; i < cores / nodes; i++)
|
for (int i = 0; i < cores / nodes; i++)
|
||||||
groups.push_back(n);
|
groups.push_back(n);
|
||||||
|
@ -684,7 +684,7 @@ static int best_node(size_t idx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// bindThisThread() set the group affinity of the current thread
|
// bindThisThread() sets the group affinity of the current thread
|
||||||
|
|
||||||
void bindThisThread(size_t idx) {
|
void bindThisThread(size_t idx) {
|
||||||
|
|
||||||
|
@ -751,7 +751,7 @@ void init([[maybe_unused]] int argc, char* argv[]) {
|
||||||
pathSeparator = "\\";
|
pathSeparator = "\\";
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
// Under windows argv[0] may not have the extension. Also _get_pgmptr() had
|
// Under windows argv[0] may not have the extension. Also _get_pgmptr() had
|
||||||
// issues in some windows 10 versions, so check returned values carefully.
|
// issues in some Windows 10 versions, so check returned values carefully.
|
||||||
char* pgmptr = nullptr;
|
char* pgmptr = nullptr;
|
||||||
if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
|
if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
|
||||||
argv0 = pgmptr;
|
argv0 = pgmptr;
|
||||||
|
|
44
src/misc.h
44
src/misc.h
|
@ -74,7 +74,7 @@ T* align_ptr_up(T* ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// IsLittleEndian : true if and only if the binary is compiled on a little endian machine
|
// IsLittleEndian : true if and only if the binary is compiled on a little-endian machine
|
||||||
static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 };
|
static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 };
|
||||||
static inline const bool IsLittleEndian = (Le.c[0] == 4);
|
static inline const bool IsLittleEndian = (Le.c[0] == 4);
|
||||||
|
|
||||||
|
@ -95,20 +95,20 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// xorshift64star Pseudo-Random Number Generator
|
// xorshift64star Pseudo-Random Number Generator
|
||||||
/// This class is based on original code written and dedicated
|
// This class is based on original code written and dedicated
|
||||||
/// to the public domain by Sebastiano Vigna (2014).
|
// to the public domain by Sebastiano Vigna (2014).
|
||||||
/// It has the following characteristics:
|
// It has the following characteristics:
|
||||||
///
|
//
|
||||||
/// - Outputs 64-bit numbers
|
// - Outputs 64-bit numbers
|
||||||
/// - Passes Dieharder and SmallCrush test batteries
|
// - Passes Dieharder and SmallCrush test batteries
|
||||||
/// - Does not require warm-up, no zeroland to escape
|
// - Does not require warm-up, no zeroland to escape
|
||||||
/// - Internal state is a single 64-bit integer
|
// - Internal state is a single 64-bit integer
|
||||||
/// - Period is 2^64 - 1
|
// - Period is 2^64 - 1
|
||||||
/// - Speed: 1.60 ns/call (Core i7 @3.40GHz)
|
// - Speed: 1.60 ns/call (Core i7 @3.40GHz)
|
||||||
///
|
//
|
||||||
/// For further analysis see
|
// For further analysis see
|
||||||
/// <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
|
// <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
|
||||||
|
|
||||||
class PRNG {
|
class PRNG {
|
||||||
|
|
||||||
|
@ -125,8 +125,8 @@ public:
|
||||||
|
|
||||||
template<typename T> T rand() { return T(rand64()); }
|
template<typename T> T rand() { return T(rand64()); }
|
||||||
|
|
||||||
/// Special generator used to fast init magic numbers.
|
// Special generator used to fast init magic numbers.
|
||||||
/// Output values only have 1/8th of their bits set on average.
|
// Output values only have 1/8th of their bits set on average.
|
||||||
template<typename T> T sparse_rand()
|
template<typename T> T sparse_rand()
|
||||||
{ return T(rand64() & rand64() & rand64()); }
|
{ return T(rand64() & rand64() & rand64()); }
|
||||||
};
|
};
|
||||||
|
@ -145,11 +145,11 @@ inline uint64_t mul_hi64(uint64_t a, uint64_t b) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Under Windows it is not possible for a process to run on more than one
|
// Under Windows it is not possible for a process to run on more than one
|
||||||
/// logical processor group. This usually means to be limited to use max 64
|
// logical processor group. This usually means being limited to using max 64
|
||||||
/// cores. To overcome this, some special platform specific API should be
|
// cores. To overcome this, some special platform-specific API should be
|
||||||
/// called to set group affinity for each thread. Original code from Texel by
|
// called to set group affinity for each thread. Original code from Texel by
|
||||||
/// Peter Österlund.
|
// Peter Österlund.
|
||||||
|
|
||||||
namespace WinProcGroup {
|
namespace WinProcGroup {
|
||||||
void bindThisThread(size_t idx);
|
void bindThisThread(size_t idx);
|
||||||
|
|
|
@ -234,14 +234,14 @@ namespace {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// <CAPTURES> Generates all pseudo-legal captures plus queen promotions
|
// <CAPTURES> Generates all pseudo-legal captures plus queen promotions
|
||||||
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions
|
// <QUIETS> Generates all pseudo-legal non-captures and underpromotions
|
||||||
/// <EVASIONS> Generates all pseudo-legal check evasions
|
// <EVASIONS> Generates all pseudo-legal check evasions
|
||||||
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
|
// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
|
||||||
/// <QUIET_CHECKS> Generates all pseudo-legal non-captures giving check,
|
// <QUIET_CHECKS> Generates all pseudo-legal non-captures giving check,
|
||||||
/// except castling and promotions
|
// except castling and promotions
|
||||||
///
|
//
|
||||||
/// Returns a pointer to the end of the move list.
|
// Returns a pointer to the end of the move list.
|
||||||
|
|
||||||
template<GenType Type>
|
template<GenType Type>
|
||||||
ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
||||||
|
@ -263,7 +263,7 @@ template ExtMove* generate<QUIET_CHECKS>(const Position&, ExtMove*);
|
||||||
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
|
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
|
||||||
|
|
||||||
|
|
||||||
/// generate<LEGAL> generates all the legal moves in the given position
|
// generate<LEGAL> generates all the legal moves in the given position
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
|
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
|
||||||
|
|
|
@ -56,9 +56,9 @@ inline bool operator<(const ExtMove& f, const ExtMove& s) {
|
||||||
template<GenType>
|
template<GenType>
|
||||||
ExtMove* generate(const Position& pos, ExtMove* moveList);
|
ExtMove* generate(const Position& pos, ExtMove* moveList);
|
||||||
|
|
||||||
/// The MoveList struct wraps the generate() function and returns a convenient
|
// The MoveList struct wraps the generate() function and returns a convenient
|
||||||
/// list of moves. Using MoveList is sometimes preferable to directly calling
|
// list of moves. Using MoveList is sometimes preferable to directly calling
|
||||||
/// the lower level generate() function.
|
// the lower level generate() function.
|
||||||
template<GenType T>
|
template<GenType T>
|
||||||
struct MoveList {
|
struct MoveList {
|
||||||
|
|
||||||
|
|
|
@ -55,13 +55,13 @@ namespace {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// Constructors of the MovePicker class. As arguments, we pass information
|
// Constructors of the MovePicker class. As arguments, we pass information
|
||||||
/// to help it return the (presumably) good moves first, to decide which
|
// to help it return the (presumably) good moves first, to decide which
|
||||||
/// moves to return (in the quiescence search, for instance, we only want to
|
// moves to return (in the quiescence search, for instance, we only want to
|
||||||
/// search captures, promotions, and some checks) and how important a good
|
// search captures, promotions, and some checks) and how important a good
|
||||||
/// move ordering is at the current node.
|
// move ordering is at the current node.
|
||||||
|
|
||||||
/// MovePicker constructor for the main search
|
// MovePicker constructor for the main search
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
||||||
const CapturePieceToHistory* cph,
|
const CapturePieceToHistory* cph,
|
||||||
const PieceToHistory** ch,
|
const PieceToHistory** ch,
|
||||||
|
@ -76,7 +76,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
|
||||||
!(ttm && pos.pseudo_legal(ttm));
|
!(ttm && pos.pseudo_legal(ttm));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MovePicker constructor for quiescence search
|
// MovePicker constructor for quiescence search
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
||||||
const CapturePieceToHistory* cph,
|
const CapturePieceToHistory* cph,
|
||||||
const PieceToHistory** ch,
|
const PieceToHistory** ch,
|
||||||
|
@ -90,8 +90,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
|
||||||
&& pos.pseudo_legal(ttm));
|
&& pos.pseudo_legal(ttm));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MovePicker constructor for ProbCut: we generate captures with SEE greater
|
// MovePicker constructor for ProbCut: we generate captures with SEE greater
|
||||||
/// than or equal to the given threshold.
|
// than or equal to the given threshold.
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph)
|
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph)
|
||||||
: pos(p), captureHistory(cph), ttMove(ttm), threshold(th)
|
: pos(p), captureHistory(cph), ttMove(ttm), threshold(th)
|
||||||
{
|
{
|
||||||
|
@ -102,9 +102,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece
|
||||||
&& pos.see_ge(ttm, threshold));
|
&& pos.see_ge(ttm, threshold));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MovePicker::score() assigns a numerical value to each move in a list, used
|
// MovePicker::score() assigns a numerical value to each move in a list, used
|
||||||
/// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
|
// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
|
||||||
/// captures with a good history. Quiets moves are ordered using the history tables.
|
// captures with a good history. Quiets moves are ordered using the history tables.
|
||||||
template<GenType Type>
|
template<GenType Type>
|
||||||
void MovePicker::score() {
|
void MovePicker::score() {
|
||||||
|
|
||||||
|
@ -180,8 +180,8 @@ void MovePicker::score() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MovePicker::select() returns the next move satisfying a predicate function.
|
// MovePicker::select() returns the next move satisfying a predicate function.
|
||||||
/// It never returns the TT move.
|
// It never returns the TT move.
|
||||||
template<MovePicker::PickType T, typename Pred>
|
template<MovePicker::PickType T, typename Pred>
|
||||||
Move MovePicker::select(Pred filter) {
|
Move MovePicker::select(Pred filter) {
|
||||||
|
|
||||||
|
@ -198,9 +198,9 @@ Move MovePicker::select(Pred filter) {
|
||||||
return MOVE_NONE;
|
return MOVE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MovePicker::next_move() is the most important method of the MovePicker class. It
|
// MovePicker::next_move() is the most important method of the MovePicker class. It
|
||||||
/// returns a new pseudo-legal move every time it is called until there are no more
|
// returns a new pseudo-legal move every time it is called until there are no more
|
||||||
/// moves left, picking the move with the highest score from a list of generated moves.
|
// moves left, picking the move with the highest score from a list of generated moves.
|
||||||
Move MovePicker::next_move(bool skipQuiets) {
|
Move MovePicker::next_move(bool skipQuiets) {
|
||||||
|
|
||||||
top:
|
top:
|
||||||
|
|
|
@ -32,10 +32,10 @@
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
class Position;
|
class Position;
|
||||||
|
|
||||||
/// StatsEntry stores the stat table value. It is usually a number but could
|
// StatsEntry stores the stat table value. It is usually a number but could
|
||||||
/// be a move or even a nested history. We use a class instead of naked value
|
// be a move or even a nested history. We use a class instead of a naked value
|
||||||
/// to directly call history update operator<<() on the entry so to use stats
|
// to directly call history update operator<<() on the entry so to use stats
|
||||||
/// tables at caller sites as simple multi-dim arrays.
|
// tables at caller sites as simple multi-dim arrays.
|
||||||
template<typename T, int D>
|
template<typename T, int D>
|
||||||
class StatsEntry {
|
class StatsEntry {
|
||||||
|
|
||||||
|
@ -57,11 +57,11 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Stats is a generic N-dimensional array used to store various statistics.
|
// Stats is a generic N-dimensional array used to store various statistics.
|
||||||
/// The first template parameter T is the base type of the array, the second
|
// The first template parameter T is the base type of the array, and the second
|
||||||
/// template parameter D limits the range of updates in [-D, D] when we update
|
// template parameter D limits the range of updates in [-D, D] when we update
|
||||||
/// values with the << operator, while the last parameters (Size and Sizes)
|
// values with the << operator, while the last parameters (Size and Sizes)
|
||||||
/// encode the dimensions of the array.
|
// encode the dimensions of the array.
|
||||||
template <typename T, int D, int Size, int... Sizes>
|
template <typename T, int D, int Size, int... Sizes>
|
||||||
struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
|
struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
|
||||||
{
|
{
|
||||||
|
@ -69,7 +69,7 @@ struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
|
||||||
|
|
||||||
void fill(const T& v) {
|
void fill(const T& v) {
|
||||||
|
|
||||||
// For standard-layout 'this' points to first struct member
|
// For standard-layout 'this' points to the first struct member
|
||||||
assert(std::is_standard_layout_v<stats>);
|
assert(std::is_standard_layout_v<stats>);
|
||||||
|
|
||||||
using entry = StatsEntry<T, D>;
|
using entry = StatsEntry<T, D>;
|
||||||
|
@ -81,40 +81,40 @@ struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
|
||||||
template <typename T, int D, int Size>
|
template <typename T, int D, int Size>
|
||||||
struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
|
struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
|
||||||
|
|
||||||
/// In stats table, D=0 means that the template parameter is not used
|
// In stats table, D=0 means that the template parameter is not used
|
||||||
enum StatsParams { NOT_USED = 0 };
|
enum StatsParams { NOT_USED = 0 };
|
||||||
enum StatsType { NoCaptures, Captures };
|
enum StatsType { NoCaptures, Captures };
|
||||||
|
|
||||||
/// ButterflyHistory records how often quiet moves have been successful or
|
// ButterflyHistory records how often quiet moves have been successful or
|
||||||
/// unsuccessful during the current search, and is used for reduction and move
|
// unsuccessful during the current search, and is used for reduction and move
|
||||||
/// ordering decisions. It uses 2 tables (one for each color) indexed by
|
// ordering decisions. It uses 2 tables (one for each color) indexed by
|
||||||
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
|
// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
|
||||||
/// (~11 elo)
|
// (~11 elo)
|
||||||
using ButterflyHistory = Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
|
using ButterflyHistory = Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
|
||||||
|
|
||||||
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
|
// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
|
||||||
/// move, see www.chessprogramming.org/Countermove_Heuristic
|
// move, see www.chessprogramming.org/Countermove_Heuristic
|
||||||
using CounterMoveHistory = Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB>;
|
using CounterMoveHistory = Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB>;
|
||||||
|
|
||||||
/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
|
// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
|
||||||
using CapturePieceToHistory = Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>;
|
using CapturePieceToHistory = Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>;
|
||||||
|
|
||||||
/// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
|
// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
|
||||||
using PieceToHistory = Stats<int16_t, 29952, PIECE_NB, SQUARE_NB>;
|
using PieceToHistory = Stats<int16_t, 29952, PIECE_NB, SQUARE_NB>;
|
||||||
|
|
||||||
/// ContinuationHistory is the combined history of a given pair of moves, usually
|
// ContinuationHistory is the combined history of a given pair of moves, usually
|
||||||
/// the current one given a previous one. The nested history table is based on
|
// the current one given a previous one. The nested history table is based on
|
||||||
/// PieceToHistory instead of ButterflyBoards.
|
// PieceToHistory instead of ButterflyBoards.
|
||||||
/// (~63 elo)
|
// (~63 elo)
|
||||||
using ContinuationHistory = Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB>;
|
using ContinuationHistory = Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB>;
|
||||||
|
|
||||||
|
|
||||||
/// MovePicker class is used to pick one pseudo-legal move at a time from the
|
// MovePicker class is used to pick one pseudo-legal move at a time from the
|
||||||
/// current position. The most important method is next_move(), which returns a
|
// current position. The most important method is next_move(), which returns a
|
||||||
/// new pseudo-legal move each time it is called, until there are no moves left,
|
// new pseudo-legal move each time it is called, until there are no moves left,
|
||||||
/// when MOVE_NONE is returned. In order to improve the efficiency of the
|
// when MOVE_NONE is returned. In order to improve the efficiency of the
|
||||||
/// alpha-beta algorithm, MovePicker attempts to return the moves which are most
|
// alpha-beta algorithm, MovePicker attempts to return the moves which are most
|
||||||
/// likely to get a cut-off first.
|
// likely to get a cut-off first.
|
||||||
class MovePicker {
|
class MovePicker {
|
||||||
|
|
||||||
enum PickType { Next, Best };
|
enum PickType { Next, Best };
|
||||||
|
|
|
@ -375,7 +375,7 @@ namespace Stockfish::Eval::NNUE {
|
||||||
return write_parameters(stream);
|
return write_parameters(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save eval, to a file given by its name
|
// Save eval, to a file given by its name
|
||||||
bool save_eval(const std::optional<std::string>& filename) {
|
bool save_eval(const std::optional<std::string>& filename) {
|
||||||
|
|
||||||
std::string actualFilename;
|
std::string actualFilename;
|
||||||
|
|
104
src/position.cpp
104
src/position.cpp
|
@ -61,7 +61,7 @@ constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// operator<<(Position) returns an ASCII representation of the position
|
// operator<<(Position) returns an ASCII representation of the position
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const Position& pos) {
|
std::ostream& operator<<(std::ostream& os, const Position& pos) {
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ Key cuckoo[8192];
|
||||||
Move cuckooMove[8192];
|
Move cuckooMove[8192];
|
||||||
|
|
||||||
|
|
||||||
/// Position::init() initializes at startup the various arrays used to compute hash keys
|
// Position::init() initializes at startup the various arrays used to compute hash keys
|
||||||
|
|
||||||
void Position::init() {
|
void Position::init() {
|
||||||
|
|
||||||
|
@ -160,9 +160,9 @@ void Position::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::set() initializes the position object with the given FEN string.
|
// Position::set() initializes the position object with the given FEN string.
|
||||||
/// This function is not very robust - make sure that input FENs are correct,
|
// This function is not very robust - make sure that input FENs are correct,
|
||||||
/// this is assumed to be the responsibility of the GUI.
|
// this is assumed to be the responsibility of the GUI.
|
||||||
|
|
||||||
Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
|
Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
|
||||||
/*
|
/*
|
||||||
|
@ -297,8 +297,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::set_castling_right() is a helper function used to set castling
|
// Position::set_castling_right() is a helper function used to set castling
|
||||||
/// rights given the corresponding color and the rook starting square.
|
// rights given the corresponding color and the rook starting square.
|
||||||
|
|
||||||
void Position::set_castling_right(Color c, Square rfrom) {
|
void Position::set_castling_right(Color c, Square rfrom) {
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ void Position::set_castling_right(Color c, Square rfrom) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::set_check_info() sets king attacks to detect if a move gives check
|
// Position::set_check_info() sets king attacks to detect if a move gives check
|
||||||
|
|
||||||
void Position::set_check_info() const {
|
void Position::set_check_info() const {
|
||||||
|
|
||||||
|
@ -336,9 +336,9 @@ void Position::set_check_info() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::set_state() computes the hash keys of the position, and other
|
// Position::set_state() computes the hash keys of the position, and other
|
||||||
/// data that once computed is updated incrementally as moves are made.
|
// data that once computed is updated incrementally as moves are made.
|
||||||
/// The function is only used when a new position is set up
|
// The function is only used when a new position is set up
|
||||||
|
|
||||||
void Position::set_state() const {
|
void Position::set_state() const {
|
||||||
|
|
||||||
|
@ -372,9 +372,9 @@ void Position::set_state() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::set() is an overload to initialize the position object with
|
// Position::set() is an overload to initialize the position object with
|
||||||
/// the given endgame code string like "KBPKN". It is mainly a helper to
|
// the given endgame code string like "KBPKN". It is mainly a helper to
|
||||||
/// get the material key out of an endgame code.
|
// get the material key out of an endgame code.
|
||||||
|
|
||||||
Position& Position::set(const string& code, Color c, StateInfo* si) {
|
Position& Position::set(const string& code, Color c, StateInfo* si) {
|
||||||
|
|
||||||
|
@ -395,8 +395,8 @@ Position& Position::set(const string& code, Color c, StateInfo* si) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::fen() returns a FEN representation of the position. In case of
|
// Position::fen() returns a FEN representation of the position. In case of
|
||||||
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
|
// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
|
||||||
|
|
||||||
string Position::fen() const {
|
string Position::fen() const {
|
||||||
|
|
||||||
|
@ -444,9 +444,9 @@ string Position::fen() const {
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// update_slider_blockers() calculates st->blockersForKing[c] and st->pinners[~c],
|
// update_slider_blockers() calculates st->blockersForKing[c] and st->pinners[~c],
|
||||||
/// which store respectively the pieces preventing king of color c from being in check
|
// which store respectively the pieces preventing king of color c from being in check
|
||||||
/// and the slider pieces of color ~c pinning pieces of color c to the king.
|
// and the slider pieces of color ~c pinning pieces of color c to the king.
|
||||||
void Position::update_slider_blockers(Color c) const {
|
void Position::update_slider_blockers(Color c) const {
|
||||||
|
|
||||||
Square ksq = square<KING>(c);
|
Square ksq = square<KING>(c);
|
||||||
|
@ -474,8 +474,8 @@ void Position::update_slider_blockers(Color c) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::attackers_to() computes a bitboard of all pieces which attack a
|
// Position::attackers_to() computes a bitboard of all pieces which attack a
|
||||||
/// given square. Slider attacks use the occupied bitboard to indicate occupancy.
|
// given square. Slider attacks use the occupied bitboard to indicate occupancy.
|
||||||
|
|
||||||
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
||||||
|
|
||||||
|
@ -488,7 +488,7 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::legal() tests whether a pseudo-legal move is legal
|
// Position::legal() tests whether a pseudo-legal move is legal
|
||||||
|
|
||||||
bool Position::legal(Move m) const {
|
bool Position::legal(Move m) const {
|
||||||
|
|
||||||
|
@ -532,7 +532,7 @@ bool Position::legal(Move m) const {
|
||||||
if (attackers_to(s) & pieces(~us))
|
if (attackers_to(s) & pieces(~us))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// In case of Chess960, verify if the Rook blocks some checks
|
// In case of Chess960, verify if the Rook blocks some checks.
|
||||||
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
|
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
|
||||||
return !chess960 || !(blockers_for_king(us) & to_sq(m));
|
return !chess960 || !(blockers_for_king(us) & to_sq(m));
|
||||||
}
|
}
|
||||||
|
@ -549,9 +549,9 @@ bool Position::legal(Move m) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::pseudo_legal() takes a random move and tests whether the move is
|
// Position::pseudo_legal() takes a random move and tests whether the move is
|
||||||
/// pseudo-legal. It is used to validate moves from TT that can be corrupted
|
// pseudo-legal. It is used to validate moves from TT that can be corrupted
|
||||||
/// due to SMP concurrent access or hash position key aliasing.
|
// due to SMP concurrent access or hash position key aliasing.
|
||||||
|
|
||||||
bool Position::pseudo_legal(const Move m) const {
|
bool Position::pseudo_legal(const Move m) const {
|
||||||
|
|
||||||
|
@ -622,7 +622,7 @@ bool Position::pseudo_legal(const Move m) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::gives_check() tests whether a pseudo-legal move gives a check
|
// Position::gives_check() tests whether a pseudo-legal move gives a check
|
||||||
|
|
||||||
bool Position::gives_check(Move m) const {
|
bool Position::gives_check(Move m) const {
|
||||||
|
|
||||||
|
@ -672,9 +672,9 @@ bool Position::gives_check(Move m) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::do_move() makes a move, and saves all information necessary
|
// Position::do_move() makes a move, and saves all information necessary
|
||||||
/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
|
// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
|
||||||
/// moves should be filtered out before this function is called.
|
// moves should be filtered out before this function is called.
|
||||||
|
|
||||||
void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||||
|
|
||||||
|
@ -870,8 +870,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::undo_move() unmakes a move. When it returns, the position should
|
// Position::undo_move() unmakes a move. When it returns, the position should
|
||||||
/// be restored to exactly the same state as before the move was made.
|
// be restored to exactly the same state as before the move was made.
|
||||||
|
|
||||||
void Position::undo_move(Move m) {
|
void Position::undo_move(Move m) {
|
||||||
|
|
||||||
|
@ -934,8 +934,8 @@ void Position::undo_move(Move m) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::do_castling() is a helper used to do/undo a castling move. This
|
// Position::do_castling() is a helper used to do/undo a castling move. This
|
||||||
/// is a bit tricky in Chess960 where from/to squares can overlap.
|
// is a bit tricky in Chess960 where from/to squares can overlap.
|
||||||
template<bool Do>
|
template<bool Do>
|
||||||
void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) {
|
void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) {
|
||||||
|
|
||||||
|
@ -965,8 +965,8 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::do_null_move() is used to do a "null move": it flips
|
// Position::do_null_move() is used to do a "null move": it flips
|
||||||
/// the side to move without executing any move on the board.
|
// the side to move without executing any move on the board.
|
||||||
|
|
||||||
void Position::do_null_move(StateInfo& newSt) {
|
void Position::do_null_move(StateInfo& newSt) {
|
||||||
|
|
||||||
|
@ -1005,7 +1005,7 @@ void Position::do_null_move(StateInfo& newSt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::undo_null_move() must be used to undo a "null move"
|
// Position::undo_null_move() must be used to undo a "null move"
|
||||||
|
|
||||||
void Position::undo_null_move() {
|
void Position::undo_null_move() {
|
||||||
|
|
||||||
|
@ -1016,9 +1016,9 @@ void Position::undo_null_move() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::key_after() computes the new hash key after the given move. Needed
|
// Position::key_after() computes the new hash key after the given move. Needed
|
||||||
/// for speculative prefetch. It doesn't recognize special moves like castling,
|
// for speculative prefetch. It doesn't recognize special moves like castling,
|
||||||
/// en passant and promotions.
|
// en passant and promotions.
|
||||||
|
|
||||||
Key Position::key_after(Move m) const {
|
Key Position::key_after(Move m) const {
|
||||||
|
|
||||||
|
@ -1038,9 +1038,9 @@ Key Position::key_after(Move m) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
|
// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
|
||||||
/// SEE value of move is greater or equal to the given threshold. We'll use an
|
// SEE value of move is greater or equal to the given threshold. We'll use an
|
||||||
/// algorithm similar to alpha-beta pruning with a null window.
|
// algorithm similar to alpha-beta pruning with a null window.
|
||||||
|
|
||||||
bool Position::see_ge(Move m, Value threshold) const {
|
bool Position::see_ge(Move m, Value threshold) const {
|
||||||
|
|
||||||
|
@ -1143,8 +1143,8 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||||
return bool(res);
|
return bool(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Position::is_draw() tests whether the position is drawn by 50-move rule
|
// Position::is_draw() tests whether the position is drawn by 50-move rule
|
||||||
/// or by repetition. It does not detect stalemates.
|
// or by repetition. It does not detect stalemates.
|
||||||
|
|
||||||
bool Position::is_draw(int ply) const {
|
bool Position::is_draw(int ply) const {
|
||||||
|
|
||||||
|
@ -1175,8 +1175,8 @@ bool Position::has_repeated() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::has_game_cycle() tests if the position has a move which draws by repetition,
|
// Position::has_game_cycle() tests if the position has a move which draws by repetition,
|
||||||
/// or an earlier position has a move that directly reaches the current position.
|
// or an earlier position has a move that directly reaches the current position.
|
||||||
|
|
||||||
bool Position::has_game_cycle(int ply) const {
|
bool Position::has_game_cycle(int ply) const {
|
||||||
|
|
||||||
|
@ -1224,8 +1224,8 @@ bool Position::has_game_cycle(int ply) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::flip() flips position with the white and black sides reversed. This
|
// Position::flip() flips position with the white and black sides reversed. This
|
||||||
/// is only useful for debugging e.g. for finding evaluation symmetry bugs.
|
// is only useful for debugging e.g. for finding evaluation symmetry bugs.
|
||||||
|
|
||||||
void Position::flip() {
|
void Position::flip() {
|
||||||
|
|
||||||
|
@ -1259,9 +1259,9 @@ void Position::flip() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::pos_is_ok() performs some consistency checks for the
|
// Position::pos_is_ok() performs some consistency checks for the
|
||||||
/// position object and raise an assert if something wrong is detected.
|
// position object and raise an assert if something wrong is detected.
|
||||||
/// This is meant to be helpful when debugging.
|
// This is meant to be helpful when debugging.
|
||||||
|
|
||||||
bool Position::pos_is_ok() const {
|
bool Position::pos_is_ok() const {
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,9 @@
|
||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
/// StateInfo struct stores information needed to restore a Position object to
|
// StateInfo struct stores information needed to restore a Position object to
|
||||||
/// its previous state when we retract a move. Whenever a move is made on the
|
// its previous state when we retract a move. Whenever a move is made on the
|
||||||
/// board (by calling Position::do_move), a StateInfo object must be passed.
|
// board (by calling Position::do_move), a StateInfo object must be passed.
|
||||||
|
|
||||||
struct StateInfo {
|
struct StateInfo {
|
||||||
|
|
||||||
|
@ -61,17 +61,17 @@ struct StateInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// A list to keep track of the position states along the setup moves (from the
|
// A list to keep track of the position states along the setup moves (from the
|
||||||
/// start position to the position just before the search starts). Needed by
|
// start position to the position just before the search starts). Needed by
|
||||||
/// 'draw by repetition' detection. Use a std::deque because pointers to
|
// 'draw by repetition' detection. Use a std::deque because pointers to
|
||||||
/// elements are not invalidated upon list resizing.
|
// elements are not invalidated upon list resizing.
|
||||||
using StateListPtr = std::unique_ptr<std::deque<StateInfo>>;
|
using StateListPtr = std::unique_ptr<std::deque<StateInfo>>;
|
||||||
|
|
||||||
|
|
||||||
/// Position class stores information regarding the board representation as
|
// Position class stores information regarding the board representation as
|
||||||
/// pieces, side to move, hash keys, castling info, etc. Important methods are
|
// pieces, side to move, hash keys, castling info, etc. Important methods are
|
||||||
/// do_move() and undo_move(), used by the search to update node info when
|
// do_move() and undo_move(), used by the search to update node info when
|
||||||
/// traversing the search tree.
|
// traversing the search tree.
|
||||||
class Thread;
|
class Thread;
|
||||||
|
|
||||||
class Position {
|
class Position {
|
||||||
|
@ -342,8 +342,8 @@ inline bool Position::capture(Move m) const {
|
||||||
|| type_of(m) == EN_PASSANT;
|
|| type_of(m) == EN_PASSANT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if a move is generated from the capture stage
|
// Returns true if a move is generated from the capture stage, having also
|
||||||
// having also queen promotions covered, i.e. consistency with the capture stage move generation
|
// queen promotions covered, i.e. consistency with the capture stage move generation
|
||||||
// is needed to avoid the generation of duplicate moves.
|
// is needed to avoid the generation of duplicate moves.
|
||||||
inline bool Position::capture_stage(Move m) const {
|
inline bool Position::capture_stage(Move m) const {
|
||||||
assert(is_ok(m));
|
assert(is_ok(m));
|
||||||
|
|
|
@ -169,7 +169,7 @@ namespace {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// Search::init() is called at startup to initialize various lookup tables
|
// Search::init() is called at startup to initialize various lookup tables
|
||||||
|
|
||||||
void Search::init() {
|
void Search::init() {
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ void Search::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Search::clear() resets search state to its initial value
|
// Search::clear() resets search state to its initial value
|
||||||
|
|
||||||
void Search::clear() {
|
void Search::clear() {
|
||||||
|
|
||||||
|
@ -191,8 +191,8 @@ void Search::clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// MainThread::search() is started when the program receives the UCI 'go'
|
// MainThread::search() is started when the program receives the UCI 'go'
|
||||||
/// command. It searches from the root position and outputs the "bestmove".
|
// command. It searches from the root position and outputs the "bestmove".
|
||||||
|
|
||||||
void MainThread::search() {
|
void MainThread::search() {
|
||||||
|
|
||||||
|
@ -268,9 +268,9 @@ void MainThread::search() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Thread::search() is the main iterative deepening loop. It calls search()
|
// Thread::search() is the main iterative deepening loop. It calls search()
|
||||||
/// repeatedly with increasing depth until the allocated thinking time has been
|
// repeatedly with increasing depth until the allocated thinking time has been
|
||||||
/// consumed, the user stops the search, or the maximum search depth is reached.
|
// consumed, the user stops the search, or the maximum search depth is reached.
|
||||||
|
|
||||||
void Thread::search() {
|
void Thread::search() {
|
||||||
|
|
||||||
|
@ -1837,8 +1837,8 @@ moves_loop: // When in check, search starts here
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// MainThread::check_time() is used to print debug info and, more importantly,
|
// MainThread::check_time() is used to print debug info and, more importantly,
|
||||||
/// to detect when we are out of available time and thus stop the search.
|
// to detect when we are out of available time and thus stop the search.
|
||||||
|
|
||||||
void MainThread::check_time() {
|
void MainThread::check_time() {
|
||||||
|
|
||||||
|
@ -1870,8 +1870,8 @@ void MainThread::check_time() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// UCI::pv() formats PV information according to the UCI protocol. UCI requires
|
// UCI::pv() formats PV information according to the UCI protocol. UCI requires
|
||||||
/// that all (if any) unsearched PV lines are sent using a previous search score.
|
// that all (if any) unsearched PV lines are sent using a previous search score.
|
||||||
|
|
||||||
string UCI::pv(const Position& pos, Depth depth) {
|
string UCI::pv(const Position& pos, Depth depth) {
|
||||||
|
|
||||||
|
@ -1929,10 +1929,10 @@ string UCI::pv(const Position& pos, Depth depth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// RootMove::extract_ponder_from_tt() is called in case we have no ponder move
|
// RootMove::extract_ponder_from_tt() is called in case we have no ponder move
|
||||||
/// before exiting the search, for instance, in case we stop the search during a
|
// before exiting the search, for instance, in case we stop the search during a
|
||||||
/// fail high at root. We try hard to have a ponder move to return to the GUI,
|
// fail high at root. We try hard to have a ponder move to return to the GUI,
|
||||||
/// otherwise in case of 'ponder on' we have nothing to think about.
|
// otherwise in case of 'ponder on' we have nothing to think about.
|
||||||
|
|
||||||
bool RootMove::extract_ponder_from_tt(Position& pos) {
|
bool RootMove::extract_ponder_from_tt(Position& pos) {
|
||||||
|
|
||||||
|
|
16
src/search.h
16
src/search.h
|
@ -33,9 +33,9 @@ class Position;
|
||||||
namespace Search {
|
namespace Search {
|
||||||
|
|
||||||
|
|
||||||
/// Stack struct keeps track of the information we need to remember from nodes
|
// Stack struct keeps track of the information we need to remember from nodes
|
||||||
/// shallower and deeper in the tree during the search. Each search thread has
|
// shallower and deeper in the tree during the search. Each search thread has
|
||||||
/// its own array of Stack objects, indexed by the current ply.
|
// its own array of Stack objects, indexed by the current ply.
|
||||||
|
|
||||||
struct Stack {
|
struct Stack {
|
||||||
Move* pv;
|
Move* pv;
|
||||||
|
@ -55,9 +55,9 @@ struct Stack {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// RootMove struct is used for moves at the root of the tree. For each root move
|
// RootMove struct is used for moves at the root of the tree. For each root move
|
||||||
/// we store a score and a PV (really a refutation in the case of moves which
|
// we store a score and a PV (really a refutation in the case of moves which
|
||||||
/// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
|
// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
|
||||||
|
|
||||||
struct RootMove {
|
struct RootMove {
|
||||||
|
|
||||||
|
@ -84,8 +84,8 @@ struct RootMove {
|
||||||
using RootMoves = std::vector<RootMove>;
|
using RootMoves = std::vector<RootMove>;
|
||||||
|
|
||||||
|
|
||||||
/// LimitsType struct stores information sent by GUI about available time to
|
// LimitsType struct stores information sent by GUI about available time to
|
||||||
/// search the current move, maximum depth/time, or if we are in analysis mode.
|
// search the current move, maximum depth/time, or if we are in analysis mode.
|
||||||
|
|
||||||
struct LimitsType {
|
struct LimitsType {
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ template <typename T> int sign_of(T val) {
|
||||||
return (T(0) < val) - (val < T(0));
|
return (T(0) < val) - (val < T(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Numbers in little endian used by sparseIndex[] to point into blockLength[]
|
// Numbers in little-endian used by sparseIndex[] to point into blockLength[]
|
||||||
struct SparseEntry {
|
struct SparseEntry {
|
||||||
char block[4]; // Number of block
|
char block[4]; // Number of block
|
||||||
char offset[2]; // Offset within the block
|
char offset[2]; // Offset within the block
|
||||||
|
@ -153,7 +153,7 @@ struct LR {
|
||||||
enum Side { Left, Right };
|
enum Side { Left, Right };
|
||||||
|
|
||||||
uint8_t lr[3]; // The first 12 bits is the left-hand symbol, the second 12
|
uint8_t lr[3]; // The first 12 bits is the left-hand symbol, the second 12
|
||||||
// bits is the right-hand symbol. If symbol has length 1,
|
// bits is the right-hand symbol. If the symbol has length 1,
|
||||||
// then the left-hand symbol is the stored value.
|
// then the left-hand symbol is the stored value.
|
||||||
template<Side S>
|
template<Side S>
|
||||||
Sym get() {
|
Sym get() {
|
||||||
|
@ -301,9 +301,9 @@ public:
|
||||||
|
|
||||||
std::string TBFile::Paths;
|
std::string TBFile::Paths;
|
||||||
|
|
||||||
// struct PairsData contains low level indexing information to access TB data.
|
// struct PairsData contains low-level indexing information to access TB data.
|
||||||
// There are 8, 4 or 2 PairsData records for each TBTable, according to type of
|
// There are 8, 4, or 2 PairsData records for each TBTable, according to the type
|
||||||
// table and if positions have pawns or not. It is populated at first access.
|
// of table and if positions have pawns or not. It is populated at first access.
|
||||||
struct PairsData {
|
struct PairsData {
|
||||||
uint8_t flags; // Table flags, see enum TBFlag
|
uint8_t flags; // Table flags, see enum TBFlag
|
||||||
uint8_t maxSymLen; // Maximum length in bits of the Huffman symbols
|
uint8_t maxSymLen; // Maximum length in bits of the Huffman symbols
|
||||||
|
@ -379,7 +379,7 @@ TBTable<WDL>::TBTable(const std::string& code) : TBTable() {
|
||||||
hasUniquePieces = true;
|
hasUniquePieces = true;
|
||||||
|
|
||||||
// Set the leading color. In case both sides have pawns the leading color
|
// Set the leading color. In case both sides have pawns the leading color
|
||||||
// is the side with less pawns because this leads to better compression.
|
// is the side with fewer pawns because this leads to better compression.
|
||||||
bool c = !pos.count<PAWN>(BLACK)
|
bool c = !pos.count<PAWN>(BLACK)
|
||||||
|| ( pos.count<PAWN>(WHITE)
|
|| ( pos.count<PAWN>(WHITE)
|
||||||
&& pos.count<PAWN>(BLACK) >= pos.count<PAWN>(WHITE));
|
&& pos.count<PAWN>(BLACK) >= pos.count<PAWN>(WHITE));
|
||||||
|
@ -404,7 +404,7 @@ TBTable<DTZ>::TBTable(const TBTable<WDL>& wdl) : TBTable() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// class TBTables creates and keeps ownership of the TBTable objects, one for
|
// class TBTables creates and keeps ownership of the TBTable objects, one for
|
||||||
// each TB file found. It supports a fast, hash based, table lookup. Populated
|
// each TB file found. It supports a fast, hash-based, table lookup. Populated
|
||||||
// at init time, accessed at probe time.
|
// at init time, accessed at probe time.
|
||||||
class TBTables {
|
class TBTables {
|
||||||
|
|
||||||
|
@ -511,9 +511,9 @@ void TBTables::add(const std::vector<PieceType>& pieces) {
|
||||||
// mostly-draw or mostly-win tables this can leave many 64-byte blocks only half-filled, so
|
// mostly-draw or mostly-win tables this can leave many 64-byte blocks only half-filled, so
|
||||||
// in such cases blocks are 32 bytes long. The blocks of DTZ tables are up to 1024 bytes long.
|
// in such cases blocks are 32 bytes long. The blocks of DTZ tables are up to 1024 bytes long.
|
||||||
// The generator picks the size that leads to the smallest table. The "book" of symbols and
|
// The generator picks the size that leads to the smallest table. The "book" of symbols and
|
||||||
// Huffman codes is the same for all blocks in the table. A non-symmetric pawnless TB file
|
// Huffman codes are the same for all blocks in the table. A non-symmetric pawnless TB file
|
||||||
// will have one table for wtm and one for btm, a TB file with pawns will have tables per
|
// will have one table for wtm and one for btm, a TB file with pawns will have tables per
|
||||||
// file a,b,c,d also in this case one set for wtm and one for btm.
|
// file a,b,c,d also, in this case, one set for wtm and one for btm.
|
||||||
int decompress_pairs(PairsData* d, uint64_t idx) {
|
int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||||
|
|
||||||
// Special case where all table positions store the same value
|
// Special case where all table positions store the same value
|
||||||
|
@ -541,7 +541,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||||
uint32_t block = number<uint32_t, LittleEndian>(&d->sparseIndex[k].block);
|
uint32_t block = number<uint32_t, LittleEndian>(&d->sparseIndex[k].block);
|
||||||
int offset = number<uint16_t, LittleEndian>(&d->sparseIndex[k].offset);
|
int offset = number<uint16_t, LittleEndian>(&d->sparseIndex[k].offset);
|
||||||
|
|
||||||
// Now compute the difference idx - I(k). From definition of k we know that
|
// Now compute the difference idx - I(k). From the definition of k, we know that
|
||||||
//
|
//
|
||||||
// idx = k * d->span + idx % d->span (2)
|
// idx = k * d->span + idx % d->span (2)
|
||||||
//
|
//
|
||||||
|
@ -551,7 +551,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||||
// Sum the above to offset to find the offset corresponding to our idx
|
// Sum the above to offset to find the offset corresponding to our idx
|
||||||
offset += diff;
|
offset += diff;
|
||||||
|
|
||||||
// Move to previous/next block, until we reach the correct block that contains idx,
|
// Move to the previous/next block, until we reach the correct block that contains idx,
|
||||||
// that is when 0 <= offset <= d->blockLength[block]
|
// that is when 0 <= offset <= d->blockLength[block]
|
||||||
while (offset < 0)
|
while (offset < 0)
|
||||||
offset += d->blockLength[--block] + 1;
|
offset += d->blockLength[--block] + 1;
|
||||||
|
@ -564,7 +564,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||||
|
|
||||||
// Read the first 64 bits in our block, this is a (truncated) sequence of
|
// Read the first 64 bits in our block, this is a (truncated) sequence of
|
||||||
// unknown number of symbols of unknown length but we know the first one
|
// unknown number of symbols of unknown length but we know the first one
|
||||||
// is at the beginning of this 64 bits sequence.
|
// is at the beginning of this 64-bit sequence.
|
||||||
uint64_t buf64 = number<uint64_t, BigEndian>(ptr); ptr += 2;
|
uint64_t buf64 = number<uint64_t, BigEndian>(ptr); ptr += 2;
|
||||||
int buf64Size = 64;
|
int buf64Size = 64;
|
||||||
Sym sym;
|
Sym sym;
|
||||||
|
@ -587,8 +587,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||||
// Now add the value of the lowest symbol of length len to get our symbol
|
// Now add the value of the lowest symbol of length len to get our symbol
|
||||||
sym += number<Sym, LittleEndian>(&d->lowestSym[len]);
|
sym += number<Sym, LittleEndian>(&d->lowestSym[len]);
|
||||||
|
|
||||||
// If our offset is within the number of values represented by symbol sym
|
// If our offset is within the number of values represented by symbol sym,
|
||||||
// we are done...
|
// we are done.
|
||||||
if (offset < d->symlen[sym] + 1)
|
if (offset < d->symlen[sym] + 1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -604,7 +604,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok, now we have our symbol that expands into d->symlen[sym] + 1 symbols.
|
// Now we have our symbol that expands into d->symlen[sym] + 1 symbols.
|
||||||
// We binary-search for our value recursively expanding into the left and
|
// We binary-search for our value recursively expanding into the left and
|
||||||
// right child symbols until we reach a leaf node where symlen[sym] + 1 == 1
|
// right child symbols until we reach a leaf node where symlen[sym] + 1 == 1
|
||||||
// that will store the value we need.
|
// that will store the value we need.
|
||||||
|
@ -614,7 +614,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||||
|
|
||||||
// If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and
|
// If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and
|
||||||
// expands in a pair (d->symlen[left] = 23, d->symlen[right] = 11), then
|
// expands in a pair (d->symlen[left] = 23, d->symlen[right] = 11), then
|
||||||
// we know that, for instance the ten-th value (offset = 10) will be on
|
// we know that, for instance, the tenth value (offset = 10) will be on
|
||||||
// the left side because in Recursive Pairing child symbols are adjacent.
|
// the left side because in Recursive Pairing child symbols are adjacent.
|
||||||
if (offset < d->symlen[left] + 1)
|
if (offset < d->symlen[left] + 1)
|
||||||
sym = left;
|
sym = left;
|
||||||
|
@ -639,7 +639,7 @@ bool check_dtz_stm(TBTable<DTZ>* entry, int stm, File f) {
|
||||||
// DTZ scores are sorted by frequency of occurrence and then assigned the
|
// DTZ scores are sorted by frequency of occurrence and then assigned the
|
||||||
// values 0, 1, 2, ... in order of decreasing frequency. This is done for each
|
// values 0, 1, 2, ... in order of decreasing frequency. This is done for each
|
||||||
// of the four WDLScore values. The mapping information necessary to reconstruct
|
// of the four WDLScore values. The mapping information necessary to reconstruct
|
||||||
// the original values is stored in the TB file and read during map[] init.
|
// the original values are stored in the TB file and read during map[] init.
|
||||||
WDLScore map_score(TBTable<WDL>*, File, int value, WDLScore) { return WDLScore(value - 2); }
|
WDLScore map_score(TBTable<WDL>*, File, int value, WDLScore) { return WDLScore(value - 2); }
|
||||||
|
|
||||||
int map_score(TBTable<DTZ>* entry, File f, int value, WDLScore wdl) {
|
int map_score(TBTable<DTZ>* entry, File f, int value, WDLScore wdl) {
|
||||||
|
@ -658,7 +658,7 @@ int map_score(TBTable<DTZ>* entry, File f, int value, WDLScore wdl) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DTZ tables store distance to zero in number of moves or plies. We
|
// DTZ tables store distance to zero in number of moves or plies. We
|
||||||
// want to return plies, so we have convert to plies when needed.
|
// want to return plies, so we have to convert to plies when needed.
|
||||||
if ( (wdl == WDLWin && !(flags & TBFlag::WinPlies))
|
if ( (wdl == WDLWin && !(flags & TBFlag::WinPlies))
|
||||||
|| (wdl == WDLLoss && !(flags & TBFlag::LossPlies))
|
|| (wdl == WDLLoss && !(flags & TBFlag::LossPlies))
|
||||||
|| wdl == WDLCursedWin
|
|| wdl == WDLCursedWin
|
||||||
|
@ -669,7 +669,7 @@ int map_score(TBTable<DTZ>* entry, File f, int value, WDLScore wdl) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute a unique index out of a position and use it to probe the TB file. To
|
// Compute a unique index out of a position and use it to probe the TB file. To
|
||||||
// encode k pieces of same type and color, first sort the pieces by square in
|
// encode k pieces of the same type and color, first sort the pieces by square in
|
||||||
// ascending order s1 <= s2 <= ... <= sk then compute the unique index as:
|
// ascending order s1 <= s2 <= ... <= sk then compute the unique index as:
|
||||||
//
|
//
|
||||||
// idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk]
|
// idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk]
|
||||||
|
@ -687,13 +687,13 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
||||||
|
|
||||||
// A given TB entry like KRK has associated two material keys: KRvk and Kvkr.
|
// A given TB entry like KRK has associated two material keys: KRvk and Kvkr.
|
||||||
// If both sides have the same pieces keys are equal. In this case TB tables
|
// If both sides have the same pieces keys are equal. In this case TB tables
|
||||||
// only store the 'white to move' case, so if the position to lookup has black
|
// only stores the 'white to move' case, so if the position to lookup has black
|
||||||
// to move, we need to switch the color and flip the squares before to lookup.
|
// to move, we need to switch the color and flip the squares before to lookup.
|
||||||
bool symmetricBlackToMove = (entry->key == entry->key2 && pos.side_to_move());
|
bool symmetricBlackToMove = (entry->key == entry->key2 && pos.side_to_move());
|
||||||
|
|
||||||
// TB files are calculated for white as stronger side. For instance we have
|
// TB files are calculated for white as the stronger side. For instance, we
|
||||||
// KRvK, not KvKR. A position where stronger side is white will have its
|
// have KRvK, not KvKR. A position where the stronger side is white will have
|
||||||
// material key == entry->key, otherwise we have to switch the color and
|
// its material key == entry->key, otherwise we have to switch the color and
|
||||||
// flip the squares before to lookup.
|
// flip the squares before to lookup.
|
||||||
bool blackStronger = (pos.material_key() != entry->key);
|
bool blackStronger = (pos.material_key() != entry->key);
|
||||||
|
|
||||||
|
@ -816,7 +816,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
||||||
// Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be
|
// Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be
|
||||||
// swapped and still get the same position.)
|
// swapped and still get the same position.)
|
||||||
//
|
//
|
||||||
// In case we have at least 3 unique pieces (included kings) we encode them
|
// In case we have at least 3 unique pieces (including kings) we encode them
|
||||||
// together.
|
// together.
|
||||||
if (entry->hasUniquePieces) {
|
if (entry->hasUniquePieces) {
|
||||||
|
|
||||||
|
@ -861,7 +861,7 @@ encode_remaining:
|
||||||
idx *= d->groupIdx[0];
|
idx *= d->groupIdx[0];
|
||||||
Square* groupSq = squares + d->groupLen[0];
|
Square* groupSq = squares + d->groupLen[0];
|
||||||
|
|
||||||
// Encode remaining pawns then pieces according to square, in ascending order
|
// Encode remaining pawns and then pieces according to square, in ascending order
|
||||||
bool remainingPawns = entry->hasPawns && entry->pawnCount[1];
|
bool remainingPawns = entry->hasPawns && entry->pawnCount[1];
|
||||||
|
|
||||||
while (d->groupLen[++next])
|
while (d->groupLen[++next])
|
||||||
|
@ -870,7 +870,7 @@ encode_remaining:
|
||||||
uint64_t n = 0;
|
uint64_t n = 0;
|
||||||
|
|
||||||
// Map down a square if "comes later" than a square in the previous
|
// Map down a square if "comes later" than a square in the previous
|
||||||
// groups (similar to what done earlier for leading group pieces).
|
// groups (similar to what was done earlier for leading group pieces).
|
||||||
for (int i = 0; i < d->groupLen[next]; ++i)
|
for (int i = 0; i < d->groupLen[next]; ++i)
|
||||||
{
|
{
|
||||||
auto f = [&](Square s) { return groupSq[i] > s; };
|
auto f = [&](Square s) { return groupSq[i] > s; };
|
||||||
|
@ -888,7 +888,7 @@ encode_remaining:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group together pieces that will be encoded together. The general rule is that
|
// Group together pieces that will be encoded together. The general rule is that
|
||||||
// a group contains pieces of same type and color. The exception is the leading
|
// a group contains pieces of the same type and color. The exception is the leading
|
||||||
// group that, in case of positions without pawns, can be formed by 3 different
|
// group that, in case of positions without pawns, can be formed by 3 different
|
||||||
// pieces (default) or by the king pair when there is not a unique piece apart
|
// pieces (default) or by the king pair when there is not a unique piece apart
|
||||||
// from the kings. When there are pawns, pawns are always first in pieces[].
|
// from the kings. When there are pawns, pawns are always first in pieces[].
|
||||||
|
@ -953,7 +953,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) {
|
||||||
|
|
||||||
// In Recursive Pairing each symbol represents a pair of children symbols. So
|
// In Recursive Pairing each symbol represents a pair of children symbols. So
|
||||||
// read d->btree[] symbols data and expand each one in his left and right child
|
// read d->btree[] symbols data and expand each one in his left and right child
|
||||||
// symbol until reaching the leafs that represent the symbol value.
|
// symbol until reaching the leaves that represent the symbol value.
|
||||||
uint8_t set_symlen(PairsData* d, Sym s, std::vector<bool>& visited) {
|
uint8_t set_symlen(PairsData* d, Sym s, std::vector<bool>& visited) {
|
||||||
|
|
||||||
visited[s] = true; // We can set it now because tree is acyclic
|
visited[s] = true; // We can set it now because tree is acyclic
|
||||||
|
@ -1002,7 +1002,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) {
|
||||||
|
|
||||||
// See https://en.wikipedia.org/wiki/Huffman_coding
|
// See https://en.wikipedia.org/wiki/Huffman_coding
|
||||||
// The canonical code is ordered such that longer symbols (in terms of
|
// The canonical code is ordered such that longer symbols (in terms of
|
||||||
// the number of bits of their Huffman code) have lower numeric value,
|
// the number of bits of their Huffman code) have a lower numeric value,
|
||||||
// so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian).
|
// so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian).
|
||||||
// Starting from this we compute a base64[] table indexed by symbol length
|
// Starting from this we compute a base64[] table indexed by symbol length
|
||||||
// and containing 64 bit values so that d->base64[i] >= d->base64[i+1].
|
// and containing 64 bit values so that d->base64[i] >= d->base64[i+1].
|
||||||
|
@ -1072,7 +1072,7 @@ uint8_t* set_dtz_map(TBTable<DTZ>& e, uint8_t* data, File maxFile) {
|
||||||
return data += uintptr_t(data) & 1; // Word alignment
|
return data += uintptr_t(data) & 1; // Word alignment
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate entry's PairsData records with data from the just memory mapped file.
|
// Populate entry's PairsData records with data from the just memory-mapped file.
|
||||||
// Called at first access.
|
// Called at first access.
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void set(T& e, uint8_t* data) {
|
void set(T& e, uint8_t* data) {
|
||||||
|
@ -1138,9 +1138,9 @@ void set(T& e, uint8_t* data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the TB file corresponding to the given position is already memory mapped
|
// If the TB file corresponding to the given position is already memory-mapped
|
||||||
// then return its base address, otherwise try to memory map and init it. Called
|
// then return its base address, otherwise, try to memory map and init it. Called
|
||||||
// at every probe, memory map and init only at first access. Function is thread
|
// at every probe, memory map, and init only at first access. Function is thread
|
||||||
// safe and can be called concurrently.
|
// safe and can be called concurrently.
|
||||||
template<TBType Type>
|
template<TBType Type>
|
||||||
void* mapped(TBTable<Type>& e, const Position& pos) {
|
void* mapped(TBTable<Type>& e, const Position& pos) {
|
||||||
|
@ -1191,7 +1191,7 @@ Ret probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For a position where the side to move has a winning capture it is not necessary
|
// For a position where the side to move has a winning capture it is not necessary
|
||||||
// to store a winning value so the generator treats such positions as "don't cares"
|
// to store a winning value so the generator treats such positions as "don't care"
|
||||||
// and tries to assign to it a value that improves the compression ratio. Similarly,
|
// and tries to assign to it a value that improves the compression ratio. Similarly,
|
||||||
// if the side to move has a drawing capture, then the position is at least drawn.
|
// if the side to move has a drawing capture, then the position is at least drawn.
|
||||||
// If the position is won, then the TB needs to store a win value. But if the
|
// If the position is won, then the TB needs to store a win value. But if the
|
||||||
|
@ -1200,7 +1200,7 @@ Ret probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw)
|
||||||
// their results and must probe the position itself. The "best" result of these
|
// their results and must probe the position itself. The "best" result of these
|
||||||
// probes is the correct result for the position.
|
// probes is the correct result for the position.
|
||||||
// DTZ tables do not store values when a following move is a zeroing winning move
|
// DTZ tables do not store values when a following move is a zeroing winning move
|
||||||
// (winning capture or winning pawn move). Also DTZ store wrong values for positions
|
// (winning capture or winning pawn move). Also, DTZ store wrong values for positions
|
||||||
// where the best move is an ep-move (even if losing). So in all these cases set
|
// where the best move is an ep-move (even if losing). So in all these cases set
|
||||||
// the state to ZEROING_BEST_MOVE.
|
// the state to ZEROING_BEST_MOVE.
|
||||||
template<bool CheckZeroingMoves>
|
template<bool CheckZeroingMoves>
|
||||||
|
@ -1268,9 +1268,9 @@ WDLScore search(Position& pos, ProbeState* result) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// Tablebases::init() is called at startup and after every change to
|
// Tablebases::init() is called at startup and after every change to
|
||||||
/// "SyzygyPath" UCI option to (re)create the various tables. It is not thread
|
// "SyzygyPath" UCI option to (re)create the various tables. It is not thread
|
||||||
/// safe, nor it needs to be.
|
// safe, nor it needs to be.
|
||||||
void Tablebases::init(const std::string& paths) {
|
void Tablebases::init(const std::string& paths) {
|
||||||
|
|
||||||
TBTables.clear();
|
TBTables.clear();
|
||||||
|
@ -1302,7 +1302,7 @@ void Tablebases::init(const std::string& paths) {
|
||||||
|
|
||||||
// MapKK[] encodes all the 462 possible legal positions of two kings where
|
// MapKK[] encodes all the 462 possible legal positions of two kings where
|
||||||
// the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4
|
// the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4
|
||||||
// diagonal, the other one shall not to be above the a1-h8 diagonal.
|
// diagonal, the other one shall not be above the a1-h8 diagonal.
|
||||||
std::vector<std::pair<int, Square>> bothOnDiagonal;
|
std::vector<std::pair<int, Square>> bothOnDiagonal;
|
||||||
code = 0;
|
code = 0;
|
||||||
for (int idx = 0; idx < 10; idx++)
|
for (int idx = 0; idx < 10; idx++)
|
||||||
|
@ -1323,7 +1323,7 @@ void Tablebases::init(const std::string& paths) {
|
||||||
MapKK[idx][s2] = code++;
|
MapKK[idx][s2] = code++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legal positions with both kings on diagonal are encoded as last ones
|
// Legal positions with both kings on a diagonal are encoded as last ones
|
||||||
for (auto p : bothOnDiagonal)
|
for (auto p : bothOnDiagonal)
|
||||||
MapKK[p.first][p.second] = code++;
|
MapKK[p.first][p.second] = code++;
|
||||||
|
|
||||||
|
@ -1338,8 +1338,8 @@ void Tablebases::init(const std::string& paths) {
|
||||||
|
|
||||||
// MapPawns[s] encodes squares a2-h7 to 0..47. This is the number of possible
|
// MapPawns[s] encodes squares a2-h7 to 0..47. This is the number of possible
|
||||||
// available squares when the leading one is in 's'. Moreover the pawn with
|
// available squares when the leading one is in 's'. Moreover the pawn with
|
||||||
// highest MapPawns[] is the leading pawn, the one nearest the edge and,
|
// highest MapPawns[] is the leading pawn, the one nearest the edge, and
|
||||||
// among pawns with same file, the one with lowest rank.
|
// among pawns with the same file, the one with the lowest rank.
|
||||||
int availableSquares = 47; // Available squares when lead pawn is in a2
|
int availableSquares = 47; // Available squares when lead pawn is in a2
|
||||||
|
|
||||||
// Init the tables for the encoding of leading pawns group: with 7-men TB we
|
// Init the tables for the encoding of leading pawns group: with 7-men TB we
|
||||||
|
@ -1463,7 +1463,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
|
||||||
if (*result == FAIL || wdl == WDLDraw) // DTZ tables don't store draws
|
if (*result == FAIL || wdl == WDLDraw) // DTZ tables don't store draws
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// DTZ stores a 'don't care' value in this case, or even a plain wrong
|
// DTZ stores a 'don't care value in this case, or even a plain wrong
|
||||||
// one as in case the best move is a losing ep, so it cannot be probed.
|
// one as in case the best move is a losing ep, so it cannot be probed.
|
||||||
if (*result == ZEROING_BEST_MOVE)
|
if (*result == ZEROING_BEST_MOVE)
|
||||||
return dtz_before_zeroing(wdl);
|
return dtz_before_zeroing(wdl);
|
||||||
|
@ -1490,7 +1490,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
|
||||||
// For zeroing moves we want the dtz of the move _before_ doing it,
|
// For zeroing moves we want the dtz of the move _before_ doing it,
|
||||||
// otherwise we will get the dtz of the next move sequence. Search the
|
// otherwise we will get the dtz of the next move sequence. Search the
|
||||||
// position after the move to get the score sign (because even in a
|
// position after the move to get the score sign (because even in a
|
||||||
// winning position we could make a losing capture or going for a draw).
|
// winning position we could make a losing capture or go for a draw).
|
||||||
dtz = zeroing ? -dtz_before_zeroing(search<false>(pos, result))
|
dtz = zeroing ? -dtz_before_zeroing(search<false>(pos, result))
|
||||||
: -probe_dtz(pos, result);
|
: -probe_dtz(pos, result);
|
||||||
|
|
||||||
|
@ -1548,10 +1548,9 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) {
|
||||||
}
|
}
|
||||||
else if (pos.is_draw(1))
|
else if (pos.is_draw(1))
|
||||||
{
|
{
|
||||||
// In case a root move leads to a draw by repetition or
|
// In case a root move leads to a draw by repetition or 50-move rule,
|
||||||
// 50-move rule, we set dtz to zero. Note: since we are
|
// we set dtz to zero. Note: since we are only 1 ply from the root,
|
||||||
// only 1 ply from the root, this must be a true 3-fold
|
// this must be a true 3-fold repetition inside the game history.
|
||||||
// repetition inside the game history.
|
|
||||||
dtz = 0;
|
dtz = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -40,8 +40,8 @@ namespace Stockfish {
|
||||||
ThreadPool Threads; // Global object
|
ThreadPool Threads; // Global object
|
||||||
|
|
||||||
|
|
||||||
/// Thread constructor launches the thread and waits until it goes to sleep
|
// Thread constructor launches the thread and waits until it goes to sleep
|
||||||
/// in idle_loop(). Note that 'searching' and 'exit' should be already set.
|
// in idle_loop(). Note that 'searching' and 'exit' should be already set.
|
||||||
|
|
||||||
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
|
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
|
||||||
|
|
||||||
|
@ -49,8 +49,8 @@ Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Thread destructor wakes up the thread in idle_loop() and waits
|
// Thread destructor wakes up the thread in idle_loop() and waits
|
||||||
/// for its termination. Thread should be already waiting.
|
// for its termination. Thread should be already waiting.
|
||||||
|
|
||||||
Thread::~Thread() {
|
Thread::~Thread() {
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ Thread::~Thread() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Thread::clear() reset histories, usually before a new game
|
// Thread::clear() reset histories, usually before a new game
|
||||||
|
|
||||||
void Thread::clear() {
|
void Thread::clear() {
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ void Thread::clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Thread::start_searching() wakes up the thread that will start the search
|
// Thread::start_searching() wakes up the thread that will start the search
|
||||||
|
|
||||||
void Thread::start_searching() {
|
void Thread::start_searching() {
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
|
@ -88,8 +88,8 @@ void Thread::start_searching() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Thread::wait_for_search_finished() blocks on the condition variable
|
// Thread::wait_for_search_finished() blocks on the condition variable
|
||||||
/// until the thread has finished searching.
|
// until the thread has finished searching.
|
||||||
|
|
||||||
void Thread::wait_for_search_finished() {
|
void Thread::wait_for_search_finished() {
|
||||||
|
|
||||||
|
@ -98,15 +98,15 @@ void Thread::wait_for_search_finished() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Thread::idle_loop() is where the thread is parked, blocked on the
|
// Thread::idle_loop() is where the thread is parked, blocked on the
|
||||||
/// condition variable, when it has no work to do.
|
// condition variable, when it has no work to do.
|
||||||
|
|
||||||
void Thread::idle_loop() {
|
void Thread::idle_loop() {
|
||||||
|
|
||||||
// If OS already scheduled us on a different group than 0 then don't overwrite
|
// If OS already scheduled us on a different group than 0 then don't overwrite
|
||||||
// the choice, eventually we are one of many one-threaded processes running on
|
// the choice, eventually we are one of many one-threaded processes running on
|
||||||
// some Windows NUMA hardware, for instance in fishtest. To make it simple,
|
// some Windows NUMA hardware, for instance in fishtest. To make it simple,
|
||||||
// just check if running threads are below a threshold, in this case all this
|
// just check if running threads are below a threshold, in this case, all this
|
||||||
// NUMA machinery is not needed.
|
// NUMA machinery is not needed.
|
||||||
if (Options["Threads"] > 8)
|
if (Options["Threads"] > 8)
|
||||||
WinProcGroup::bindThisThread(idx);
|
WinProcGroup::bindThisThread(idx);
|
||||||
|
@ -127,9 +127,9 @@ void Thread::idle_loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ThreadPool::set() creates/destroys threads to match the requested number.
|
// ThreadPool::set() creates/destroys threads to match the requested number.
|
||||||
/// Created and launched threads will immediately go to sleep in idle_loop.
|
// Created and launched threads will immediately go to sleep in idle_loop.
|
||||||
/// Upon resizing, threads are recreated to allow for binding if necessary.
|
// Upon resizing, threads are recreated to allow for binding if necessary.
|
||||||
|
|
||||||
void ThreadPool::set(size_t requested) {
|
void ThreadPool::set(size_t requested) {
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ void ThreadPool::set(size_t requested) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// ThreadPool::clear() sets threadPool data to initial values
|
// ThreadPool::clear() sets threadPool data to initial values
|
||||||
|
|
||||||
void ThreadPool::clear() {
|
void ThreadPool::clear() {
|
||||||
|
|
||||||
|
@ -172,8 +172,8 @@ void ThreadPool::clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and
|
// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and
|
||||||
/// returns immediately. Main thread will wake up other threads and start the search.
|
// returns immediately. Main thread will wake up other threads and start the search.
|
||||||
|
|
||||||
void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
||||||
const Search::LimitsType& limits, bool ponderMode) {
|
const Search::LimitsType& limits, bool ponderMode) {
|
||||||
|
@ -225,7 +225,7 @@ Thread* ThreadPool::get_best_thread() const {
|
||||||
std::map<Move, int64_t> votes;
|
std::map<Move, int64_t> votes;
|
||||||
Value minScore = VALUE_NONE;
|
Value minScore = VALUE_NONE;
|
||||||
|
|
||||||
// Find minimum score of all threads
|
// Find the minimum score of all threads
|
||||||
for (Thread* th: threads)
|
for (Thread* th: threads)
|
||||||
minScore = std::min(minScore, th->rootMoves[0].score);
|
minScore = std::min(minScore, th->rootMoves[0].score);
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ Thread* ThreadPool::get_best_thread() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Start non-main threads
|
// Start non-main threads
|
||||||
|
|
||||||
void ThreadPool::start_searching() {
|
void ThreadPool::start_searching() {
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ void ThreadPool::start_searching() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Wait for non-main threads
|
// Wait for non-main threads
|
||||||
|
|
||||||
void ThreadPool::wait_for_search_finished() const {
|
void ThreadPool::wait_for_search_finished() const {
|
||||||
|
|
||||||
|
|
16
src/thread.h
16
src/thread.h
|
@ -34,10 +34,10 @@
|
||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
/// Thread class keeps together all the thread-related stuff. We use
|
// Thread class keeps together all the thread-related stuff. We use
|
||||||
/// per-thread pawn and material hash tables so that once we get a
|
// per-thread pawn and material hash tables so that once we get a
|
||||||
/// pointer to an entry its life time is unlimited and we don't have
|
// pointer to an entry its lifetime is unlimited and we don't have
|
||||||
/// to care about someone changing the entry under our feet.
|
// to care about someone changing the entry under our feet.
|
||||||
|
|
||||||
class Thread {
|
class Thread {
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// MainThread is a derived class specific for main thread
|
// MainThread is a derived class specific for main thread
|
||||||
|
|
||||||
struct MainThread : public Thread {
|
struct MainThread : public Thread {
|
||||||
|
|
||||||
|
@ -94,9 +94,9 @@ struct MainThread : public Thread {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// ThreadPool struct handles all the threads-related stuff like init, starting,
|
// ThreadPool struct handles all the threads-related stuff like init, starting,
|
||||||
/// parking and, most importantly, launching a thread. All the access to threads
|
// parking and, most importantly, launching a thread. All the access to threads
|
||||||
/// is done through this class.
|
// is done through this class.
|
||||||
|
|
||||||
struct ThreadPool {
|
struct ThreadPool {
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,11 @@
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
/// On OSX threads other than the main thread are created with a reduced stack
|
// On OSX threads other than the main thread are created with a reduced stack
|
||||||
/// size of 512KB by default, this is too low for deep searches, which require
|
// size of 512KB by default, this is too low for deep searches, which require
|
||||||
/// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
|
// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
|
||||||
/// The implementation calls pthread_create() with the stack size parameter
|
// The implementation calls pthread_create() with the stack size parameter
|
||||||
/// equal to the linux 8MB default, on platforms that support it.
|
// equal to the Linux 8MB default, on platforms that support it.
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS)
|
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS)
|
||||||
|
|
||||||
|
|
|
@ -29,14 +29,14 @@ namespace Stockfish {
|
||||||
TimeManagement Time; // Our global time management object
|
TimeManagement Time; // Our global time management object
|
||||||
|
|
||||||
|
|
||||||
/// TimeManagement::init() is called at the beginning of the search and calculates
|
// TimeManagement::init() is called at the beginning of the search and calculates
|
||||||
/// the bounds of time allowed for the current game ply. We currently support:
|
// the bounds of time allowed for the current game ply. We currently support:
|
||||||
// 1) x basetime (+ z increment)
|
// 1) x basetime (+ z increment)
|
||||||
// 2) x moves in y seconds (+ z increment)
|
// 2) x moves in y seconds (+ z increment)
|
||||||
|
|
||||||
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
||||||
|
|
||||||
// if we have no time, no need to initialize TM, except for the start time,
|
// If we have no time, no need to initialize TM, except for the start time,
|
||||||
// which is used by movetime.
|
// which is used by movetime.
|
||||||
startTime = limits.startTime;
|
startTime = limits.startTime;
|
||||||
if (limits.time[us] == 0)
|
if (limits.time[us] == 0)
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
/// The TimeManagement class computes the optimal time to think depending on
|
// The TimeManagement class computes the optimal time to think depending on
|
||||||
/// the maximum available time, the game move number and other parameters.
|
// the maximum available time, the game move number, and other parameters.
|
||||||
|
|
||||||
class TimeManagement {
|
class TimeManagement {
|
||||||
public:
|
public:
|
||||||
|
|
28
src/tt.cpp
28
src/tt.cpp
|
@ -33,8 +33,8 @@ namespace Stockfish {
|
||||||
|
|
||||||
TranspositionTable TT; // Our global transposition table
|
TranspositionTable TT; // Our global transposition table
|
||||||
|
|
||||||
/// TTEntry::save() populates the TTEntry with a new node's data, possibly
|
// TTEntry::save() populates the TTEntry with a new node's data, possibly
|
||||||
/// overwriting an old position. Update is not atomic and can be racy.
|
// overwriting an old position. The update is not atomic and can be racy.
|
||||||
|
|
||||||
void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
|
void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
|
||||||
|
|
||||||
|
@ -59,9 +59,9 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::resize() sets the size of the transposition table,
|
// TranspositionTable::resize() sets the size of the transposition table,
|
||||||
/// measured in megabytes. Transposition table consists of a power of 2 number
|
// measured in megabytes. Transposition table consists of a power of 2 number
|
||||||
/// of clusters and each cluster consists of ClusterSize number of TTEntry.
|
// of clusters and each cluster consists of ClusterSize number of TTEntry.
|
||||||
|
|
||||||
void TranspositionTable::resize(size_t mbSize) {
|
void TranspositionTable::resize(size_t mbSize) {
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ void TranspositionTable::resize(size_t mbSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::clear() initializes the entire transposition table to zero,
|
// TranspositionTable::clear() initializes the entire transposition table to zero,
|
||||||
// in a multi-threaded way.
|
// in a multi-threaded way.
|
||||||
|
|
||||||
void TranspositionTable::clear() {
|
void TranspositionTable::clear() {
|
||||||
|
@ -113,12 +113,12 @@ void TranspositionTable::clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::probe() looks up the current position in the transposition
|
// TranspositionTable::probe() looks up the current position in the transposition
|
||||||
/// table. It returns true and a pointer to the TTEntry if the position is found.
|
// table. It returns true and a pointer to the TTEntry if the position is found.
|
||||||
/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry
|
// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry
|
||||||
/// to be replaced later. The replace value of an entry is calculated as its depth
|
// to be replaced later. The replace value of an entry is calculated as its depth
|
||||||
/// minus 8 times its relative age. TTEntry t1 is considered more valuable than
|
// minus 8 times its relative age. TTEntry t1 is considered more valuable than
|
||||||
/// TTEntry t2 if its replace value is greater than that of t2.
|
// TTEntry t2 if its replace value is greater than that of t2.
|
||||||
|
|
||||||
TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
||||||
|
|
||||||
|
@ -149,8 +149,8 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::hashfull() returns an approximation of the hashtable
|
// TranspositionTable::hashfull() returns an approximation of the hashtable
|
||||||
/// occupation during a search. The hash is x permill full, as per UCI protocol.
|
// occupation during a search. The hash is x permill full, as per UCI protocol.
|
||||||
|
|
||||||
int TranspositionTable::hashfull() const {
|
int TranspositionTable::hashfull() const {
|
||||||
|
|
||||||
|
|
30
src/tt.h
30
src/tt.h
|
@ -27,16 +27,16 @@
|
||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
/// TTEntry struct is the 10 bytes transposition table entry, defined as below:
|
// TTEntry struct is the 10 bytes transposition table entry, defined as below:
|
||||||
///
|
//
|
||||||
/// key 16 bit
|
// key 16 bit
|
||||||
/// depth 8 bit
|
// depth 8 bit
|
||||||
/// generation 5 bit
|
// generation 5 bit
|
||||||
/// pv node 1 bit
|
// pv node 1 bit
|
||||||
/// bound type 2 bit
|
// bound type 2 bit
|
||||||
/// move 16 bit
|
// move 16 bit
|
||||||
/// value 16 bit
|
// value 16 bit
|
||||||
/// eval value 16 bit
|
// eval value 16 bit
|
||||||
|
|
||||||
struct TTEntry {
|
struct TTEntry {
|
||||||
|
|
||||||
|
@ -60,11 +60,11 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// A TranspositionTable is an array of Cluster, of size clusterCount. Each
|
// A TranspositionTable is an array of Cluster, of size clusterCount. Each
|
||||||
/// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry
|
// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry
|
||||||
/// contains information on exactly one position. The size of a Cluster should
|
// contains information on exactly one position. The size of a Cluster should
|
||||||
/// divide the size of a cache line for best performance, as the cacheline is
|
// divide the size of a cache line for best performance, as the cacheline is
|
||||||
/// prefetched when possible.
|
// prefetched when possible.
|
||||||
|
|
||||||
class TranspositionTable {
|
class TranspositionTable {
|
||||||
|
|
||||||
|
|
51
src/tune.h
51
src/tune.h
|
@ -49,31 +49,30 @@ struct SetRange {
|
||||||
#define SetDefaultRange SetRange(default_range)
|
#define SetDefaultRange SetRange(default_range)
|
||||||
|
|
||||||
|
|
||||||
/// Tune class implements the 'magic' code that makes the setup of a fishtest
|
// Tune class implements the 'magic' code that makes the setup of a fishtest tuning
|
||||||
/// tuning session as easy as it can be. Mainly you have just to remove const
|
// session as easy as it can be. Mainly you have just to remove const qualifiers
|
||||||
/// qualifiers from the variables you want to tune and flag them for tuning, so
|
// from the variables you want to tune and flag them for tuning, so if you have:
|
||||||
/// if you have:
|
//
|
||||||
///
|
// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } };
|
||||||
/// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } };
|
//
|
||||||
///
|
// If you have a my_post_update() function to run after values have been updated,
|
||||||
/// If you have a my_post_update() function to run after values have been updated,
|
// and a my_range() function to set custom Option's min-max values, then you just
|
||||||
/// and a my_range() function to set custom Option's min-max values, then you just
|
// remove the 'const' qualifiers and write somewhere below in the file:
|
||||||
/// remove the 'const' qualifiers and write somewhere below in the file:
|
//
|
||||||
///
|
// TUNE(SetRange(my_range), myValue, my_post_update);
|
||||||
/// TUNE(SetRange(my_range), myValue, my_post_update);
|
//
|
||||||
///
|
// You can also set the range directly, and restore the default at the end
|
||||||
/// You can also set the range directly, and restore the default at the end
|
//
|
||||||
///
|
// TUNE(SetRange(-100, 100), myValue, SetDefaultRange);
|
||||||
/// TUNE(SetRange(-100, 100), myValue, SetDefaultRange);
|
//
|
||||||
///
|
// In case update function is slow and you have many parameters, you can add:
|
||||||
/// In case update function is slow and you have many parameters, you can add:
|
//
|
||||||
///
|
// UPDATE_ON_LAST();
|
||||||
/// UPDATE_ON_LAST();
|
//
|
||||||
///
|
// And the values update, including post update function call, will be done only
|
||||||
/// And the values update, including post update function call, will be done only
|
// once, after the engine receives the last UCI option, that is the one defined
|
||||||
/// once, after the engine receives the last UCI option, that is the one defined
|
// and created as the last one, so the GUI should send the options in the same
|
||||||
/// and created as the last one, so the GUI should send the options in the same
|
// order in which have been defined.
|
||||||
/// order in which have been defined.
|
|
||||||
|
|
||||||
class Tune {
|
class Tune {
|
||||||
|
|
||||||
|
@ -151,7 +150,7 @@ public:
|
||||||
static bool update_on_last;
|
static bool update_on_last;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Some macro magic :-) we define a dummy int variable that compiler initializes calling Tune::add()
|
// Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add()
|
||||||
#define STRINGIFY(x) #x
|
#define STRINGIFY(x) #x
|
||||||
#define UNIQUE2(x, y) x ## y
|
#define UNIQUE2(x, y) x ## y
|
||||||
#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__
|
#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__
|
||||||
|
|
74
src/types.h
74
src/types.h
|
@ -19,22 +19,22 @@
|
||||||
#ifndef TYPES_H_INCLUDED
|
#ifndef TYPES_H_INCLUDED
|
||||||
#define TYPES_H_INCLUDED
|
#define TYPES_H_INCLUDED
|
||||||
|
|
||||||
/// When compiling with provided Makefile (e.g. for Linux and OSX), configuration
|
// When compiling with provided Makefile (e.g. for Linux and OSX), configuration
|
||||||
/// is done automatically. To get started type 'make help'.
|
// is done automatically. To get started type 'make help'.
|
||||||
///
|
//
|
||||||
/// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches
|
// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches
|
||||||
/// need to be set manually:
|
// need to be set manually:
|
||||||
///
|
//
|
||||||
/// -DNDEBUG | Disable debugging mode. Always use this for release.
|
// -DNDEBUG | Disable debugging mode. Always use this for release.
|
||||||
///
|
//
|
||||||
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to
|
// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to
|
||||||
/// | run on some very old machines.
|
// | run on some very old machines.
|
||||||
///
|
//
|
||||||
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
|
// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
|
||||||
/// | only in 64-bit mode and requires hardware with popcnt support.
|
// | only in 64-bit mode and requires hardware with popcnt support.
|
||||||
///
|
//
|
||||||
/// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works
|
// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works
|
||||||
/// | only in 64-bit mode and requires hardware with pext support.
|
// | only in 64-bit mode and requires hardware with pext support.
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
@ -46,14 +46,14 @@
|
||||||
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
|
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Predefined macros hell:
|
// Predefined macros hell:
|
||||||
///
|
//
|
||||||
/// __GNUC__ Compiler is GCC, Clang or ICX
|
// __GNUC__ Compiler is GCC, Clang or ICX
|
||||||
/// __clang__ Compiler is Clang or ICX
|
// __clang__ Compiler is Clang or ICX
|
||||||
/// __INTEL_LLVM_COMPILER Compiler is ICX
|
// __INTEL_LLVM_COMPILER Compiler is ICX
|
||||||
/// _MSC_VER Compiler is MSVC
|
// _MSC_VER Compiler is MSVC
|
||||||
/// _WIN32 Building on Windows (any)
|
// _WIN32 Building on Windows (any)
|
||||||
/// _WIN64 Building on Windows 64 bit
|
// _WIN64 Building on Windows 64 bit
|
||||||
|
|
||||||
#if defined(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__)
|
#if defined(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__)
|
||||||
#define ALIGNAS_ON_STACK_VARIABLES_BROKEN
|
#define ALIGNAS_ON_STACK_VARIABLES_BROKEN
|
||||||
|
@ -107,17 +107,17 @@ using Bitboard = uint64_t;
|
||||||
constexpr int MAX_MOVES = 256;
|
constexpr int MAX_MOVES = 256;
|
||||||
constexpr int MAX_PLY = 246;
|
constexpr int MAX_PLY = 246;
|
||||||
|
|
||||||
/// A move needs 16 bits to be stored
|
// A move needs 16 bits to be stored
|
||||||
///
|
//
|
||||||
/// bit 0- 5: destination square (from 0 to 63)
|
// bit 0- 5: destination square (from 0 to 63)
|
||||||
/// bit 6-11: origin square (from 0 to 63)
|
// bit 6-11: origin square (from 0 to 63)
|
||||||
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
|
// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
|
||||||
/// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
|
// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
|
||||||
/// NOTE: en passant bit is set only when a pawn can be captured
|
// NOTE: en passant bit is set only when a pawn can be captured
|
||||||
///
|
//
|
||||||
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
|
// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
|
||||||
/// any normal move destination square is always different from origin square
|
// any normal move destination square is always different from origin square
|
||||||
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
|
// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
|
||||||
|
|
||||||
enum Move : int {
|
enum Move : int {
|
||||||
MOVE_NONE,
|
MOVE_NONE,
|
||||||
|
@ -291,7 +291,7 @@ ENABLE_INCR_OPERATORS_ON(Rank)
|
||||||
#undef ENABLE_INCR_OPERATORS_ON
|
#undef ENABLE_INCR_OPERATORS_ON
|
||||||
#undef ENABLE_BASE_OPERATORS_ON
|
#undef ENABLE_BASE_OPERATORS_ON
|
||||||
|
|
||||||
/// Additional operators to add a Direction to a Square
|
// Additional operators to add a Direction to a Square
|
||||||
constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
|
constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
|
||||||
constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }
|
constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }
|
||||||
inline Square& operator+=(Square& s, Direction d) { return s = s + d; }
|
inline Square& operator+=(Square& s, Direction d) { return s = s + d; }
|
||||||
|
@ -405,7 +405,7 @@ constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
|
||||||
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
|
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Based on a congruential pseudo-random number generator
|
// Based on a congruential pseudo-random number generator
|
||||||
constexpr Key make_key(uint64_t seed) {
|
constexpr Key make_key(uint64_t seed) {
|
||||||
return seed * 6364136223846793005ULL + 1442695040888963407ULL;
|
return seed * 6364136223846793005ULL + 1442695040888963407ULL;
|
||||||
}
|
}
|
||||||
|
|
42
src/uci.cpp
42
src/uci.cpp
|
@ -233,11 +233,11 @@ namespace {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// UCI::loop() waits for a command from the stdin, parses it, and then calls the appropriate
|
// UCI::loop() waits for a command from the stdin, parses it, and then calls the appropriate
|
||||||
/// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a
|
// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a
|
||||||
/// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments,
|
// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments,
|
||||||
/// like running 'bench', the function returns immediately after the command is executed.
|
// like running 'bench', the function returns immediately after the command is executed.
|
||||||
/// In addition to the UCI ones, some additional debug commands are also supported.
|
// In addition to the UCI ones, some additional debug commands are also supported.
|
||||||
|
|
||||||
void UCI::loop(int argc, char* argv[]) {
|
void UCI::loop(int argc, char* argv[]) {
|
||||||
|
|
||||||
|
@ -310,18 +310,18 @@ void UCI::loop(int argc, char* argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Turns a Value to an integer centipawn number,
|
// Turns a Value to an integer centipawn number,
|
||||||
/// without treatment of mate and similar special scores.
|
// without treatment of mate and similar special scores.
|
||||||
int UCI::to_cp(Value v) {
|
int UCI::to_cp(Value v) {
|
||||||
|
|
||||||
return 100 * v / UCI::NormalizeToPawnValue;
|
return 100 * v / UCI::NormalizeToPawnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UCI::value() converts a Value to a string by adhering to the UCI protocol specification:
|
// UCI::value() converts a Value to a string by adhering to the UCI protocol specification:
|
||||||
///
|
//
|
||||||
/// cp <x> The score from the engine's point of view in centipawns.
|
// cp <x> The score from the engine's point of view in centipawns.
|
||||||
/// mate <y> Mate in 'y' moves (not plies). If the engine is getting mated,
|
// mate <y> Mate in 'y' moves (not plies). If the engine is getting mated,
|
||||||
/// uses negative values for 'y'.
|
// uses negative values for 'y'.
|
||||||
|
|
||||||
std::string UCI::value(Value v) {
|
std::string UCI::value(Value v) {
|
||||||
|
|
||||||
|
@ -343,8 +343,8 @@ std::string UCI::value(Value v) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// UCI::wdl() reports the win-draw-loss (WDL) statistics given an evaluation
|
// UCI::wdl() reports the win-draw-loss (WDL) statistics given an evaluation
|
||||||
/// and a game ply based on the data gathered for fishtest LTC games.
|
// and a game ply based on the data gathered for fishtest LTC games.
|
||||||
|
|
||||||
std::string UCI::wdl(Value v, int ply) {
|
std::string UCI::wdl(Value v, int ply) {
|
||||||
|
|
||||||
|
@ -359,17 +359,17 @@ std::string UCI::wdl(Value v, int ply) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
|
// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
|
||||||
|
|
||||||
std::string UCI::square(Square s) {
|
std::string UCI::square(Square s) {
|
||||||
return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
|
return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q).
|
// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q).
|
||||||
/// The only special case is castling where the e1g1 notation is printed in
|
// The only special case is castling where the e1g1 notation is printed in
|
||||||
/// standard chess mode and in e1h1 notation it is printed in Chess960 mode.
|
// standard chess mode and in e1h1 notation it is printed in Chess960 mode.
|
||||||
/// Internally, all castling moves are always encoded as 'king captures rook'.
|
// Internally, all castling moves are always encoded as 'king captures rook'.
|
||||||
|
|
||||||
std::string UCI::move(Move m, bool chess960) {
|
std::string UCI::move(Move m, bool chess960) {
|
||||||
|
|
||||||
|
@ -394,8 +394,8 @@ std::string UCI::move(Move m, bool chess960) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// UCI::to_move() converts a string representing a move in coordinate notation
|
// UCI::to_move() converts a string representing a move in coordinate notation
|
||||||
/// (g1f3, a7a8q) to the corresponding legal Move, if any.
|
// (g1f3, a7a8q) to the corresponding legal Move, if any.
|
||||||
|
|
||||||
Move UCI::to_move(const Position& pos, std::string& str) {
|
Move UCI::to_move(const Position& pos, std::string& str) {
|
||||||
|
|
||||||
|
|
|
@ -41,15 +41,15 @@ const int NormalizeToPawnValue = 328;
|
||||||
|
|
||||||
class Option;
|
class Option;
|
||||||
|
|
||||||
/// Define a custom comparator, because the UCI options should be case-insensitive
|
// Define a custom comparator, because the UCI options should be case-insensitive
|
||||||
struct CaseInsensitiveLess {
|
struct CaseInsensitiveLess {
|
||||||
bool operator() (const std::string&, const std::string&) const;
|
bool operator() (const std::string&, const std::string&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The options container is defined as a std::map
|
// The options container is defined as a std::map
|
||||||
using OptionsMap = std::map<std::string, Option, CaseInsensitiveLess>;
|
using OptionsMap = std::map<std::string, Option, CaseInsensitiveLess>;
|
||||||
|
|
||||||
/// The Option class implements each option as specified by the UCI protocol
|
// The Option class implements each option as specified by the UCI protocol
|
||||||
class Option {
|
class Option {
|
||||||
|
|
||||||
using OnChange = void (*)(const Option&);
|
using OnChange = void (*)(const Option&);
|
||||||
|
|
|
@ -44,7 +44,7 @@ UCI::OptionsMap Options; // Global object
|
||||||
|
|
||||||
namespace UCI {
|
namespace UCI {
|
||||||
|
|
||||||
/// 'On change' actions, triggered by an option's value change
|
// 'On change' actions, triggered by an option's value change
|
||||||
static void on_clear_hash(const Option&) { Search::clear(); }
|
static void on_clear_hash(const Option&) { Search::clear(); }
|
||||||
static void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
|
static void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
|
||||||
static void on_logger(const Option& o) { start_logger(o); }
|
static void on_logger(const Option& o) { start_logger(o); }
|
||||||
|
@ -52,7 +52,7 @@ static void on_threads(const Option& o) { Threads.set(size_t(o)); }
|
||||||
static void on_tb_path(const Option& o) { Tablebases::init(o); }
|
static void on_tb_path(const Option& o) { Tablebases::init(o); }
|
||||||
static void on_eval_file(const Option&) { Eval::NNUE::init(); }
|
static void on_eval_file(const Option&) { Eval::NNUE::init(); }
|
||||||
|
|
||||||
/// Our case insensitive less() function as required by UCI protocol
|
// Our case insensitive less() function as required by UCI protocol
|
||||||
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
||||||
|
|
||||||
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
|
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
|
||||||
|
@ -60,7 +60,7 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// UCI::init() initializes the UCI options to their hard-coded default values
|
// UCI::init() initializes the UCI options to their hard-coded default values
|
||||||
|
|
||||||
void init(OptionsMap& o) {
|
void init(OptionsMap& o) {
|
||||||
|
|
||||||
|
@ -89,8 +89,8 @@ void init(OptionsMap& o) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// operator<<() is used to print all the options default values in chronological
|
// operator<<() is used to print all the options default values in chronological
|
||||||
/// insertion order (the idx field) and in the format defined by the UCI protocol.
|
// insertion order (the idx field) and in the format defined by the UCI protocol.
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Option class constructors and conversion operators
|
// Option class constructors and conversion operators
|
||||||
|
|
||||||
Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f)
|
Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f)
|
||||||
{ defaultValue = currentValue = v; }
|
{ defaultValue = currentValue = v; }
|
||||||
|
@ -150,7 +150,7 @@ bool Option::operator==(const char* s) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// operator<<() inits options and assigns idx in the correct printing order
|
// operator<<() inits options and assigns idx in the correct printing order
|
||||||
|
|
||||||
void Option::operator<<(const Option& o) {
|
void Option::operator<<(const Option& o) {
|
||||||
|
|
||||||
|
@ -161,9 +161,9 @@ void Option::operator<<(const Option& o) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// operator=() updates currentValue and triggers on_change() action. It's up to
|
// operator=() updates currentValue and triggers on_change() action. It's up to
|
||||||
/// the GUI to check for option's limits, but we could receive the new value
|
// the GUI to check for option's limits, but we could receive the new value
|
||||||
/// from the user by console window, so let's check the bounds anyway.
|
// from the user by console window, so let's check the bounds anyway.
|
||||||
|
|
||||||
Option& Option::operator=(const string& v) {
|
Option& Option::operator=(const string& v) {
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue