mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 08:43:09 +00:00
Big assorted spelling fixes
No functional change.
This commit is contained in:
parent
500b9b0eb3
commit
13a73f67c0
30 changed files with 265 additions and 249 deletions
|
@ -8,7 +8,7 @@ documentation for your GUI of choice for information about how to use
|
||||||
Stockfish with it.
|
Stockfish with it.
|
||||||
|
|
||||||
This version of Stockfish supports up to 64 CPUs. The engine defaults
|
This version of Stockfish supports up to 64 CPUs. The engine defaults
|
||||||
to one search thread it is therefore recommended to inspect the value of
|
to one search thread, so it is therefore recommended to inspect the value of
|
||||||
the *Threads* UCI parameter, and to make sure it equals the number of CPU
|
the *Threads* UCI parameter, and to make sure it equals the number of CPU
|
||||||
cores on your computer.
|
cores on your computer.
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ Stockfish has support for 32 or 64-bit CPUs, the hardware POPCNT
|
||||||
instruction, big-endian machines such as Power PC, and other platforms.
|
instruction, big-endian machines such as Power PC, and other platforms.
|
||||||
|
|
||||||
In general it is recommended to run `make help` to see a list of make
|
In general it is recommended to run `make help` to see a list of make
|
||||||
targets with corresponding descriptions. When not using Makefile to
|
targets with corresponding descriptions. When not using the Makefile to
|
||||||
compile (for instance with Microsoft MSVC) you need to manually
|
compile (for instance with Microsoft MSVC) you need to manually
|
||||||
set/unset some switches in the compiler command line; see file *types.h*
|
set/unset some switches in the compiler command line; see file *types.h*
|
||||||
for a quick reference.
|
for a quick reference.
|
||||||
|
|
|
@ -66,7 +66,7 @@ static const char* Defaults[] = {
|
||||||
|
|
||||||
|
|
||||||
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set
|
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set
|
||||||
/// of positions for a given limit each. There are five parameters; the
|
/// of positions for a given limit each. There are five parameters, the
|
||||||
/// transposition table size, the number of search threads that should
|
/// transposition table size, the number of search threads that should
|
||||||
/// be used, the limit value spent for each position (optional, default is
|
/// be used, the limit value spent for each position (optional, default is
|
||||||
/// depth 12), an optional file name where to look for positions in fen
|
/// depth 12), an optional file name where to look for positions in fen
|
||||||
|
@ -150,7 +150,7 @@ void benchmark(const Position& current, istream& is) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero'
|
elapsed = Time::now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
|
||||||
|
|
||||||
cerr << "\n==========================="
|
cerr << "\n==========================="
|
||||||
<< "\nTotal time (ms) : " << elapsed
|
<< "\nTotal time (ms) : " << elapsed
|
||||||
|
|
|
@ -80,8 +80,8 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
|
/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard.
|
||||||
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
|
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard.
|
||||||
|
|
||||||
#ifndef USE_BSFQ
|
#ifndef USE_BSFQ
|
||||||
|
|
||||||
|
|
|
@ -265,8 +265,8 @@ inline Bitboard attacks_bb(Piece p, Square s, Bitboard occ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
|
/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard.
|
||||||
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
|
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard.
|
||||||
|
|
||||||
#ifdef USE_BSFQ
|
#ifdef USE_BSFQ
|
||||||
|
|
||||||
|
|
|
@ -32,14 +32,14 @@ enum BitCountType {
|
||||||
CNT_HW_POPCNT
|
CNT_HW_POPCNT
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Determine at compile time the best popcount<> specialization according if
|
/// Determine at compile time the best popcount<> specialization according to
|
||||||
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count
|
/// whether the platform is 32 or 64 bits, to the maximum number of non-zero
|
||||||
/// and if hardware popcnt instruction is available.
|
/// bits to count and if the hardware popcnt instruction is available.
|
||||||
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
|
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
|
||||||
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
|
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
|
||||||
|
|
||||||
|
|
||||||
/// popcount() counts the number of nonzero bits in a bitboard
|
/// popcount() counts the number of non-zero bits in a bitboard
|
||||||
template<BitCountType> inline int popcount(Bitboard);
|
template<BitCountType> inline int popcount(Bitboard);
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|
17
src/book.cpp
17
src/book.cpp
|
@ -354,7 +354,7 @@ PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
|
||||||
|
|
||||||
|
|
||||||
/// operator>>() reads sizeof(T) chars from the file's binary byte stream and
|
/// operator>>() reads sizeof(T) chars from the file's binary byte stream and
|
||||||
/// converts them in a number of type T. A Polyglot book stores numbers in
|
/// converts them into a number of type T. A Polyglot book stores numbers in
|
||||||
/// big-endian format.
|
/// big-endian format.
|
||||||
|
|
||||||
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
|
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
|
||||||
|
@ -382,14 +382,15 @@ bool PolyglotBook::open(const char* fName) {
|
||||||
ifstream::open(fName, ifstream::in | ifstream::binary);
|
ifstream::open(fName, ifstream::in | ifstream::binary);
|
||||||
|
|
||||||
fileName = is_open() ? fName : "";
|
fileName = is_open() ? fName : "";
|
||||||
ifstream::clear(); // Reset any error flag to allow retry ifstream::open()
|
ifstream::clear(); // Reset any error flag to allow a retry ifstream::open()
|
||||||
return !fileName.empty();
|
return !fileName.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// probe() tries to find a book move for the given position. If no move is
|
/// probe() tries to find a book move for the given position. If no move is
|
||||||
/// found returns MOVE_NONE. If pickBest is true returns always the highest
|
/// found, it returns MOVE_NONE. If pickBest is true, then it always returns
|
||||||
/// rated move, otherwise randomly chooses one, based on the move score.
|
/// the highest-rated move, otherwise it randomly chooses one based on the
|
||||||
|
/// move score.
|
||||||
|
|
||||||
Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) {
|
Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) {
|
||||||
|
|
||||||
|
@ -426,10 +427,10 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
|
||||||
// bit 6-11: origin square (from 0 to 63)
|
// bit 6-11: origin square (from 0 to 63)
|
||||||
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
|
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
|
||||||
//
|
//
|
||||||
// Castling moves follow "king captures rook" representation. So in case book
|
// Castling moves follow the "king captures rook" representation. If a book
|
||||||
// move is a promotion we have to convert to our representation, in all the
|
// move is a promotion, we have to convert it to our representation and in
|
||||||
// other cases we can directly compare with a Move after having masked out
|
// all other cases, we can directly compare with a Move after having masked
|
||||||
// the special Move's flags (bit 14-15) that are not supported by PolyGlot.
|
// out the special Move flags (bit 14-15) that are not supported by PolyGlot.
|
||||||
int pt = (move >> 12) & 7;
|
int pt = (move >> 12) & 7;
|
||||||
if (pt)
|
if (pt)
|
||||||
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
|
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
|
||||||
|
|
|
@ -145,7 +145,7 @@ void Endgames::add(const string& code) {
|
||||||
|
|
||||||
|
|
||||||
/// Mate with KX vs K. This function is used to evaluate positions with
|
/// Mate with KX vs K. This function is used to evaluate positions with
|
||||||
/// King and plenty of material vs a lone king. It simply gives the
|
/// king and plenty of material vs a lone king. It simply gives the
|
||||||
/// attacking side a bonus for driving the defending king towards the edge
|
/// attacking side a bonus for driving the defending king towards the edge
|
||||||
/// of the board, and for keeping the distance between the two kings small.
|
/// of the board, and for keeping the distance between the two kings small.
|
||||||
template<>
|
template<>
|
||||||
|
@ -187,9 +187,9 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
|
||||||
Square loserKSq = pos.king_square(weakSide);
|
Square loserKSq = pos.king_square(weakSide);
|
||||||
Square bishopSq = pos.list<BISHOP>(strongSide)[0];
|
Square bishopSq = pos.list<BISHOP>(strongSide)[0];
|
||||||
|
|
||||||
// kbnk_mate_table() tries to drive toward corners A1 or H8,
|
// kbnk_mate_table() tries to drive toward corners A1 or H8, if we have a
|
||||||
// if we have a bishop that cannot reach the above squares we
|
// bishop that cannot reach the above squares we flip the kings in order
|
||||||
// flip the kings so to drive enemy toward corners A8 or H1.
|
// to drive the enemy toward corners A8 or H1.
|
||||||
if (opposite_colors(bishopSq, SQ_A1))
|
if (opposite_colors(bishopSq, SQ_A1))
|
||||||
{
|
{
|
||||||
winnerKSq = ~winnerKSq;
|
winnerKSq = ~winnerKSq;
|
||||||
|
@ -301,9 +301,10 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// KQ vs KP. In general, a win for the stronger side, however, there are a few
|
/// KQ vs KP. In general, this is a win for the stronger side, but there are a
|
||||||
/// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can
|
/// few important exceptions. A pawn on 7th rank and on the A,C,F or H files
|
||||||
/// be a draw, so we scale down to distance between kings only.
|
/// with a king positioned next to it can be a draw, so in that case, we only
|
||||||
|
/// use the distance between the kings.
|
||||||
template<>
|
template<>
|
||||||
Value Endgame<KQKP>::operator()(const Position& pos) const {
|
Value Endgame<KQKP>::operator()(const Position& pos) const {
|
||||||
|
|
||||||
|
@ -405,20 +406,20 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
||||||
return SCALE_FACTOR_DRAW;
|
return SCALE_FACTOR_DRAW;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All pawns on same B or G file? Then potential draw
|
// If all the pawns are on the same B or G file, then it's potentially a draw
|
||||||
if ( (pawnFile == FILE_B || pawnFile == FILE_G)
|
if ( (pawnFile == FILE_B || pawnFile == FILE_G)
|
||||||
&& !(pos.pieces(PAWN) & ~file_bb(pawnFile))
|
&& !(pos.pieces(PAWN) & ~file_bb(pawnFile))
|
||||||
&& pos.non_pawn_material(weakSide) == 0
|
&& pos.non_pawn_material(weakSide) == 0
|
||||||
&& pos.count<PAWN>(weakSide) >= 1)
|
&& pos.count<PAWN>(weakSide) >= 1)
|
||||||
{
|
{
|
||||||
// Get weakSide pawn that is closest to home rank
|
// Get weakSide pawn that is closest to the home rank
|
||||||
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN));
|
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN));
|
||||||
|
|
||||||
Square strongKingSq = pos.king_square(strongSide);
|
Square strongKingSq = pos.king_square(strongSide);
|
||||||
Square weakKingSq = pos.king_square(weakSide);
|
Square weakKingSq = pos.king_square(weakSide);
|
||||||
Square bishopSq = pos.list<BISHOP>(strongSide)[0];
|
Square bishopSq = pos.list<BISHOP>(strongSide)[0];
|
||||||
|
|
||||||
// Potential for a draw if our pawn is blocked on the 7th rank
|
// There's potential for a draw if our pawn is blocked on the 7th rank
|
||||||
// the bishop cannot attack it or they only have one pawn left
|
// the bishop cannot attack it or they only have one pawn left
|
||||||
if ( relative_rank(strongSide, weakPawnSq) == RANK_7
|
if ( relative_rank(strongSide, weakPawnSq) == RANK_7
|
||||||
&& (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide)))
|
&& (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide)))
|
||||||
|
@ -427,7 +428,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
||||||
int strongKingDist = square_distance(weakPawnSq, strongKingSq);
|
int strongKingDist = square_distance(weakPawnSq, strongKingSq);
|
||||||
int weakKingDist = square_distance(weakPawnSq, weakKingSq);
|
int weakKingDist = square_distance(weakPawnSq, weakKingSq);
|
||||||
|
|
||||||
// Draw if the weak king is on it's back two ranks, within 2
|
// It's a draw if the weak king is on its back two ranks, within 2
|
||||||
// squares of the blocking pawn and the strong king is not
|
// squares of the blocking pawn and the strong king is not
|
||||||
// closer. (I think this rule only fails in practically
|
// closer. (I think this rule only fails in practically
|
||||||
// unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
|
// unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
|
||||||
|
@ -473,7 +474,7 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
|
||||||
/// probably be a good idea to add more knowledge in the future.
|
/// probably be a good idea to add more knowledge in the future.
|
||||||
///
|
///
|
||||||
/// It would also be nice to rewrite the actual code for this function,
|
/// It would also be nice to rewrite the actual code for this function,
|
||||||
/// which is mostly copied from Glaurung 1.x, and not very pretty.
|
/// which is mostly copied from Glaurung 1.x, and isn't very pretty.
|
||||||
template<>
|
template<>
|
||||||
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
||||||
|
|
||||||
|
@ -760,8 +761,8 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
||||||
switch (file_distance(psq1, psq2))
|
switch (file_distance(psq1, psq2))
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
// Both pawns are on the same file. Easy draw if defender firmly controls
|
// Both pawns are on the same file. It's an easy draw if the defender firmly
|
||||||
// some square in the frontmost pawn's path.
|
// controls some square in the frontmost pawn's path.
|
||||||
if ( file_of(ksq) == file_of(blockSq1)
|
if ( file_of(ksq) == file_of(blockSq1)
|
||||||
&& relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1)
|
&& relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1)
|
||||||
&& opposite_colors(ksq, wbsq))
|
&& opposite_colors(ksq, wbsq))
|
||||||
|
@ -770,9 +771,9 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
||||||
return SCALE_FACTOR_NONE;
|
return SCALE_FACTOR_NONE;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
// Pawns on adjacent files. Draw if defender firmly controls the square
|
// Pawns on adjacent files. It's a draw if the defender firmly controls the
|
||||||
// in front of the frontmost pawn's path, and the square diagonally behind
|
// square in front of the frontmost pawn's path, and the square diagonally
|
||||||
// this square on the file of the other pawn.
|
// behind this square on the file of the other pawn.
|
||||||
if ( ksq == blockSq1
|
if ( ksq == blockSq1
|
||||||
&& opposite_colors(ksq, wbsq)
|
&& opposite_colors(ksq, wbsq)
|
||||||
&& ( bbsq == blockSq2
|
&& ( bbsq == blockSq2
|
||||||
|
|
|
@ -64,9 +64,9 @@ enum EndgameType {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Endgame functions can be of two types according if return a Value or a
|
/// Endgame functions can be of two types depending on whether they return a
|
||||||
/// ScaleFactor. Type eg_fun<int>::type equals to either ScaleFactor or Value
|
/// Value or a ScaleFactor. Type eg_fun<int>::type returns either ScaleFactor
|
||||||
/// depending if the template parameter is 0 or 1.
|
/// or Value depending on whether the template parameter is 0 or 1.
|
||||||
|
|
||||||
template<int> struct eg_fun { typedef Value type; };
|
template<int> struct eg_fun { typedef Value type; };
|
||||||
template<> struct eg_fun<1> { typedef ScaleFactor type; };
|
template<> struct eg_fun<1> { typedef ScaleFactor type; };
|
||||||
|
@ -95,9 +95,9 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Endgames class stores in two std::map the pointers to endgame evaluation
|
/// The Endgames class stores the pointers to endgame evaluation and scaling
|
||||||
/// and scaling base objects. Then we use polymorphism to invoke the actual
|
/// base objects in two std::map typedefs. We then use polymorphism to invoke
|
||||||
/// endgame function calling its operator() that is virtual.
|
/// the actual endgame function by calling its virtual operator().
|
||||||
|
|
||||||
class Endgames {
|
class Endgames {
|
||||||
|
|
||||||
|
|
|
@ -269,9 +269,10 @@ namespace Eval {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// trace() is like evaluate() but instead of a value returns a string suitable
|
/// trace() is like evaluate(), but instead of returning a value, it returns
|
||||||
/// to be print on stdout with the detailed descriptions and values of each
|
/// a string (suitable for outputting to stdout) that contains the detailed
|
||||||
/// evaluation term. Used mainly for debugging.
|
/// descriptions and values of each evaluation term. It's mainly used for
|
||||||
|
/// debugging.
|
||||||
std::string trace(const Position& pos) {
|
std::string trace(const Position& pos) {
|
||||||
return Tracing::do_trace(pos);
|
return Tracing::do_trace(pos);
|
||||||
}
|
}
|
||||||
|
@ -316,7 +317,7 @@ Value do_evaluate(const Position& pos) {
|
||||||
Thread* th = pos.this_thread();
|
Thread* th = pos.this_thread();
|
||||||
|
|
||||||
// Initialize score by reading the incrementally updated scores included
|
// Initialize score by reading the incrementally updated scores included
|
||||||
// in the position object (material + piece square tables) and adding
|
// in the position object (material + piece square tables) and adding a
|
||||||
// Tempo bonus. Score is computed from the point of view of white.
|
// Tempo bonus. Score is computed from the point of view of white.
|
||||||
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
|
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
|
||||||
|
|
||||||
|
@ -378,7 +379,8 @@ Value do_evaluate(const Position& pos) {
|
||||||
&& pos.opposite_bishops()
|
&& pos.opposite_bishops()
|
||||||
&& sf == SCALE_FACTOR_NORMAL)
|
&& sf == SCALE_FACTOR_NORMAL)
|
||||||
{
|
{
|
||||||
// Only the two bishops ?
|
// Ignoring any pawns, do both sides only have a single bishop and no
|
||||||
|
// other pieces ?
|
||||||
if ( pos.non_pawn_material(WHITE) == BishopValueMg
|
if ( pos.non_pawn_material(WHITE) == BishopValueMg
|
||||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||||
{
|
{
|
||||||
|
@ -510,7 +512,7 @@ Value do_evaluate(const Position& pos) {
|
||||||
|
|
||||||
mobility[Us] += MobilityBonus[Piece][mob];
|
mobility[Us] += MobilityBonus[Piece][mob];
|
||||||
|
|
||||||
// Decrease score if we are attacked by an enemy pawn. Remaining part
|
// Decrease score if we are attacked by an enemy pawn. The remaining part
|
||||||
// of threat evaluation must be done later when we have full attack info.
|
// of threat evaluation must be done later when we have full attack info.
|
||||||
if (ei.attackedBy[Them][PAWN] & s)
|
if (ei.attackedBy[Them][PAWN] & s)
|
||||||
score -= ThreatenedByPawn[Piece];
|
score -= ThreatenedByPawn[Piece];
|
||||||
|
@ -568,8 +570,8 @@ Value do_evaluate(const Position& pos) {
|
||||||
|
|
||||||
Square ksq = pos.king_square(Us);
|
Square ksq = pos.king_square(Us);
|
||||||
|
|
||||||
// Penalize rooks which are trapped inside a king. Penalize more if
|
// Penalize rooks which are trapped by a king. Penalize more if the
|
||||||
// king has lost castling availability.
|
// king has lost its castling capability.
|
||||||
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
|
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
|
||||||
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
|
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
|
||||||
&& !ei.pi->semiopen_on_side(Us, file_of(ksq), file_of(ksq) < FILE_E))
|
&& !ei.pi->semiopen_on_side(Us, file_of(ksq), file_of(ksq) < FILE_E))
|
||||||
|
@ -644,7 +646,7 @@ Value do_evaluate(const Position& pos) {
|
||||||
if ( ei.kingAttackersCount[Them] >= 2
|
if ( ei.kingAttackersCount[Them] >= 2
|
||||||
&& ei.kingAdjacentZoneAttacksCount[Them])
|
&& ei.kingAdjacentZoneAttacksCount[Them])
|
||||||
{
|
{
|
||||||
// Find the attacked squares around the king which has no defenders
|
// Find the attacked squares around the king which have no defenders
|
||||||
// apart from the king itself
|
// apart from the king itself
|
||||||
undefended = ei.attackedBy[Them][ALL_PIECES]
|
undefended = ei.attackedBy[Them][ALL_PIECES]
|
||||||
& ei.attackedBy[Us][KING]
|
& ei.attackedBy[Us][KING]
|
||||||
|
@ -662,12 +664,13 @@ Value do_evaluate(const Position& pos) {
|
||||||
+ KingExposed[relative_square(Us, ksq)]
|
+ KingExposed[relative_square(Us, ksq)]
|
||||||
- mg_value(score) / 32;
|
- mg_value(score) / 32;
|
||||||
|
|
||||||
// Analyse enemy's safe queen contact checks. First find undefended
|
// Analyse the enemy's safe queen contact checks. Firstly, find the
|
||||||
// squares around the king attacked by enemy queen...
|
// undefended squares around the king that are attacked by the enemy's
|
||||||
|
// queen...
|
||||||
b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them);
|
b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them);
|
||||||
if (b)
|
if (b)
|
||||||
{
|
{
|
||||||
// ...then remove squares not supported by another enemy piece
|
// ...and then remove squares not supported by another enemy piece
|
||||||
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
|
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
|
||||||
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
|
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
|
||||||
if (b)
|
if (b)
|
||||||
|
@ -676,16 +679,17 @@ Value do_evaluate(const Position& pos) {
|
||||||
* (Them == pos.side_to_move() ? 2 : 1);
|
* (Them == pos.side_to_move() ? 2 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analyse enemy's safe rook contact checks. First find undefended
|
// Analyse the enemy's safe rook contact checks. Firstly, find the
|
||||||
// squares around the king attacked by enemy rooks...
|
// undefended squares around the king that are attacked by the enemy's
|
||||||
|
// rooks...
|
||||||
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them);
|
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them);
|
||||||
|
|
||||||
// Consider only squares where the enemy rook gives check
|
// Consider only squares where the enemy's rook gives check
|
||||||
b &= PseudoAttacks[ROOK][ksq];
|
b &= PseudoAttacks[ROOK][ksq];
|
||||||
|
|
||||||
if (b)
|
if (b)
|
||||||
{
|
{
|
||||||
// ...then remove squares not supported by another enemy piece
|
// ...and then remove squares not supported by another enemy piece
|
||||||
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
|
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
|
||||||
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
|
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
|
||||||
if (b)
|
if (b)
|
||||||
|
@ -694,7 +698,7 @@ Value do_evaluate(const Position& pos) {
|
||||||
* (Them == pos.side_to_move() ? 2 : 1);
|
* (Them == pos.side_to_move() ? 2 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analyse enemy's safe distance checks for sliders and knights
|
// Analyse the enemy's safe distance checks for sliders and knights
|
||||||
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]);
|
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]);
|
||||||
|
|
||||||
b1 = pos.attacks_from<ROOK>(ksq) & safe;
|
b1 = pos.attacks_from<ROOK>(ksq) & safe;
|
||||||
|
@ -746,7 +750,7 @@ Value do_evaluate(const Position& pos) {
|
||||||
Bitboard b, undefendedMinors, weakEnemies;
|
Bitboard b, undefendedMinors, weakEnemies;
|
||||||
Score score = SCORE_ZERO;
|
Score score = SCORE_ZERO;
|
||||||
|
|
||||||
// Undefended minors get penalized even if not under attack
|
// Undefended minors get penalized even if they are not under attack
|
||||||
undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT)
|
undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT)
|
||||||
& ~ei.attackedBy[Them][ALL_PIECES];
|
& ~ei.attackedBy[Them][ALL_PIECES];
|
||||||
|
|
||||||
|
@ -807,7 +811,7 @@ Value do_evaluate(const Position& pos) {
|
||||||
{
|
{
|
||||||
Square blockSq = s + pawn_push(Us);
|
Square blockSq = s + pawn_push(Us);
|
||||||
|
|
||||||
// Adjust bonus based on kings proximity
|
// Adjust bonus based on the king's proximity
|
||||||
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr)
|
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr)
|
||||||
- Value(square_distance(pos.king_square(Us ), blockSq) * 2 * rr);
|
- Value(square_distance(pos.king_square(Us ), blockSq) * 2 * rr);
|
||||||
|
|
||||||
|
@ -815,7 +819,7 @@ Value do_evaluate(const Position& pos) {
|
||||||
if (relative_rank(Us, blockSq) != RANK_8)
|
if (relative_rank(Us, blockSq) != RANK_8)
|
||||||
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
|
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
|
||||||
|
|
||||||
// If the pawn is free to advance, increase bonus
|
// If the pawn is free to advance, then increase the bonus
|
||||||
if (pos.empty(blockSq))
|
if (pos.empty(blockSq))
|
||||||
{
|
{
|
||||||
squaresToQueen = forward_bb(Us, s);
|
squaresToQueen = forward_bb(Us, s);
|
||||||
|
@ -835,12 +839,14 @@ Value do_evaluate(const Position& pos) {
|
||||||
else
|
else
|
||||||
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
|
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
|
||||||
|
|
||||||
// If there aren't enemy attacks huge bonus, a bit smaller if at
|
// If there aren't any enemy attacks, then assign a huge bonus.
|
||||||
// least block square is not attacked, otherwise smallest bonus.
|
// The bonus will be a bit smaller if at least the block square
|
||||||
|
// isn't attacked, otherwise assign the smallest possible bonus.
|
||||||
int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3;
|
int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3;
|
||||||
|
|
||||||
// Big bonus if the path to queen is fully defended, a bit less
|
// Assign a big bonus if the path to the queen is fully defended,
|
||||||
// if at least block square is defended.
|
// otherwise assign a bit less of a bonus if at least the block
|
||||||
|
// square is defended.
|
||||||
if (defendedSquares == squaresToQueen)
|
if (defendedSquares == squaresToQueen)
|
||||||
k += 6;
|
k += 6;
|
||||||
|
|
||||||
|
@ -892,7 +898,7 @@ Value do_evaluate(const Position& pos) {
|
||||||
|
|
||||||
// evaluate_unstoppable_pawns() scores the most advanced among the passed and
|
// evaluate_unstoppable_pawns() scores the most advanced among the passed and
|
||||||
// candidate pawns. In case opponent has no pieces but pawns, this is somewhat
|
// candidate pawns. In case opponent has no pieces but pawns, this is somewhat
|
||||||
// related to the possibility pawns are unstoppable.
|
// related to the possibility that pawns are unstoppable.
|
||||||
|
|
||||||
Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) {
|
Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) {
|
||||||
|
|
||||||
|
|
|
@ -60,8 +60,8 @@ namespace {
|
||||||
{ 106, 101, 3, 151, 171, 0 } // Queen
|
{ 106, 101, 3, 151, 171, 0 } // Queen
|
||||||
};
|
};
|
||||||
|
|
||||||
// Endgame evaluation and scaling functions accessed direcly and not through
|
// Endgame evaluation and scaling functions are accessed directly and not through
|
||||||
// the function maps because correspond to more then one material hash key.
|
// the function maps because they correspond to more then one material hash key.
|
||||||
Endgame<KmmKm> EvaluateKmmKm[] = { Endgame<KmmKm>(WHITE), Endgame<KmmKm>(BLACK) };
|
Endgame<KmmKm> EvaluateKmmKm[] = { Endgame<KmmKm>(WHITE), Endgame<KmmKm>(BLACK) };
|
||||||
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
|
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ namespace {
|
||||||
&& pos.count<PAWN>(Them) >= 1;
|
&& pos.count<PAWN>(Them) >= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// imbalance() calculates imbalance comparing piece count of each
|
/// imbalance() calculates the imbalance by comparing the piece count of each
|
||||||
/// piece type for both colors.
|
/// piece type for both colors.
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
|
@ -147,9 +147,9 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||||
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
|
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
|
||||||
e->gamePhase = game_phase(pos);
|
e->gamePhase = game_phase(pos);
|
||||||
|
|
||||||
// Let's look if we have a specialized evaluation function for this
|
// Let's look if we have a specialized evaluation function for this particular
|
||||||
// particular material configuration. First we look for a fixed
|
// material configuration. Firstly we look for a fixed configuration one, then
|
||||||
// configuration one, then a generic one if previous search failed.
|
// for a generic one if the previous search failed.
|
||||||
if (endgames.probe(key, e->evaluationFunction))
|
if (endgames.probe(key, e->evaluationFunction))
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic scaling functions that refer to more then one material
|
// Generic scaling functions that refer to more then one material
|
||||||
// distribution. Should be probed after the specialized ones.
|
// distribution. They should be probed after the specialized ones.
|
||||||
// Note that these ones don't return after setting the function.
|
// Note that these ones don't return after setting the function.
|
||||||
if (is_KBPsKs<WHITE>(pos))
|
if (is_KBPsKs<WHITE>(pos))
|
||||||
e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
|
e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
|
||||||
|
@ -256,7 +256,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
|
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
|
||||||
// for the bishop pair "extended piece", this allow us to be more flexible
|
// for the bishop pair "extended piece", which allows us to be more flexible
|
||||||
// in defining bishop pair bonuses.
|
// in defining bishop pair bonuses.
|
||||||
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
|
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
|
||||||
{ pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
|
{ pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
|
||||||
|
|
18
src/misc.cpp
18
src/misc.cpp
|
@ -81,7 +81,7 @@ void dbg_print() {
|
||||||
|
|
||||||
/// 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 while preserving
|
/// can toggle the logging of std::cout and std:cin at runtime whilst preserving
|
||||||
/// usual i/o functionality and without changing a single line of code!
|
/// usual i/o functionality and 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
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Used to serialize access to std::cout to avoid multiple threads to write 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) {
|
||||||
|
@ -158,8 +158,8 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
|
||||||
void start_logger(bool b) { Logger::start(b); }
|
void start_logger(bool b) { Logger::start(b); }
|
||||||
|
|
||||||
|
|
||||||
/// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap
|
/// timed_wait() waits for msec milliseconds. It is mainly a helper to wrap
|
||||||
/// conversion from milliseconds to struct timespec, as used by pthreads.
|
/// the conversion from milliseconds to struct timespec, as used by pthreads.
|
||||||
|
|
||||||
void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
|
void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
|
||||||
|
|
||||||
|
@ -177,9 +177,9 @@ void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// prefetch() preloads the given address in L1/L2 cache. This is a non
|
/// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
|
||||||
/// blocking function and do not stalls the CPU waiting for data to be
|
/// function that doesn't stall the CPU waiting for data to be loaded from memory,
|
||||||
/// loaded from memory, that can be quite slow.
|
/// which can be quite slow.
|
||||||
#ifdef NO_PREFETCH
|
#ifdef NO_PREFETCH
|
||||||
|
|
||||||
void prefetch(char*) {}
|
void prefetch(char*) {}
|
||||||
|
@ -189,8 +189,8 @@ void prefetch(char*) {}
|
||||||
void prefetch(char* addr) {
|
void prefetch(char* addr) {
|
||||||
|
|
||||||
# if defined(__INTEL_COMPILER)
|
# if defined(__INTEL_COMPILER)
|
||||||
// This hack prevents prefetches to be optimized away by
|
// This hack prevents prefetches from being optimized away by
|
||||||
// Intel compiler. Both MSVC and gcc seems not affected.
|
// Intel compiler. Both MSVC and gcc seem not be affected by this.
|
||||||
__asm__ ("");
|
__asm__ ("");
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
|
|
@ -88,8 +88,8 @@ namespace {
|
||||||
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Knight-promotion is the only one that can give a direct check not
|
// Knight promotion is the only promotion that can give a direct check
|
||||||
// already included in the queen-promotion.
|
// that's not already included in the queen promotion.
|
||||||
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
|
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
|
||||||
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||||
else
|
else
|
||||||
|
@ -144,7 +144,7 @@ namespace {
|
||||||
// Add pawn pushes which give discovered check. This is possible only
|
// Add pawn pushes which give discovered check. This is possible only
|
||||||
// if the pawn is not on the same file as the enemy king, because we
|
// if the pawn is not on the same file as the enemy king, because we
|
||||||
// don't generate captures. Note that a possible discovery check
|
// don't generate captures. Note that a possible discovery check
|
||||||
// promotion has been already generated among captures.
|
// promotion has been already generated amongst the captures.
|
||||||
if (pawnsNotOn7 & ci->dcCandidates)
|
if (pawnsNotOn7 & ci->dcCandidates)
|
||||||
{
|
{
|
||||||
dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
|
dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
|
||||||
|
@ -355,8 +355,9 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
|
||||||
|
|
||||||
assert(pos.checkers());
|
assert(pos.checkers());
|
||||||
|
|
||||||
// Find squares attacked by slider checkers, we will remove them from the king
|
// Find all the squares attacked by slider checkers. We will remove them from
|
||||||
// evasions so to skip known illegal moves avoiding useless legality check later.
|
// the king evasions in order to skip known illegal moves, which avoids any
|
||||||
|
// useless legality checks later on.
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
++checkersCnt;
|
++checkersCnt;
|
||||||
|
|
|
@ -36,8 +36,8 @@ class Position;
|
||||||
template<GenType>
|
template<GenType>
|
||||||
ExtMove* generate(const Position& pos, ExtMove* mlist);
|
ExtMove* generate(const Position& pos, ExtMove* mlist);
|
||||||
|
|
||||||
/// The MoveList struct is a simple wrapper around generate(), sometimes comes
|
/// The MoveList struct is a simple wrapper around generate(). It sometimes comes
|
||||||
/// handy to use this class instead of the low level generate() function.
|
/// in handy to use this class instead of the low level generate() function.
|
||||||
template<GenType T>
|
template<GenType T>
|
||||||
struct MoveList {
|
struct MoveList {
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace {
|
||||||
STOP
|
STOP
|
||||||
};
|
};
|
||||||
|
|
||||||
// Our insertion sort, guaranteed to be stable, as is needed
|
// Our insertion sort, which is guaranteed (and also needed) to be stable
|
||||||
void insertion_sort(ExtMove* begin, ExtMove* end)
|
void insertion_sort(ExtMove* begin, ExtMove* end)
|
||||||
{
|
{
|
||||||
ExtMove tmp, *p, *q;
|
ExtMove tmp, *p, *q;
|
||||||
|
@ -53,9 +53,9 @@ namespace {
|
||||||
// ones so to sort separately the two sets, and with the second sort delayed.
|
// ones so to sort separately the two sets, and with the second sort delayed.
|
||||||
inline bool has_positive_score(const ExtMove& ms) { return ms.score > 0; }
|
inline bool has_positive_score(const ExtMove& ms) { return ms.score > 0; }
|
||||||
|
|
||||||
// Picks and moves to the front the best move in the range [begin, end),
|
// Picks the best move in the range (begin, end) and moves it to the front.
|
||||||
// it is faster than sorting all the moves in advance when moves are few, as
|
// It's faster than sorting all the moves in advance when there are few
|
||||||
// normally are the possible captures.
|
// moves e.g. possible captures.
|
||||||
inline ExtMove* pick_best(ExtMove* begin, ExtMove* end)
|
inline ExtMove* pick_best(ExtMove* begin, ExtMove* end)
|
||||||
{
|
{
|
||||||
std::swap(*begin, *std::max_element(begin, end));
|
std::swap(*begin, *std::max_element(begin, end));
|
||||||
|
@ -105,7 +105,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
|
||||||
{
|
{
|
||||||
stage = QSEARCH_1;
|
stage = QSEARCH_1;
|
||||||
|
|
||||||
// Skip TT move if is not a capture or a promotion, this avoids qsearch
|
// Skip TT move if is not a capture or a promotion. This avoids qsearch
|
||||||
// tree explosion due to a possible perpetual check or similar rare cases
|
// tree explosion due to a possible perpetual check or similar rare cases
|
||||||
// when TT table is full.
|
// when TT table is full.
|
||||||
if (ttm && !pos.capture_or_promotion(ttm))
|
if (ttm && !pos.capture_or_promotion(ttm))
|
||||||
|
@ -129,7 +129,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Piece
|
||||||
|
|
||||||
stage = PROBCUT;
|
stage = PROBCUT;
|
||||||
|
|
||||||
// In ProbCut we generate only captures better than parent's captured piece
|
// In ProbCut we generate only captures that are better than the parent's
|
||||||
|
// captured piece.
|
||||||
captureThreshold = PieceValue[MG][pt];
|
captureThreshold = PieceValue[MG][pt];
|
||||||
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||||
|
|
||||||
|
@ -153,10 +154,10 @@ void MovePicker::score<CAPTURES>() {
|
||||||
// where it is possible to recapture with the hanging piece). Exchanging
|
// where it is possible to recapture with the hanging piece). Exchanging
|
||||||
// big pieces before capturing a hanging piece probably helps to reduce
|
// big pieces before capturing a hanging piece probably helps to reduce
|
||||||
// the subtree size.
|
// the subtree size.
|
||||||
// In main search we want to push captures with negative SEE values to
|
// In main search we want to push captures with negative SEE values to the
|
||||||
// badCaptures[] array, but instead of doing it now we delay till when
|
// badCaptures[] array, but instead of doing it now we delay until the move
|
||||||
// the move has been picked up in pick_move_from_list(), this way we save
|
// has been picked up in pick_move_from_list(). This way we save some SEE
|
||||||
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
|
// calls in case we get a cutoff.
|
||||||
Move m;
|
Move m;
|
||||||
|
|
||||||
for (ExtMove* it = moves; it != end; ++it)
|
for (ExtMove* it = moves; it != end; ++it)
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
/// and is used for reduction and move ordering decisions. Gains records the move's
|
/// and is used for reduction and move ordering decisions. Gains records the move's
|
||||||
/// best evaluation gain from one ply to the next and is used for pruning decisions.
|
/// best evaluation gain from one ply to the next and is used for pruning decisions.
|
||||||
/// Countermoves store the move that refute a previous one. Entries are stored
|
/// Countermoves store the move that refute a previous one. Entries are stored
|
||||||
/// according only to moving piece and destination square, hence two moves with
|
/// using only the moving piece and destination square, hence two moves with
|
||||||
/// different origin but same destination and piece will be considered identical.
|
/// different origin but same destination and piece will be considered identical.
|
||||||
template<bool Gain, typename T>
|
template<bool Gain, typename T>
|
||||||
struct Stats {
|
struct Stats {
|
||||||
|
|
|
@ -126,8 +126,8 @@ const string move_to_san(Position& pos, Move m) {
|
||||||
{
|
{
|
||||||
san = PieceToChar[WHITE][pt]; // Upper case
|
san = PieceToChar[WHITE][pt]; // Upper case
|
||||||
|
|
||||||
// Disambiguation if we have more then one piece of type 'pt' that can
|
// A disambiguation occurs if we have more then one piece of type 'pt'
|
||||||
// reach 'to' with a legal move.
|
// that can reach 'to' with a legal move.
|
||||||
others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
|
others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
|
||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
|
@ -175,7 +175,7 @@ const string move_to_san(Position& pos, Move m) {
|
||||||
|
|
||||||
/// pretty_pv() formats human-readable search information, typically to be
|
/// pretty_pv() formats human-readable search information, typically to be
|
||||||
/// appended to the search log file. It uses the two helpers below to pretty
|
/// appended to the search log file. It uses the two helpers below to pretty
|
||||||
/// format time and score respectively.
|
/// format the time and score respectively.
|
||||||
|
|
||||||
static string time_to_string(int64_t msecs) {
|
static string time_to_string(int64_t msecs) {
|
||||||
|
|
||||||
|
|
|
@ -252,8 +252,9 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Entry::update_safety() calculates and caches a bonus for king safety. It is
|
/// Entry::update_safety() calculates and caches a bonus for king safety.
|
||||||
/// called only when king square changes, about 20% of total king_safety() calls.
|
/// It is called only when king square changes, which is about 20% of total
|
||||||
|
/// king_safety() calls.
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
Score Entry::update_safety(const Position& pos, Square ksq) {
|
Score Entry::update_safety(const Position& pos, Square ksq) {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
// Disable some silly and noisy warning from MSVC compiler
|
// Disable some silly and noisy warnings from MSVC compiler
|
||||||
#pragma warning(disable: 4127) // Conditional expression is constant
|
#pragma warning(disable: 4127) // Conditional expression is constant
|
||||||
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
|
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
|
||||||
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
|
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
|
||||||
|
@ -89,14 +89,14 @@ inline int64_t system_time_to_msec() {
|
||||||
#undef WIN32_LEAN_AND_MEAN
|
#undef WIN32_LEAN_AND_MEAN
|
||||||
#undef NOMINMAX
|
#undef NOMINMAX
|
||||||
|
|
||||||
// We use critical sections on Windows to support Windows XP and older versions,
|
// We use critical sections on Windows to support Windows XP and older versions.
|
||||||
// unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject()
|
// Unfortunately, cond_wait() is racy between lock_release() and WaitForSingleObject()
|
||||||
// but apart from this they have the same speed performance of SRW locks.
|
// but apart from this they have the same speed performance of SRW locks.
|
||||||
typedef CRITICAL_SECTION Lock;
|
typedef CRITICAL_SECTION Lock;
|
||||||
typedef HANDLE WaitCondition;
|
typedef HANDLE WaitCondition;
|
||||||
typedef HANDLE NativeHandle;
|
typedef HANDLE NativeHandle;
|
||||||
|
|
||||||
// On Windows 95 and 98 parameter lpThreadId my not be null
|
// On Windows 95 and 98 parameter lpThreadId may not be null
|
||||||
inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
|
inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
|
||||||
|
|
||||||
# define lock_init(x) InitializeCriticalSection(&(x))
|
# define lock_init(x) InitializeCriticalSection(&(x))
|
||||||
|
|
|
@ -82,7 +82,7 @@ PieceType min_attacker(const Bitboard* bb, const Square& to, const Bitboard& stm
|
||||||
|
|
||||||
template<> FORCE_INLINE
|
template<> FORCE_INLINE
|
||||||
PieceType min_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) {
|
PieceType min_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) {
|
||||||
return KING; // No need to update bitboards, it is the last cycle
|
return KING; // No need to update bitboards: it is the last cycle
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -109,9 +109,9 @@ CheckInfo::CheckInfo(const Position& pos) {
|
||||||
|
|
||||||
/// Position::init() initializes at startup the various arrays used to compute
|
/// Position::init() initializes at startup the various arrays used to compute
|
||||||
/// hash keys and the piece square tables. The latter is a two-step operation:
|
/// hash keys and the piece square tables. The latter is a two-step operation:
|
||||||
/// First, the white halves of the tables are copied from PSQT[] tables. Second,
|
/// Firstly, the white halves of the tables are copied from PSQT[] tables.
|
||||||
/// the black halves of the tables are initialized by flipping and changing the
|
/// Secondly, the black halves of the tables are initialized by flipping and
|
||||||
/// sign of the white scores.
|
/// changing the sign of the white scores.
|
||||||
|
|
||||||
void Position::init() {
|
void Position::init() {
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ void Position::init() {
|
||||||
|
|
||||||
|
|
||||||
/// Position::operator=() creates a copy of 'pos'. We want the new born Position
|
/// Position::operator=() creates a copy of 'pos'. We want the new born Position
|
||||||
/// object do not depend on any external data so we detach state pointer from
|
/// object to not depend on any external data so we detach state pointer from
|
||||||
/// the source one.
|
/// the source one.
|
||||||
|
|
||||||
Position& Position::operator=(const Position& pos) {
|
Position& Position::operator=(const Position& pos) {
|
||||||
|
@ -182,11 +182,11 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
|
||||||
A FEN string contains six fields separated by a space. The fields are:
|
A FEN string contains six fields separated by a space. The fields are:
|
||||||
|
|
||||||
1) Piece placement (from white's perspective). Each rank is described, starting
|
1) Piece placement (from white's perspective). Each rank is described, starting
|
||||||
with rank 8 and ending with rank 1; within each rank, the contents of each
|
with rank 8 and ending with rank 1. Within each rank, the contents of each
|
||||||
square are described from file A through file H. Following the Standard
|
square are described from file A through file H. Following the Standard
|
||||||
Algebraic Notation (SAN), each piece is identified by a single letter taken
|
Algebraic Notation (SAN), each piece is identified by a single letter taken
|
||||||
from the standard English names. White pieces are designated using upper-case
|
from the standard English names. White pieces are designated using upper-case
|
||||||
letters ("PNBRQK") while Black take lowercase ("pnbrqk"). Blank squares are
|
letters ("PNBRQK") whilst Black uses lowercase ("pnbrqk"). Blank squares are
|
||||||
noted using digits 1 through 8 (the number of blank squares), and "/"
|
noted using digits 1 through 8 (the number of blank squares), and "/"
|
||||||
separates ranks.
|
separates ranks.
|
||||||
|
|
||||||
|
@ -324,8 +324,8 @@ void Position::set_castling_flag(Color c, Square rfrom) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::fen() returns a FEN representation of the position. In case
|
/// Position::fen() returns a FEN representation of the position. In case of
|
||||||
/// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function.
|
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
|
||||||
|
|
||||||
const string Position::fen() const {
|
const string Position::fen() const {
|
||||||
|
|
||||||
|
@ -416,14 +416,14 @@ const string Position::pretty(Move move) const {
|
||||||
|
|
||||||
|
|
||||||
/// Position:hidden_checkers() returns a bitboard of all pinned / discovery check
|
/// Position:hidden_checkers() returns a bitboard of all pinned / discovery check
|
||||||
/// pieces, according to the call parameters. Pinned pieces protect our king,
|
/// pieces, according to the call parameters. Pinned pieces protect our king and
|
||||||
/// discovery check pieces attack the enemy king.
|
/// discovery check pieces attack the enemy king.
|
||||||
|
|
||||||
Bitboard Position::hidden_checkers(Square ksq, Color c, Color toMove) const {
|
Bitboard Position::hidden_checkers(Square ksq, Color c, Color toMove) const {
|
||||||
|
|
||||||
Bitboard b, pinners, result = 0;
|
Bitboard b, pinners, result = 0;
|
||||||
|
|
||||||
// Pinners are sliders that give check when pinned piece is removed
|
// Pinners are sliders that give check when a pinned piece is removed
|
||||||
pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq])
|
pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq])
|
||||||
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(c);
|
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(c);
|
||||||
|
|
||||||
|
@ -439,7 +439,7 @@ Bitboard Position::hidden_checkers(Square ksq, Color c, Color toMove) 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 occ bitboard as occupancy.
|
/// given square. Slider attacks use the occ bitboard to indicate occupancy.
|
||||||
|
|
||||||
Bitboard Position::attackers_to(Square s, Bitboard occ) const {
|
Bitboard Position::attackers_to(Square s, Bitboard occ) const {
|
||||||
|
|
||||||
|
@ -593,8 +593,8 @@ bool Position::pseudo_legal(const Move m) const {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Evasions generator already takes care to avoid some kind of illegal moves
|
// Evasions generator already takes care to avoid some kind of illegal moves
|
||||||
// and pl_move_is_legal() relies on this. So we have to take care that the
|
// and pl_move_is_legal() relies on this. We therefore have to take care that
|
||||||
// same kind of moves are filtered out here.
|
// the same kind of moves are filtered out here.
|
||||||
if (checkers())
|
if (checkers())
|
||||||
{
|
{
|
||||||
if (type_of(pc) != KING)
|
if (type_of(pc) != KING)
|
||||||
|
@ -629,11 +629,11 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
|
||||||
Square to = to_sq(m);
|
Square to = to_sq(m);
|
||||||
PieceType pt = type_of(piece_on(from));
|
PieceType pt = type_of(piece_on(from));
|
||||||
|
|
||||||
// Direct check ?
|
// Is there a direct check ?
|
||||||
if (ci.checkSq[pt] & to)
|
if (ci.checkSq[pt] & to)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Discovered check ?
|
// Is there a discovered check ?
|
||||||
if ( unlikely(ci.dcCandidates)
|
if ( unlikely(ci.dcCandidates)
|
||||||
&& (ci.dcCandidates & from)
|
&& (ci.dcCandidates & from)
|
||||||
&& !aligned(from, to, king_square(~sideToMove)))
|
&& !aligned(from, to, king_square(~sideToMove)))
|
||||||
|
@ -652,7 +652,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
|
||||||
return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
|
return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
|
||||||
|
|
||||||
// En passant capture with check ? We have already handled the case
|
// En passant capture with check ? We have already handled the case
|
||||||
// of direct checks and ordinary discovered check, the only case we
|
// of direct checks and ordinary discovered check, so the only case we
|
||||||
// need to handle is the unusual case of a discovered check through
|
// need to handle is the unusual case of a discovered check through
|
||||||
// the captured pawn.
|
// the captured pawn.
|
||||||
case ENPASSANT:
|
case ENPASSANT:
|
||||||
|
@ -700,7 +700,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||||
|
|
||||||
// Copy some fields of old state to our new StateInfo object except the ones
|
// Copy some fields of old state to our new StateInfo object except the ones
|
||||||
// which are going to be recalculated from scratch anyway, then switch our state
|
// which are going to be recalculated from scratch anyway, then switch our state
|
||||||
// pointer to point to the new, ready to be updated, state.
|
// pointer to point to the new (ready to be updated) state.
|
||||||
std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
|
std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
|
||||||
|
|
||||||
newSt.previous = st;
|
newSt.previous = st;
|
||||||
|
@ -709,7 +709,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||||
// Update side to move
|
// Update side to move
|
||||||
k ^= Zobrist::side;
|
k ^= Zobrist::side;
|
||||||
|
|
||||||
// Increment ply counters.In particular rule50 will be later reset it to zero
|
// Increment ply counters.In particular rule50 will be reset to zero later on
|
||||||
// in case of a capture or a pawn move.
|
// in case of a capture or a pawn move.
|
||||||
++gamePly;
|
++gamePly;
|
||||||
++st->rule50;
|
++st->rule50;
|
||||||
|
@ -812,7 +812,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||||
// If the moving piece is a pawn do some special extra work
|
// If the moving piece is a pawn do some special extra work
|
||||||
if (pt == PAWN)
|
if (pt == PAWN)
|
||||||
{
|
{
|
||||||
// Set en-passant square, only if moved pawn can be captured
|
// Set en-passant square if the moved pawn can be captured
|
||||||
if ( (int(to) ^ int(from)) == 16
|
if ( (int(to) ^ int(from)) == 16
|
||||||
&& (attacks_from<PAWN>(from + pawn_push(us), us) & pieces(them, PAWN)))
|
&& (attacks_from<PAWN>(from + pawn_push(us), us) & pieces(them, PAWN)))
|
||||||
{
|
{
|
||||||
|
@ -860,7 +860,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||||
// Update the key with the final value
|
// Update the key with the final value
|
||||||
st->key = k;
|
st->key = k;
|
||||||
|
|
||||||
// Update checkers bitboard, piece must be already moved
|
// Update checkers bitboard: piece must be already moved
|
||||||
st->checkersBB = 0;
|
st->checkersBB = 0;
|
||||||
|
|
||||||
if (moveIsCheck)
|
if (moveIsCheck)
|
||||||
|
@ -1133,7 +1133,7 @@ void Position::clear() {
|
||||||
|
|
||||||
|
|
||||||
/// Position::compute_key() computes the hash key of the position. The hash
|
/// Position::compute_key() computes the hash key of the position. The hash
|
||||||
/// key is usually updated incrementally as moves are made and unmade, the
|
/// key is usually updated incrementally as moves are made and unmade. The
|
||||||
/// compute_key() function is only used when a new position is set up, and
|
/// compute_key() function is only used when a new position is set up, and
|
||||||
/// to verify the correctness of the hash key when running in debug mode.
|
/// to verify the correctness of the hash key when running in debug mode.
|
||||||
|
|
||||||
|
@ -1158,8 +1158,8 @@ Key Position::compute_key() const {
|
||||||
|
|
||||||
|
|
||||||
/// Position::compute_pawn_key() computes the hash key of the position. The
|
/// Position::compute_pawn_key() computes the hash key of the position. The
|
||||||
/// hash key is usually updated incrementally as moves are made and unmade,
|
/// hash key is usually updated incrementally as moves are made and unmade.
|
||||||
/// the compute_pawn_key() function is only used when a new position is set
|
/// The compute_pawn_key() function is only used when a new position is set
|
||||||
/// up, and to verify the correctness of the pawn hash key when running in
|
/// up, and to verify the correctness of the pawn hash key when running in
|
||||||
/// debug mode.
|
/// debug mode.
|
||||||
|
|
||||||
|
@ -1178,8 +1178,8 @@ Key Position::compute_pawn_key() const {
|
||||||
|
|
||||||
|
|
||||||
/// Position::compute_material_key() computes the hash key of the position.
|
/// Position::compute_material_key() computes the hash key of the position.
|
||||||
/// The hash key is usually updated incrementally as moves are made and unmade,
|
/// The hash key is usually updated incrementally as moves are made and unmade.
|
||||||
/// the compute_material_key() function is only used when a new position is set
|
/// The compute_material_key() function is only used when a new position is set
|
||||||
/// up, and to verify the correctness of the material hash key when running in
|
/// up, and to verify the correctness of the material hash key when running in
|
||||||
/// debug mode.
|
/// debug mode.
|
||||||
|
|
||||||
|
@ -1218,7 +1218,7 @@ Score Position::compute_psq_score() const {
|
||||||
|
|
||||||
/// Position::compute_non_pawn_material() computes the total non-pawn middle
|
/// Position::compute_non_pawn_material() computes the total non-pawn middle
|
||||||
/// game material value for the given side. Material values are updated
|
/// game material value for the given side. Material values are updated
|
||||||
/// incrementally during the search, this function is only used while
|
/// incrementally during the search. This function is only used when
|
||||||
/// initializing a new Position object.
|
/// initializing a new Position object.
|
||||||
|
|
||||||
Value Position::compute_non_pawn_material(Color c) const {
|
Value Position::compute_non_pawn_material(Color c) const {
|
||||||
|
@ -1233,7 +1233,7 @@ Value Position::compute_non_pawn_material(Color c) const {
|
||||||
|
|
||||||
|
|
||||||
/// Position::is_draw() tests whether the position is drawn by material,
|
/// Position::is_draw() tests whether the position is drawn by material,
|
||||||
/// repetition, or the 50 moves rule. It does not detect stalemates, this
|
/// repetition, or the 50 moves rule. It does not detect stalemates: this
|
||||||
/// must be done by the search.
|
/// must be done by the search.
|
||||||
bool Position::is_draw() const {
|
bool Position::is_draw() const {
|
||||||
|
|
||||||
|
@ -1268,7 +1268,7 @@ bool Position::is_draw() 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 especially for finding evaluation symmetry bugs.
|
/// is only useful for debugging e.g. for finding evaluation symmetry bugs.
|
||||||
|
|
||||||
static char toggle_case(char c) {
|
static char toggle_case(char c) {
|
||||||
return char(islower(c) ? toupper(c) : tolower(c));
|
return char(islower(c) ? toupper(c) : tolower(c));
|
||||||
|
@ -1305,7 +1305,7 @@ void Position::flip() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::pos_is_ok() performs some consitency checks for the position object.
|
/// Position::pos_is_ok() performs some consistency checks for the position object.
|
||||||
/// This is meant to be helpful when debugging.
|
/// This is meant to be helpful when debugging.
|
||||||
|
|
||||||
bool Position::pos_is_ok(int* failedStep) const {
|
bool Position::pos_is_ok(int* failedStep) const {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on
|
/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on
|
||||||
/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined
|
/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined
|
||||||
/// for white side, for black side the tables are symmetric.
|
/// for the white side and the tables are symmetric for the black side.
|
||||||
|
|
||||||
static const Score PSQT[][SQUARE_NB] = {
|
static const Score PSQT[][SQUARE_NB] = {
|
||||||
{ },
|
{ },
|
||||||
|
|
145
src/search.cpp
145
src/search.cpp
|
@ -263,11 +263,11 @@ finalize:
|
||||||
sync_cout << "info nodes " << RootPos.nodes_searched()
|
sync_cout << "info nodes " << RootPos.nodes_searched()
|
||||||
<< " time " << Time::now() - SearchTime + 1 << sync_endl;
|
<< " time " << Time::now() - SearchTime + 1 << sync_endl;
|
||||||
|
|
||||||
// When we reach max depth we arrive here even without Signals.stop being raised,
|
// When we reach the maximum depth, we can arrive here without a raise of
|
||||||
// but if we are pondering or in infinite search, according to UCI protocol,
|
// Signals.stop. However, if we are pondering or in an infinite search,
|
||||||
// we shouldn't print the best move before the GUI sends a "stop" or "ponderhit"
|
// the UCI protocol states that we shouldn't print the best move before the
|
||||||
// command. We simply wait here until GUI sends one of those commands (that
|
// GUI sends a "stop" or "ponderhit" command. We therefore simply wait here
|
||||||
// raise Signals.stop).
|
// until the GUI sends one of those commands (which also raises Signals.stop).
|
||||||
if (!Signals.stop && (Limits.ponder || Limits.infinite))
|
if (!Signals.stop && (Limits.ponder || Limits.infinite))
|
||||||
{
|
{
|
||||||
Signals.stopOnPonderhit = true;
|
Signals.stopOnPonderhit = true;
|
||||||
|
@ -322,8 +322,8 @@ namespace {
|
||||||
// Age out PV variability metric
|
// Age out PV variability metric
|
||||||
BestMoveChanges *= 0.8;
|
BestMoveChanges *= 0.8;
|
||||||
|
|
||||||
// Save last iteration's scores before first PV line is searched and all
|
// Save the last iteration's scores before first PV line is searched and
|
||||||
// the move scores except the (new) PV are set to -VALUE_INFINITE.
|
// all the move scores except the (new) PV are set to -VALUE_INFINITE.
|
||||||
for (size_t i = 0; i < RootMoves.size(); ++i)
|
for (size_t i = 0; i < RootMoves.size(); ++i)
|
||||||
RootMoves[i].prevScore = RootMoves[i].score;
|
RootMoves[i].prevScore = RootMoves[i].score;
|
||||||
|
|
||||||
|
@ -338,16 +338,17 @@ namespace {
|
||||||
beta = std::min(RootMoves[PVIdx].prevScore + delta, VALUE_INFINITE);
|
beta = std::min(RootMoves[PVIdx].prevScore + delta, VALUE_INFINITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start with a small aspiration window and, in case of fail high/low,
|
// Start with a small aspiration window and, in the case of a fail
|
||||||
// research with bigger window until not failing high/low anymore.
|
// high/low, re-search with a bigger window until we're not failing
|
||||||
|
// high/low anymore.
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
bestValue = search<Root>(pos, ss, alpha, beta, depth * ONE_PLY, false);
|
bestValue = search<Root>(pos, ss, alpha, beta, depth * ONE_PLY, false);
|
||||||
|
|
||||||
// Bring to front the best move. It is critical that sorting is
|
// Bring the best move to the front. It is critical that sorting
|
||||||
// done with a stable algorithm because all the values but the first
|
// is done with a stable algorithm because all the values but the
|
||||||
// and eventually the new best one are set to -VALUE_INFINITE and
|
// first and eventually the new best one are set to -VALUE_INFINITE
|
||||||
// we want to keep the same order for all the moves but the new
|
// and we want to keep the same order for all the moves but the new
|
||||||
// PV that goes to the front. Note that in case of MultiPV search
|
// PV that goes to the front. Note that in case of MultiPV search
|
||||||
// the already searched PV lines are preserved.
|
// the already searched PV lines are preserved.
|
||||||
std::stable_sort(RootMoves.begin() + PVIdx, RootMoves.end());
|
std::stable_sort(RootMoves.begin() + PVIdx, RootMoves.end());
|
||||||
|
@ -359,18 +360,18 @@ namespace {
|
||||||
|
|
||||||
// If search has been stopped break immediately. Sorting and
|
// If search has been stopped break immediately. Sorting and
|
||||||
// writing PV back to TT is safe becuase RootMoves is still
|
// writing PV back to TT is safe becuase RootMoves is still
|
||||||
// valid, although refers to previous iteration.
|
// valid, although it refers to previous iteration.
|
||||||
if (Signals.stop)
|
if (Signals.stop)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// When failing high/low give some update (without cluttering
|
// When failing high/low give some update (without cluttering
|
||||||
// the UI) before to research.
|
// the UI) before a re-search.
|
||||||
if ( (bestValue <= alpha || bestValue >= beta)
|
if ( (bestValue <= alpha || bestValue >= beta)
|
||||||
&& Time::now() - SearchTime > 3000)
|
&& Time::now() - SearchTime > 3000)
|
||||||
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
|
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
|
||||||
|
|
||||||
// In case of failing low/high increase aspiration window and
|
// In case of failing low/high increase aspiration window and
|
||||||
// research, otherwise exit the loop.
|
// re-search, otherwise exit the loop.
|
||||||
if (bestValue <= alpha)
|
if (bestValue <= alpha)
|
||||||
{
|
{
|
||||||
alpha = std::max(bestValue - delta, -VALUE_INFINITE);
|
alpha = std::max(bestValue - delta, -VALUE_INFINITE);
|
||||||
|
@ -396,7 +397,7 @@ namespace {
|
||||||
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
|
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we now need to pick now the sub-optimal best move ?
|
// If skill levels are enabled and time is up, pick a sub-optimal best move
|
||||||
if (skill.enabled() && skill.time_to_pick(depth))
|
if (skill.enabled() && skill.time_to_pick(depth))
|
||||||
skill.pick_move();
|
skill.pick_move();
|
||||||
|
|
||||||
|
@ -426,13 +427,13 @@ namespace {
|
||||||
if (depth > 4 && depth < 50 && PVSize == 1)
|
if (depth > 4 && depth < 50 && PVSize == 1)
|
||||||
TimeMgr.pv_instability(BestMoveChanges);
|
TimeMgr.pv_instability(BestMoveChanges);
|
||||||
|
|
||||||
// Stop search if most of the available time is already consumed. We
|
// Stop the search if most of the available time has been used. We
|
||||||
// probably don't have enough time to search the first move at the
|
// probably don't have enough time to search the first move at the
|
||||||
// next iteration anyway.
|
// next iteration anyway.
|
||||||
if (Time::now() - SearchTime > (TimeMgr.available_time() * 62) / 100)
|
if (Time::now() - SearchTime > (TimeMgr.available_time() * 62) / 100)
|
||||||
stop = true;
|
stop = true;
|
||||||
|
|
||||||
// Stop search early if one move seems to be much better than others
|
// Stop the search early if one move seems to be much better than others
|
||||||
if ( depth >= 12
|
if ( depth >= 12
|
||||||
&& BestMoveChanges <= DBL_EPSILON
|
&& BestMoveChanges <= DBL_EPSILON
|
||||||
&& !stop
|
&& !stop
|
||||||
|
@ -455,7 +456,7 @@ namespace {
|
||||||
if (stop)
|
if (stop)
|
||||||
{
|
{
|
||||||
// If we are allowed to ponder do not stop the search now but
|
// If we are allowed to ponder do not stop the search now but
|
||||||
// keep pondering until GUI sends "ponderhit" or "stop".
|
// keep pondering until the GUI sends "ponderhit" or "stop".
|
||||||
if (Limits.ponder)
|
if (Limits.ponder)
|
||||||
Signals.stopOnPonderhit = true;
|
Signals.stopOnPonderhit = true;
|
||||||
else
|
else
|
||||||
|
@ -469,9 +470,9 @@ namespace {
|
||||||
// search<>() is the main search function for both PV and non-PV nodes and for
|
// search<>() is the main search function for both PV and non-PV nodes and for
|
||||||
// normal and SplitPoint nodes. When called just after a split point the search
|
// normal and SplitPoint nodes. When called just after a split point the search
|
||||||
// is simpler because we have already probed the hash table, done a null move
|
// is simpler because we have already probed the hash table, done a null move
|
||||||
// search, and searched the first move before splitting, we don't have to repeat
|
// search, and searched the first move before splitting, so we don't have to
|
||||||
// all this work again. We also don't need to store anything to the hash table
|
// repeat all this work again. We also don't need to store anything to the hash
|
||||||
// here: This is taken care of after we return from the split point.
|
// table here: This is taken care of after we return from the split point.
|
||||||
|
|
||||||
template <NodeType NT>
|
template <NodeType NT>
|
||||||
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) {
|
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) {
|
||||||
|
@ -535,9 +536,9 @@ namespace {
|
||||||
// Step 3. Mate distance pruning. Even if we mate at the next move our score
|
// Step 3. Mate distance pruning. Even if we mate at the next move our score
|
||||||
// would be at best mate_in(ss->ply+1), but if alpha is already bigger because
|
// would be at best mate_in(ss->ply+1), but if alpha is already bigger because
|
||||||
// a shorter mate was found upward in the tree then there is no need to search
|
// a shorter mate was found upward in the tree then there is no need to search
|
||||||
// further, we will never beat current alpha. Same logic but with reversed signs
|
// because we will never beat the current alpha. Same logic but with reversed
|
||||||
// applies also in the opposite condition of being mated instead of giving mate,
|
// signs applies also in the opposite condition of being mated instead of giving
|
||||||
// in this case return a fail-high score.
|
// mate. In this case return a fail-high score.
|
||||||
alpha = std::max(mated_in(ss->ply), alpha);
|
alpha = std::max(mated_in(ss->ply), alpha);
|
||||||
beta = std::min(mate_in(ss->ply+1), beta);
|
beta = std::min(mate_in(ss->ply+1), beta);
|
||||||
if (alpha >= beta)
|
if (alpha >= beta)
|
||||||
|
@ -553,8 +554,8 @@ namespace {
|
||||||
ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE;
|
ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE;
|
||||||
ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
|
ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
|
||||||
|
|
||||||
// At PV nodes we check for exact scores, while at non-PV nodes we check for
|
// At PV nodes we check for exact scores, whilst at non-PV nodes we check for
|
||||||
// a fail high/low. Biggest advantage at probing at PV nodes is to have a
|
// a fail high/low. The biggest advantage to probing at PV nodes is to have a
|
||||||
// smooth experience in analysis mode. We don't probe at Root nodes otherwise
|
// smooth experience in analysis mode. We don't probe at Root nodes otherwise
|
||||||
// we should also update RootMoveList to avoid bogus output.
|
// we should also update RootMoveList to avoid bogus output.
|
||||||
if ( !RootNode
|
if ( !RootNode
|
||||||
|
@ -782,7 +783,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
|
|
||||||
if (SpNode)
|
if (SpNode)
|
||||||
{
|
{
|
||||||
// Shared counter cannot be decremented later if move turns out to be illegal
|
// Shared counter cannot be decremented later if the move turns out to be illegal
|
||||||
if (!pos.legal(move, ci.pinned))
|
if (!pos.legal(move, ci.pinned))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -816,8 +817,8 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
// Singular extension search. If all moves but one fail low on a search of
|
// Singular extension search. If all moves but one fail low on a search of
|
||||||
// (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move
|
// (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move
|
||||||
// is singular and should be extended. To verify this we do a reduced search
|
// is singular and should be extended. To verify this we do a reduced search
|
||||||
// on all the other moves but the ttMove, if result is lower than ttValue minus
|
// on all the other moves but the ttMove and if the result is lower than
|
||||||
// a margin then we extend ttMove.
|
// ttValue minus a margin then we extend the ttMove.
|
||||||
if ( singularExtensionNode
|
if ( singularExtensionNode
|
||||||
&& move == ttMove
|
&& move == ttMove
|
||||||
&& !ext
|
&& !ext
|
||||||
|
@ -837,7 +838,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
ext = ONE_PLY;
|
ext = ONE_PLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update current move (this must be done after singular extension search)
|
// Update the current move (this must be done after singular extension search)
|
||||||
newDepth = depth - ONE_PLY + ext;
|
newDepth = depth - ONE_PLY + ext;
|
||||||
|
|
||||||
// Step 13. Pruning at shallow depth (exclude PV nodes)
|
// Step 13. Pruning at shallow depth (exclude PV nodes)
|
||||||
|
@ -891,7 +892,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for legality only before to do the move
|
// Check for legality just before making the move
|
||||||
if (!RootNode && !SpNode && !pos.legal(move, ci.pinned))
|
if (!RootNode && !SpNode && !pos.legal(move, ci.pinned))
|
||||||
{
|
{
|
||||||
moveCount--;
|
moveCount--;
|
||||||
|
@ -950,8 +951,8 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
: - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
|
: - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only for PV nodes do a full PV search on the first move or after a fail
|
// For PV nodes only, do a full PV search on the first move or after a fail
|
||||||
// high, in the latter case search only if value < beta, otherwise let the
|
// high (in the latter case search only if value < beta), otherwise let the
|
||||||
// parent node fail low with value <= alpha and to try another move.
|
// parent node fail low with value <= alpha and to try another move.
|
||||||
if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta))))
|
if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta))))
|
||||||
value = newDepth < ONE_PLY ?
|
value = newDepth < ONE_PLY ?
|
||||||
|
@ -995,9 +996,9 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
++BestMoveChanges;
|
++BestMoveChanges;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// All other moves but the PV are set to the lowest value, this
|
// All other moves but the PV are set to the lowest value: this is
|
||||||
// is not a problem when sorting becuase sort is stable and move
|
// not a problem when sorting because the sort is stable and the
|
||||||
// position in the list is preserved, just the PV is pushed up.
|
// move position in the list is preserved - just the PV is pushed up.
|
||||||
rm.score = -VALUE_INFINITE;
|
rm.score = -VALUE_INFINITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1047,7 +1048,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
// case of Signals.stop or thread.cutoff_occurred() are set, but this is
|
// case of Signals.stop or thread.cutoff_occurred() are set, but this is
|
||||||
// harmless because return value is discarded anyhow in the parent nodes.
|
// harmless because return value is discarded anyhow in the parent nodes.
|
||||||
// If we are in a singular extension search then return a fail low score.
|
// If we are in a singular extension search then return a fail low score.
|
||||||
// A split node has at least one move, the one tried before to be splitted.
|
// A split node has at least one move - the one tried before to be splitted.
|
||||||
if (!moveCount)
|
if (!moveCount)
|
||||||
return excludedMove ? alpha
|
return excludedMove ? alpha
|
||||||
: inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()];
|
: inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()];
|
||||||
|
@ -1122,11 +1123,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
ss->currentMove = bestMove = MOVE_NONE;
|
ss->currentMove = bestMove = MOVE_NONE;
|
||||||
ss->ply = (ss-1)->ply + 1;
|
ss->ply = (ss-1)->ply + 1;
|
||||||
|
|
||||||
// Check for an instant draw or maximum ply reached
|
// Check for an instant draw or if the maximum ply has been reached
|
||||||
if (pos.is_draw() || ss->ply > MAX_PLY)
|
if (pos.is_draw() || ss->ply > MAX_PLY)
|
||||||
return DrawValue[pos.side_to_move()];
|
return DrawValue[pos.side_to_move()];
|
||||||
|
|
||||||
// Decide whether or not to include checks, this fixes also the type of
|
// Decide whether or not to include checks: this fixes also the type of
|
||||||
// TT entry depth that we are going to use. Note that in qsearch we use
|
// TT entry depth that we are going to use. Note that in qsearch we use
|
||||||
// only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS.
|
// only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS.
|
||||||
ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
|
ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
|
||||||
|
@ -1230,7 +1231,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect non-capture evasions that are candidate to be pruned
|
// Detect non-capture evasions that are candidates to be pruned
|
||||||
evasionPrunable = InCheck
|
evasionPrunable = InCheck
|
||||||
&& bestValue > VALUE_MATED_IN_MAX_PLY
|
&& bestValue > VALUE_MATED_IN_MAX_PLY
|
||||||
&& !pos.capture(move)
|
&& !pos.capture(move)
|
||||||
|
@ -1244,7 +1245,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
&& pos.see_sign(move) < 0)
|
&& pos.see_sign(move) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check for legality only before to do the move
|
// Check for legality just before making the move
|
||||||
if (!pos.legal(move, ci.pinned))
|
if (!pos.legal(move, ci.pinned))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1298,7 +1299,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
|
|
||||||
// value_to_tt() adjusts a mate score from "plies to mate from the root" to
|
// value_to_tt() adjusts a mate score from "plies to mate from the root" to
|
||||||
// "plies to mate from the current position". Non-mate scores are unchanged.
|
// "plies to mate from the current position". Non-mate scores are unchanged.
|
||||||
// The function is called before storing a value to the transposition table.
|
// The function is called before storing a value in the transposition table.
|
||||||
|
|
||||||
Value value_to_tt(Value v, int ply) {
|
Value value_to_tt(Value v, int ply) {
|
||||||
|
|
||||||
|
@ -1310,7 +1311,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
|
|
||||||
|
|
||||||
// value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score
|
// value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score
|
||||||
// from the transposition table (where refers to the plies to mate/be mated
|
// from the transposition table (which refers to the plies to mate/be mated
|
||||||
// from current position) to "plies to mate/be mated from the root".
|
// from current position) to "plies to mate/be mated from the root".
|
||||||
|
|
||||||
Value value_from_tt(Value v, int ply) {
|
Value value_from_tt(Value v, int ply) {
|
||||||
|
@ -1322,9 +1323,9 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
|
|
||||||
|
|
||||||
// allows() tests whether the 'first' move at previous ply somehow makes the
|
// allows() tests whether the 'first' move at previous ply somehow makes the
|
||||||
// 'second' move possible, for instance if the moving piece is the same in
|
// 'second' move possible e.g. if the moving piece is the same in both moves.
|
||||||
// both moves. Normally the second move is the threat (the best move returned
|
// Normally the second move is the threat (the best move returned from a null
|
||||||
// from a null search that fails low).
|
// search that fails low).
|
||||||
|
|
||||||
bool allows(const Position& pos, Move first, Move second) {
|
bool allows(const Position& pos, Move first, Move second) {
|
||||||
|
|
||||||
|
@ -1338,7 +1339,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
Square m1to = to_sq(first);
|
Square m1to = to_sq(first);
|
||||||
Square m2to = to_sq(second);
|
Square m2to = to_sq(second);
|
||||||
|
|
||||||
// The piece is the same or second's destination was vacated by the first move
|
// The piece is the same or second's destination was vacated by the first move.
|
||||||
// We exclude the trivial case where a sliding piece does in two moves what
|
// We exclude the trivial case where a sliding piece does in two moves what
|
||||||
// it could do in one move: eg. Ra1a2, Ra2a3.
|
// it could do in one move: eg. Ra1a2, Ra2a3.
|
||||||
if ( m2to == m1from
|
if ( m2to == m1from
|
||||||
|
@ -1383,8 +1384,8 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
if (m1from == m2to)
|
if (m1from == m2to)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// If the threatened piece has value less than or equal to the value of the
|
// If the threatened piece has a value less than or equal to the value of
|
||||||
// threat piece, don't prune moves which defend it.
|
// the threat piece, don't prune moves which defend it.
|
||||||
if ( pos.capture(second)
|
if ( pos.capture(second)
|
||||||
&& ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)]
|
&& ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)]
|
||||||
|| type_of(pos.piece_on(m2from)) == KING))
|
|| type_of(pos.piece_on(m2from)) == KING))
|
||||||
|
@ -1393,7 +1394,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from;
|
Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from;
|
||||||
Piece pc = pos.piece_on(m1from);
|
Piece pc = pos.piece_on(m1from);
|
||||||
|
|
||||||
// The moved piece attacks the square 'tto' ?
|
// Does the moved piece attack the square 'm2to' ?
|
||||||
if (attacks_bb(pc, m1to, occ) & m2to)
|
if (attacks_bb(pc, m1to, occ) & m2to)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -1401,7 +1402,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, ROOK))
|
Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, ROOK))
|
||||||
| (attacks_bb<BISHOP>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, BISHOP));
|
| (attacks_bb<BISHOP>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, BISHOP));
|
||||||
|
|
||||||
// Verify attackers are triggered by our move and not already existing
|
// Verify attackers are triggered by our move and not already exist
|
||||||
if (unlikely(xray) && (xray & ~pos.attacks_from<QUEEN>(m2to)))
|
if (unlikely(xray) && (xray & ~pos.attacks_from<QUEEN>(m2to)))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1414,8 +1415,8 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// When playing with strength handicap choose best move among the MultiPV set
|
// When playing with a strength handicap, choose best move among the MultiPV
|
||||||
// using a statistical rule dependent on 'level'. Idea by Heinz van Saanen.
|
// set using a statistical rule dependent on 'level'. Idea by Heinz van Saanen.
|
||||||
|
|
||||||
Move Skill::pick_move() {
|
Move Skill::pick_move() {
|
||||||
|
|
||||||
|
@ -1432,7 +1433,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
best = MOVE_NONE;
|
best = MOVE_NONE;
|
||||||
|
|
||||||
// Choose best move. For each move score we add two terms both dependent on
|
// Choose best move. For each move score we add two terms both dependent on
|
||||||
// weakness, one deterministic and bigger for weaker moves, and one random,
|
// weakness. One deterministic and bigger for weaker moves, and one random,
|
||||||
// then we choose the move with the resulting highest score.
|
// then we choose the move with the resulting highest score.
|
||||||
for (size_t i = 0; i < PVSize; ++i)
|
for (size_t i = 0; i < PVSize; ++i)
|
||||||
{
|
{
|
||||||
|
@ -1456,9 +1457,9 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// uci_pv() formats PV information according to UCI protocol. UCI requires
|
// uci_pv() formats PV information according to the UCI protocol. UCI
|
||||||
// to send all the PV lines also if are still to be searched and so refer to
|
// requires that all (if any) unsearched PV lines are sent using a previous
|
||||||
// the previous search score.
|
// search score.
|
||||||
|
|
||||||
string uci_pv(const Position& pos, int depth, Value alpha, Value beta) {
|
string uci_pv(const Position& pos, int depth, Value alpha, Value beta) {
|
||||||
|
|
||||||
|
@ -1504,9 +1505,9 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||||
|
|
||||||
|
|
||||||
/// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table.
|
/// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table.
|
||||||
/// We consider also failing high nodes and not only BOUND_EXACT nodes so to
|
/// We also consider both failing high nodes and BOUND_EXACT nodes here to
|
||||||
/// allow to always have a ponder move even when we fail high at root, and a
|
/// ensure that we have a ponder move even when we fail high at root. This
|
||||||
/// long PV to print that is important for position analysis.
|
/// results in a long PV to print that is important for position analysis.
|
||||||
|
|
||||||
void RootMove::extract_pv_from_tt(Position& pos) {
|
void RootMove::extract_pv_from_tt(Position& pos) {
|
||||||
|
|
||||||
|
@ -1568,7 +1569,7 @@ void RootMove::insert_pv_in_tt(Position& pos) {
|
||||||
void Thread::idle_loop() {
|
void Thread::idle_loop() {
|
||||||
|
|
||||||
// Pointer 'this_sp' is not null only if we are called from split(), and not
|
// Pointer 'this_sp' is not null only if we are called from split(), and not
|
||||||
// at the thread creation. So it means we are the split point's master.
|
// at the thread creation. This means we are the split point's master.
|
||||||
SplitPoint* this_sp = splitPointsSize ? activeSplitPoint : NULL;
|
SplitPoint* this_sp = splitPointsSize ? activeSplitPoint : NULL;
|
||||||
|
|
||||||
assert(!this_sp || (this_sp->masterThread == this && searching));
|
assert(!this_sp || (this_sp->masterThread == this && searching));
|
||||||
|
@ -1595,7 +1596,7 @@ void Thread::idle_loop() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do sleep after retesting sleep conditions under lock protection, in
|
// Do sleep after retesting sleep conditions under lock protection. In
|
||||||
// particular we need to avoid a deadlock in case a master thread has,
|
// particular we need to avoid a deadlock in case a master thread has,
|
||||||
// in the meanwhile, allocated us and sent the notify_one() call before
|
// in the meanwhile, allocated us and sent the notify_one() call before
|
||||||
// we had the chance to grab the lock.
|
// we had the chance to grab the lock.
|
||||||
|
@ -1651,8 +1652,8 @@ void Thread::idle_loop() {
|
||||||
sp->slavesMask &= ~(1ULL << idx);
|
sp->slavesMask &= ~(1ULL << idx);
|
||||||
sp->nodes += pos.nodes_searched();
|
sp->nodes += pos.nodes_searched();
|
||||||
|
|
||||||
// Wake up master thread so to allow it to return from the idle loop
|
// Wake up the master thread so to allow it to return from the idle
|
||||||
// in case we are the last slave of the split point.
|
// loop in case we are the last slave of the split point.
|
||||||
if ( Threads.sleepWhileIdle
|
if ( Threads.sleepWhileIdle
|
||||||
&& this != sp->masterThread
|
&& this != sp->masterThread
|
||||||
&& !sp->slavesMask)
|
&& !sp->slavesMask)
|
||||||
|
@ -1661,10 +1662,10 @@ void Thread::idle_loop() {
|
||||||
sp->masterThread->notify_one();
|
sp->masterThread->notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
// After releasing the lock we cannot access anymore any SplitPoint
|
// After releasing the lock we can't access any SplitPoint related data
|
||||||
// related data in a safe way becuase it could have been released under
|
// in a safe way because it could have been released under our feet by
|
||||||
// our feet by the sp master. Also accessing other Thread objects is
|
// the sp master. Also accessing other Thread objects is unsafe because
|
||||||
// unsafe because if we are exiting there is a chance are already freed.
|
// if we are exiting there is a chance that they are already freed.
|
||||||
sp->mutex.unlock();
|
sp->mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1683,8 +1684,8 @@ void Thread::idle_loop() {
|
||||||
|
|
||||||
|
|
||||||
/// check_time() is called by the timer thread when the timer triggers. It is
|
/// check_time() is called by the timer thread when the timer triggers. It is
|
||||||
/// used to print debug info and, more important, to detect when we are out of
|
/// used to print debug info and, more importantly, to detect when we are out of
|
||||||
/// available time and so stop the search.
|
/// available time and thus stop the search.
|
||||||
|
|
||||||
void check_time() {
|
void check_time() {
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ struct LimitsType {
|
||||||
|
|
||||||
|
|
||||||
/// The SignalsType struct stores volatile flags updated during the search
|
/// The SignalsType struct stores volatile flags updated during the search
|
||||||
/// typically in an async fashion, for instance to stop the search by the GUI.
|
/// typically in an async fashion e.g. to stop the search by the GUI.
|
||||||
|
|
||||||
struct SignalsType {
|
struct SignalsType {
|
||||||
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
|
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace {
|
||||||
|
|
||||||
|
|
||||||
// Helpers to launch a thread after creation and joining before delete. Must be
|
// Helpers to launch a thread after creation and joining before delete. Must be
|
||||||
// outside Thread c'tor and d'tor because object shall be fully initialized
|
// outside Thread c'tor and d'tor because the object will be fully initialized
|
||||||
// when start_routine (and hence virtual idle_loop) is called and when joining.
|
// when start_routine (and hence virtual idle_loop) is called and when joining.
|
||||||
|
|
||||||
template<typename T> T* new_thread() {
|
template<typename T> T* new_thread() {
|
||||||
|
@ -77,8 +77,8 @@ void ThreadBase::wait_for(volatile const bool& b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Thread c'tor just inits data but does not launch any thread of execution that
|
// Thread c'tor just inits data and does not launch any execution thread.
|
||||||
// instead will be started only upon c'tor returns.
|
// Such a thread will only be started when c'tor returns.
|
||||||
|
|
||||||
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
|
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ void TimerThread::idle_loop() {
|
||||||
|
|
||||||
|
|
||||||
// MainThread::idle_loop() is where the main thread is parked waiting to be started
|
// MainThread::idle_loop() is where the main thread is parked waiting to be started
|
||||||
// when there is a new search. Main thread will launch all the slave threads.
|
// when there is a new search. The main thread will launch all the slave threads.
|
||||||
|
|
||||||
void MainThread::idle_loop() {
|
void MainThread::idle_loop() {
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ void MainThread::idle_loop() {
|
||||||
|
|
||||||
while (!thinking && !exit)
|
while (!thinking && !exit)
|
||||||
{
|
{
|
||||||
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
|
Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed
|
||||||
sleepCondition.wait(mutex);
|
sleepCondition.wait(mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ bool Thread::available_to(const Thread* master) const {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Make a local copy to be sure doesn't become zero under our feet while
|
// Make a local copy to be sure doesn't become zero under our feet while
|
||||||
// testing next condition and so leading to an out of bound access.
|
// testing next condition and so leading to an out of bounds access.
|
||||||
int size = splitPointsSize;
|
int size = splitPointsSize;
|
||||||
|
|
||||||
// No split points means that the thread is available as a slave for any
|
// No split points means that the thread is available as a slave for any
|
||||||
|
@ -206,8 +206,9 @@ void ThreadPool::exit() {
|
||||||
|
|
||||||
// read_uci_options() updates internal threads parameters from the corresponding
|
// read_uci_options() updates internal threads parameters from the corresponding
|
||||||
// UCI options and creates/destroys threads to match the requested number. Thread
|
// UCI options and creates/destroys threads to match the requested number. Thread
|
||||||
// objects are dynamically allocated to avoid creating in advance all possible
|
// objects are dynamically allocated to avoid creating all possible threads
|
||||||
// threads, with included pawns and material tables, if only few are used.
|
// in advance (which include pawns and material tables), even if only a few
|
||||||
|
// are to be used.
|
||||||
|
|
||||||
void ThreadPool::read_uci_options() {
|
void ThreadPool::read_uci_options() {
|
||||||
|
|
||||||
|
@ -323,8 +324,9 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
|
||||||
|
|
||||||
Thread::idle_loop(); // Force a call to base class idle_loop()
|
Thread::idle_loop(); // Force a call to base class idle_loop()
|
||||||
|
|
||||||
// In helpful master concept a master can help only a sub-tree of its split
|
// In the helpful master concept, a master can help only a sub-tree of its
|
||||||
// point, and because here is all finished is not possible master is booked.
|
// split point and because everything is finished here, it's not possible
|
||||||
|
// for the master to be booked.
|
||||||
assert(!searching);
|
assert(!searching);
|
||||||
assert(!activePosition);
|
assert(!activePosition);
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ struct TimerThread : public ThreadBase {
|
||||||
|
|
||||||
|
|
||||||
/// ThreadPool struct handles all the threads related stuff like init, starting,
|
/// ThreadPool struct handles all the threads related stuff like init, starting,
|
||||||
/// parking and, the most important, launching a slave thread at a split point.
|
/// parking and, most importantly, launching a slave thread at a split point.
|
||||||
/// All the access to shared thread data is done through this class.
|
/// All the access to shared thread data is done through this class.
|
||||||
|
|
||||||
struct ThreadPool : public std::vector<Thread*> {
|
struct ThreadPool : public std::vector<Thread*> {
|
||||||
|
|
|
@ -112,11 +112,11 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u
|
||||||
unstablePVExtraTime = 0;
|
unstablePVExtraTime = 0;
|
||||||
optimumSearchTime = maximumSearchTime = limits.time[us];
|
optimumSearchTime = maximumSearchTime = limits.time[us];
|
||||||
|
|
||||||
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
|
// We calculate optimum time usage for different hypothetical "moves to go"-values and choose the
|
||||||
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
|
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
|
||||||
for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); ++hypMTG)
|
for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); ++hypMTG)
|
||||||
{
|
{
|
||||||
// Calculate thinking time for hypothetic "moves to go"-value
|
// Calculate thinking time for hypothetical "moves to go"-value
|
||||||
hypMyTime = limits.time[us]
|
hypMyTime = limits.time[us]
|
||||||
+ limits.inc[us] * (hypMTG - 1)
|
+ limits.inc[us] * (hypMTG - 1)
|
||||||
- emergencyBaseTime
|
- emergencyBaseTime
|
||||||
|
|
10
src/tt.cpp
10
src/tt.cpp
|
@ -83,11 +83,11 @@ const TTEntry* TranspositionTable::probe(const Key key) const {
|
||||||
|
|
||||||
/// TranspositionTable::store() writes a new entry containing position key and
|
/// TranspositionTable::store() writes a new entry containing position key and
|
||||||
/// valuable information of current position. The lowest order bits of position
|
/// valuable information of current position. The lowest order bits of position
|
||||||
/// key are used to decide on which cluster the position will be placed.
|
/// key are used to decide in which cluster the position will be placed.
|
||||||
/// When a new entry is written and there are no empty entries available in cluster,
|
/// When a new entry is written and there are no empty entries available in the
|
||||||
/// it replaces the least valuable of entries. A TTEntry t1 is considered to be
|
/// cluster, it replaces the least valuable of the entries. A TTEntry t1 is considered
|
||||||
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
|
/// to be more valuable than a TTEntry t2 if t1 is from the current search and t2
|
||||||
/// a previous search, or if the depth of t1 is bigger than the depth of t2.
|
/// is from a previous search, or if the depth of t1 is bigger than the depth of t2.
|
||||||
|
|
||||||
void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) {
|
void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) {
|
||||||
|
|
||||||
|
|
8
src/tt.h
8
src/tt.h
|
@ -66,9 +66,9 @@ private:
|
||||||
|
|
||||||
/// A TranspositionTable consists of a power of 2 number of clusters and each
|
/// A TranspositionTable consists of a power of 2 number of clusters and each
|
||||||
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
|
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
|
||||||
/// contains information of exactly one position. Size of a cluster shall not be
|
/// contains information of exactly one position. The size of a cluster should
|
||||||
/// bigger than a cache line size. In case it is less, it should be padded to
|
/// not be bigger than a cache line size. In case it is less, it should be padded
|
||||||
/// guarantee always aligned accesses.
|
/// to guarantee always aligned accesses.
|
||||||
|
|
||||||
class TranspositionTable {
|
class TranspositionTable {
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ inline TTEntry* TranspositionTable::first_entry(const Key key) const {
|
||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::refresh() updates the 'generation' value of the TTEntry
|
/// TranspositionTable::refresh() updates the 'generation' value of the TTEntry
|
||||||
/// to avoid aging. Normally called after a TT hit.
|
/// to avoid aging. It is normally called after a TT hit.
|
||||||
|
|
||||||
inline void TranspositionTable::refresh(const TTEntry* tte) const {
|
inline void TranspositionTable::refresh(const TTEntry* tte) const {
|
||||||
|
|
||||||
|
|
11
src/types.h
11
src/types.h
|
@ -26,7 +26,7 @@
|
||||||
/// For Windows, part of the configuration is detected automatically, but some
|
/// For Windows, part of the configuration is detected automatically, but some
|
||||||
/// switches need to be set manually:
|
/// switches need to be set manually:
|
||||||
///
|
///
|
||||||
/// -DNDEBUG | Disable debugging mode. Use always.
|
/// -DNDEBUG | Disable debugging mode. Always use this.
|
||||||
///
|
///
|
||||||
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want
|
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want
|
||||||
/// | the executable to run on some very old machines.
|
/// | the executable to run on some very old machines.
|
||||||
|
@ -236,10 +236,11 @@ enum Rank {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Score enum keeps a midgame and an endgame value in a single integer (enum),
|
/// The Score enum stores a midgame and an endgame value in a single integer
|
||||||
/// first LSB 16 bits are used to store endgame value, while upper bits are used
|
/// (enum). The least significant 16 bits are used to store the endgame value
|
||||||
/// for midgame value. Compiler is free to choose the enum type as long as can
|
/// and the upper 16 bits are used to store the midgame value. The compiler is
|
||||||
/// keep its data, so ensure Score to be an integer type.
|
/// free to choose the enum type as long as it can store the data, so we
|
||||||
|
/// ensure that Score is an integer type by assigning some big int values.
|
||||||
enum Score {
|
enum Score {
|
||||||
SCORE_ZERO,
|
SCORE_ZERO,
|
||||||
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
|
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
|
||||||
|
|
11
src/uci.cpp
11
src/uci.cpp
|
@ -39,8 +39,9 @@ namespace {
|
||||||
// FEN string of the initial position, normal chess
|
// FEN string of the initial position, normal chess
|
||||||
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||||
|
|
||||||
// Keep track of position keys along the setup moves (from start position to the
|
// Keep a track of the position keys along the setup moves (from the start position
|
||||||
// position just before to start searching). Needed by repetition draw detection.
|
// to the position just before the search starts). This is needed by the repetition
|
||||||
|
// draw detection code.
|
||||||
Search::StateStackPtr SetupStates;
|
Search::StateStackPtr SetupStates;
|
||||||
|
|
||||||
void setoption(istringstream& up);
|
void setoption(istringstream& up);
|
||||||
|
@ -69,11 +70,11 @@ void UCI::loop(const string& args) {
|
||||||
|
|
||||||
if (token == "quit" || token == "stop" || token == "ponderhit")
|
if (token == "quit" || token == "stop" || token == "ponderhit")
|
||||||
{
|
{
|
||||||
// GUI sends 'ponderhit' to tell us to ponder on the same move the
|
// The GUI sends 'ponderhit' to tell us to ponder on the same move the
|
||||||
// opponent has played. In case Signals.stopOnPonderhit is set we are
|
// opponent has played. In case Signals.stopOnPonderhit is set we are
|
||||||
// waiting for 'ponderhit' to stop the search (for instance because we
|
// waiting for 'ponderhit' to stop the search (for instance because we
|
||||||
// already ran out of time), otherwise we should continue searching but
|
// already ran out of time), otherwise we should continue searching but
|
||||||
// switching from pondering to normal search.
|
// switch from pondering to normal search.
|
||||||
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
|
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
|
||||||
{
|
{
|
||||||
Search::Signals.stop = true;
|
Search::Signals.stop = true;
|
||||||
|
@ -121,7 +122,7 @@ void UCI::loop(const string& args) {
|
||||||
|
|
||||||
} while (token != "quit" && args.empty()); // Args have one-shot behaviour
|
} while (token != "quit" && args.empty()); // Args have one-shot behaviour
|
||||||
|
|
||||||
Threads.wait_for_think_finished(); // Cannot quit while search is running
|
Threads.wait_for_think_finished(); // Cannot quit whilst the search is running
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// init() initializes the UCI options to their hard coded default values
|
/// init() initializes the UCI options to their hard-coded default values
|
||||||
|
|
||||||
void init(OptionsMap& o) {
|
void init(OptionsMap& o) {
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue