DroidFish: Updated stockfish engine to version 4.

This commit is contained in:
Peter Osterlund
2013-08-20 18:02:33 +00:00
parent 96a8ec79ac
commit bfc5782f52
41 changed files with 1477 additions and 1673 deletions

View File

@@ -4,12 +4,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE := stockfish LOCAL_MODULE := stockfish
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
evaluate.cpp notation.cpp search.cpp \ benchmark.cpp book.cpp main.cpp movegen.cpp pawns.cpp thread.cpp uci.cpp \
benchmark.cpp movegen.cpp tt.cpp \ bitbase.cpp endgame.cpp material.cpp movepick.cpp position.cpp timeman.cpp ucioption.cpp \
bitbase.cpp main.cpp movepick.cpp uci.cpp \ bitboard.cpp evaluate.cpp misc.cpp notation.cpp search.cpp tt.cpp
bitboard.cpp pawns.cpp ucioption.cpp \
book.cpp material.cpp position.cpp \
endgame.cpp misc.cpp timeman.cpp thread.cpp
LOCAL_CFLAGS := -DNO_PREFETCH=1 -O2 LOCAL_CFLAGS := -DNO_PREFETCH=1 -O2

View File

@@ -118,7 +118,7 @@ void benchmark(const Position& current, istream& is) {
for (size_t i = 0; i < fens.size(); i++) for (size_t i = 0; i < fens.size(); i++)
{ {
Position pos(fens[i], Options["UCI_Chess960"], Threads.main_thread()); Position pos(fens[i], Options["UCI_Chess960"], Threads.main());
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;

View File

@@ -39,9 +39,9 @@ namespace {
// bit 6-11: black king square (from SQ_A1 to SQ_H8) // bit 6-11: black king square (from SQ_A1 to SQ_H8)
// bit 12: side to move (WHITE or BLACK) // bit 12: side to move (WHITE or BLACK)
// bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn 6 - rank (from 6 - RANK_7 to 6 - RANK_2) // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
unsigned index(Color us, Square bksq, Square wksq, Square psq) { unsigned index(Color us, Square bksq, Square wksq, Square psq) {
return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((6 - rank_of(psq)) << 15); return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((RANK_7 - rank_of(psq)) << 15);
} }
enum Result { enum Result {
@@ -107,10 +107,10 @@ namespace {
Result KPKPosition::classify_leaf(unsigned idx) { Result KPKPosition::classify_leaf(unsigned idx) {
wksq = Square((idx >> 0) & 0x3F); wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F); bksq = Square((idx >> 6) & 0x3F);
us = Color((idx >> 12) & 0x01); us = Color ((idx >> 12) & 0x01);
psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15)); psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15));
// Check if two pieces are on the same square or if a king can be captured // Check if two pieces are on the same square or if a king can be captured
if ( wksq == psq || wksq == bksq || bksq == psq if ( wksq == psq || wksq == bksq || bksq == psq
@@ -148,12 +148,14 @@ namespace {
// as WIN, the position is classified WIN otherwise the current position is // as WIN, the position is classified WIN otherwise the current position is
// classified UNKNOWN. // classified UNKNOWN.
const Color Them = (Us == WHITE ? BLACK : WHITE);
Result r = INVALID; Result r = INVALID;
Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq]; Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq];
while (b) while (b)
r |= Us == WHITE ? db[index(~Us, bksq, pop_lsb(&b), psq)] r |= Us == WHITE ? db[index(Them, bksq, pop_lsb(&b), psq)]
: db[index(~Us, pop_lsb(&b), wksq, psq)]; : db[index(Them, pop_lsb(&b), wksq, psq)];
if (Us == WHITE && rank_of(psq) < RANK_7) if (Us == WHITE && rank_of(psq) < RANK_7)
{ {

View File

@@ -42,14 +42,13 @@ Bitboard SquareBB[SQUARE_NB];
Bitboard FileBB[FILE_NB]; Bitboard FileBB[FILE_NB];
Bitboard RankBB[RANK_NB]; Bitboard RankBB[RANK_NB];
Bitboard AdjacentFilesBB[FILE_NB]; Bitboard AdjacentFilesBB[FILE_NB];
Bitboard ThisAndAdjacentFilesBB[FILE_NB];
Bitboard InFrontBB[COLOR_NB][RANK_NB]; Bitboard InFrontBB[COLOR_NB][RANK_NB];
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingsBB[SQUARE_NB][8]; Bitboard DistanceRingsBB[SQUARE_NB][8];
Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB]; Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
int SquareDistance[SQUARE_NB][SQUARE_NB]; int SquareDistance[SQUARE_NB][SQUARE_NB];
@@ -84,7 +83,7 @@ namespace {
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. /// lsb()/msb() finds the least/most significant bit in a nonzero 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 nonzero bitboard.
#if !defined(USE_BSFQ) #ifndef USE_BSFQ
Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; } Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; }
@@ -123,7 +122,7 @@ Square msb(Bitboard b) {
return (Square)(result + MS1BTable[b32]); return (Square)(result + MS1BTable[b32]);
} }
#endif // !defined(USE_BSFQ) #endif // ifndef USE_BSFQ
/// Bitboards::print() prints a bitboard in an easily readable format to the /// Bitboards::print() prints a bitboard in an easily readable format to the
@@ -171,10 +170,7 @@ void Bitboards::init() {
} }
for (File f = FILE_A; f <= FILE_H; f++) for (File f = FILE_A; f <= FILE_H; f++)
{
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0); AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndAdjacentFilesBB[f] = FileBB[f] | AdjacentFilesBB[f];
}
for (Rank r = RANK_1; r < RANK_8; r++) for (Rank r = RANK_1; r < RANK_8; r++)
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]); InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
@@ -183,19 +179,17 @@ void Bitboards::init() {
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
{ {
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)]; ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
PassedPawnMask[c][s] = InFrontBB[c][rank_of(s)] & ThisAndAdjacentFilesBB[file_of(s)]; PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
AttackSpanMask[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)]; PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s];
} }
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
{
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2)); SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
if (s1 != s2)
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) DistanceRingsBB[s1][SquareDistance[s1][s2] - 1] |= s2;
for (int d = 1; d < 8; d++) }
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (SquareDistance[s1][s2] == d)
DistanceRingsBB[s1][d - 1] |= s2;
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 }, int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } }; {}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
@@ -322,7 +316,7 @@ namespace {
do magics[s] = pick_random(rk, booster); do magics[s] = pick_random(rk, booster);
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6); while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
memset(attacks[s], 0, size * sizeof(Bitboard)); std::memset(attacks[s], 0, size * sizeof(Bitboard));
// A good magic must map every possible occupancy to an index that // A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database. // looks up the correct sliding attack in the attacks[s] database.

View File

@@ -18,7 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(BITBOARD_H_INCLUDED) #ifndef BITBOARD_H_INCLUDED
#define BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED
#include "types.h" #include "types.h"
@@ -37,6 +37,24 @@ bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
} }
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
extern Bitboard RMasks[SQUARE_NB]; extern Bitboard RMasks[SQUARE_NB];
@@ -53,17 +71,18 @@ extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard FileBB[FILE_NB]; extern Bitboard FileBB[FILE_NB];
extern Bitboard RankBB[RANK_NB]; extern Bitboard RankBB[RANK_NB];
extern Bitboard AdjacentFilesBB[FILE_NB]; extern Bitboard AdjacentFilesBB[FILE_NB];
extern Bitboard ThisAndAdjacentFilesBB[FILE_NB];
extern Bitboard InFrontBB[COLOR_NB][RANK_NB]; extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingsBB[SQUARE_NB][8]; extern Bitboard DistanceRingsBB[SQUARE_NB][8];
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB]; extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
const Bitboard BlackSquares = 0xAA55AA55AA55AA55ULL; extern int SquareDistance[SQUARE_NB][SQUARE_NB];
const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
/// Overloads of bitwise operators between a Bitboard and a Square for testing /// Overloads of bitwise operators between a Bitboard and a Square for testing
/// whether a given bit is set in a bitboard, and for setting and clearing bits. /// whether a given bit is set in a bitboard, and for setting and clearing bits.
@@ -88,13 +107,34 @@ inline Bitboard operator^(Bitboard b, Square s) {
return b ^ SquareBB[s]; return b ^ SquareBB[s];
} }
/// more_than_one() returns true if in 'b' there is more than one bit set
inline bool more_than_one(Bitboard b) { inline bool more_than_one(Bitboard b) {
return b & (b - 1); return b & (b - 1);
} }
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline int file_distance(Square s1, Square s2) {
return abs(file_of(s1) - file_of(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(rank_of(s1) - rank_of(s2));
}
/// shift_bb() moves bitboard one step along direction Delta. Mainly for pawns.
template<Square Delta>
inline Bitboard shift_bb(Bitboard b) {
return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8
: Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7
: Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9
: 0;
}
/// rank_bb() and file_bb() take a file or a square as input and return /// rank_bb() and file_bb() take a file or a square as input and return
/// a bitboard representing all squares on the given file or rank. /// a bitboard representing all squares on the given file or rank.
@@ -116,7 +156,7 @@ inline Bitboard file_bb(Square s) {
} }
/// adjacent_files_bb takes a file as input and returns a bitboard representing /// adjacent_files_bb() takes a file as input and returns a bitboard representing
/// all squares on the adjacent files. /// all squares on the adjacent files.
inline Bitboard adjacent_files_bb(File f) { inline Bitboard adjacent_files_bb(File f) {
@@ -124,30 +164,17 @@ inline Bitboard adjacent_files_bb(File f) {
} }
/// this_and_adjacent_files_bb takes a file as input and returns a bitboard /// in_front_bb() takes a color and a rank as input, and returns a bitboard
/// representing all squares on the given and adjacent files. /// representing all the squares on all ranks in front of the rank, from the
/// given color's point of view. For instance, in_front_bb(BLACK, RANK_3) will
inline Bitboard this_and_adjacent_files_bb(File f) { /// give all squares on ranks 1 and 2.
return ThisAndAdjacentFilesBB[f];
}
/// in_front_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks in front of the rank
/// (or square), from the given color's point of view. For instance,
/// in_front_bb(WHITE, RANK_5) will give all squares on ranks 6, 7 and 8, while
/// in_front_bb(BLACK, SQ_D3) will give all squares on ranks 1 and 2.
inline Bitboard in_front_bb(Color c, Rank r) { inline Bitboard in_front_bb(Color c, Rank r) {
return InFrontBB[c][r]; return InFrontBB[c][r];
} }
inline Bitboard in_front_bb(Color c, Square s) {
return InFrontBB[c][rank_of(s)];
}
/// between_bb() returns a bitboard representing all squares between two squares.
/// between_bb returns a bitboard representing all squares between two squares.
/// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for /// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for
/// square d5 and e6 set. If s1 and s2 are not on the same line, file or diagonal, /// square d5 and e6 set. If s1 and s2 are not on the same line, file or diagonal,
/// 0 is returned. /// 0 is returned.
@@ -157,7 +184,7 @@ inline Bitboard between_bb(Square s1, Square s2) {
} }
/// forward_bb takes a color and a square as input, and returns a bitboard /// forward_bb() takes a color and a square as input, and returns a bitboard
/// representing all squares along the line in front of the square, from the /// representing all squares along the line in front of the square, from the
/// point of view of the given color. Definition of the table is: /// point of view of the given color. Definition of the table is:
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s) /// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
@@ -167,27 +194,35 @@ inline Bitboard forward_bb(Color c, Square s) {
} }
/// passed_pawn_mask takes a color and a square as input, and returns a /// pawn_attack_span() takes a color and a square as input, and returns a bitboard
/// representing all squares that can be attacked by a pawn of the given color
/// when it moves along its file starting from the given square. Definition is:
/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
inline Bitboard pawn_attack_span(Color c, Square s) {
return PawnAttackSpan[c][s];
}
/// passed_pawn_mask() takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on /// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. Definition of the table is: /// the given square is a passed pawn. Definition of the table is:
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_adjacent_files_bb(s) /// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s)
inline Bitboard passed_pawn_mask(Color c, Square s) { inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s]; return PassedPawnMask[c][s];
} }
/// attack_span_mask takes a color and a square as input, and returns a bitboard /// squares_of_color() returns a bitboard representing all squares with the same
/// representing all squares that can be attacked by a pawn of the given color /// color of the given square.
/// when it moves along its file starting from the given square. Definition is:
/// AttackSpanMask[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
inline Bitboard attack_span_mask(Color c, Square s) { inline Bitboard squares_of_color(Square s) {
return AttackSpanMask[c][s]; return DarkSquares & s ? DarkSquares : ~DarkSquares;
} }
/// squares_aligned returns true if the squares s1, s2 and s3 are aligned /// squares_aligned() returns true if the squares s1, s2 and s3 are aligned
/// either on a straight or on a diagonal line. /// either on a straight or on a diagonal line.
inline bool squares_aligned(Square s1, Square s2, Square s3) { inline bool squares_aligned(Square s1, Square s2, Square s3) {
@@ -196,14 +231,6 @@ inline bool squares_aligned(Square s1, Square s2, Square s3) {
} }
/// same_color_squares() returns a bitboard representing all squares with
/// the same color of the given square.
inline Bitboard same_color_squares(Square s) {
return BlackSquares & s ? BlackSquares : ~BlackSquares;
}
/// Functions for computing sliding attack bitboards. Function attacks_bb() takes /// Functions for computing sliding attack bitboards. Function attacks_bb() takes
/// a square and a bitboard of occupied squares as input, and returns a bitboard /// a square and a bitboard of occupied squares as input, and returns a bitboard
/// representing all squares attacked by Pt (bishop or rook) on the given square. /// representing all squares attacked by Pt (bishop or rook) on the given square.
@@ -231,7 +258,7 @@ inline Bitboard attacks_bb(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 nonzero 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 nonzero bitboard.
#if defined(USE_BSFQ) #ifdef USE_BSFQ
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
@@ -284,7 +311,7 @@ FORCE_INLINE Square pop_lsb(Bitboard* b) {
return s; return s;
} }
#else // if !defined(USE_BSFQ) #else // if defined(USE_BSFQ)
extern Square msb(Bitboard b); extern Square msb(Bitboard b);
extern Square lsb(Bitboard b); extern Square lsb(Bitboard b);
@@ -292,4 +319,4 @@ extern Square pop_lsb(Bitboard* b);
#endif #endif
#endif // !defined(BITBOARD_H_INCLUDED) #endif // #ifndef BITBOARD_H_INCLUDED

View File

@@ -18,7 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(BITCOUNT_H_INCLUDED) #ifndef BITCOUNT_H_INCLUDED
#define BITCOUNT_H_INCLUDED #define BITCOUNT_H_INCLUDED
#include <cassert> #include <cassert>
@@ -81,7 +81,7 @@ inline int popcount<CNT_32_MAX15>(Bitboard b) {
template<> template<>
inline int popcount<CNT_HW_POPCNT>(Bitboard b) { inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT) #ifndef USE_POPCNT
assert(false); assert(false);
return b != 0; // Avoid 'b not used' warning return b != 0; // Avoid 'b not used' warning
@@ -102,4 +102,4 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#endif #endif
} }
#endif // !defined(BITCOUNT_H_INCLUDED) #endif // #ifndef BITCOUNT_H_INCLUDED

View File

@@ -436,9 +436,9 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1)); move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
// Add 'special move' flags and verify it is legal // Add 'special move' flags and verify it is legal
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml) for (MoveList<LEGAL> it(pos); *it; ++it)
if (move == (ml.move() ^ type_of(ml.move()))) if (move == (*it ^ type_of(*it)))
return ml.move(); return *it;
return MOVE_NONE; return MOVE_NONE;
} }

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(BOOK_H_INCLUDED) #ifndef BOOK_H_INCLUDED
#define BOOK_H_INCLUDED #define BOOK_H_INCLUDED
#include <fstream> #include <fstream>
@@ -42,4 +42,4 @@ private:
std::string fileName; std::string fileName;
}; };
#endif // !defined(BOOK_H_INCLUDED) #endif // #ifndef BOOK_H_INCLUDED

View File

@@ -89,7 +89,10 @@ namespace {
Endgames::Endgames() { Endgames::Endgames() {
add<KK>("KK");
add<KPK>("KPK"); add<KPK>("KPK");
add<KBK>("KBK");
add<KNK>("KNK");
add<KNNK>("KNNK"); add<KNNK>("KNNK");
add<KBNK>("KBNK"); add<KBNK>("KBNK");
add<KRKP>("KRKP"); add<KRKP>("KRKP");
@@ -130,28 +133,25 @@ template<>
Value Endgame<KXK>::operator()(const Position& pos) const { Value Endgame<KXK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO); assert(!pos.count<PAWN>(weakerSide));
assert(!pos.checkers()); // Eval is never called when in check
// Stalemate detection with lone king // Stalemate detection with lone king
if ( pos.side_to_move() == weakerSide if (pos.side_to_move() == weakerSide && !MoveList<LEGAL>(pos).size())
&& !pos.checkers() return VALUE_DRAW;
&& !MoveList<LEGAL>(pos).size()) {
return VALUE_DRAW;
}
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Value result = pos.non_pawn_material(strongerSide) Value result = pos.non_pawn_material(strongerSide)
+ pos.piece_count(strongerSide, PAWN) * PawnValueEg + pos.count<PAWN>(strongerSide) * PawnValueEg
+ MateTable[loserKSq] + MateTable[loserKSq]
+ DistanceBonus[square_distance(winnerKSq, loserKSq)]; + DistanceBonus[square_distance(winnerKSq, loserKSq)];
if ( pos.piece_count(strongerSide, QUEEN) if ( pos.count<QUEEN>(strongerSide)
|| pos.piece_count(strongerSide, ROOK) || pos.count<ROOK>(strongerSide)
|| pos.bishop_pair(strongerSide)) { || pos.bishop_pair(strongerSide))
result += VALUE_KNOWN_WIN; result += VALUE_KNOWN_WIN;
}
return strongerSide == pos.side_to_move() ? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -162,16 +162,16 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
template<> template<>
Value Endgame<KBNK>::operator()(const Position& pos) const { Value Endgame<KBNK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg); assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.count<KNIGHT>(strongerSide) == 1);
assert(pos.count< PAWN>(strongerSide) == 0);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square bishopSq = pos.list<BISHOP>(strongerSide)[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 bishop that cannot reach the above squares we // if we have a bishop that cannot reach the above squares we
@@ -196,8 +196,8 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.count<PAWN>(strongerSide) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<PAWN>(weakerSide ) == 0);
Square wksq, bksq, wpsq; Square wksq, bksq, wpsq;
Color us; Color us;
@@ -206,14 +206,14 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
{ {
wksq = pos.king_square(WHITE); wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK); bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN)[0]; wpsq = pos.list<PAWN>(WHITE)[0];
us = pos.side_to_move(); us = pos.side_to_move();
} }
else else
{ {
wksq = ~pos.king_square(BLACK); wksq = ~pos.king_square(BLACK);
bksq = ~pos.king_square(WHITE); bksq = ~pos.king_square(WHITE);
wpsq = ~pos.piece_list(BLACK, PAWN)[0]; wpsq = ~pos.list<PAWN>(BLACK)[0];
us = ~pos.side_to_move(); us = ~pos.side_to_move();
} }
@@ -241,17 +241,17 @@ template<>
Value Endgame<KRKP>::operator()(const Position& pos) const { Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == 0); assert(pos.non_pawn_material(weakerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 1); assert(pos.count<PAWN>(strongerSide) == 0);
assert(pos.count<PAWN>(weakerSide ) == 1);
Square wksq, wrsq, bksq, bpsq; Square wksq, wrsq, bksq, bpsq;
int tempo = (pos.side_to_move() == strongerSide); int tempo = (pos.side_to_move() == strongerSide);
wksq = pos.king_square(strongerSide); wksq = pos.king_square(strongerSide);
wrsq = pos.piece_list(strongerSide, ROOK)[0];
bksq = pos.king_square(weakerSide); bksq = pos.king_square(weakerSide);
bpsq = pos.piece_list(weakerSide, PAWN)[0]; wrsq = pos.list<ROOK>(strongerSide)[0];
bpsq = pos.list<PAWN>(weakerSide)[0];
if (strongerSide == BLACK) if (strongerSide == BLACK)
{ {
@@ -298,10 +298,10 @@ template<>
Value Endgame<KRKB>::operator()(const Position& pos) const { Value Endgame<KRKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
assert(pos.non_pawn_material(weakerSide) == BishopValueMg); assert(pos.count<BISHOP>(weakerSide ) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count< PAWN>(weakerSide ) == 0);
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.count< PAWN>(strongerSide) == 0);
Value result = Value(MateTable[pos.king_square(weakerSide)]); Value result = Value(MateTable[pos.king_square(weakerSide)]);
return strongerSide == pos.side_to_move() ? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
@@ -314,15 +314,15 @@ template<>
Value Endgame<KRKN>::operator()(const Position& pos) const { Value Endgame<KRKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
assert(pos.non_pawn_material(weakerSide) == KnightValueMg); assert(pos.count<KNIGHT>(weakerSide ) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count< PAWN>(weakerSide ) == 0);
assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.count< PAWN>(strongerSide) == 0);
const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 }; const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 };
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square bnsq = pos.piece_list(weakerSide, KNIGHT)[0]; Square bnsq = pos.list<KNIGHT>(weakerSide)[0];
Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]); Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]);
return strongerSide == pos.side_to_move() ? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -335,13 +335,13 @@ template<>
Value Endgame<KQKP>::operator()(const Position& pos) const { Value Endgame<KQKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == 0); assert(pos.count<PAWN>(strongerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 1); assert(pos.count<PAWN>(weakerSide ) == 1);
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Square pawnSq = pos.piece_list(weakerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(weakerSide)[0];
Value result = QueenValueEg Value result = QueenValueEg
- PawnValueEg - PawnValueEg
@@ -368,9 +368,9 @@ template<>
Value Endgame<KQKR>::operator()(const Position& pos) const { Value Endgame<KQKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.non_pawn_material(weakerSide ) == RookValueMg);
assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.count<PAWN>(strongerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<PAWN>(weakerSide ) == 0);
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
@@ -386,16 +386,16 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
template<> template<>
Value Endgame<KBBKN>::operator()(const Position& pos) const { Value Endgame<KBBKN>::operator()(const Position& pos) const {
assert(pos.piece_count(strongerSide, BISHOP) == 2); assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMg); assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.count<BISHOP>(strongerSide) == 2);
assert(pos.non_pawn_material(weakerSide) == KnightValueMg); assert(pos.count<KNIGHT>(weakerSide ) == 1);
assert(!pos.pieces(PAWN)); assert(!pos.pieces(PAWN));
Value result = BishopValueEg; Value result = BishopValueEg;
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square nsq = pos.piece_list(weakerSide, KNIGHT)[0]; Square nsq = pos.list<KNIGHT>(weakerSide)[0];
// Bonus for attacking king close to defending king // Bonus for attacking king close to defending king
result += Value(DistanceBonus[square_distance(wksq, bksq)]); result += Value(DistanceBonus[square_distance(wksq, bksq)]);
@@ -410,17 +410,13 @@ Value Endgame<KBBKN>::operator()(const Position& pos) const {
} }
/// K and two minors vs K and one or two minors or K and two knights against /// Some cases of trivial draws
/// king alone are always draw. template<> Value Endgame<KK>::operator()(const Position&) const { return VALUE_DRAW; }
template<> template<> Value Endgame<KBK>::operator()(const Position&) const { return VALUE_DRAW; }
Value Endgame<KmmKm>::operator()(const Position&) const { template<> Value Endgame<KNK>::operator()(const Position&) const { return VALUE_DRAW; }
return VALUE_DRAW; template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
} template<> Value Endgame<KmmKm>::operator()(const Position&) const { return VALUE_DRAW; }
template<>
Value Endgame<KNNK>::operator()(const Position&) const {
return VALUE_DRAW;
}
/// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and /// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and
/// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
@@ -430,20 +426,20 @@ template<>
ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.piece_count(strongerSide, PAWN) >= 1); assert(pos.count< PAWN>(strongerSide) >= 1);
// No assertions about the material of weakerSide, because we want draws to // No assertions about the material of weakerSide, because we want draws to
// be detected even when the weaker side has some pawns. // be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(strongerSide, PAWN); Bitboard pawns = pos.pieces(strongerSide, PAWN);
File pawnFile = file_of(pos.piece_list(strongerSide, PAWN)[0]); File pawnFile = file_of(pos.list<PAWN>(strongerSide)[0]);
// All pawns are on a single rook file ? // All pawns are on a single rook file ?
if ( (pawnFile == FILE_A || pawnFile == FILE_H) if ( (pawnFile == FILE_A || pawnFile == FILE_H)
&& !(pawns & ~file_bb(pawnFile))) && !(pawns & ~file_bb(pawnFile)))
{ {
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square bishopSq = pos.list<BISHOP>(strongerSide)[0];
Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8); Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8);
Square kingSq = pos.king_square(weakerSide); Square kingSq = pos.king_square(weakerSide);
@@ -477,7 +473,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
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(weakerSide) == 0 && pos.non_pawn_material(weakerSide) == 0
&& pos.piece_count(weakerSide, PAWN) >= 1) && pos.count<PAWN>(weakerSide) >= 1)
{ {
// Get weaker pawn closest to opponent's queening square // Get weaker pawn closest to opponent's queening square
Bitboard wkPawns = pos.pieces(weakerSide, PAWN); Bitboard wkPawns = pos.pieces(weakerSide, PAWN);
@@ -485,7 +481,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
Square strongerKingSq = pos.king_square(strongerSide); Square strongerKingSq = pos.king_square(strongerSide);
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square bishopSq = pos.list<BISHOP>(strongerSide)[0];
// Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and // Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and
// weaker king can stop opposing opponent's king from penetrating. // weaker king can stop opposing opponent's king from penetrating.
@@ -505,19 +501,19 @@ template<>
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const { ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, QUEEN) == 1); assert(pos.count<QUEEN>(strongerSide) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.count< PAWN>(strongerSide) == 0);
assert(pos.piece_count(weakerSide, ROOK) == 1); assert(pos.count< ROOK>(weakerSide ) == 1);
assert(pos.piece_count(weakerSide, PAWN) >= 1); assert(pos.count< PAWN>(weakerSide ) >= 1);
Square kingSq = pos.king_square(weakerSide); Square kingSq = pos.king_square(weakerSide);
if ( relative_rank(weakerSide, kingSq) <= RANK_2 if ( relative_rank(weakerSide, kingSq) <= RANK_2
&& relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4 && relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4
&& (pos.pieces(weakerSide, ROOK) & rank_bb(relative_rank(weakerSide, RANK_3))) && (pos.pieces(weakerSide, ROOK) & rank_bb(relative_rank(weakerSide, RANK_3)))
&& (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2))) && (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2)))
&& (pos.attacks_from<KING>(kingSq) & pos.pieces(weakerSide, PAWN))) && (pos.attacks_from<KING>(kingSq) & pos.pieces(weakerSide, PAWN)))
{ {
Square rsq = pos.piece_list(weakerSide, ROOK)[0]; Square rsq = pos.list<ROOK>(weakerSide)[0];
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(weakerSide, PAWN)) if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(weakerSide, PAWN))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
@@ -535,15 +531,15 @@ template<>
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const { ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.non_pawn_material(weakerSide) == RookValueMg);
assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.count<PAWN>(strongerSide) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<PAWN>(weakerSide ) == 0);
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square wrsq = pos.piece_list(strongerSide, ROOK)[0];
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square brsq = pos.piece_list(weakerSide, ROOK)[0]; Square wrsq = pos.list<ROOK>(strongerSide)[0];
Square wpsq = pos.list<PAWN>(strongerSide)[0];
Square brsq = pos.list<ROOK>(weakerSide)[0];
// Orient the board in such a way that the stronger side is white, and the // Orient the board in such a way that the stronger side is white, and the
// pawn is on the left half of the board. // pawn is on the left half of the board.
@@ -653,12 +649,12 @@ template<>
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const { ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 2); assert(pos.non_pawn_material(weakerSide) == RookValueMg);
assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.count<PAWN>(strongerSide) == 2);
assert(pos.piece_count(weakerSide, PAWN) == 1); assert(pos.count<PAWN>(weakerSide ) == 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0]; Square wpsq1 = pos.list<PAWN>(strongerSide)[0];
Square wpsq2 = pos.piece_list(strongerSide, PAWN)[1]; Square wpsq2 = pos.list<PAWN>(strongerSide)[1];
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
// Does the stronger side have a passed pawn? // Does the stronger side have a passed pawn?
@@ -691,9 +687,9 @@ template<>
ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const { ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) >= 2); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.count<PAWN>(strongerSide) >= 2);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<PAWN>(weakerSide ) == 0);
Square ksq = pos.king_square(weakerSide); Square ksq = pos.king_square(weakerSide);
Bitboard pawns = pos.pieces(strongerSide, PAWN); Bitboard pawns = pos.pieces(strongerSide, PAWN);
@@ -704,7 +700,7 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
// Does the defending king block the pawns? // Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1 if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1
|| ( file_of(ksq) == FILE_A || ( file_of(ksq) == FILE_A
&& !(in_front_bb(strongerSide, ksq) & pawns))) && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
// Are all pawns on the 'h' file? // Are all pawns on the 'h' file?
@@ -713,7 +709,7 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
// Does the defending king block the pawns? // Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1 if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1
|| ( file_of(ksq) == FILE_H || ( file_of(ksq) == FILE_H
&& !(in_front_bb(strongerSide, ksq) & pawns))) && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -728,15 +724,15 @@ template<>
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.non_pawn_material(weakerSide) == BishopValueMg); assert(pos.count<BISHOP>(weakerSide ) == 1);
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.count< PAWN>(strongerSide) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count< PAWN>(weakerSide ) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP)[0]; Square weakerBishopSq = pos.list<BISHOP>(weakerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away // Case 1: Defending king blocks the pawn, and cannot be driven away
@@ -783,21 +779,21 @@ template<>
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 2); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.non_pawn_material(weakerSide) == BishopValueMg); assert(pos.count<BISHOP>(weakerSide ) == 1);
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.count< PAWN>(strongerSide) == 2);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count< PAWN>(weakerSide ) == 0);
Square wbsq = pos.piece_list(strongerSide, BISHOP)[0]; Square wbsq = pos.list<BISHOP>(strongerSide)[0];
Square bbsq = pos.piece_list(weakerSide, BISHOP)[0]; Square bbsq = pos.list<BISHOP>(weakerSide)[0];
if (!opposite_colors(wbsq, bbsq)) if (!opposite_colors(wbsq, bbsq))
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
Square ksq = pos.king_square(weakerSide); Square ksq = pos.king_square(weakerSide);
Square psq1 = pos.piece_list(strongerSide, PAWN)[0]; Square psq1 = pos.list<PAWN>(strongerSide)[0];
Square psq2 = pos.piece_list(strongerSide, PAWN)[1]; Square psq2 = pos.list<PAWN>(strongerSide)[1];
Rank r1 = rank_of(psq1); Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2); Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2; Square blockSq1, blockSq2;
@@ -858,14 +854,14 @@ template<>
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMg); assert(pos.count<KNIGHT>(weakerSide ) == 1);
assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.count< PAWN>(strongerSide) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count< PAWN>(weakerSide ) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
if ( file_of(weakerKingSq) == file_of(pawnSq) if ( file_of(weakerKingSq) == file_of(pawnSq)
@@ -885,12 +881,12 @@ template<>
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const { ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == KnightValueMg); assert(pos.non_pawn_material(strongerSide) == KnightValueMg);
assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.count<KNIGHT>(strongerSide) == 1);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.count< PAWN>(strongerSide) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count< PAWN>(weakerSide ) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
if ( pawnSq == relative_square(strongerSide, SQ_A7) if ( pawnSq == relative_square(strongerSide, SQ_A7)
@@ -910,8 +906,8 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
template<> template<>
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square bishopSq = pos.piece_list(weakerSide, BISHOP)[0]; Square bishopSq = pos.list<BISHOP>(weakerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
// King needs to get close to promoting pawn to prevent knight from blocking. // King needs to get close to promoting pawn to prevent knight from blocking.
@@ -932,13 +928,13 @@ template<>
ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const { ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
assert(pos.piece_count(WHITE, PAWN) == 1); assert(pos.count<PAWN>(WHITE) == 1);
assert(pos.piece_count(BLACK, PAWN) == 1); assert(pos.count<PAWN>(BLACK) == 1);
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square wpsq = pos.piece_list(strongerSide, PAWN)[0]; Square wpsq = pos.list<PAWN>(strongerSide)[0];
Color us = pos.side_to_move(); Color us = pos.side_to_move();
if (strongerSide == BLACK) if (strongerSide == BLACK)

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(ENDGAME_H_INCLUDED) #ifndef ENDGAME_H_INCLUDED
#define ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED
#include <map> #include <map>
@@ -33,6 +33,10 @@ enum EndgameType {
// Evaluation functions // Evaluation functions
KK, // K vs K
KBK, // KB vs K
KNK, // KN vs K
KNNK, // KNN vs K
KXK, // Generic "mate lone king" eval KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K KBNK, // KBN vs K
KPK, // KP vs K KPK, // KP vs K
@@ -42,7 +46,6 @@ enum EndgameType {
KQKP, // KQ vs KP KQKP, // KQ vs KP
KQKR, // KQ vs KR KQKR, // KQ vs KR
KBBKN, // KBB vs KN KBBKN, // KBB vs KN
KNNK, // KNN vs K
KmmKm, // K and two minors vs K and one or two minors KmmKm, // K and two minors vs K and one or two minors
@@ -119,4 +122,4 @@ public:
{ return eg = map(eg).count(key) ? map(eg)[key] : NULL; } { return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
}; };
#endif // !defined(ENDGAME_H_INCLUDED) #endif // #ifndef ENDGAME_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(EVALUATE_H_INCLUDED) #ifndef EVALUATE_H_INCLUDED
#define EVALUATE_H_INCLUDED #define EVALUATE_H_INCLUDED
#include "types.h" #include "types.h"
@@ -32,4 +32,4 @@ extern std::string trace(const Position& pos);
} }
#endif // !defined(EVALUATE_H_INCLUDED) #endif // #ifndef EVALUATE_H_INCLUDED

View File

@@ -34,7 +34,7 @@ int main(int argc, char* argv[]) {
UCI::init(Options); UCI::init(Options);
Bitboards::init(); Bitboards::init();
Zobrist::init(); Position::init();
Bitbases::init_kpk(); Bitbases::init_kpk();
Search::init(); Search::init();
Eval::init(); Eval::init();

View File

@@ -35,8 +35,8 @@ namespace {
const int NoPawnsSF[4] = { 6, 12, 32 }; const int NoPawnsSF[4] = { 6, 12, 32 };
// Polynomial material balance parameters // Polynomial material balance parameters
const Value RedundantQueenPenalty = Value(320); const Value RedundantQueen = Value(320);
const Value RedundantRookPenalty = Value(554); const Value RedundantRook = Value(554);
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
@@ -75,24 +75,24 @@ namespace {
// Helper templates used to detect a given material distribution // Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) { template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.non_pawn_material(Them) == VALUE_ZERO return !pos.count<PAWN>(Them)
&& pos.piece_count(Them, PAWN) == 0 && pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.non_pawn_material(Us) >= RookValueMg; && pos.non_pawn_material(Us) >= RookValueMg;
} }
template<Color Us> bool is_KBPsKs(const Position& pos) { template<Color Us> bool is_KBPsKs(const Position& pos) {
return pos.non_pawn_material(Us) == BishopValueMg return pos.non_pawn_material(Us) == BishopValueMg
&& pos.piece_count(Us, BISHOP) == 1 && pos.count<BISHOP>(Us) == 1
&& pos.piece_count(Us, PAWN) >= 1; && pos.count<PAWN >(Us) >= 1;
} }
template<Color Us> bool is_KQKRPs(const Position& pos) { template<Color Us> bool is_KQKRPs(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.piece_count(Us, PAWN) == 0 return !pos.count<PAWN>(Us)
&& pos.non_pawn_material(Us) == QueenValueMg && pos.non_pawn_material(Us) == QueenValueMg
&& pos.piece_count(Us, QUEEN) == 1 && pos.count<QUEEN>(Us) == 1
&& pos.piece_count(Them, ROOK) == 1 && pos.count<ROOK>(Them) == 1
&& pos.piece_count(Them, PAWN) >= 1; && pos.count<PAWN>(Them) >= 1;
} }
/// imbalance() calculates imbalance comparing piece count of each /// imbalance() calculates imbalance comparing piece count of each
@@ -109,8 +109,8 @@ namespace {
// Redundancy of major pieces, formula based on Kaufman's paper // Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess" // "The Evaluation of Material Imbalances in Chess"
if (pieceCount[Us][ROOK] > 0) if (pieceCount[Us][ROOK] > 0)
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1) value -= RedundantRook * (pieceCount[Us][ROOK] - 1)
+ RedundantQueenPenalty * pieceCount[Us][QUEEN]; + RedundantQueen * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad // Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++) for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
@@ -150,7 +150,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
if (e->key == key) if (e->key == key)
return e; return e;
memset(e, 0, sizeof(Entry)); std::memset(e, 0, sizeof(Entry));
e->key = key; e->key = key;
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);
@@ -180,8 +180,8 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP))); assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP)));
assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP))); assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP)));
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2 if ( pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2) && pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK) <= 2)
{ {
e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()]; e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
return e; return e;
@@ -221,17 +221,17 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
if (npm_w + npm_b == VALUE_ZERO) if (npm_w + npm_b == VALUE_ZERO)
{ {
if (pos.piece_count(BLACK, PAWN) == 0) if (!pos.count<PAWN>(BLACK))
{ {
assert(pos.piece_count(WHITE, PAWN) >= 2); assert(pos.count<PAWN>(WHITE) >= 2);
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
} }
else if (pos.piece_count(WHITE, PAWN) == 0) else if (!pos.count<PAWN>(WHITE))
{ {
assert(pos.piece_count(BLACK, PAWN) >= 2); assert(pos.count<PAWN>(BLACK) >= 2);
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
} }
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1) else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
{ {
// This is a special case because we set scaling functions // This is a special case because we set scaling functions
// for both colors instead of only one. // for both colors instead of only one.
@@ -241,35 +241,35 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
} }
// No pawns makes it difficult to win, even with a material advantage // No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMg) if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
{ {
e->factor[WHITE] = (uint8_t) e->factor[WHITE] = (uint8_t)
(npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]); (npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(WHITE), 2)]);
} }
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMg) if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
{ {
e->factor[BLACK] = (uint8_t) e->factor[BLACK] = (uint8_t)
(npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]); (npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(BLACK), 2)]);
} }
// Compute the space weight // Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
{ {
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP) int minorPieceCount = pos.count<KNIGHT>(WHITE) + pos.count<BISHOP>(WHITE)
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP); + pos.count<KNIGHT>(BLACK) + pos.count<BISHOP>(BLACK);
e->spaceWeight = minorPieceCount * minorPieceCount; e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0);
} }
// 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", this allow 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.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT), { pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) }, pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), { pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } }; pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16); e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return e; return e;

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(MATERIAL_H_INCLUDED) #ifndef MATERIAL_H_INCLUDED
#define MATERIAL_H_INCLUDED #define MATERIAL_H_INCLUDED
#include "endgame.h" #include "endgame.h"
@@ -39,7 +39,7 @@ namespace Material {
struct Entry { struct Entry {
Score material_value() const { return make_score(value, value); } Score material_value() const { return make_score(value, value); }
int space_weight() const { return spaceWeight; } Score space_weight() const { return spaceWeight; }
Phase game_phase() const { return gamePhase; } Phase game_phase() const { return gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != NULL; } bool specialized_eval_exists() const { return evaluationFunction != NULL; }
Value evaluate(const Position& p) const { return (*evaluationFunction)(p); } Value evaluate(const Position& p) const { return (*evaluationFunction)(p); }
@@ -50,7 +50,7 @@ struct Entry {
uint8_t factor[COLOR_NB]; uint8_t factor[COLOR_NB];
EndgameBase<Value>* evaluationFunction; EndgameBase<Value>* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB];
int spaceWeight; Score spaceWeight;
Phase gamePhase; Phase gamePhase;
}; };
@@ -74,4 +74,4 @@ inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const {
} }
#endif // !defined(MATERIAL_H_INCLUDED) #endif // #ifndef MATERIAL_H_INCLUDED

View File

@@ -24,17 +24,11 @@
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
#if defined(__hpux)
# include <sys/pstat.h>
#endif
using namespace std; using namespace std;
/// Version number. If Version is left empty, then Tag plus current /// Version number. If Version is left empty, then compile date, in the
/// date, in the format DD-MM-YY, are used as a version number. /// format DD-MM-YY, is shown in engine_info.
static const string Version = "4";
static const string Version = "3";
static const string Tag = "";
/// engine_info() returns the full name of the current Stockfish version. This /// engine_info() returns the full name of the current Stockfish version. This
@@ -45,36 +39,26 @@ static const string Tag = "";
const string engine_info(bool to_uci) { const string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
const string cpu64(Is64Bit ? " 64bit" : "");
const string popcnt(HasPopCnt ? " SSE4.2" : "");
string month, day, year; string month, day, year;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008" stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
s << "Stockfish " << Version; s << "Stockfish " << Version << setfill('0');
if (Version.empty()) if (Version.empty())
{ {
date >> month >> day >> year; date >> month >> day >> year;
s << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
s << Tag << string(Tag.empty() ? "" : " ") << setfill('0') << setw(2) << day
<< "-" << setw(2) << (1 + months.find(month) / 4) << "-" << year.substr(2);
} }
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ") s << (Is64Bit ? " 64" : "")
<< (HasPopCnt ? " SSE4.2" : "")
<< (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski"; << "Tord Romstad, Marco Costalba and Joona Kiiski";
return s.str(); return s.str();
} }
/// Convert system time to milliseconds. That's all we need.
Time::point Time::now() {
sys_time_t t; system_time(&t); return time_to_msec(t);
}
/// Debug functions used mainly to collect run-time statistics /// Debug functions used mainly to collect run-time statistics
static uint64_t hits[2], means[2]; static uint64_t hits[2], means[2];
@@ -174,37 +158,12 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
void start_logger(bool b) { Logger::start(b); } void start_logger(bool b) { Logger::start(b); }
/// cpu_count() tries to detect the number of CPU cores
int cpu_count() {
#if defined(_WIN32) || defined(_WIN64)
SYSTEM_INFO s;
GetSystemInfo(&s);
return s.dwNumberOfProcessors;
#else
# if defined(_SC_NPROCESSORS_ONLN)
return sysconf(_SC_NPROCESSORS_ONLN);
# elif defined(__hpux)
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return psd.psd_proc_cnt;
# else
return 1;
# endif
#endif
}
/// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap /// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap
/// conversion from milliseconds to struct timespec, as used by pthreads. /// 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) {
#if defined(_WIN32) || defined(_WIN64) #ifdef _WIN32
int tm = msec; int tm = msec;
#else #else
timespec ts, *tm = &ts; timespec ts, *tm = &ts;
@@ -221,7 +180,7 @@ 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 function and do not stalls the CPU waiting for data to be /// blocking function and do not stalls the CPU waiting for data to be
/// loaded from memory, that can be quite slow. /// loaded from memory, that can be quite slow.
#if defined(NO_PREFETCH) #ifdef NO_PREFETCH
void prefetch(char*) {} void prefetch(char*) {}

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(MISC_H_INCLUDED) #ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED #define MISC_H_INCLUDED
#include <fstream> #include <fstream>
@@ -27,7 +27,6 @@
#include "types.h" #include "types.h"
extern const std::string engine_info(bool to_uci = false); extern const std::string engine_info(bool to_uci = false);
extern int cpu_count();
extern void timed_wait(WaitCondition&, Lock&, int); extern void timed_wait(WaitCondition&, Lock&, int);
extern void prefetch(char* addr); extern void prefetch(char* addr);
extern void start_logger(bool b); extern void start_logger(bool b);
@@ -46,7 +45,7 @@ struct Log : public std::ofstream {
namespace Time { namespace Time {
typedef int64_t point; typedef int64_t point;
point now(); inline point now() { return system_time_to_msec(); }
} }
@@ -66,4 +65,4 @@ std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << io_lock #define sync_cout std::cout << io_lock
#define sync_endl std::endl << io_unlock #define sync_endl std::endl << io_unlock
#endif // !defined(MISC_H_INCLUDED) #endif // #ifndef MISC_H_INCLUDED

View File

@@ -24,15 +24,15 @@
/// Simple macro to wrap a very common while loop, no facny, no flexibility, /// Simple macro to wrap a very common while loop, no facny, no flexibility,
/// hardcoded names 'mlist' and 'from'. /// hardcoded names 'mlist' and 'from'.
#define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_lsb(&b)) #define SERIALIZE(b) while (b) (mlist++)->move = make_move(from, pop_lsb(&b))
/// Version used for pawns, where the 'from' square is given as a delta from the 'to' square /// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \ #define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \
(*mlist++).move = make_move(to - (d), to); } (mlist++)->move = make_move(to - (d), to); }
namespace { namespace {
template<CastlingSide Side, bool Checks, bool Chess960> template<CastlingSide Side, bool Checks, bool Chess960>
MoveStack* generate_castle(const Position& pos, MoveStack* mlist, Color us) { ExtMove* generate_castle(const Position& pos, ExtMove* mlist, Color us) {
if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side))) if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side)))
return mlist; return mlist;
@@ -59,7 +59,7 @@ namespace {
if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies)) if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies))
return mlist; return mlist;
(*mlist++).move = make<CASTLE>(kfrom, rfrom); (mlist++)->move = make<CASTLE>(kfrom, rfrom);
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos))) if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
mlist--; mlist--;
@@ -68,42 +68,30 @@ namespace {
} }
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8
: Delta == DELTA_S ? p >> 8
: Delta == DELTA_NE ? (p & ~FileHBB) << 9
: Delta == DELTA_SE ? (p & ~FileHBB) >> 7
: Delta == DELTA_NW ? (p & ~FileABB) << 7
: Delta == DELTA_SW ? (p & ~FileABB) >> 9 : 0;
}
template<GenType Type, Square Delta> template<GenType Type, Square Delta>
inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7, inline ExtMove* generate_promotions(ExtMove* mlist, Bitboard pawnsOn7,
Bitboard target, const CheckInfo* ci) { Bitboard target, const CheckInfo* ci) {
Bitboard b = move_pawns<Delta>(pawnsOn7) & target; Bitboard b = shift_bb<Delta>(pawnsOn7) & target;
while (b) while (b)
{ {
Square to = pop_lsb(&b); Square to = pop_lsb(&b);
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
(*mlist++).move = make<PROMOTION>(to - Delta, to, QUEEN); (mlist++)->move = make<PROMOTION>(to - Delta, to, QUEEN);
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{ {
(*mlist++).move = make<PROMOTION>(to - Delta, to, ROOK); (mlist++)->move = make<PROMOTION>(to - Delta, to, ROOK);
(*mlist++).move = make<PROMOTION>(to - Delta, to, BISHOP); (mlist++)->move = make<PROMOTION>(to - Delta, to, BISHOP);
(*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 one that can give a direct check not
// already included in the queen-promotion. // 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
(void)ci; // Silence a warning under MSVC (void)ci; // Silence a warning under MSVC
} }
@@ -113,8 +101,8 @@ namespace {
template<Color Us, GenType Type> template<Color Us, GenType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, ExtMove* generate_pawn_moves(const Position& pos, ExtMove* mlist,
Bitboard target, const CheckInfo* ci) { Bitboard target, const CheckInfo* ci) {
// Compute our parametrized parameters at compile time, named according to // Compute our parametrized parameters at compile time, named according to
// the point of view of white side. // the point of view of white side.
@@ -122,9 +110,9 @@ namespace {
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S); const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT = (Us == WHITE ? DELTA_NE : DELTA_SW); const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT = (Us == WHITE ? DELTA_NW : DELTA_SE); const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b1, b2, dc1, dc2, emptySquares; Bitboard b1, b2, dc1, dc2, emptySquares;
@@ -139,8 +127,8 @@ namespace {
{ {
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
b1 = move_pawns<UP>(pawnsNotOn7) & emptySquares; b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
b2 = move_pawns<UP>(b1 & TRank3BB) & emptySquares; b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
if (Type == EVASIONS) // Consider only blocking squares if (Type == EVASIONS) // Consider only blocking squares
{ {
@@ -159,16 +147,16 @@ namespace {
// promotion has been already generated among captures. // promotion has been already generated among captures.
if (pawnsNotOn7 & ci->dcCandidates) if (pawnsNotOn7 & ci->dcCandidates)
{ {
dc1 = move_pawns<UP>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares; dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1; b1 |= dc1;
b2 |= dc2; b2 |= dc2;
} }
} }
SERIALIZE_PAWNS(b1, UP); SERIALIZE_PAWNS(b1, Up);
SERIALIZE_PAWNS(b2, UP + UP); SERIALIZE_PAWNS(b2, Up + Up);
} }
// Promotions and underpromotions // Promotions and underpromotions
@@ -180,19 +168,19 @@ namespace {
if (Type == EVASIONS) if (Type == EVASIONS)
emptySquares &= target; emptySquares &= target;
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ci); mlist = generate_promotions<Type, Right>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ci); mlist = generate_promotions<Type, Left >(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ci); mlist = generate_promotions<Type, Up>(mlist, pawnsOn7, emptySquares, ci);
} }
// Standard and en-passant captures // Standard and en-passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{ {
b1 = move_pawns<RIGHT>(pawnsNotOn7) & enemies; b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
b2 = move_pawns<LEFT >(pawnsNotOn7) & enemies; b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
SERIALIZE_PAWNS(b1, RIGHT); SERIALIZE_PAWNS(b1, Right);
SERIALIZE_PAWNS(b2, LEFT); SERIALIZE_PAWNS(b2, Left);
if (pos.ep_square() != SQ_NONE) if (pos.ep_square() != SQ_NONE)
{ {
@@ -201,7 +189,7 @@ namespace {
// An en passant capture can be an evasion only if the checking piece // An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this // is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise. // is a discovery check and we are forced to do otherwise.
if (Type == EVASIONS && !(target & (pos.ep_square() - UP))) if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
return mlist; return mlist;
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them); b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
@@ -209,7 +197,7 @@ namespace {
assert(b1); assert(b1);
while (b1) while (b1)
(*mlist++).move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square()); (mlist++)->move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
} }
} }
@@ -218,12 +206,12 @@ namespace {
template<PieceType Pt, bool Checks> FORCE_INLINE template<PieceType Pt, bool Checks> FORCE_INLINE
MoveStack* generate_moves(const Position& pos, MoveStack* mlist, Color us, ExtMove* generate_moves(const Position& pos, ExtMove* mlist, Color us,
Bitboard target, const CheckInfo* ci) { Bitboard target, const CheckInfo* ci) {
assert(Pt != KING && Pt != PAWN); assert(Pt != KING && Pt != PAWN);
const Square* pl = pos.piece_list(us, Pt); const Square* pl = pos.list<Pt>(us);
for (Square from = *pl; from != SQ_NONE; from = *++pl) for (Square from = *pl; from != SQ_NONE; from = *++pl)
{ {
@@ -233,7 +221,7 @@ namespace {
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt])) && !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
continue; continue;
if (ci->dcCandidates && (ci->dcCandidates & from)) if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from))
continue; continue;
} }
@@ -249,38 +237,36 @@ namespace {
} }
template<GenType Type> FORCE_INLINE template<Color Us, GenType Type> FORCE_INLINE
MoveStack* generate_all(const Position& pos, MoveStack* mlist, Color us, ExtMove* generate_all(const Position& pos, ExtMove* mlist, Bitboard target,
Bitboard target, const CheckInfo* ci = NULL) { const CheckInfo* ci = NULL) {
const bool Checks = Type == QUIET_CHECKS; const bool Checks = Type == QUIET_CHECKS;
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci) mlist = generate_pawn_moves<Us, Type>(pos, mlist, target, ci);
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci)); mlist = generate_moves<KNIGHT, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<BISHOP, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<KNIGHT, Checks>(pos, mlist, us, target, ci); mlist = generate_moves< ROOK, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<BISHOP, Checks>(pos, mlist, us, target, ci); mlist = generate_moves< QUEEN, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<ROOK, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<QUEEN, Checks>(pos, mlist, us, target, ci);
if (Type != QUIET_CHECKS && Type != EVASIONS) if (Type != QUIET_CHECKS && Type != EVASIONS)
{ {
Square from = pos.king_square(us); Square from = pos.king_square(Us);
Bitboard b = pos.attacks_from<KING>(from) & target; Bitboard b = pos.attacks_from<KING>(from) & target;
SERIALIZE(b); SERIALIZE(b);
} }
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us)) if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
{ {
if (pos.is_chess960()) if (pos.is_chess960())
{ {
mlist = generate_castle<KING_SIDE, Checks, true>(pos, mlist, us); mlist = generate_castle< KING_SIDE, Checks, true>(pos, mlist, Us);
mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, us); mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, Us);
} }
else else
{ {
mlist = generate_castle<KING_SIDE, Checks, false>(pos, mlist, us); mlist = generate_castle< KING_SIDE, Checks, false>(pos, mlist, Us);
mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, us); mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, Us);
} }
} }
@@ -301,7 +287,7 @@ namespace {
/// non-captures. Returns a pointer to the end of the move list. /// non-captures. Returns a pointer to the end of the move list.
template<GenType Type> template<GenType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) { ExtMove* generate(const Position& pos, ExtMove* mlist) {
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
assert(!pos.checkers()); assert(!pos.checkers());
@@ -312,22 +298,24 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) {
: Type == QUIETS ? ~pos.pieces() : Type == QUIETS ? ~pos.pieces()
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0; : Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
return generate_all<Type>(pos, mlist, us, target); return us == WHITE ? generate_all<WHITE, Type>(pos, mlist, target)
: generate_all<BLACK, Type>(pos, mlist, target);
} }
// Explicit template instantiations // Explicit template instantiations
template MoveStack* generate<CAPTURES>(const Position&, MoveStack*); template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
template MoveStack* generate<QUIETS>(const Position&, MoveStack*); template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
template MoveStack* generate<NON_EVASIONS>(const Position&, MoveStack*); template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight /// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list. /// underpromotions that give check. Returns a pointer to the end of the move list.
template<> template<>
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) { ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* mlist) {
assert(!pos.checkers()); assert(!pos.checkers());
Color us = pos.side_to_move();
CheckInfo ci(pos); CheckInfo ci(pos);
Bitboard dc = ci.dcCandidates; Bitboard dc = ci.dcCandidates;
@@ -347,21 +335,21 @@ MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
SERIALIZE(b); SERIALIZE(b);
} }
return generate_all<QUIET_CHECKS>(pos, mlist, pos.side_to_move(), ~pos.pieces(), &ci); return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci)
: generate_all<BLACK, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci);
} }
/// generate<EVASIONS> generates all pseudo-legal check evasions when the side /// generate<EVASIONS> generates all pseudo-legal check evasions when the side
/// to move is in check. Returns a pointer to the end of the move list. /// to move is in check. Returns a pointer to the end of the move list.
template<> template<>
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) { ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
assert(pos.checkers()); assert(pos.checkers());
Square from, checksq;
int checkersCnt = 0; int checkersCnt = 0;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(us); Square ksq = pos.king_square(us), from = ksq /* For SERIALIZE */, checksq;
Bitboard sliderAttacks = 0; Bitboard sliderAttacks = 0;
Bitboard b = pos.checkers(); Bitboard b = pos.checkers();
@@ -400,25 +388,25 @@ MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks; b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
from = ksq;
SERIALIZE(b); SERIALIZE(b);
if (checkersCnt > 1) if (checkersCnt > 1)
return mlist; // Double check, only a king move can save the day return mlist; // Double check, only a king move can save the day
// Generate blocking evasions or captures of the checking piece // Generate blocking evasions or captures of the checking piece
Bitboard target = between_bb(checksq, ksq) | pos.checkers(); Bitboard target = between_bb(checksq, ksq) | checksq;
return generate_all<EVASIONS>(pos, mlist, us, target); return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, mlist, target)
: generate_all<BLACK, EVASIONS>(pos, mlist, target);
} }
/// generate<LEGAL> generates all the legal moves in the given position /// generate<LEGAL> generates all the legal moves in the given position
template<> template<>
MoveStack* generate<LEGAL>(const Position& pos, MoveStack* mlist) { ExtMove* generate<LEGAL>(const Position& pos, ExtMove* mlist) {
MoveStack *end, *cur = mlist; ExtMove *end, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(); Bitboard pinned = pos.pinned_pieces();
Square ksq = pos.king_square(pos.side_to_move()); Square ksq = pos.king_square(pos.side_to_move());

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(MOVEGEN_H_INCLUDED) #ifndef MOVEGEN_H_INCLUDED
#define MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED
#include "types.h" #include "types.h"
@@ -34,26 +34,25 @@ enum GenType {
class Position; class Position;
template<GenType> template<GenType>
MoveStack* generate(const Position& pos, MoveStack* 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(), sometimes comes
/// handy to use this class instead of the low level generate() function. /// handy to use this class instead of the low level generate() function.
template<GenType T> template<GenType T>
struct MoveList { struct MoveList {
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {} explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) { last->move = MOVE_NONE; }
void operator++() { cur++; } void operator++() { cur++; }
bool end() const { return cur == last; } Move operator*() const { return cur->move; }
Move move() const { return cur->move; }
size_t size() const { return last - mlist; } size_t size() const { return last - mlist; }
bool contains(Move m) const { bool contains(Move m) const {
for (const MoveStack* it(mlist); it != last; ++it) if (it->move == m) return true; for (const ExtMove* it(mlist); it != last; ++it) if (it->move == m) return true;
return false; return false;
} }
private: private:
MoveStack mlist[MAX_MOVES]; ExtMove mlist[MAX_MOVES];
MoveStack *cur, *last; ExtMove *cur, *last;
}; };
#endif // !defined(MOVEGEN_H_INCLUDED) #endif // #ifndef MOVEGEN_H_INCLUDED

View File

@@ -25,7 +25,7 @@
namespace { namespace {
enum Sequencer { enum Stages {
MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1, MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1,
EVASION, EVASIONS_S2, EVASION, EVASIONS_S2,
QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3, QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3,
@@ -36,9 +36,9 @@ namespace {
}; };
// Our insertion sort, guaranteed to be stable, as is needed // Our insertion sort, guaranteed to be stable, as is needed
void insertion_sort(MoveStack* begin, MoveStack* end) void insertion_sort(ExtMove* begin, ExtMove* end)
{ {
MoveStack tmp, *p, *q; ExtMove tmp, *p, *q;
for (p = begin + 1; p < end; ++p) for (p = begin + 1; p < end; ++p)
{ {
@@ -51,12 +51,12 @@ namespace {
// Unary predicate used by std::partition to split positive scores from remaining // Unary predicate used by std::partition to split positive scores from remaining
// 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 MoveStack& 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 and moves to the front the best move in the range [begin, end),
// it is faster than sorting all the moves in advance when moves are few, as // it is faster than sorting all the moves in advance when moves are few, as
// normally are the possible captures. // normally are the possible captures.
inline MoveStack* pick_best(MoveStack* begin, MoveStack* 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));
return begin; return begin;
@@ -70,53 +70,40 @@ namespace {
/// search captures, promotions and some checks) and about how important good /// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node. /// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Search::Stack* s, Value beta) : pos(p), Hist(h), depth(d) { Move* cm, Search::Stack* s) : pos(p), history(h), depth(d) {
assert(d > DEPTH_ZERO); assert(d > DEPTH_ZERO);
captureThreshold = 0;
cur = end = moves; cur = end = moves;
endBadCaptures = moves + MAX_MOVES - 1; endBadCaptures = moves + MAX_MOVES - 1;
countermoves = cm;
ss = s; ss = s;
if (p.checkers()) if (p.checkers())
phase = EVASION; stage = EVASION;
else else
{ stage = MAIN_SEARCH;
phase = MAIN_SEARCH;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
// Consider sligtly negative captures as good if at low depth and far from beta
if (ss && ss->staticEval < beta - PawnValueMg && d < 3 * ONE_PLY)
captureThreshold = -PawnValueMg;
// Consider negative captures as good if still enough to reach beta
else if (ss && ss->staticEval > beta)
captureThreshold = beta - ss->staticEval;
}
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
end += (ttMove != MOVE_NONE); end += (ttMove != MOVE_NONE);
} }
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Square sq) : pos(p), Hist(h), cur(moves), end(moves) { Square sq) : pos(p), history(h), cur(moves), end(moves) {
assert(d <= DEPTH_ZERO); assert(d <= DEPTH_ZERO);
if (p.checkers()) if (p.checkers())
phase = EVASION; stage = EVASION;
else if (d > DEPTH_QS_NO_CHECKS) else if (d > DEPTH_QS_NO_CHECKS)
phase = QSEARCH_0; stage = QSEARCH_0;
else if (d > DEPTH_QS_RECAPTURES) else if (d > DEPTH_QS_RECAPTURES)
{ {
phase = 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
@@ -126,7 +113,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
} }
else else
{ {
phase = RECAPTURE; stage = RECAPTURE;
recaptureSquare = sq; recaptureSquare = sq;
ttm = MOVE_NONE; ttm = MOVE_NONE;
} }
@@ -135,12 +122,12 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
end += (ttMove != MOVE_NONE); end += (ttMove != MOVE_NONE);
} }
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt) MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, PieceType pt)
: pos(p), Hist(h), cur(moves), end(moves) { : pos(p), history(h), cur(moves), end(moves) {
assert(!pos.checkers()); assert(!pos.checkers());
phase = PROBCUT; stage = PROBCUT;
// In ProbCut we generate only captures better than parent's captured piece // In ProbCut we generate only captures better than parent's captured piece
captureThreshold = PieceValue[MG][pt]; captureThreshold = PieceValue[MG][pt];
@@ -172,7 +159,7 @@ void MovePicker::score<CAPTURES>() {
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez). // some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
Move m; Move m;
for (MoveStack* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
it->score = PieceValue[MG][pos.piece_on(to_sq(m))] it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
@@ -191,10 +178,10 @@ void MovePicker::score<QUIETS>() {
Move m; Move m;
for (MoveStack* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
it->score = Hist[pos.piece_moved(m)][to_sq(m)]; it->score = history[pos.piece_moved(m)][to_sq(m)];
} }
} }
@@ -206,17 +193,17 @@ void MovePicker::score<EVASIONS>() {
Move m; Move m;
int seeScore; int seeScore;
for (MoveStack* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
if ((seeScore = pos.see_sign(m)) < 0) if ((seeScore = pos.see_sign(m)) < 0)
it->score = seeScore - History::Max; // At the bottom it->score = seeScore - HistoryStats::Max; // At the bottom
else if (pos.is_capture(m)) else if (pos.is_capture(m))
it->score = PieceValue[MG][pos.piece_on(to_sq(m))] it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m)) + History::Max; - type_of(pos.piece_moved(m)) + HistoryStats::Max;
else else
it->score = Hist[pos.piece_moved(m)][to_sq(m)]; it->score = history[pos.piece_moved(m)][to_sq(m)];
} }
} }
@@ -228,7 +215,7 @@ void MovePicker::generate_next() {
cur = moves; cur = moves;
switch (++phase) { switch (++stage) {
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
end = generate<CAPTURES>(pos, moves); end = generate<CAPTURES>(pos, moves);
@@ -238,6 +225,19 @@ void MovePicker::generate_next() {
case KILLERS_S1: case KILLERS_S1:
cur = killers; cur = killers;
end = cur + 2; end = cur + 2;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
killers[2].move = killers[3].move = MOVE_NONE;
// Be sure countermoves are different from killers
for (int i = 0; i < 2; i++)
if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move)
(end++)->move = countermoves[i];
if (countermoves[1] && countermoves[1] == countermoves[0]) // Due to SMP races
killers[3].move = MOVE_NONE;
return; return;
case QUIETS_1_S1: case QUIETS_1_S1:
@@ -271,7 +271,7 @@ void MovePicker::generate_next() {
return; return;
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
phase = STOP; stage = STOP;
case STOP: case STOP:
end = cur + 1; // Avoid another next_phase() call end = cur + 1; // Avoid another next_phase() call
return; return;
@@ -296,7 +296,7 @@ Move MovePicker::next_move<false>() {
while (cur == end) while (cur == end)
generate_next(); generate_next();
switch (phase) { switch (stage) {
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
cur++; cur++;
@@ -306,9 +306,7 @@ Move MovePicker::next_move<false>() {
move = pick_best(cur++, end)->move; move = pick_best(cur++, end)->move;
if (move != ttMove) if (move != ttMove)
{ {
assert(captureThreshold <= 0); // Otherwise we cannot use see_sign() if (pos.see_sign(move) >= 0)
if (pos.see_sign(move) >= captureThreshold)
return move; return move;
// Losing capture, move it to the tail of the array // Losing capture, move it to the tail of the array
@@ -329,7 +327,9 @@ Move MovePicker::next_move<false>() {
move = (cur++)->move; move = (cur++)->move;
if ( move != ttMove if ( move != ttMove
&& move != killers[0].move && move != killers[0].move
&& move != killers[1].move) && move != killers[1].move
&& move != killers[2].move
&& move != killers[3].move)
return move; return move;
break; break;

View File

@@ -17,11 +17,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined MOVEPICK_H_INCLUDED #ifndef MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED
#include <algorithm> // For std::max #include <algorithm> // For std::max
#include <cstring> // For memset #include <cstring> // For std::memset
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
@@ -30,20 +30,29 @@
/// The Stats struct stores moves statistics. According to the template parameter /// The Stats struct stores moves statistics. According to the template parameter
/// the class can store both History and Gains type statistics. History records /// the class can store History, Gains and Countermoves. History records how often
/// how often different moves have been successful or unsuccessful during the /// different moves have been successful or unsuccessful during the current search
/// current search and is used for reduction and move ordering decisions. Gains /// and is used for reduction and move ordering decisions. Gains records the move's
/// records the move's best evaluation gain from one ply to the next and is used /// best evaluation gain from one ply to the next and is used for pruning decisions.
/// for pruning decisions. Entries are stored according only to moving piece and /// Countermoves store the move that refute a previous one. Entries are stored
/// destination square, in particular two moves with different origin but same /// according only to moving piece and destination square, hence two moves with
/// destination and same piece will be considered identical. /// different origin but same destination and piece will be considered identical.
template<bool Gain> template<bool Gain, typename T>
struct Stats { struct Stats {
static const Value Max = Value(2000); static const Value Max = Value(2000);
const Value* operator[](Piece p) const { return &table[p][0]; } const T* operator[](Piece p) const { return table[p]; }
void clear() { memset(table, 0, sizeof(table)); } void clear() { std::memset(table, 0, sizeof(table)); }
void update(Piece p, Square to, Move m) {
if (m == table[p][to].first)
return;
table[p][to].second = table[p][to].first;
table[p][to].first = m;
}
void update(Piece p, Square to, Value v) { void update(Piece p, Square to, Value v) {
@@ -55,11 +64,12 @@ struct Stats {
} }
private: private:
Value table[PIECE_NB][SQUARE_NB]; T table[PIECE_NB][SQUARE_NB];
}; };
typedef Stats<false> History; typedef Stats< true, Value> GainsStats;
typedef Stats<true> Gains; typedef Stats<false, Value> HistoryStats;
typedef Stats<false, std::pair<Move, Move> > CountermovesStats;
/// MovePicker class is used to pick one pseudo legal move at a time from the /// MovePicker class is used to pick one pseudo legal move at a time from the
@@ -74,9 +84,10 @@ class MovePicker {
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
public: public:
MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value); MovePicker(const Position&, Move, Depth, const HistoryStats&, Square);
MovePicker(const Position&, Move, Depth, const History&, Square); MovePicker(const Position&, Move, const HistoryStats&, PieceType);
MovePicker(const Position&, Move, const History&, PieceType); MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Search::Stack*);
template<bool SpNode> Move next_move(); template<bool SpNode> Move next_move();
private: private:
@@ -84,15 +95,16 @@ private:
void generate_next(); void generate_next();
const Position& pos; const Position& pos;
const History& Hist; const HistoryStats& history;
Search::Stack* ss; Search::Stack* ss;
Move* countermoves;
Depth depth; Depth depth;
Move ttMove; Move ttMove;
MoveStack killers[2]; ExtMove killers[4];
Square recaptureSquare; Square recaptureSquare;
int captureThreshold, phase; int captureThreshold, stage;
MoveStack *cur, *end, *endQuiets, *endBadCaptures; ExtMove *cur, *end, *endQuiets, *endBadCaptures;
MoveStack moves[MAX_MOVES]; ExtMove moves[MAX_MOVES];
}; };
#endif // !defined(MOVEPICK_H_INCLUDED) #endif // #ifndef MOVEPICK_H_INCLUDED

View File

@@ -89,9 +89,9 @@ Move move_from_uci(const Position& pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase if (str.length() == 5) // Junior could send promotion piece in uppercase
str[4] = char(tolower(str[4])); str[4] = char(tolower(str[4]));
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml) for (MoveList<LEGAL> it(pos); *it; ++it)
if (str == move_to_uci(ml.move(), pos.is_chess960())) if (str == move_to_uci(*it, pos.is_chess960()))
return ml.move(); return *it;
return MOVE_NONE; return MOVE_NONE;
} }

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(NOTATION_H_INCLUDED) #ifndef NOTATION_H_INCLUDED
#define NOTATION_H_INCLUDED #define NOTATION_H_INCLUDED
#include <string> #include <string>
@@ -32,4 +32,4 @@ const std::string move_to_uci(Move m, bool chess960);
const std::string move_to_san(Position& pos, Move m); const std::string move_to_san(Position& pos, Move m);
std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]); std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]);
#endif // !defined(NOTATION_H_INCLUDED) #endif // #ifndef NOTATION_H_INCLUDED

View File

@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "bitboard.h" #include "bitboard.h"
@@ -30,49 +31,48 @@ namespace {
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file // Doubled pawn penalty by opposed flag and file
const Score DoubledPawnPenalty[2][FILE_NB] = { const Score Doubled[2][FILE_NB] = {
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48), { S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }, S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48), { S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }}; S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
// Isolated pawn penalty by opposed flag and file // Isolated pawn penalty by opposed flag and file
const Score IsolatedPawnPenalty[2][FILE_NB] = { const Score Isolated[2][FILE_NB] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52), { S(37, 45), S(54, 52), S(60, 52), S(60, 52),
S(60, 52), S(60, 52), S(54, 52), S(37, 45) }, S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35), { S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }}; S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
// Backward pawn penalty by opposed flag and file // Backward pawn penalty by opposed flag and file
const Score BackwardPawnPenalty[2][FILE_NB] = { const Score Backward[2][FILE_NB] = {
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46), { S(30, 42), S(43, 46), S(49, 46), S(49, 46),
S(49, 46), S(49, 46), S(43, 46), S(30, 42) }, S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31), { S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }}; S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
// Pawn chain membership bonus by file // Pawn chain membership bonus by file
const Score ChainBonus[FILE_NB] = { const Score ChainMember[FILE_NB] = {
S(11,-1), S(13,-1), S(13,-1), S(14,-1), S(11,-1), S(13,-1), S(13,-1), S(14,-1),
S(14,-1), S(13,-1), S(13,-1), S(11,-1) S(14,-1), S(13,-1), S(13,-1), S(11,-1)
}; };
// Candidate passed pawn bonus by rank // Candidate passed pawn bonus by rank
const Score CandidateBonus[RANK_NB] = { const Score CandidatePassed[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29), S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0) S(34,68), S(83,166), S(0, 0), S( 0, 0)
}; };
const Score PawnStructureWeight = S(233, 201); // Weakness of our pawn shelter in front of the king indexed by [rank]
const Value ShelterWeakness[RANK_NB] =
{ V(100), V(0), V(27), V(73), V(92), V(101), V(101) };
// Weakness of our pawn shelter in front of the king indexed by [king pawn][rank] // Danger of enemy pawns moving toward our king indexed by
const Value ShelterWeakness[2][RANK_NB] = // [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn]
{ { V(141), V(0), V(38), V(102), V(128), V(141), V(141) }, const Value StormDanger[3][RANK_NB] = {
{ V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } }; { V( 0), V(64), V(128), V(51), V(26) },
{ V(26), V(32), V( 96), V(38), V(20) },
// Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank] { V( 0), V( 0), V( 64), V(25), V(13) }};
const Value StormDanger[2][RANK_NB] =
{ { V(26), V(0), V(128), V(51), V(26) },
{ V(13), V(0), V( 64), V(25), V(13) } };
// Max bonus for king safety. Corresponds to start position with all the pawns // Max bonus for king safety. Corresponds to start position with all the pawns
// in front of the king and no enemy pawn on the horizont. // in front of the king and no enemy pawn on the horizont.
@@ -82,10 +82,12 @@ namespace {
#undef V #undef V
template<Color Us> template<Color Us>
Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Score evaluate(const Position& pos, Pawns::Entry* e) {
Bitboard theirPawns, Pawns::Entry* e) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b; Bitboard b;
Square s; Square s;
@@ -93,7 +95,17 @@ namespace {
Rank r; Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate; bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO; Score value = SCORE_ZERO;
const Square* pl = pos.piece_list(Us, PAWN); const Square* pl = pos.list<PAWN>(Us);
Bitboard ourPawns = pos.pieces(Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
e->semiopenFiles[Us] = 0xFF;
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & DarkSquares);
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
// Loop through all pawns of the current color and score each pawn // Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE) while ((s = *pl++) != SQ_NONE)
@@ -103,8 +115,8 @@ namespace {
f = file_of(s); f = file_of(s);
r = rank_of(s); r = rank_of(s);
// This file cannot be half open // This file cannot be semi-open
e->halfOpenFiles[Us] &= ~(1 << f); e->semiopenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection // Our rank plus previous one. Used for chain detection
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
@@ -124,7 +136,7 @@ namespace {
// be backward. If there are friendly pawns behind on adjacent files // be backward. If there are friendly pawns behind on adjacent files
// or if can capture an enemy pawn it cannot be backward either. // or if can capture an enemy pawn it cannot be backward either.
if ( !(passed | isolated | chain) if ( !(passed | isolated | chain)
&& !(ourPawns & attack_span_mask(Them, s)) && !(ourPawns & pawn_attack_span(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns)) && !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
{ {
// We now know that there are no friendly pawns beside or behind this // We now know that there are no friendly pawns beside or behind this
@@ -136,22 +148,22 @@ namespace {
// Note that we are sure to find something because pawn is not passed // Note that we are sure to find something because pawn is not passed
// nor isolated, so loop is potentially infinite, but it isn't. // nor isolated, so loop is potentially infinite, but it isn't.
while (!(b & (ourPawns | theirPawns))) while (!(b & (ourPawns | theirPawns)))
Us == WHITE ? b <<= 8 : b >>= 8; b = shift_bb<Up>(b);
// The friendly pawn needs to be at least two ranks closer than the // The friendly pawn needs to be at least two ranks closer than the
// enemy pawn in order to help the potentially backward pawn advance. // enemy pawn in order to help the potentially backward pawn advance.
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns; backward = (b | shift_bb<Up>(b)) & theirPawns;
} }
assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns)); assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
// A not passed pawn is a candidate to become passed if it is free to // A not passed pawn is a candidate to become passed if it is free to
// advance and if the number of friendly pawns beside or behind this // advance and if the number of friendly pawns beside or behind this
// pawn on adjacent files is higher or equal than the number of // pawn on adjacent files is higher or equal than the number of
// enemy pawns in the forward direction on the adjacent files. // enemy pawns in the forward direction on the adjacent files.
candidate = !(opposed | passed | backward | isolated) candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0 && (b = pawn_attack_span(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns); && popcount<Max15>(b) >= popcount<Max15>(pawn_attack_span(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need // Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed // full attack info to evaluate passed pawns. Only the frontmost passed
@@ -161,30 +173,25 @@ namespace {
// Score this pawn // Score this pawn
if (isolated) if (isolated)
value -= IsolatedPawnPenalty[opposed][f]; value -= Isolated[opposed][f];
if (doubled) if (doubled)
value -= DoubledPawnPenalty[opposed][f]; value -= Doubled[opposed][f];
if (backward) if (backward)
value -= BackwardPawnPenalty[opposed][f]; value -= Backward[opposed][f];
if (chain) if (chain)
value += ChainBonus[f]; value += ChainMember[f];
if (candidate) if (candidate)
value += CandidateBonus[relative_rank(Us, s)]; value += CandidatePassed[relative_rank(Us, s)];
} }
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & BlackSquares);
e->pawnsOnSquares[Us][WHITE] = pos.piece_count(Us, PAWN) - e->pawnsOnSquares[Us][BLACK];
e->pawnsOnSquares[Them][BLACK] = popcount<Max15>(theirPawns & BlackSquares);
e->pawnsOnSquares[Them][WHITE] = pos.piece_count(Them, PAWN) - e->pawnsOnSquares[Them][BLACK];
return value; return value;
} }
}
} // namespace
namespace Pawns { namespace Pawns {
@@ -197,27 +204,11 @@ Entry* probe(const Position& pos, Table& entries) {
Key key = pos.pawn_key(); Key key = pos.pawn_key();
Entry* e = entries[key]; Entry* e = entries[key];
// If e->key matches the position's pawn hash key, it means that we
// have analysed this pawn structure before, and we can simply return
// the information we found the last time instead of recomputing it.
if (e->key == key) if (e->key == key)
return e; return e;
e->key = key; e->key = key;
e->passedPawns[WHITE] = e->passedPawns[BLACK] = 0; e->value = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
e->kingSquares[WHITE] = e->kingSquares[BLACK] = SQ_NONE;
e->halfOpenFiles[WHITE] = e->halfOpenFiles[BLACK] = 0xFF;
Bitboard wPawns = pos.pieces(WHITE, PAWN);
Bitboard bPawns = pos.pieces(BLACK, PAWN);
e->pawnAttacks[WHITE] = ((wPawns & ~FileHBB) << 9) | ((wPawns & ~FileABB) << 7);
e->pawnAttacks[BLACK] = ((bPawns & ~FileHBB) >> 7) | ((bPawns & ~FileABB) >> 9);
e->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, e)
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, e);
e->value = apply_weight(e->value, PawnStructureWeight);
return e; return e;
} }
@@ -231,8 +222,8 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Value safety = MaxSafetyBonus; Value safety = MaxSafetyBonus;
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, ksq) | rank_bb(ksq)); Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us) & ~rank_bb(ksq); Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them); Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem; Rank rkUs, rkThem;
File kf = file_of(ksq); File kf = file_of(ksq);
@@ -241,15 +232,13 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
for (int f = kf - 1; f <= kf + 1; f++) for (int f = kf - 1; f <= kf + 1; f++)
{ {
// Shelter penalty is higher for the pawn in front of the king
b = ourPawns & FileBB[f]; b = ourPawns & FileBB[f];
rkUs = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1; rkUs = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
safety -= ShelterWeakness[f != kf][rkUs]; safety -= ShelterWeakness[rkUs];
// Storm danger is smaller if enemy pawn is blocked
b = theirPawns & FileBB[f]; b = theirPawns & FileBB[f];
rkThem = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1; rkThem = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
safety -= StormDanger[rkThem == rkUs + 1][rkThem]; safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
} }
return safety; return safety;

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(PAWNS_H_INCLUDED) #ifndef PAWNS_H_INCLUDED
#define PAWNS_H_INCLUDED #define PAWNS_H_INCLUDED
#include "misc.h" #include "misc.h"
@@ -37,10 +37,12 @@ struct Entry {
Score pawns_value() const { return value; } Score pawns_value() const { return value; }
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
int file_is_half_open(Color c, File f) const { return halfOpenFiles[c] & (1 << int(f)); } int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; }
int has_open_file_to_left(Color c, File f) const { return halfOpenFiles[c] & ((1 << int(f)) - 1); } int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); }
int has_open_file_to_right(Color c, File f) const { return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); } int semiopen_on_side(Color c, File f, bool left) const {
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(BlackSquares & s)]; }
return semiopenFiles[c] & (left ? ((1 << int(f)) - 1) : ~((1 << int(f+1)) - 1));
}
template<Color Us> template<Color Us>
Score king_safety(const Position& pos, Square ksq) { Score king_safety(const Position& pos, Square ksq) {
@@ -62,7 +64,7 @@ struct Entry {
int minKPdistance[COLOR_NB]; int minKPdistance[COLOR_NB];
int castleRights[COLOR_NB]; int castleRights[COLOR_NB];
Score value; Score value;
int halfOpenFiles[COLOR_NB]; int semiopenFiles[COLOR_NB];
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; int pawnsOnSquares[COLOR_NB][COLOR_NB];
}; };
@@ -73,4 +75,4 @@ Entry* probe(const Position& pos, Table& entries);
} }
#endif // !defined(PAWNS_H_INCLUDED) #endif // #ifndef PAWNS_H_INCLUDED

View File

@@ -17,10 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(PLATFORM_H_INCLUDED) #ifndef PLATFORM_H_INCLUDED
#define PLATFORM_H_INCLUDED #define PLATFORM_H_INCLUDED
#if defined(_MSC_VER) #ifdef _MSC_VER
// Disable some silly and noisy warning from MSVC compiler // Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant #pragma warning(disable: 4127) // Conditional expression is constant
@@ -40,16 +40,17 @@ typedef unsigned __int64 uint64_t;
#else #else
# include <inttypes.h> # include <inttypes.h>
# include <unistd.h> // Used by sysconf(_SC_NPROCESSORS_ONLN)
#endif #endif
#if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix #ifndef _WIN32 // Linux - Unix
# include <sys/time.h> # include <sys/time.h>
typedef timeval sys_time_t;
inline void system_time(sys_time_t* t) { gettimeofday(t, NULL); } inline int64_t system_time_to_msec() {
inline int64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; } timeval t;
gettimeofday(&t, NULL);
return t.tv_sec * 1000LL + t.tv_usec / 1000;
}
# include <pthread.h> # include <pthread.h>
typedef pthread_mutex_t Lock; typedef pthread_mutex_t Lock;
@@ -66,18 +67,20 @@ typedef void*(*pt_start_fn)(void*);
# define cond_signal(x) pthread_cond_signal(&(x)) # define cond_signal(x) pthread_cond_signal(&(x))
# define cond_wait(x,y) pthread_cond_wait(&(x),&(y)) # define cond_wait(x,y) pthread_cond_wait(&(x),&(y))
# define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z) # define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z)
# define thread_create(x,f,t) !pthread_create(&(x),NULL,(pt_start_fn)f,t) # define thread_create(x,f,t) pthread_create(&(x),NULL,(pt_start_fn)f,t)
# define thread_join(x) pthread_join(x, NULL) # define thread_join(x) pthread_join(x, NULL)
#else // Windows and MinGW #else // Windows and MinGW
# include <sys/timeb.h> # include <sys/timeb.h>
typedef _timeb sys_time_t;
inline void system_time(sys_time_t* t) { _ftime(t); } inline int64_t system_time_to_msec() {
inline int64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; } _timeb t;
_ftime(&t);
return t.time * 1000LL + t.millitm;
}
#if !defined(NOMINMAX) #ifndef NOMINMAX
# define NOMINMAX // disable macros min() and max() # define NOMINMAX // disable macros min() and max()
#endif #endif
@@ -105,9 +108,9 @@ inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
# define cond_signal(x) SetEvent(x) # define cond_signal(x) SetEvent(x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); } # define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); } # define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); }
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()), x != NULL) # define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()))
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); } # define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
#endif #endif
#endif // !defined(PLATFORM_H_INCLUDED) #endif // #ifndef PLATFORM_H_INCLUDED

View File

@@ -17,12 +17,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <algorithm>
#include "bitcount.h" #include "bitcount.h"
#include "movegen.h" #include "movegen.h"
@@ -41,96 +41,50 @@ static const string PieceToChar(" PNBRQK pnbrqk");
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
Score pieceSquareTable[PIECE_NB][SQUARE_NB]; Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Value PieceValue[PHASE_NB][PIECE_NB] = { Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
namespace Zobrist { namespace Zobrist {
Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Key enpassant[FILE_NB]; Key enpassant[FILE_NB];
Key castle[CASTLE_RIGHT_NB]; Key castle[CASTLE_RIGHT_NB];
Key side; Key side;
Key exclusion; Key exclusion;
/// init() initializes at startup the various arrays used to compute 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, the black
/// halves of the tables are initialized by flipping and changing the sign of
/// the white scores.
void init() {
RKISS rk;
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
psq[c][pt][s] = rk.rand<Key>();
for (File f = FILE_A; f <= FILE_H; f++)
enpassant[f] = rk.rand<Key>();
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
{
Bitboard b = cr;
while (b)
{
Key k = castle[1ULL << pop_lsb(&b)];
castle[cr] ^= k ? k : rk.rand<Key>();
}
}
side = rk.rand<Key>();
exclusion = rk.rand<Key>();
for (PieceType pt = PAWN; pt <= KING; pt++)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
pieceSquareTable[make_piece(WHITE, pt)][ s] = (v + PSQT[pt][s]);
pieceSquareTable[make_piece(BLACK, pt)][~s] = -(v + PSQT[pt][s]);
}
}
} }
} // namespace Zobrist Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;}
namespace { namespace {
/// next_attacker() is an helper function used by see() to locate the least // min_attacker() is an helper function used by see() to locate the least
/// valuable attacker for the side to move, remove the attacker we just found // valuable attacker for the side to move, remove the attacker we just found
/// from the 'occupied' bitboard and scan for new X-ray attacks behind it. // from the bitboards and scan for new X-ray attacks behind it.
template<int Pt> FORCE_INLINE template<int Pt> FORCE_INLINE
PieceType next_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers, PieceType min_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers,
Bitboard& occupied, Bitboard& attackers) { Bitboard& occupied, Bitboard& attackers) {
if (stmAttackers & bb[Pt]) Bitboard b = stmAttackers & bb[Pt];
{ if (!b)
Bitboard b = stmAttackers & bb[Pt]; return min_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
occupied ^= b & ~(b - 1);
if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN) occupied ^= b & ~(b - 1);
attackers |= attacks_bb<BISHOP>(to, occupied) & (bb[BISHOP] | bb[QUEEN]);
if (Pt == ROOK || Pt == QUEEN) if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
attackers |= attacks_bb<ROOK>(to, occupied) & (bb[ROOK] | bb[QUEEN]); attackers |= attacks_bb<BISHOP>(to, occupied) & (bb[BISHOP] | bb[QUEEN]);
return (PieceType)Pt; if (Pt == ROOK || Pt == QUEEN)
} attackers |= attacks_bb<ROOK>(to, occupied) & (bb[ROOK] | bb[QUEEN]);
return next_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
attackers &= occupied; // After X-ray that may add already processed pieces
return (PieceType)Pt;
} }
template<> FORCE_INLINE template<> FORCE_INLINE
PieceType next_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
} }
@@ -156,13 +110,60 @@ CheckInfo::CheckInfo(const Position& pos) {
} }
/// 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:
/// First, the white halves of the tables are copied from PSQT[] tables. Second,
/// the black halves of the tables are initialized by flipping and changing the
/// sign of the white scores.
void Position::init() {
RKISS rk;
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
Zobrist::psq[c][pt][s] = rk.rand<Key>();
for (File f = FILE_A; f <= FILE_H; f++)
Zobrist::enpassant[f] = rk.rand<Key>();
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
{
Bitboard b = cr;
while (b)
{
Key k = Zobrist::castle[1ULL << pop_lsb(&b)];
Zobrist::castle[cr] ^= k ? k : rk.rand<Key>();
}
}
Zobrist::side = rk.rand<Key>();
Zobrist::exclusion = rk.rand<Key>();
for (PieceType pt = PAWN; pt <= KING; pt++)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
psq[WHITE][pt][ s] = (v + PSQT[pt][s]);
psq[BLACK][pt][~s] = -(v + PSQT[pt][s]);
}
}
}
/// 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 do 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) {
memcpy(this, &pos, sizeof(Position)); std::memcpy(this, &pos, sizeof(Position));
startState = *st; startState = *st;
st = &startState; st = &startState;
nodes = 0; nodes = 0;
@@ -231,7 +232,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
else if ((p = PieceToChar.find(token)) != string::npos) else if ((p = PieceToChar.find(token)) != string::npos)
{ {
put_piece(Piece(p), sq); put_piece(sq, color_of(Piece(p)), type_of(Piece(p)));
sq++; sq++;
} }
} }
@@ -288,7 +289,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
st->key = compute_key(); st->key = compute_key();
st->pawnKey = compute_pawn_key(); st->pawnKey = compute_pawn_key();
st->materialKey = compute_material_key(); st->materialKey = compute_material_key();
st->psqScore = compute_psq_score(); st->psq = compute_psq_score();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE); st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK); st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
@@ -391,16 +392,18 @@ const string Position::pretty(Move move) const {
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine; string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
for (Bitboard b = pieces(); b; )
{
Square s = pop_lsb(&b);
brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)];
}
std::ostringstream ss; std::ostringstream ss;
if (move) if (move)
ss << "\nMove: " << (sideToMove == BLACK ? ".." : "") ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
<< move_to_san(*const_cast<Position*>(this), move); << move_to_san(*const_cast<Position*>(this), move);
for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
if (piece_on(sq) != NO_PIECE)
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << st->key << "\nCheckers: "; << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
@@ -408,43 +411,35 @@ const string Position::pretty(Move move) const {
ss << square_to_string(pop_lsb(&b)) << " "; ss << square_to_string(pop_lsb(&b)) << " ";
ss << "\nLegal moves: "; ss << "\nLegal moves: ";
for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml) for (MoveList<LEGAL> it(*this); *it; ++it)
ss << move_to_san(*const_cast<Position*>(this), ml.move()) << " "; ss << move_to_san(*const_cast<Position*>(this), *it) << " ";
return ss.str(); return ss.str();
} }
/// Position:hidden_checkers<>() returns a bitboard of all pinned (against the /// Position:hidden_checkers() returns a bitboard of all pinned / discovery check
/// king) pieces for the given color. Or, when template parameter FindPinned is /// pieces, according to the call parameters. Pinned pieces protect our king,
/// false, the function return the pieces of the given color candidate for a /// discovery check pieces attack the enemy king.
/// discovery check against the enemy king.
template<bool FindPinned>
Bitboard Position::hidden_checkers() const {
// Pinned pieces protect our king, dicovery checks attack the enemy king Bitboard Position::hidden_checkers(Square ksq, Color c) const {
Bitboard b, result = 0;
Bitboard pinners = pieces(FindPinned ? ~sideToMove : sideToMove);
Square ksq = king_square(FindPinned ? sideToMove : ~sideToMove);
// Pinners are sliders, that give check when candidate pinned is removed Bitboard b, pinners, result = 0;
pinners &= (pieces(ROOK, QUEEN) & PseudoAttacks[ROOK][ksq])
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq]); // Pinners are sliders that give check when pinned piece is removed
pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq])
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(c);
while (pinners) while (pinners)
{ {
b = between_bb(ksq, pop_lsb(&pinners)) & pieces(); b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
if (b && !more_than_one(b) && (b & pieces(sideToMove))) if (!more_than_one(b))
result |= b; result |= b & pieces(sideToMove);
} }
return result; return result;
} }
// Explicit template instantiations
template Bitboard Position::hidden_checkers<true>() const;
template Bitboard Position::hidden_checkers<false>() 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 occ bitboard as occupancy.
@@ -549,7 +544,7 @@ bool Position::is_pseudo_legal(const Move m) const {
return false; return false;
// The destination square cannot be occupied by a friendly piece // The destination square cannot be occupied by a friendly piece
if (piece_on(to) != NO_PIECE && color_of(piece_on(to)) == us) if (pieces(us) & to)
return false; return false;
// Handle the special case of a pawn move // Handle the special case of a pawn move
@@ -659,7 +654,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
return true; return true;
// Discovery check ? // Discovery check ?
if (ci.dcCandidates && (ci.dcCandidates & from)) if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from))
{ {
// For pawn and king moves we need to verify also direction // For pawn and king moves we need to verify also direction
if ( (pt != PAWN && pt != KING) if ( (pt != PAWN && pt != KING)
@@ -697,9 +692,9 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
Square rfrom = to; // 'King captures the rook' notation Square rfrom = to; // 'King captures the rook' notation
Square kto = relative_square(us, rfrom > kfrom ? SQ_G1 : SQ_C1); Square kto = relative_square(us, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, rfrom > kfrom ? SQ_F1 : SQ_D1); Square rto = relative_square(us, rfrom > kfrom ? SQ_F1 : SQ_D1);
Bitboard b = (pieces() ^ kfrom ^ rfrom) | rto | kto;
return attacks_bb<ROOK>(rto, b) & ksq; return (PseudoAttacks[ROOK][rto] & ksq)
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ksq);
} }
default: default:
assert(false); assert(false);
@@ -729,7 +724,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.
memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t)); std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
newSt.previous = st; newSt.previous = st;
st = &newSt; st = &newSt;
@@ -747,17 +742,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Color them = ~us; Color them = ~us;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece piece = piece_on(from); Piece pc = piece_on(from);
PieceType pt = type_of(piece); PieceType pt = type_of(pc);
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
assert(color_of(piece) == us); assert(color_of(pc) == us);
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE); assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
assert(capture != KING); assert(capture != KING);
if (type_of(m) == CASTLE) if (type_of(m) == CASTLE)
{ {
assert(piece == make_piece(us, KING)); assert(pc == make_piece(us, KING));
bool kingSide = to > from; bool kingSide = to > from;
Square rfrom = to; // Castle is encoded as "king captures friendly rook" Square rfrom = to; // Castle is encoded as "king captures friendly rook"
@@ -767,7 +762,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
do_castle(from, to, rfrom, rto); do_castle(from, to, rfrom, rto);
st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto); st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom];
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
} }
@@ -797,22 +792,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
else else
st->npMaterial[them] -= PieceValue[MG][capture]; st->npMaterial[them] -= PieceValue[MG][capture];
// Remove the captured piece // Update board and piece lists
byTypeBB[ALL_PIECES] ^= capsq; remove_piece(capsq, them, capture);
byTypeBB[capture] ^= capsq;
byColorBB[them] ^= capsq;
// Update piece list, move the last piece at index[capsq] position and
// shrink the list.
//
// WARNING: This is a not reversible operation. When we will reinsert the
// captured piece in undo_move() we will put it at the end of the list and
// not in its original place, it means index[] and pieceList[] are not
// guaranteed to be invariant to a do_move() + undo_move() sequence.
Square lastSquare = pieceList[them][capture][--pieceCount[them][capture]];
index[lastSquare] = index[capsq];
pieceList[them][capture][index[lastSquare]] = lastSquare;
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
// Update material hash key and prefetch access to materialTable // Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[them][capture][capsq]; k ^= Zobrist::psq[them][capture][capsq];
@@ -820,7 +801,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
prefetch((char*)thisThread->materialTable[st->materialKey]); prefetch((char*)thisThread->materialTable[st->materialKey]);
// Update incremental scores // Update incremental scores
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq]; st->psq -= psq[them][capture][capsq];
// Reset rule 50 counter // Reset rule 50 counter
st->rule50 = 0; st->rule50 = 0;
@@ -849,20 +830,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Move the piece. The tricky Chess960 castle is handled earlier // Move the piece. The tricky Chess960 castle is handled earlier
if (type_of(m) != CASTLE) if (type_of(m) != CASTLE)
{ move_piece(from, to, us, pt);
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb;
board[from] = NO_PIECE;
board[to] = piece;
// Update piece lists, index[from] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[to] = index[from];
pieceList[us][pt][index[to]] = to;
}
// 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)
@@ -882,29 +850,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
assert(relative_rank(us, to) == RANK_8); assert(relative_rank(us, to) == RANK_8);
assert(promotion >= KNIGHT && promotion <= QUEEN); assert(promotion >= KNIGHT && promotion <= QUEEN);
// Replace the pawn with the promoted piece remove_piece(to, us, PAWN);
byTypeBB[PAWN] ^= to; put_piece(to, us, promotion);
byTypeBB[promotion] |= to;
board[to] = make_piece(us, promotion);
// Update piece lists, move the last pawn at index[to] position
// and shrink the list. Add a new promotion piece to the list.
Square lastSquare = pieceList[us][PAWN][--pieceCount[us][PAWN]];
index[lastSquare] = index[to];
pieceList[us][PAWN][index[lastSquare]] = lastSquare;
pieceList[us][PAWN][pieceCount[us][PAWN]] = SQ_NONE;
index[to] = pieceCount[us][promotion];
pieceList[us][promotion][index[to]] = to;
// Update hash keys // Update hash keys
k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to];
st->pawnKey ^= Zobrist::psq[us][PAWN][to]; st->pawnKey ^= Zobrist::psq[us][PAWN][to];
st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]++] st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1]
^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]];
// Update incremental score // Update incremental score
st->psqScore += pieceSquareTable[make_piece(us, promotion)][to] st->psq += psq[us][promotion][to] - psq[us][PAWN][to];
- pieceSquareTable[make_piece(us, PAWN)][to];
// Update material // Update material
st->npMaterial[us] += PieceValue[MG][promotion]; st->npMaterial[us] += PieceValue[MG][promotion];
@@ -919,7 +875,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
} }
// Update incremental scores // Update incremental scores
st->psqScore += psq_delta(piece, from, to); st->psq += psq[us][pt][to] - psq[us][pt][from];
// Set capture piece // Set capture piece
st->capturedType = capture; st->capturedType = capture;
@@ -985,20 +941,8 @@ void Position::undo_move(Move m) {
assert(relative_rank(us, to) == RANK_8); assert(relative_rank(us, to) == RANK_8);
assert(promotion >= KNIGHT && promotion <= QUEEN); assert(promotion >= KNIGHT && promotion <= QUEEN);
// Replace the promoted piece with the pawn remove_piece(to, us, promotion);
byTypeBB[promotion] ^= to; put_piece(to, us, PAWN);
byTypeBB[PAWN] |= to;
board[to] = make_piece(us, PAWN);
// Update piece lists, move the last promoted piece at index[to] position
// and shrink the list. Add a new pawn to the list.
Square lastSquare = pieceList[us][promotion][--pieceCount[us][promotion]];
index[lastSquare] = index[to];
pieceList[us][promotion][index[lastSquare]] = lastSquare;
pieceList[us][promotion][pieceCount[us][promotion]] = SQ_NONE;
index[to] = pieceCount[us][PAWN]++;
pieceList[us][PAWN][index[to]] = to;
pt = PAWN; pt = PAWN;
} }
@@ -1013,21 +957,7 @@ void Position::undo_move(Move m) {
do_castle(to, from, rto, rfrom); do_castle(to, from, rto, rfrom);
} }
else else
{ move_piece(to, from, us, pt); // Put the piece back at the source square
// Put the piece back at the source square
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb;
board[to] = NO_PIECE;
board[from] = make_piece(us, pt);
// Update piece lists, index[to] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[from] = index[to];
pieceList[us][pt][index[from]] = from;
}
if (capture) if (capture)
{ {
@@ -1043,16 +973,7 @@ void Position::undo_move(Move m) {
assert(piece_on(capsq) == NO_PIECE); assert(piece_on(capsq) == NO_PIECE);
} }
// Restore the captured piece put_piece(capsq, them, capture); // Restore the captured piece
byTypeBB[ALL_PIECES] |= capsq;
byTypeBB[capture] |= capsq;
byColorBB[them] |= capsq;
board[capsq] = make_piece(them, capture);
// Update piece list, add a new captured piece in capsq square
index[capsq] = pieceCount[them][capture]++;
pieceList[them][capture][index[capsq]] = capsq;
} }
// Finally point our state pointer back to the previous state // Finally point our state pointer back to the previous state
@@ -1068,25 +989,12 @@ void Position::undo_move(Move m) {
void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) { void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) {
Color us = sideToMove; // Remove both pieces first since squares could overlap in Chess960
Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto]; remove_piece(kfrom, sideToMove, KING);
Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto]; remove_piece(rfrom, sideToMove, ROOK);
byTypeBB[KING] ^= k_from_to_bb; board[kfrom] = board[rfrom] = NO_PIECE; // Since remove_piece doesn't do it for us
byTypeBB[ROOK] ^= r_from_to_bb; put_piece(kto, sideToMove, KING);
byTypeBB[ALL_PIECES] ^= k_from_to_bb ^ r_from_to_bb; put_piece(rto, sideToMove, ROOK);
byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb;
// Could be from == to, so first set NO_PIECE then KING and ROOK
board[kfrom] = board[rfrom] = NO_PIECE;
board[kto] = make_piece(us, KING);
board[rto] = make_piece(us, ROOK);
// Could be kfrom == rto, so use a 'tmp' variable
int tmp = index[kfrom];
index[rto] = index[rfrom];
index[kto] = tmp;
pieceList[us][KING][index[kto]] = kto;
pieceList[us][ROOK][index[rto]] = rto;
} }
@@ -1097,7 +1005,7 @@ void Position::do_null_move(StateInfo& newSt) {
assert(!checkers()); assert(!checkers());
memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here std::memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here
newSt.previous = st; newSt.previous = st;
st = &newSt; st = &newSt;
@@ -1141,7 +1049,7 @@ int Position::see_sign(Move m) const {
// Early return if SEE cannot be negative because captured piece value // Early return if SEE cannot be negative because captured piece value
// is not less then capturing one. Note that king moves always return // is not less then capturing one. Note that king moves always return
// here because king midgame value is set to 0. // here because king midgame value is set to 0.
if (PieceValue[MG][piece_on(to_sq(m))] >= PieceValue[MG][piece_moved(m)]) if (PieceValue[MG][piece_moved(m)] <= PieceValue[MG][piece_on(to_sq(m))])
return 1; return 1;
return see(m); return see(m);
@@ -1159,36 +1067,31 @@ int Position::see(Move m, int asymmThreshold) const {
from = from_sq(m); from = from_sq(m);
to = to_sq(m); to = to_sq(m);
captured = type_of(piece_on(to)); swapList[0] = PieceValue[MG][type_of(piece_on(to))];
stm = color_of(piece_on(from));
occupied = pieces() ^ from; occupied = pieces() ^ from;
// Handle en passant moves // Castle moves are implemented as king capturing the rook so cannot be
// handled correctly. Simply return 0 that is always the correct value
// unless in the rare case the rook ends up under attack.
if (type_of(m) == CASTLE)
return 0;
if (type_of(m) == ENPASSANT) if (type_of(m) == ENPASSANT)
{ {
Square capQq = to - pawn_push(sideToMove); occupied ^= to - pawn_push(stm); // Remove the captured pawn
swapList[0] = PieceValue[MG][PAWN];
assert(!captured);
assert(type_of(piece_on(capQq)) == PAWN);
// Remove the captured pawn
occupied ^= capQq;
captured = PAWN;
} }
else if (type_of(m) == CASTLE)
// Castle moves are implemented as king capturing the rook so cannot be
// handled correctly. Simply return 0 that is always the correct value
// unless the rook is ends up under attack.
return 0;
// Find all attackers to the destination square, with the moving piece // Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it. // removed, but possibly an X-ray attacker added behind it.
attackers = attackers_to(to, occupied); attackers = attackers_to(to, occupied) & occupied;
// If the opponent has no attackers we are finished // If the opponent has no attackers we are finished
stm = ~color_of(piece_on(from)); stm = ~stm;
stmAttackers = attackers & pieces(stm); stmAttackers = attackers & pieces(stm);
if (!stmAttackers) if (!stmAttackers)
return PieceValue[MG][captured]; return swapList[0];
// The destination square is defended, which makes things rather more // The destination square is defended, which makes things rather more
// difficult to compute. We proceed by building up a "swap list" containing // difficult to compute. We proceed by building up a "swap list" containing
@@ -1196,7 +1099,6 @@ int Position::see(Move m, int asymmThreshold) const {
// destination square, where the sides alternately capture, and always // destination square, where the sides alternately capture, and always
// capture with the least valuable piece. After each capture, we look for // capture with the least valuable piece. After each capture, we look for
// new X-ray attacks from behind the capturing piece. // new X-ray attacks from behind the capturing piece.
swapList[0] = PieceValue[MG][captured];
captured = type_of(piece_on(from)); captured = type_of(piece_on(from));
do { do {
@@ -1206,19 +1108,15 @@ int Position::see(Move m, int asymmThreshold) const {
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured]; swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
slIndex++; slIndex++;
// Locate and remove from 'occupied' the next least valuable attacker // Locate and remove the next least valuable attacker
captured = next_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers); captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
attackers &= occupied; // Remove the just found attacker
stm = ~stm; stm = ~stm;
stmAttackers = attackers & pieces(stm); stmAttackers = attackers & pieces(stm);
if (captured == KING) // Stop before processing a king capture
if (captured == KING && stmAttackers)
{ {
// Stop before processing a king capture swapList[slIndex++] = QueenValueMg * 16;
if (stmAttackers)
swapList[slIndex++] = QueenValueMg * 16;
break; break;
} }
@@ -1247,7 +1145,7 @@ int Position::see(Move m, int asymmThreshold) const {
void Position::clear() { void Position::clear() {
memset(this, 0, sizeof(Position)); std::memset(this, 0, sizeof(Position));
startState.epSquare = SQ_NONE; startState.epSquare = SQ_NONE;
st = &startState; st = &startState;
@@ -1257,24 +1155,6 @@ void Position::clear() {
} }
/// Position::put_piece() puts a piece on the given square of the board,
/// updating the board array, pieces list, bitboards, and piece counts.
void Position::put_piece(Piece p, Square s) {
Color c = color_of(p);
PieceType pt = type_of(p);
board[s] = p;
index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s;
byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s;
byColorBB[c] |= s;
}
/// 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
@@ -1332,7 +1212,7 @@ Key Position::compute_material_key() const {
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= QUEEN; pt++) for (PieceType pt = PAWN; pt <= QUEEN; pt++)
for (int cnt = 0; cnt < piece_count(c, pt); cnt++) for (int cnt = 0; cnt < pieceCount[c][pt]; cnt++)
k ^= Zobrist::psq[c][pt][cnt]; k ^= Zobrist::psq[c][pt][cnt];
return k; return k;
@@ -1350,7 +1230,8 @@ Score Position::compute_psq_score() const {
for (Bitboard b = pieces(); b; ) for (Bitboard b = pieces(); b; )
{ {
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
score += pieceSquareTable[piece_on(s)][s]; Piece pc = piece_on(s);
score += psq[color_of(pc)][type_of(pc)][s];
} }
return score; return score;
@@ -1367,7 +1248,7 @@ Value Position::compute_non_pawn_material(Color c) const {
Value value = VALUE_ZERO; Value value = VALUE_ZERO;
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++) for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
value += piece_count(c, pt) * PieceValue[MG][pt]; value += pieceCount[c][pt] * PieceValue[MG][pt];
return value; return value;
} }
@@ -1412,42 +1293,36 @@ 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 especially for finding evaluation symmetry bugs.
static char toggle_case(char c) {
return char(islower(c) ? toupper(c) : tolower(c));
}
void Position::flip() { void Position::flip() {
const Position pos(*this); string f, token;
std::stringstream ss(fen());
clear(); for (Rank rank = RANK_8; rank >= RANK_1; rank--) // Piece placement
{
std::getline(ss, token, rank > RANK_1 ? '/' : ' ');
f.insert(0, token + (f.empty() ? " " : "/"));
}
sideToMove = ~pos.side_to_move(); ss >> token; // Active color
thisThread = pos.this_thread(); f += (token == "w" ? "B " : "W "); // Will be lowercased later
nodes = pos.nodes_searched();
chess960 = pos.is_chess960();
gamePly = pos.game_ply();
for (Square s = SQ_A1; s <= SQ_H8; s++) ss >> token; // Castling availability
if (!pos.is_empty(s)) f += token + " ";
put_piece(Piece(pos.piece_on(s) ^ 8), ~s);
if (pos.can_castle(WHITE_OO)) std::transform(f.begin(), f.end(), f.begin(), toggle_case);
set_castle_right(BLACK, ~pos.castle_rook_square(WHITE, KING_SIDE));
if (pos.can_castle(WHITE_OOO))
set_castle_right(BLACK, ~pos.castle_rook_square(WHITE, QUEEN_SIDE));
if (pos.can_castle(BLACK_OO))
set_castle_right(WHITE, ~pos.castle_rook_square(BLACK, KING_SIDE));
if (pos.can_castle(BLACK_OOO))
set_castle_right(WHITE, ~pos.castle_rook_square(BLACK, QUEEN_SIDE));
if (pos.st->epSquare != SQ_NONE) ss >> token; // En passant square
st->epSquare = ~pos.st->epSquare; f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3"));
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); std::getline(ss, token); // Half and full moves
f += token;
st->key = compute_key(); set(f, is_chess960(), this_thread());
st->pawnKey = compute_pawn_key();
st->materialKey = compute_material_key();
st->psqScore = compute_psq_score();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
assert(pos_is_ok()); assert(pos_is_ok());
} }
@@ -1536,15 +1411,13 @@ bool Position::pos_is_ok(int* failedStep) const {
if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key()) if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key())
return false; return false;
if ((*step)++, debugIncrementalEval && st->psqScore != compute_psq_score()) if ((*step)++, debugIncrementalEval && st->psq != compute_psq_score())
return false; return false;
if ((*step)++, debugNonPawnMaterial) if ((*step)++, debugNonPawnMaterial)
{
if ( st->npMaterial[WHITE] != compute_non_pawn_material(WHITE) if ( st->npMaterial[WHITE] != compute_non_pawn_material(WHITE)
|| st->npMaterial[BLACK] != compute_non_pawn_material(BLACK)) || st->npMaterial[BLACK] != compute_non_pawn_material(BLACK))
return false; return false;
}
if ((*step)++, debugPieceCounts) if ((*step)++, debugPieceCounts)
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
@@ -1556,14 +1429,10 @@ bool Position::pos_is_ok(int* failedStep) const {
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++) for (PieceType pt = PAWN; pt <= KING; pt++)
for (int i = 0; i < pieceCount[c][pt]; i++) for (int i = 0; i < pieceCount[c][pt]; i++)
{ if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
if (piece_on(piece_list(c, pt)[i]) != make_piece(c, pt)) || index[pieceList[c][pt][i]] != i)
return false; return false;
if (index[piece_list(c, pt)[i]] != i)
return false;
}
if ((*step)++, debugCastleSquares) if ((*step)++, debugCastleSquares)
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
@@ -1573,10 +1442,8 @@ bool Position::pos_is_ok(int* failedStep) const {
if (!can_castle(cr)) if (!can_castle(cr))
continue; continue;
if ((castleRightsMask[king_square(c)] & cr) != cr) if ( (castleRightsMask[king_square(c)] & cr) != cr
return false; || piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK)
if ( piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK)
|| castleRightsMask[castleRookSquare[c][s]] != cr) || castleRightsMask[castleRookSquare[c][s]] != cr)
return false; return false;
} }

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(POSITION_H_INCLUDED) #ifndef POSITION_H_INCLUDED
#define POSITION_H_INCLUDED #define POSITION_H_INCLUDED
#include <cassert> #include <cassert>
@@ -52,7 +52,7 @@ struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
Value npMaterial[COLOR_NB]; Value npMaterial[COLOR_NB];
int castleRights, rule50, pliesFromNull; int castleRights, rule50, pliesFromNull;
Score psqScore; Score psq;
Square epSquare; Square epSquare;
Key key; Key key;
@@ -95,6 +95,7 @@ public:
Position(const Position& p, Thread* t) { *this = p; thisThread = t; } Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); } Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
Position& operator=(const Position&); Position& operator=(const Position&);
static void init();
// Text input/output // Text input/output
void set(const std::string& fen, bool isChess960, Thread* th); void set(const std::string& fen, bool isChess960, Thread* th);
@@ -112,8 +113,8 @@ public:
Square king_square(Color c) const; Square king_square(Color c) const;
Square ep_square() const; Square ep_square() const;
bool is_empty(Square s) const; bool is_empty(Square s) const;
const Square* piece_list(Color c, PieceType pt) const; template<PieceType Pt> int count(Color c) const;
int piece_count(Color c, PieceType pt) const; template<PieceType Pt> const Square* list(Color c) const;
// Castling // Castling
int can_castle(CastleRight f) const; int can_castle(CastleRight f) const;
@@ -169,7 +170,6 @@ public:
// Incremental piece-square evaluation // Incremental piece-square evaluation
Score psq_score() const; Score psq_score() const;
Score psq_delta(Piece p, Square from, Square to) const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
// Other properties of the position // Other properties of the position
@@ -188,12 +188,14 @@ public:
private: private:
// Initialization helpers (used while setting up a position) // Initialization helpers (used while setting up a position)
void clear(); void clear();
void put_piece(Piece p, Square s);
void set_castle_right(Color c, Square rfrom); void set_castle_right(Color c, Square rfrom);
// Helper functions // Helper functions
void do_castle(Square kfrom, Square kto, Square rfrom, Square rto); void do_castle(Square kfrom, Square kto, Square rfrom, Square rto);
template<bool FindPinned> Bitboard hidden_checkers() const; Bitboard hidden_checkers(Square ksq, Color c) const;
void put_piece(Square s, Color c, PieceType pt);
void remove_piece(Square s, Color c, PieceType pt);
void move_piece(Square from, Square to, Color c, PieceType pt);
// Computing hash keys from scratch (for initialization and debugging) // Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const; Key compute_key() const;
@@ -273,12 +275,12 @@ inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]); return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
} }
inline int Position::piece_count(Color c, PieceType pt) const { template<PieceType Pt> inline int Position::count(Color c) const {
return pieceCount[c][pt]; return pieceCount[c][Pt];
} }
inline const Square* Position::piece_list(Color c, PieceType pt) const { template<PieceType Pt> inline const Square* Position::list(Color c) const {
return pieceList[c][pt]; return pieceList[c][Pt];
} }
inline Square Position::ep_square() const { inline Square Position::ep_square() const {
@@ -331,11 +333,11 @@ inline Bitboard Position::checkers() const {
} }
inline Bitboard Position::discovered_check_candidates() const { inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers<false>(); return hidden_checkers(king_square(~sideToMove), sideToMove);
} }
inline Bitboard Position::pinned_pieces() const { inline Bitboard Position::pinned_pieces() const {
return hidden_checkers<true>(); return hidden_checkers(king_square(sideToMove), ~sideToMove);
} }
inline bool Position::pawn_is_passed(Color c, Square s) const { inline bool Position::pawn_is_passed(Color c, Square s) const {
@@ -346,10 +348,6 @@ inline Key Position::key() const {
return st->key; return st->key;
} }
inline Key Position::exclusion_key() const {
return st->key ^ Zobrist::exclusion;
}
inline Key Position::pawn_key() const { inline Key Position::pawn_key() const {
return st->pawnKey; return st->pawnKey;
} }
@@ -358,12 +356,8 @@ inline Key Position::material_key() const {
return st->materialKey; return st->materialKey;
} }
inline Score Position::psq_delta(Piece p, Square from, Square to) const {
return pieceSquareTable[p][to] - pieceSquareTable[p][from];
}
inline Score Position::psq_score() const { inline Score Position::psq_score() const {
return st->psqScore; return st->psq;
} }
inline Value Position::non_pawn_material(Color c) const { inline Value Position::non_pawn_material(Color c) const {
@@ -422,4 +416,44 @@ inline Thread* Position::this_thread() const {
return thisThread; return thisThread;
} }
#endif // !defined(POSITION_H_INCLUDED) inline void Position::put_piece(Square s, Color c, PieceType pt) {
board[s] = make_piece(c, pt);
byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s;
byColorBB[c] |= s;
index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s;
}
inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) {
// index[from] is not updated and becomes stale. This works as long
// as index[] is accessed just by known occupied squares.
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[c] ^= from_to_bb;
board[from] = NO_PIECE;
board[to] = make_piece(c, pt);
index[to] = index[from];
pieceList[c][pt][index[to]] = to;
}
inline void Position::remove_piece(Square s, Color c, PieceType pt) {
// WARNING: This is not a reversible operation. If we remove a piece in
// do_move() and then replace it in undo_move() we will put it at the end of
// the list and not in its original place, it means index[] and pieceList[]
// are not guaranteed to be invariant to a do_move() + undo_move() sequence.
byTypeBB[ALL_PIECES] ^= s;
byTypeBB[pt] ^= s;
byColorBB[c] ^= s;
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
index[lastSquare] = index[s];
pieceList[c][pt][index[lastSquare]] = lastSquare;
pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE;
}
#endif // #ifndef POSITION_H_INCLUDED

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(PSQTAB_H_INCLUDED) #ifndef PSQTAB_H_INCLUDED
#define PSQTAB_H_INCLUDED #define PSQTAB_H_INCLUDED
#include "types.h" #include "types.h"
@@ -33,12 +33,12 @@ static const Score PSQT[][SQUARE_NB] = {
{ }, { },
{ // Pawn { // Pawn
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0),
S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S( 9,-8), S(36,-8), S(36,-8), S( 9,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S( 9,-8), S(34,-8), S(34,-8), S( 9,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S(17,-8), S(58,-8), S(58,-8), S(17,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S(17,-8), S(54,-8), S(54,-8), S(17,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S(17,-8), S(36,-8), S(36,-8), S(17,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S(17,-8), S(34,-8), S(34,-8), S(17,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8),
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0) S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0)
}, },
{ // Knight { // Knight
@@ -95,4 +95,4 @@ static const Score PSQT[][SQUARE_NB] = {
#undef S #undef S
#endif // !defined(PSQTAB_H_INCLUDED) #endif // #ifndef PSQTAB_H_INCLUDED

View File

@@ -22,7 +22,7 @@
(at your option) any later version. (at your option) any later version.
*/ */
#if !defined(RKISS_H_INCLUDED) #ifndef RKISS_H_INCLUDED
#define RKISS_H_INCLUDED #define RKISS_H_INCLUDED
#include "types.h" #include "types.h"
@@ -43,14 +43,12 @@
class RKISS { class RKISS {
// Keep variables always together struct S { uint64_t a, b, c, d; } s; // Keep variables always together
struct S { uint64_t a, b, c, d; } s;
uint64_t rotate(uint64_t x, uint64_t k) const { uint64_t rotate(uint64_t x, uint64_t k) const {
return (x << k) | (x >> (64 - k)); return (x << k) | (x >> (64 - k));
} }
// Return 64 bit unsigned integer in between [0, 2^64 - 1]
uint64_t rand64() { uint64_t rand64() {
const uint64_t const uint64_t
@@ -73,4 +71,4 @@ public:
template<typename T> T rand() { return T(rand64()); } template<typename T> T rand() { return T(rand64()); }
}; };
#endif // !defined(RKISS_H_INCLUDED) #endif // #ifndef RKISS_H_INCLUDED

View File

@@ -66,7 +66,7 @@ namespace {
// Futility lookup tables (initialized at startup) and their access functions // Futility lookup tables (initialized at startup) and their access functions
Value FutilityMargins[16][64]; // [depth][moveNumber] Value FutilityMargins[16][64]; // [depth][moveNumber]
int FutilityMoveCounts[32]; // [depth] int FutilityMoveCounts[2][32]; // [improving][depth]
inline Value futility_margin(Depth d, int mn) { inline Value futility_margin(Depth d, int mn) {
@@ -75,22 +75,23 @@ namespace {
} }
// Reduction lookup tables (initialized at startup) and their access function // Reduction lookup tables (initialized at startup) and their access function
int8_t Reductions[2][64][64]; // [pv][depth][moveNumber] int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
template <bool PvNode> inline Depth reduction(Depth d, int mn) { template <bool PvNode> inline Depth reduction(bool i, Depth d, int mn) {
return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)]; return (Depth) Reductions[PvNode][i][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
} }
size_t PVSize, PVIdx; size_t PVSize, PVIdx;
TimeManager TimeMgr; TimeManager TimeMgr;
int BestMoveChanges; int BestMoveChanges;
Value DrawValue[COLOR_NB]; Value DrawValue[COLOR_NB];
History Hist; HistoryStats History;
Gains Gain; GainsStats Gains;
CountermovesStats Countermoves;
template <NodeType NT> template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
template <NodeType NT, bool InCheck> template <NodeType NT, bool InCheck>
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth);
@@ -135,8 +136,14 @@ void Search::init() {
{ {
double pvRed = log(double(hd)) * log(double(mc)) / 3.0; double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25; double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
Reductions[1][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(ONE_PLY)) : 0); Reductions[1][1][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(ONE_PLY)) : 0);
Reductions[0][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(ONE_PLY)) : 0); Reductions[0][1][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(ONE_PLY)) : 0);
Reductions[1][0][hd][mc] = Reductions[1][1][hd][mc];
Reductions[0][0][hd][mc] = Reductions[0][1][hd][mc];
if (Reductions[0][0][hd][mc] > 2 * ONE_PLY)
Reductions[0][0][hd][mc] += ONE_PLY;
} }
// Init futility margins array // Init futility margins array
@@ -145,33 +152,35 @@ void Search::init() {
// Init futility move count array // Init futility move count array
for (d = 0; d < 32; d++) for (d = 0; d < 32; d++)
FutilityMoveCounts[d] = int(3.001 + 0.25 * pow(double(d), 2.0)); {
FutilityMoveCounts[0][d] = int(3.001 + 0.3 * pow(double(d ), 1.8)) * (d < 5 ? 4 : 3) / 4;
FutilityMoveCounts[1][d] = int(3.001 + 0.3 * pow(double(d + 0.98), 1.8));
}
} }
/// Search::perft() is our utility to verify move generation. All the leaf nodes /// Search::perft() is our utility to verify move generation. All the leaf nodes
/// up to the given depth are generated and counted and the sum returned. /// up to the given depth are generated and counted and the sum returned.
size_t Search::perft(Position& pos, Depth depth) { static size_t perft(Position& pos, Depth depth) {
// At the last ply just return the number of legal moves (leaf nodes)
if (depth == ONE_PLY)
return MoveList<LEGAL>(pos).size();
StateInfo st; StateInfo st;
size_t cnt = 0; size_t cnt = 0;
CheckInfo ci(pos); CheckInfo ci(pos);
const bool leaf = depth == 2 * ONE_PLY;
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml) for (MoveList<LEGAL> it(pos); *it; ++it)
{ {
pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci)); pos.do_move(*it, st, ci, pos.move_gives_check(*it, ci));
cnt += perft(pos, depth - ONE_PLY); cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY);
pos.undo_move(ml.move()); pos.undo_move(*it);
} }
return cnt; return cnt;
} }
size_t Search::perft(Position& pos, Depth depth) {
return depth > ONE_PLY ? ::perft(pos, depth) : MoveList<LEGAL>(pos).size();
}
/// Search::think() is the external interface to Stockfish's search, and is /// Search::think() is the external interface to Stockfish's search, and is
/// called by the main thread when the program receives the UCI 'go' command. It /// called by the main thread when the program receives the UCI 'go' command. It
@@ -215,7 +224,7 @@ void Search::think() {
else else
DrawValue[WHITE] = DrawValue[BLACK] = VALUE_DRAW; DrawValue[WHITE] = DrawValue[BLACK] = VALUE_DRAW;
if (Options["Use Search Log"]) if (Options["Write Search Log"])
{ {
Log log(Options["Search Log Filename"]); Log log(Options["Search Log Filename"]);
log << "\nSearching: " << RootPos.fen() log << "\nSearching: " << RootPos.fen()
@@ -231,7 +240,7 @@ void Search::think() {
for (size_t i = 0; i < Threads.size(); i++) for (size_t i = 0; i < Threads.size(); i++)
Threads[i]->maxPly = 0; Threads[i]->maxPly = 0;
Threads.sleepWhileIdle = Options["Use Sleeping Threads"]; Threads.sleepWhileIdle = Options["Idle Threads Sleep"];
// Set best timer interval to avoid lagging under time pressure. Timer is // Set best timer interval to avoid lagging under time pressure. Timer is
// used to check for remaining available thinking time. // used to check for remaining available thinking time.
@@ -247,7 +256,7 @@ void Search::think() {
Threads.timer->msec = 0; // Stop the timer Threads.timer->msec = 0; // Stop the timer
Threads.sleepWhileIdle = true; // Send idle threads to sleep Threads.sleepWhileIdle = true; // Send idle threads to sleep
if (Options["Use Search Log"]) if (Options["Write Search Log"])
{ {
Time::point elapsed = Time::now() - SearchTime + 1; Time::point elapsed = Time::now() - SearchTime + 1;
@@ -264,6 +273,10 @@ void Search::think() {
finalize: finalize:
// When search is stopped this info is not printed
sync_cout << "info nodes " << RootPos.nodes_searched()
<< " time " << Time::now() - SearchTime + 1 << sync_endl;
// When we reach max depth we arrive here even without Signals.stop is raised, // When we reach max depth we arrive here even without Signals.stop is raised,
// but if we are pondering or in infinite search, according to UCI protocol, // but if we are pondering or in infinite search, according to UCI protocol,
// we shouldn't print the best move before the GUI sends a "stop" or "ponderhit" // we shouldn't print the best move before the GUI sends a "stop" or "ponderhit"
@@ -290,17 +303,21 @@ namespace {
void id_loop(Position& pos) { void id_loop(Position& pos) {
Stack ss[MAX_PLY_PLUS_2]; Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
int depth, prevBestMoveChanges; int depth, prevBestMoveChanges;
Value bestValue, alpha, beta, delta; Value bestValue, alpha, beta, delta;
memset(ss, 0, 4 * sizeof(Stack)); std::memset(ss-2, 0, 5 * sizeof(Stack));
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
depth = BestMoveChanges = 0; depth = BestMoveChanges = 0;
bestValue = delta = -VALUE_INFINITE; bestValue = delta = alpha = -VALUE_INFINITE;
ss->currentMove = MOVE_NULL; // Hack to skip update gains beta = VALUE_INFINITE;
TT.new_search(); TT.new_search();
Hist.clear(); History.clear();
Gain.clear(); Gains.clear();
Countermoves.clear();
PVSize = Options["MultiPV"]; PVSize = Options["MultiPV"];
Skill skill(Options["Skill Level"]); Skill skill(Options["Skill Level"]);
@@ -326,26 +343,19 @@ namespace {
// MultiPV loop. We perform a full root search for each PV line // MultiPV loop. We perform a full root search for each PV line
for (PVIdx = 0; PVIdx < PVSize; PVIdx++) for (PVIdx = 0; PVIdx < PVSize; PVIdx++)
{ {
// Set aspiration window default width // Reset aspiration window starting size
if (depth >= 5 && abs(RootMoves[PVIdx].prevScore) < VALUE_KNOWN_WIN) if (depth >= 5)
{ {
delta = Value(16); delta = Value(16);
alpha = RootMoves[PVIdx].prevScore - delta; alpha = std::max(RootMoves[PVIdx].prevScore - delta,-VALUE_INFINITE);
beta = RootMoves[PVIdx].prevScore + delta; beta = std::min(RootMoves[PVIdx].prevScore + delta, VALUE_INFINITE);
}
else
{
alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
} }
// Start with a small aspiration window and, in case of fail high/low, // Start with a small aspiration window and, in case of fail high/low,
// research with bigger window until not failing high/low anymore. // research with bigger window until not failing high/low anymore.
while (true) while (true)
{ {
// Search starts from ss+1 to allow referencing (ss-1). This is bestValue = search<Root>(pos, ss, alpha, beta, depth * ONE_PLY, false);
// needed by update gains and ss copy when splitting at Root.
bestValue = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
// Bring to front the best move. It is critical that sorting is // Bring to front the best move. It is critical that sorting is
// done with a stable algorithm because all the values but the first // done with a stable algorithm because all the values but the first
@@ -366,33 +376,28 @@ namespace {
if (Signals.stop) if (Signals.stop)
return; return;
// In case of failing high/low increase aspiration window and // When failing high/low give some update (without cluttering
// research, otherwise exit the loop. // the UI) before to research.
if (bestValue > alpha && bestValue < beta) if ( (bestValue <= alpha || bestValue >= beta)
break; && Time::now() - SearchTime > 3000)
// Give some update (without cluttering the UI) before to research
if (Time::now() - SearchTime > 3000)
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
if (abs(bestValue) >= VALUE_KNOWN_WIN) // In case of failing low/high increase aspiration window and
{ // research, otherwise exit the loop.
alpha = -VALUE_INFINITE; if (bestValue <= alpha)
beta = VALUE_INFINITE;
}
else if (bestValue >= beta)
{
beta += delta;
delta += delta / 2;
}
else
{ {
alpha = std::max(bestValue - delta, -VALUE_INFINITE);
Signals.failedLowAtRoot = true; Signals.failedLowAtRoot = true;
Signals.stopOnPonderhit = false; Signals.stopOnPonderhit = false;
alpha -= delta;
delta += delta / 2;
} }
else if (bestValue >= beta)
beta = std::min(bestValue + delta, VALUE_INFINITE);
else
break;
delta += delta / 2;
assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
} }
@@ -408,10 +413,14 @@ namespace {
if (skill.enabled() && skill.time_to_pick(depth)) if (skill.enabled() && skill.time_to_pick(depth))
skill.pick_move(); skill.pick_move();
if (Options["Use Search Log"]) if (Options["Write Search Log"])
{ {
RootMove& rm = RootMoves[0];
if (skill.best != MOVE_NONE)
rm = *std::find(RootMoves.begin(), RootMoves.end(), skill.best);
Log log(Options["Search Log Filename"]); Log log(Options["Search Log Filename"]);
log << pretty_pv(pos, depth, bestValue, Time::now() - SearchTime, &RootMoves[0].pv[0]) log << pretty_pv(pos, depth, rm.score, Time::now() - SearchTime, &rm.pv[0])
<< std::endl; << std::endl;
} }
@@ -445,11 +454,11 @@ namespace {
|| Time::now() - SearchTime > (TimeMgr.available_time() * 20) / 100)) || Time::now() - SearchTime > (TimeMgr.available_time() * 20) / 100))
{ {
Value rBeta = bestValue - 2 * PawnValueMg; Value rBeta = bestValue - 2 * PawnValueMg;
(ss+1)->excludedMove = RootMoves[0].pv[0]; ss->excludedMove = RootMoves[0].pv[0];
(ss+1)->skipNullMove = true; ss->skipNullMove = true;
Value v = search<NonPV>(pos, ss+1, rBeta - 1, rBeta, (depth - 3) * ONE_PLY); Value v = search<NonPV>(pos, ss, rBeta - 1, rBeta, (depth - 3) * ONE_PLY, true);
(ss+1)->skipNullMove = false; ss->skipNullMove = false;
(ss+1)->excludedMove = MOVE_NONE; ss->excludedMove = MOVE_NONE;
if (v < rBeta) if (v < rBeta)
stop = true; stop = true;
@@ -477,7 +486,7 @@ namespace {
// here: This is taken care of after we return from the split point. // 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) { Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) {
const bool PvNode = (NT == PV || NT == Root || NT == SplitPointPV || NT == SplitPointRoot); const bool PvNode = (NT == PV || NT == Root || NT == SplitPointPV || NT == SplitPointRoot);
const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV || NT == SplitPointRoot); const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV || NT == SplitPointRoot);
@@ -487,7 +496,7 @@ namespace {
assert(PvNode || (alpha == beta - 1)); assert(PvNode || (alpha == beta - 1));
assert(depth > DEPTH_ZERO); assert(depth > DEPTH_ZERO);
Move movesSearched[64]; Move quietsSearched[64];
StateInfo st; StateInfo st;
const TTEntry *tte; const TTEntry *tte;
SplitPoint* splitPoint; SplitPoint* splitPoint;
@@ -496,13 +505,12 @@ namespace {
Depth ext, newDepth; Depth ext, newDepth;
Value bestValue, value, ttValue; Value bestValue, value, ttValue;
Value eval, nullValue, futilityValue; Value eval, nullValue, futilityValue;
bool inCheck, givesCheck, pvMove, singularExtensionNode; bool inCheck, givesCheck, pvMove, singularExtensionNode, improving;
bool captureOrPromotion, dangerous, doFullDepthSearch; bool captureOrPromotion, dangerous, doFullDepthSearch;
int moveCount, playedMoveCount; int moveCount, quietCount;
// Step 1. Initialize node // Step 1. Initialize node
Thread* thisThread = pos.this_thread(); Thread* thisThread = pos.this_thread();
moveCount = playedMoveCount = 0;
inCheck = pos.checkers(); inCheck = pos.checkers();
if (SpNode) if (SpNode)
@@ -517,9 +525,10 @@ namespace {
assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0); assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0);
goto split_point_start; goto moves_loop;
} }
moveCount = quietCount = 0;
bestValue = -VALUE_INFINITE; bestValue = -VALUE_INFINITE;
ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
ss->ply = (ss-1)->ply + 1; ss->ply = (ss-1)->ply + 1;
@@ -566,9 +575,9 @@ namespace {
&& tte && tte
&& tte->depth() >= depth && tte->depth() >= depth
&& ttValue != VALUE_NONE // Only in case of TT access race && ttValue != VALUE_NONE // Only in case of TT access race
&& ( PvNode ? tte->type() == BOUND_EXACT && ( PvNode ? tte->bound() == BOUND_EXACT
: ttValue >= beta ? (tte->type() & BOUND_LOWER) : ttValue >= beta ? (tte->bound() & BOUND_LOWER)
: (tte->type() & BOUND_UPPER))) : (tte->bound() & BOUND_UPPER)))
{ {
TT.refresh(tte); TT.refresh(tte);
ss->currentMove = ttMove; // Can be MOVE_NONE ss->currentMove = ttMove; // Can be MOVE_NONE
@@ -586,7 +595,10 @@ namespace {
// Step 5. Evaluate the position statically and update parent's gain statistics // Step 5. Evaluate the position statically and update parent's gain statistics
if (inCheck) if (inCheck)
{
ss->staticEval = ss->evalMargin = eval = VALUE_NONE; ss->staticEval = ss->evalMargin = eval = VALUE_NONE;
goto moves_loop;
}
else if (tte) else if (tte)
{ {
@@ -597,8 +609,8 @@ namespace {
// Can ttValue be used as a better position evaluation? // Can ttValue be used as a better position evaluation?
if (ttValue != VALUE_NONE) if (ttValue != VALUE_NONE)
if ( ((tte->type() & BOUND_LOWER) && ttValue > eval) if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval)
|| ((tte->type() & BOUND_UPPER) && ttValue < eval)) || ((tte->bound() & BOUND_UPPER) && ttValue < eval))
eval = ttValue; eval = ttValue;
} }
else else
@@ -610,20 +622,19 @@ namespace {
// Update gain for the parent non-capture move given the static position // Update gain for the parent non-capture move given the static position
// evaluation before and after the move. // evaluation before and after the move.
if ( (move = (ss-1)->currentMove) != MOVE_NULL if ( !pos.captured_piece_type()
&& (ss-1)->staticEval != VALUE_NONE
&& ss->staticEval != VALUE_NONE && ss->staticEval != VALUE_NONE
&& !pos.captured_piece_type() && (ss-1)->staticEval != VALUE_NONE
&& (move = (ss-1)->currentMove) != MOVE_NULL
&& type_of(move) == NORMAL) && type_of(move) == NORMAL)
{ {
Square to = to_sq(move); Square to = to_sq(move);
Gain.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval); Gains.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval);
} }
// Step 6. Razoring (is omitted in PV nodes) // Step 6. Razoring (skipped when in check)
if ( !PvNode if ( !PvNode
&& depth < 4 * ONE_PLY && depth < 4 * ONE_PLY
&& !inCheck
&& eval + razor_margin(depth) < beta && eval + razor_margin(depth) < beta
&& ttMove == MOVE_NONE && ttMove == MOVE_NONE
&& abs(beta) < VALUE_MATE_IN_MAX_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY
@@ -637,13 +648,12 @@ namespace {
return v; return v;
} }
// Step 7. Static null move pruning (is omitted in PV nodes) // Step 7. Static null move pruning (skipped when in check)
// We're betting that the opponent doesn't have a move that will reduce // We're betting that the opponent doesn't have a move that will reduce
// the score by more than futility_margin(depth) if we do a null move. // the score by more than futility_margin(depth) if we do a null move.
if ( !PvNode if ( !PvNode
&& !ss->skipNullMove && !ss->skipNullMove
&& depth < 4 * ONE_PLY && depth < 4 * ONE_PLY
&& !inCheck
&& eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta && eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY
&& abs(eval) < VALUE_KNOWN_WIN && abs(eval) < VALUE_KNOWN_WIN
@@ -654,7 +664,6 @@ namespace {
if ( !PvNode if ( !PvNode
&& !ss->skipNullMove && !ss->skipNullMove
&& depth > ONE_PLY && depth > ONE_PLY
&& !inCheck
&& eval >= beta && eval >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY
&& pos.non_pawn_material(pos.side_to_move())) && pos.non_pawn_material(pos.side_to_move()))
@@ -671,7 +680,7 @@ namespace {
pos.do_null_move(st); pos.do_null_move(st);
(ss+1)->skipNullMove = true; (ss+1)->skipNullMove = true;
nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO) nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R); : - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R, !cutNode);
(ss+1)->skipNullMove = false; (ss+1)->skipNullMove = false;
pos.undo_null_move(); pos.undo_null_move();
@@ -686,7 +695,7 @@ namespace {
// Do verification search at high depths // Do verification search at high depths
ss->skipNullMove = true; ss->skipNullMove = true;
Value v = search<NonPV>(pos, ss, alpha, beta, depth-R); Value v = search<NonPV>(pos, ss, alpha, beta, depth-R, false);
ss->skipNullMove = false; ss->skipNullMove = false;
if (v >= beta) if (v >= beta)
@@ -706,19 +715,17 @@ namespace {
&& (ss-1)->reduction && (ss-1)->reduction
&& threatMove != MOVE_NONE && threatMove != MOVE_NONE
&& allows(pos, (ss-1)->currentMove, threatMove)) && allows(pos, (ss-1)->currentMove, threatMove))
return beta - 1; return alpha;
} }
} }
// Step 9. ProbCut (is omitted in PV nodes) // Step 9. ProbCut (skipped when in check)
// If we have a very good capture (i.e. SEE > seeValues[captured_piece_type]) // If we have a very good capture (i.e. SEE > seeValues[captured_piece_type])
// and a reduced search returns a value much above beta, we can (almost) safely // and a reduced search returns a value much above beta, we can (almost) safely
// prune the previous move. // prune the previous move.
if ( !PvNode if ( !PvNode
&& depth >= 5 * ONE_PLY && depth >= 5 * ONE_PLY
&& !inCheck
&& !ss->skipNullMove && !ss->skipNullMove
&& excludedMove == MOVE_NONE
&& abs(beta) < VALUE_MATE_IN_MAX_PLY) && abs(beta) < VALUE_MATE_IN_MAX_PLY)
{ {
Value rbeta = beta + 200; Value rbeta = beta + 200;
@@ -728,7 +735,7 @@ namespace {
assert((ss-1)->currentMove != MOVE_NONE); assert((ss-1)->currentMove != MOVE_NONE);
assert((ss-1)->currentMove != MOVE_NULL); assert((ss-1)->currentMove != MOVE_NULL);
MovePicker mp(pos, ttMove, Hist, pos.captured_piece_type()); MovePicker mp(pos, ttMove, History, pos.captured_piece_type());
CheckInfo ci(pos); CheckInfo ci(pos);
while ((move = mp.next_move<false>()) != MOVE_NONE) while ((move = mp.next_move<false>()) != MOVE_NONE)
@@ -736,39 +743,47 @@ namespace {
{ {
ss->currentMove = move; ss->currentMove = move;
pos.do_move(move, st, ci, pos.move_gives_check(move, ci)); pos.do_move(move, st, ci, pos.move_gives_check(move, ci));
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth); value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode);
pos.undo_move(move); pos.undo_move(move);
if (value >= rbeta) if (value >= rbeta)
return value; return value;
} }
} }
// Step 10. Internal iterative deepening // Step 10. Internal iterative deepening (skipped when in check)
if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY) if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY)
&& ttMove == MOVE_NONE && ttMove == MOVE_NONE
&& (PvNode || (!inCheck && ss->staticEval + Value(256) >= beta))) && (PvNode || ss->staticEval + Value(256) >= beta))
{ {
Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4); Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4);
ss->skipNullMove = true; ss->skipNullMove = true;
search<PvNode ? PV : NonPV>(pos, ss, alpha, beta, d); search<PvNode ? PV : NonPV>(pos, ss, alpha, beta, d, true);
ss->skipNullMove = false; ss->skipNullMove = false;
tte = TT.probe(posKey); tte = TT.probe(posKey);
ttMove = tte ? tte->move() : MOVE_NONE; ttMove = tte ? tte->move() : MOVE_NONE;
} }
split_point_start: // At split points actual search starts from here moves_loop: // When in check and at SpNode search starts from here
MovePicker mp(pos, ttMove, depth, Hist, ss, PvNode ? -VALUE_INFINITE : beta); Square prevMoveSq = to_sq((ss-1)->currentMove);
Move countermoves[] = { Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].first,
Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].second };
MovePicker mp(pos, ttMove, depth, History, countermoves, ss);
CheckInfo ci(pos); CheckInfo ci(pos);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
improving = ss->staticEval >= (ss-2)->staticEval
|| ss->staticEval == VALUE_NONE
||(ss-2)->staticEval == VALUE_NONE;
singularExtensionNode = !RootNode singularExtensionNode = !RootNode
&& !SpNode && !SpNode
&& depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY) && depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY)
&& ttMove != MOVE_NONE && ttMove != MOVE_NONE
&& !excludedMove // Recursive singular search is not allowed && !excludedMove // Recursive singular search is not allowed
&& (tte->type() & BOUND_LOWER) && (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3 * ONE_PLY; && tte->depth() >= depth - 3 * ONE_PLY;
// Step 11. Loop through moves // Step 11. Loop through moves
@@ -802,7 +817,7 @@ split_point_start: // At split points actual search starts from here
{ {
Signals.firstRootMove = (moveCount == 1); Signals.firstRootMove = (moveCount == 1);
if (thisThread == Threads.main_thread() && Time::now() - SearchTime > 3000) if (thisThread == Threads.main() && Time::now() - SearchTime > 3000)
sync_cout << "info depth " << depth / ONE_PLY sync_cout << "info depth " << depth / ONE_PLY
<< " currmove " << move_to_uci(move, pos.is_chess960()) << " currmove " << move_to_uci(move, pos.is_chess960())
<< " currmovenumber " << moveCount + PVIdx << sync_endl; << " currmovenumber " << moveCount + PVIdx << sync_endl;
@@ -813,12 +828,7 @@ split_point_start: // At split points actual search starts from here
givesCheck = pos.move_gives_check(move, ci); givesCheck = pos.move_gives_check(move, ci);
dangerous = givesCheck dangerous = givesCheck
|| pos.is_passed_pawn_push(move) || pos.is_passed_pawn_push(move)
|| type_of(move) == CASTLE || type_of(move) == CASTLE;
|| ( captureOrPromotion // Entering a pawn endgame?
&& type_of(pos.piece_on(to_sq(move))) != PAWN
&& type_of(move) == NORMAL
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- PieceValue[MG][pos.piece_on(to_sq(move))] == VALUE_ZERO));
// Step 12. Extend checks and, in PV nodes, also dangerous moves // Step 12. Extend checks and, in PV nodes, also dangerous moves
if (PvNode && dangerous) if (PvNode && dangerous)
@@ -843,7 +853,7 @@ split_point_start: // At split points actual search starts from here
Value rBeta = ttValue - int(depth); Value rBeta = ttValue - int(depth);
ss->excludedMove = move; ss->excludedMove = move;
ss->skipNullMove = true; ss->skipNullMove = true;
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2); value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode);
ss->skipNullMove = false; ss->skipNullMove = false;
ss->excludedMove = MOVE_NONE; ss->excludedMove = MOVE_NONE;
@@ -864,7 +874,7 @@ split_point_start: // At split points actual search starts from here
{ {
// Move count based pruning // Move count based pruning
if ( depth < 16 * ONE_PLY if ( depth < 16 * ONE_PLY
&& moveCount >= FutilityMoveCounts[depth] && moveCount >= FutilityMoveCounts[improving][depth]
&& (!threatMove || !refutes(pos, move, threatMove))) && (!threatMove || !refutes(pos, move, threatMove)))
{ {
if (SpNode) if (SpNode)
@@ -876,9 +886,9 @@ split_point_start: // At split points actual search starts from here
// Value based pruning // Value based pruning
// We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth, // We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth,
// but fixing this made program slightly weaker. // but fixing this made program slightly weaker.
Depth predictedDepth = newDepth - reduction<PvNode>(depth, moveCount); Depth predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount) futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount)
+ Gain[pos.piece_moved(move)][to_sq(move)]; + Gains[pos.piece_moved(move)][to_sq(move)];
if (futilityValue < beta) if (futilityValue < beta)
{ {
@@ -919,8 +929,8 @@ split_point_start: // At split points actual search starts from here
pvMove = PvNode && moveCount == 1; pvMove = PvNode && moveCount == 1;
ss->currentMove = move; ss->currentMove = move;
if (!SpNode && !captureOrPromotion && playedMoveCount < 64) if (!SpNode && !captureOrPromotion && quietCount < 64)
movesSearched[playedMoveCount++] = move; quietsSearched[quietCount++] = move;
// Step 14. Make the move // Step 14. Make the move
pos.do_move(move, st, ci, givesCheck); pos.do_move(move, st, ci, givesCheck);
@@ -935,12 +945,19 @@ split_point_start: // At split points actual search starts from here
&& move != ss->killers[0] && move != ss->killers[0]
&& move != ss->killers[1]) && move != ss->killers[1])
{ {
ss->reduction = reduction<PvNode>(depth, moveCount); ss->reduction = reduction<PvNode>(improving, depth, moveCount);
if (!PvNode && cutNode)
ss->reduction += ONE_PLY;
if (move == countermoves[0] || move == countermoves[1])
ss->reduction = std::max(DEPTH_ZERO, ss->reduction-ONE_PLY);
Depth d = std::max(newDepth - ss->reduction, ONE_PLY); Depth d = std::max(newDepth - ss->reduction, ONE_PLY);
if (SpNode) if (SpNode)
alpha = splitPoint->alpha; alpha = splitPoint->alpha;
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d); value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO); doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO);
ss->reduction = DEPTH_ZERO; ss->reduction = DEPTH_ZERO;
@@ -957,7 +974,7 @@ split_point_start: // At split points actual search starts from here
value = newDepth < ONE_PLY ? value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) : -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth); : - 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 // Only for PV nodes do a full PV search on the first move or after a fail
@@ -967,7 +984,7 @@ split_point_start: // At split points actual search starts from here
value = newDepth < ONE_PLY ? value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO) givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO) : -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<PV>(pos, ss+1, -beta, -alpha, newDepth); : - search<PV>(pos, ss+1, -beta, -alpha, newDepth, false);
// Step 17. Undo move // Step 17. Undo move
pos.undo_move(move); pos.undo_move(move);
@@ -1042,7 +1059,7 @@ split_point_start: // At split points actual search starts from here
assert(bestValue < beta); assert(bestValue < beta);
thisThread->split<FakeSplit>(pos, ss, alpha, beta, &bestValue, &bestMove, thisThread->split<FakeSplit>(pos, ss, alpha, beta, &bestValue, &bestMove,
depth, threatMove, moveCount, &mp, NT); depth, threatMove, moveCount, &mp, NT, cutNode);
if (bestValue >= beta) if (bestValue >= beta)
break; break;
} }
@@ -1064,41 +1081,37 @@ split_point_start: // At split points actual search starts from here
// If we have pruned all the moves without searching return a fail-low score // If we have pruned all the moves without searching return a fail-low score
if (bestValue == -VALUE_INFINITE) if (bestValue == -VALUE_INFINITE)
{
assert(!playedMoveCount);
bestValue = alpha; bestValue = alpha;
}
if (bestValue >= beta) // Failed high TT.store(posKey, value_to_tt(bestValue, ss->ply),
bestValue >= beta ? BOUND_LOWER :
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, ss->staticEval, ss->evalMargin);
// Quiet best move: update killers, history and countermoves
if ( bestValue >= beta
&& !pos.is_capture_or_promotion(bestMove)
&& !inCheck)
{ {
TT.store(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER, depth, if (ss->killers[0] != bestMove)
bestMove, ss->staticEval, ss->evalMargin);
if (!pos.is_capture_or_promotion(bestMove) && !inCheck)
{ {
if (bestMove != ss->killers[0]) ss->killers[1] = ss->killers[0];
{ ss->killers[0] = bestMove;
ss->killers[1] = ss->killers[0];
ss->killers[0] = bestMove;
}
// Increase history value of the cut-off move
Value bonus = Value(int(depth) * int(depth));
Hist.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus);
// Decrease history of all the other played non-capture moves
for (int i = 0; i < playedMoveCount - 1; i++)
{
Move m = movesSearched[i];
Hist.update(pos.piece_moved(m), to_sq(m), -bonus);
}
} }
// Increase history value of the cut-off move and decrease all the other
// played non-capture moves.
Value bonus = Value(int(depth) * int(depth));
History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus);
for (int i = 0; i < quietCount - 1; i++)
{
Move m = quietsSearched[i];
History.update(pos.piece_moved(m), to_sq(m), -bonus);
}
if (is_ok((ss-1)->currentMove))
Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, bestMove);
} }
else // Failed low or PV search
TT.store(posKey, value_to_tt(bestValue, ss->ply),
PvNode && bestMove != MOVE_NONE ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, ss->staticEval, ss->evalMargin);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
@@ -1126,7 +1139,7 @@ split_point_start: // At split points actual search starts from here
Key posKey; Key posKey;
Move ttMove, move, bestMove; Move ttMove, move, bestMove;
Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha;
bool givesCheck, enoughMaterial, evasionPrunable; bool givesCheck, evasionPrunable;
Depth ttDepth; Depth ttDepth;
// To flag BOUND_EXACT a node with eval above alpha and no available moves // To flag BOUND_EXACT a node with eval above alpha and no available moves
@@ -1146,8 +1159,7 @@ split_point_start: // At split points actual search starts from here
ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
: DEPTH_QS_NO_CHECKS; : DEPTH_QS_NO_CHECKS;
// Transposition table lookup. At PV nodes, we don't use the TT for // Transposition table lookup
// pruning, but only for move ordering.
posKey = pos.key(); posKey = pos.key();
tte = TT.probe(posKey); tte = TT.probe(posKey);
ttMove = tte ? tte->move() : MOVE_NONE; ttMove = tte ? tte->move() : MOVE_NONE;
@@ -1156,9 +1168,9 @@ split_point_start: // At split points actual search starts from here
if ( tte if ( tte
&& tte->depth() >= ttDepth && tte->depth() >= ttDepth
&& ttValue != VALUE_NONE // Only in case of TT access race && ttValue != VALUE_NONE // Only in case of TT access race
&& ( PvNode ? tte->type() == BOUND_EXACT && ( PvNode ? tte->bound() == BOUND_EXACT
: ttValue >= beta ? (tte->type() & BOUND_LOWER) : ttValue >= beta ? (tte->bound() & BOUND_LOWER)
: (tte->type() & BOUND_UPPER))) : (tte->bound() & BOUND_UPPER)))
{ {
ss->currentMove = ttMove; // Can be MOVE_NONE ss->currentMove = ttMove; // Can be MOVE_NONE
return ttValue; return ttValue;
@@ -1169,7 +1181,6 @@ split_point_start: // At split points actual search starts from here
{ {
ss->staticEval = ss->evalMargin = VALUE_NONE; ss->staticEval = ss->evalMargin = VALUE_NONE;
bestValue = futilityBase = -VALUE_INFINITE; bestValue = futilityBase = -VALUE_INFINITE;
enoughMaterial = false;
} }
else else
{ {
@@ -1197,14 +1208,13 @@ split_point_start: // At split points actual search starts from here
alpha = bestValue; alpha = bestValue;
futilityBase = ss->staticEval + ss->evalMargin + Value(128); futilityBase = ss->staticEval + ss->evalMargin + Value(128);
enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMg;
} }
// Initialize a MovePicker object for the current position, and prepare // Initialize a MovePicker object for the current position, and prepare
// to search the moves. Because the depth is <= 0 here, only captures, // to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
// be generated. // be generated.
MovePicker mp(pos, ttMove, depth, Hist, to_sq((ss-1)->currentMove)); MovePicker mp(pos, ttMove, depth, History, to_sq((ss-1)->currentMove));
CheckInfo ci(pos); CheckInfo ci(pos);
// Loop through the moves until no moves remain or a beta cutoff occurs // Loop through the moves until no moves remain or a beta cutoff occurs
@@ -1219,7 +1229,6 @@ split_point_start: // At split points actual search starts from here
&& !InCheck && !InCheck
&& !givesCheck && !givesCheck
&& move != ttMove && move != ttMove
&& enoughMaterial
&& type_of(move) != PROMOTION && type_of(move) != PROMOTION
&& !pos.is_passed_pawn_push(move)) && !pos.is_passed_pawn_push(move))
{ {
@@ -1236,7 +1245,6 @@ split_point_start: // At split points actual search starts from here
// Prune moves with negative or equal SEE and also moves with positive // Prune moves with negative or equal SEE and also moves with positive
// SEE where capturing piece loses a tempo and SEE < beta - futilityBase. // SEE where capturing piece loses a tempo and SEE < beta - futilityBase.
if ( futilityBase < beta if ( futilityBase < beta
&& depth < DEPTH_ZERO
&& pos.see(move, beta - futilityBase) <= 0) && pos.see(move, beta - futilityBase) <= 0)
{ {
bestValue = std::max(bestValue, futilityBase); bestValue = std::max(bestValue, futilityBase);
@@ -1245,8 +1253,7 @@ split_point_start: // At split points actual search starts from here
} }
// Detect non-capture evasions that are candidate to be pruned // Detect non-capture evasions that are candidate to be pruned
evasionPrunable = !PvNode evasionPrunable = InCheck
&& InCheck
&& bestValue > VALUE_MATED_IN_MAX_PLY && bestValue > VALUE_MATED_IN_MAX_PLY
&& !pos.is_capture(move) && !pos.is_capture(move)
&& !pos.can_castle(pos.side_to_move()); && !pos.can_castle(pos.side_to_move());
@@ -1449,18 +1456,18 @@ split_point_start: // At split points actual search starts from here
{ {
// Update occupancy as if the piece and the threat are moving // Update occupancy as if the piece and the threat are moving
Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from; Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from;
Piece piece = pos.piece_on(m1from); Piece pc = pos.piece_on(m1from);
// The moved piece attacks the square 'tto' ? // The moved piece attacks the square 'tto' ?
if (pos.attacks_from(piece, m1to, occ) & m2to) if (pos.attacks_from(pc, m1to, occ) & m2to)
return true; return true;
// Scan for possible X-ray attackers behind the moved piece // Scan for possible X-ray attackers behind the moved piece
Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(piece), 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(piece), 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 existing
if (xray && (xray ^ (xray & pos.attacks_from<QUEEN>(m2to)))) if (unlikely(xray) && (xray & ~pos.attacks_from<QUEEN>(m2to)))
return true; return true;
} }
@@ -1521,7 +1528,7 @@ split_point_start: // At split points actual search starts from here
string uci_pv(const Position& pos, int depth, Value alpha, Value beta) { string uci_pv(const Position& pos, int depth, Value alpha, Value beta) {
std::stringstream s; std::stringstream s;
Time::point elaspsed = Time::now() - SearchTime + 1; Time::point elapsed = Time::now() - SearchTime + 1;
size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size()); size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size());
int selDepth = 0; int selDepth = 0;
@@ -1546,8 +1553,8 @@ split_point_start: // At split points actual search starts from here
<< " seldepth " << selDepth << " seldepth " << selDepth
<< " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v))
<< " nodes " << pos.nodes_searched() << " nodes " << pos.nodes_searched()
<< " nps " << pos.nodes_searched() * 1000 / elaspsed << " nps " << pos.nodes_searched() * 1000 / elapsed
<< " time " << elaspsed << " time " << elapsed
<< " multipv " << i + 1 << " multipv " << i + 1
<< " pv"; << " pv";
@@ -1568,8 +1575,8 @@ split_point_start: // At split points actual search starts from here
void RootMove::extract_pv_from_tt(Position& pos) { void RootMove::extract_pv_from_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state; StateInfo state[MAX_PLY_PLUS_6], *st = state;
TTEntry* tte; const TTEntry* tte;
int ply = 0; int ply = 0;
Move m = pv[0]; Move m = pv[0];
@@ -1601,8 +1608,8 @@ void RootMove::extract_pv_from_tt(Position& pos) {
void RootMove::insert_pv_in_tt(Position& pos) { void RootMove::insert_pv_in_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state; StateInfo state[MAX_PLY_PLUS_6], *st = state;
TTEntry* tte; const TTEntry* tte;
int ply = 0; int ply = 0;
do { do {
@@ -1671,15 +1678,16 @@ void Thread::idle_loop() {
Threads.mutex.lock(); Threads.mutex.lock();
assert(searching); assert(searching);
assert(activeSplitPoint);
SplitPoint* sp = activeSplitPoint; SplitPoint* sp = activeSplitPoint;
Threads.mutex.unlock(); Threads.mutex.unlock();
Stack ss[MAX_PLY_PLUS_2]; Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
Position pos(*sp->pos, this); Position pos(*sp->pos, this);
memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); std::memcpy(ss-2, sp->ss-2, 5 * sizeof(Stack));
(ss+1)->splitPoint = sp; ss->splitPoint = sp;
sp->mutex.lock(); sp->mutex.lock();
@@ -1689,13 +1697,13 @@ void Thread::idle_loop() {
switch (sp->nodeType) { switch (sp->nodeType) {
case Root: case Root:
search<SplitPointRoot>(pos, ss+1, sp->alpha, sp->beta, sp->depth); search<SplitPointRoot>(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode);
break; break;
case PV: case PV:
search<SplitPointPV>(pos, ss+1, sp->alpha, sp->beta, sp->depth); search<SplitPointPV>(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode);
break; break;
case NonPV: case NonPV:
search<SplitPointNonPV>(pos, ss+1, sp->alpha, sp->beta, sp->depth); search<SplitPointNonPV>(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode);
break; break;
default: default:
assert(false); assert(false);

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(SEARCH_H_INCLUDED) #ifndef SEARCH_H_INCLUDED
#define SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED
#include <cstring> #include <cstring>
@@ -79,7 +79,7 @@ struct RootMove {
struct LimitsType { struct LimitsType {
LimitsType() { memset(this, 0, sizeof(LimitsType)); } LimitsType() { std::memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); } bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder; int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
@@ -109,4 +109,4 @@ extern void think();
} // namespace Search } // namespace Search
#endif // !defined(SEARCH_H_INCLUDED) #endif // #ifndef SEARCH_H_INCLUDED

View File

@@ -19,7 +19,6 @@
#include <algorithm> // For std::count #include <algorithm> // For std::count
#include <cassert> #include <cassert>
#include <iostream>
#include "movegen.h" #include "movegen.h"
#include "search.h" #include "search.h"
@@ -30,48 +29,66 @@ using namespace Search;
ThreadPool Threads; // Global object ThreadPool Threads; // Global object
namespace { extern "C" { namespace {
// start_routine() is the C function which is called when a new thread // start_routine() is the C function which is called when a new thread
// is launched. It is a wrapper to the virtual function idle_loop(). // is launched. It is a wrapper to the virtual function idle_loop().
long start_routine(Thread* th) { th->idle_loop(); return 0; } extern "C" { long start_routine(ThreadBase* th) { th->idle_loop(); return 0; } }
} }
// 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
// when start_routine (and hence virtual idle_loop) is called and when joining.
template<typename T> T* new_thread() {
T* th = new T();
thread_create(th->handle, start_routine, th); // Will go to sleep
return th;
}
void delete_thread(ThreadBase* th) {
th->exit = true; // Search must be already finished
th->notify_one();
thread_join(th->handle); // Wait for thread termination
delete th;
}
}
// ThreadBase::notify_one() wakes up the thread when there is some work to do
void ThreadBase::notify_one() {
mutex.lock();
sleepCondition.notify_one();
mutex.unlock();
}
// ThreadBase::wait_for() set the thread to sleep until condition 'b' turns true
void ThreadBase::wait_for(volatile const bool& b) {
mutex.lock();
while (!b) sleepCondition.wait(mutex);
mutex.unlock();
}
// Thread c'tor just inits data but does not launch any thread of execution that
// instead will be started only upon c'tor returns.
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
searching = exit = false; searching = false;
maxPly = splitPointsSize = 0; maxPly = splitPointsSize = 0;
activeSplitPoint = NULL; activeSplitPoint = NULL;
activePosition = NULL; activePosition = NULL;
idx = Threads.size(); idx = Threads.size();
} }
// Starts a newly-created thread of execution that will call
// the the virtual function idle_loop(), going immediately to sleep.
Thread* Thread::start() {
if (!thread_create(handle, start_routine, this))
{
std::cerr << "Failed to create thread number " << idx << std::endl;
::exit(EXIT_FAILURE);
}
return this;
}
Thread::~Thread() {
}
// Waits for thread termination before to return
Thread* Thread::stop() {
exit = true; // Search must be already finished
notify_one();
thread_join(handle); // Wait for thread termination
return this;
}
// TimerThread::idle_loop() is where the timer thread waits msec milliseconds // TimerThread::idle_loop() is where the timer thread waits msec milliseconds
// and then calls check_time(). If msec is 0 thread sleeps until is woken up. // and then calls check_time(). If msec is 0 thread sleeps until is woken up.
@@ -127,26 +144,6 @@ void MainThread::idle_loop() {
} }
// Thread::notify_one() wakes up the thread when there is some search to do
void Thread::notify_one() {
mutex.lock();
sleepCondition.notify_one();
mutex.unlock();
}
// Thread::wait_for() set the thread to sleep until condition 'b' turns true
void Thread::wait_for(volatile const bool& b) {
mutex.lock();
while (!b) sleepCondition.wait(mutex);
mutex.unlock();
}
// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the // Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the
// current active split point, or in some ancestor of the split point. // current active split point, or in some ancestor of the split point.
@@ -167,7 +164,7 @@ bool Thread::cutoff_occurred() const {
// which are busy searching the split point at the top of slaves split point // which are busy searching the split point at the top of slaves split point
// stack (the "helpful master concept" in YBWC terminology). // stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(Thread* master) const { bool Thread::is_available_to(const Thread* master) const {
if (searching) if (searching)
return false; return false;
@@ -190,8 +187,8 @@ bool Thread::is_available_to(Thread* master) const {
void ThreadPool::init() { void ThreadPool::init() {
sleepWhileIdle = true; sleepWhileIdle = true;
timer = new TimerThread(); timer->start(); timer = new_thread<TimerThread>();
push_back((new MainThread())->start()); push_back(new_thread<MainThread>());
read_uci_options(); read_uci_options();
} }
@@ -200,10 +197,10 @@ void ThreadPool::init() {
void ThreadPool::exit() { void ThreadPool::exit() {
delete timer->stop(); // As first because check_time() accesses threads data delete_thread(timer); // As first because check_time() accesses threads data
for (iterator it = begin(); it != end(); ++it) for (iterator it = begin(); it != end(); ++it)
delete (*it)->stop(); delete_thread(*it);
} }
@@ -220,12 +217,19 @@ void ThreadPool::read_uci_options() {
assert(requested > 0); assert(requested > 0);
// Value 0 has a special meaning: We determine the optimal minimum split depth
// automatically. Anyhow the minimumSplitDepth should never be under 4 plies.
if (!minimumSplitDepth)
minimumSplitDepth = (requested < 8 ? 4 : 7) * ONE_PLY;
else
minimumSplitDepth = std::max(4 * ONE_PLY, minimumSplitDepth);
while (size() < requested) while (size() < requested)
push_back((new Thread())->start()); push_back(new_thread<Thread>());
while (size() > requested) while (size() > requested)
{ {
delete back()->stop(); delete_thread(back());
pop_back(); pop_back();
} }
} }
@@ -234,7 +238,7 @@ void ThreadPool::read_uci_options() {
// slave_available() tries to find an idle thread which is available as a slave // slave_available() tries to find an idle thread which is available as a slave
// for the thread 'master'. // for the thread 'master'.
Thread* ThreadPool::available_slave(Thread* master) const { Thread* ThreadPool::available_slave(const Thread* master) const {
for (const_iterator it = begin(); it != end(); ++it) for (const_iterator it = begin(); it != end(); ++it)
if ((*it)->is_available_to(master)) if ((*it)->is_available_to(master))
@@ -254,9 +258,9 @@ Thread* ThreadPool::available_slave(Thread* master) const {
// search() then split() returns. // search() then split() returns.
template <bool Fake> template <bool Fake>
void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue, void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue,
Move* bestMove, Depth depth, Move threatMove, int moveCount, Move* bestMove, Depth depth, Move threatMove, int moveCount,
MovePicker* movePicker, int nodeType) { MovePicker* movePicker, int nodeType, bool cutNode) {
assert(pos.pos_is_ok()); assert(pos.pos_is_ok());
assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
@@ -278,6 +282,7 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
sp.alpha = alpha; sp.alpha = alpha;
sp.beta = beta; sp.beta = beta;
sp.nodeType = nodeType; sp.nodeType = nodeType;
sp.cutNode = cutNode;
sp.movePicker = movePicker; sp.movePicker = movePicker;
sp.moveCount = moveCount; sp.moveCount = moveCount;
sp.pos = &pos; sp.pos = &pos;
@@ -343,15 +348,15 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
} }
// Explicit template instantiations // Explicit template instantiations
template void Thread::split<false>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int); template void Thread::split<false>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool);
template void Thread::split< true>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int); template void Thread::split< true>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool);
// wait_for_think_finished() waits for main thread to go to sleep then returns // wait_for_think_finished() waits for main thread to go to sleep then returns
void ThreadPool::wait_for_think_finished() { void ThreadPool::wait_for_think_finished() {
MainThread* t = main_thread(); MainThread* t = main();
t->mutex.lock(); t->mutex.lock();
while (t->thinking) sleepCondition.wait(t->mutex); while (t->thinking) sleepCondition.wait(t->mutex);
t->mutex.unlock(); t->mutex.unlock();
@@ -370,16 +375,20 @@ void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
Signals.stopOnPonderhit = Signals.firstRootMove = false; Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false; Signals.stop = Signals.failedLowAtRoot = false;
RootMoves.clear();
RootPos = pos; RootPos = pos;
Limits = limits; Limits = limits;
SetupStates = states; // Ownership transfer here if (states.get()) // If we don't set a new position, preserve current state
RootMoves.clear(); {
SetupStates = states; // Ownership transfer here
assert(!states.get());
}
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml) for (MoveList<LEGAL> it(pos); *it; ++it)
if ( searchMoves.empty() if ( searchMoves.empty()
|| std::count(searchMoves.begin(), searchMoves.end(), ml.move())) || std::count(searchMoves.begin(), searchMoves.end(), *it))
RootMoves.push_back(RootMove(ml.move())); RootMoves.push_back(RootMove(*it));
main_thread()->thinking = true; main()->thinking = true;
main_thread()->notify_one(); // Starts main thread main()->notify_one(); // Starts main thread
} }

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(THREAD_H_INCLUDED) #ifndef THREAD_H_INCLUDED
#define THREAD_H_INCLUDED #define THREAD_H_INCLUDED
#include <vector> #include <vector>
@@ -68,6 +68,7 @@ struct SplitPoint {
Value beta; Value beta;
int nodeType; int nodeType;
Move threatMove; Move threatMove;
bool cutNode;
// Const pointers to shared data // Const pointers to shared data
MovePicker* movePicker; MovePicker* movePicker;
@@ -85,29 +86,39 @@ struct SplitPoint {
}; };
/// ThreadBase struct is the base of the hierarchy from where we derive all the
/// specialized thread classes.
struct ThreadBase {
ThreadBase() : exit(false) {}
virtual ~ThreadBase() {}
virtual void idle_loop() = 0;
void notify_one();
void wait_for(volatile const bool& b);
Mutex mutex;
ConditionVariable sleepCondition;
NativeHandle handle;
volatile bool exit;
};
/// Thread struct keeps together all the thread related stuff like locks, state /// Thread struct keeps together all the thread related stuff like locks, state
/// and especially split points. We also use per-thread pawn and material hash /// and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited /// tables so that once we get a pointer to an entry its life time is unlimited
/// and we don't have to care about someone changing the entry under our feet. /// and we don't have to care about someone changing the entry under our feet.
struct Thread { struct Thread : public ThreadBase {
Thread(); Thread();
virtual ~Thread();
Thread* start();
Thread* stop();
virtual void idle_loop(); virtual void idle_loop();
void notify_one();
bool cutoff_occurred() const; bool cutoff_occurred() const;
bool is_available_to(Thread* master) const; bool is_available_to(const Thread* master) const;
void wait_for(volatile const bool& b);
template <bool Fake> template <bool Fake>
void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType); Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode);
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
Material::Table materialTable; Material::Table materialTable;
@@ -116,17 +127,13 @@ struct Thread {
Position* activePosition; Position* activePosition;
size_t idx; size_t idx;
int maxPly; int maxPly;
Mutex mutex;
ConditionVariable sleepCondition;
NativeHandle handle;
SplitPoint* volatile activeSplitPoint; SplitPoint* volatile activeSplitPoint;
volatile int splitPointsSize; volatile int splitPointsSize;
volatile bool searching; volatile bool searching;
volatile bool exit;
}; };
/// MainThread and TimerThread are sublassed from Thread to characterize the two /// MainThread and TimerThread are derived classes used to characterize the two
/// special threads: the main one and the recurring timer. /// special threads: the main one and the recurring timer.
struct MainThread : public Thread { struct MainThread : public Thread {
@@ -135,7 +142,7 @@ struct MainThread : public Thread {
volatile bool thinking; volatile bool thinking;
}; };
struct TimerThread : public Thread { struct TimerThread : public ThreadBase {
TimerThread() : msec(0) {} TimerThread() : msec(0) {}
virtual void idle_loop(); virtual void idle_loop();
int msec; int msec;
@@ -151,9 +158,9 @@ struct ThreadPool : public std::vector<Thread*> {
void init(); // No c'tor and d'tor, threads rely on globals that should void init(); // No c'tor and d'tor, threads rely on globals that should
void exit(); // be initialized and valid during the whole thread lifetime. void exit(); // be initialized and valid during the whole thread lifetime.
MainThread* main_thread() { return static_cast<MainThread*>((*this)[0]); } MainThread* main() { return static_cast<MainThread*>((*this)[0]); }
void read_uci_options(); void read_uci_options();
Thread* available_slave(Thread* master) const; Thread* available_slave(const Thread* master) const;
void wait_for_think_finished(); void wait_for_think_finished();
void start_thinking(const Position&, const Search::LimitsType&, void start_thinking(const Position&, const Search::LimitsType&,
const std::vector<Move>&, Search::StateStackPtr&); const std::vector<Move>&, Search::StateStackPtr&);
@@ -168,4 +175,4 @@ struct ThreadPool : public std::vector<Thread*> {
extern ThreadPool Threads; extern ThreadPool Threads;
#endif // !defined(THREAD_H_INCLUDED) #endif // #ifndef THREAD_H_INCLUDED

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(TIMEMAN_H_INCLUDED) #ifndef TIMEMAN_H_INCLUDED
#define TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED
/// The TimeManager class computes the optimal time to think depending on the /// The TimeManager class computes the optimal time to think depending on the
@@ -36,4 +36,4 @@ private:
int unstablePVExtraTime; int unstablePVExtraTime;
}; };
#endif // !defined(TIMEMAN_H_INCLUDED) #endif // #ifndef TIMEMAN_H_INCLUDED

View File

@@ -39,8 +39,10 @@ void TranspositionTable::set_size(size_t mbSize) {
if (hashMask == size - ClusterSize) if (hashMask == size - ClusterSize)
return; return;
hashMask = size - ClusterSize;
free(mem); free(mem);
mem = malloc(size * sizeof(TTEntry) + (CACHE_LINE_SIZE - 1)); mem = calloc(size * sizeof(TTEntry) + CACHE_LINE_SIZE - 1, 1);
if (!mem) if (!mem)
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
@@ -48,9 +50,7 @@ void TranspositionTable::set_size(size_t mbSize) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
table = (TTEntry*)((size_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)); table = (TTEntry*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1));
hashMask = size - ClusterSize;
clear(); // Newly allocated block of memory is not initialized
} }
@@ -60,7 +60,24 @@ void TranspositionTable::set_size(size_t mbSize) {
void TranspositionTable::clear() { void TranspositionTable::clear() {
memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry)); std::memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry));
}
/// TranspositionTable::probe() looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
const TTEntry* TranspositionTable::probe(const Key key) const {
const TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;
for (unsigned i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == key32)
return tte;
return NULL;
} }
@@ -72,7 +89,7 @@ void TranspositionTable::clear() {
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from /// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
/// a previous search, or if the depth of t1 is bigger than the depth of t2. /// 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 t, Depth d, Move m, Value statV, Value kingD) { void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV, Value evalM) {
int c1, c2, c3; int c1, c2, c3;
TTEntry *tte, *replace; TTEntry *tte, *replace;
@@ -84,38 +101,21 @@ void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m,
{ {
if (!tte->key() || tte->key() == key32) // Empty or overwrite old if (!tte->key() || tte->key() == key32) // Empty or overwrite old
{ {
// Preserve any existing ttMove if (!m)
if (m == MOVE_NONE) m = tte->move(); // Preserve any existing ttMove
m = tte->move();
tte->save(key32, v, t, d, m, generation, statV, kingD); replace = tte;
return; break;
} }
// Implement replace strategy // Implement replace strategy
c1 = (replace->generation() == generation ? 2 : 0); c1 = (replace->generation() == generation ? 2 : 0);
c2 = (tte->generation() == generation || tte->type() == BOUND_EXACT ? -2 : 0); c2 = (tte->generation() == generation || tte->bound() == BOUND_EXACT ? -2 : 0);
c3 = (tte->depth() < replace->depth() ? 1 : 0); c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0) if (c1 + c2 + c3 > 0)
replace = tte; replace = tte;
} }
replace->save(key32, v, t, d, m, generation, statV, kingD);
} replace->save(key32, v, b, d, m, generation, statV, evalM);
/// TranspositionTable::probe() looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
TTEntry* TranspositionTable::probe(const Key key) const {
TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;
for (unsigned i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == key32)
return tte;
return NULL;
} }

View File

@@ -17,51 +17,43 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(TT_H_INCLUDED) #ifndef TT_H_INCLUDED
#define TT_H_INCLUDED #define TT_H_INCLUDED
#include "misc.h" #include "misc.h"
#include "types.h" #include "types.h"
/// The TTEntry is the class of transposition table entries /// The TTEntry is the 128 bit transposition table entry, defined as below:
/// ///
/// A TTEntry needs 128 bits to be stored /// key: 32 bit
/// /// move: 16 bit
/// bit 0-31: key /// bound type: 8 bit
/// bit 32-63: data /// generation: 8 bit
/// bit 64-79: value /// value: 16 bit
/// bit 80-95: depth /// depth: 16 bit
/// bit 96-111: static value /// static value: 16 bit
/// bit 112-127: margin of static value /// static margin: 16 bit
///
/// the 32 bits of the data field are so defined
///
/// bit 0-15: move
/// bit 16-20: not used
/// bit 21-22: value type
/// bit 23-31: generation
class TTEntry { struct TTEntry {
public:
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) { void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) {
key32 = (uint32_t)k; key32 = (uint32_t)k;
move16 = (uint16_t)m; move16 = (uint16_t)m;
bound = (uint8_t)b; bound8 = (uint8_t)b;
generation8 = (uint8_t)g; generation8 = (uint8_t)g;
value16 = (int16_t)v; value16 = (int16_t)v;
depth16 = (int16_t)d; depth16 = (int16_t)d;
evalValue = (int16_t)ev; evalValue = (int16_t)ev;
evalMargin = (int16_t)em; evalMargin = (int16_t)em;
} }
void set_generation(int g) { generation8 = (uint8_t)g; } void set_generation(uint8_t g) { generation8 = g; }
uint32_t key() const { return key32; } uint32_t key() const { return key32; }
Depth depth() const { return (Depth)depth16; } Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; } Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; } Value value() const { return (Value)value16; }
Bound type() const { return (Bound)bound; } Bound bound() const { return (Bound)bound8; }
int generation() const { return (int)generation8; } int generation() const { return (int)generation8; }
Value eval_value() const { return (Value)evalValue; } Value eval_value() const { return (Value)evalValue; }
Value eval_margin() const { return (Value)evalMargin; } Value eval_margin() const { return (Value)evalMargin; }
@@ -69,7 +61,7 @@ public:
private: private:
uint32_t key32; uint32_t key32;
uint16_t move16; uint16_t move16;
uint8_t bound, generation8; uint8_t bound8, generation8;
int16_t value16, depth16, evalValue, evalMargin; int16_t value16, depth16, evalValue, evalMargin;
}; };
@@ -88,7 +80,7 @@ public:
~TranspositionTable() { free(mem); } ~TranspositionTable() { free(mem); }
void new_search() { generation++; } void new_search() { generation++; }
TTEntry* probe(const Key key) const; const TTEntry* probe(const Key key) const;
TTEntry* first_entry(const Key key) const; TTEntry* first_entry(const Key key) const;
void refresh(const TTEntry* tte) const; void refresh(const TTEntry* tte) const;
void set_size(size_t mbSize); void set_size(size_t mbSize);
@@ -99,7 +91,7 @@ private:
uint32_t hashMask; uint32_t hashMask;
TTEntry* table; TTEntry* table;
void* mem; void* mem;
uint8_t generation; // Size must be not bigger then TTEntry::generation8 uint8_t generation; // Size must be not bigger than TTEntry::generation8
}; };
extern TranspositionTable TT; extern TranspositionTable TT;
@@ -123,4 +115,4 @@ inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation); const_cast<TTEntry*>(tte)->set_generation(generation);
} }
#endif // !defined(TT_H_INCLUDED) #endif // #ifndef TT_H_INCLUDED

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(TYPES_H_INCLUDED) #ifndef TYPES_H_INCLUDED
#define TYPES_H_INCLUDED #define TYPES_H_INCLUDED
/// For Linux and OSX configuration is done automatically using Makefile. To get /// For Linux and OSX configuration is done automatically using Makefile. To get
@@ -42,6 +42,8 @@
#include "platform.h" #include "platform.h"
#define unlikely(x) (x) // For code annotation purposes
#if defined(_WIN64) && !defined(IS_64BIT) #if defined(_WIN64) && !defined(IS_64BIT)
# include <intrin.h> // MSVC popcnt and bsfq instrinsics # include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT # define IS_64BIT
@@ -63,7 +65,7 @@
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE))) # define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE)))
#endif #endif
#if defined(_MSC_VER) #ifdef _MSC_VER
# define FORCE_INLINE __forceinline # define FORCE_INLINE __forceinline
#elif defined(__GNUC__) #elif defined(__GNUC__)
# define FORCE_INLINE inline __attribute__((always_inline)) # define FORCE_INLINE inline __attribute__((always_inline))
@@ -71,13 +73,13 @@
# define FORCE_INLINE inline # define FORCE_INLINE inline
#endif #endif
#if defined(USE_POPCNT) #ifdef USE_POPCNT
const bool HasPopCnt = true; const bool HasPopCnt = true;
#else #else
const bool HasPopCnt = false; const bool HasPopCnt = false;
#endif #endif
#if defined(IS_64BIT) #ifdef IS_64BIT
const bool Is64Bit = true; const bool Is64Bit = true;
#else #else
const bool Is64Bit = false; const bool Is64Bit = false;
@@ -88,26 +90,7 @@ typedef uint64_t Bitboard;
const int MAX_MOVES = 192; const int MAX_MOVES = 192;
const int MAX_PLY = 100; const int MAX_PLY = 100;
const int MAX_PLY_PLUS_2 = MAX_PLY + 2; const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
/// A move needs 16 bits to be stored /// A move needs 16 bits to be stored
/// ///
@@ -121,24 +104,24 @@ const Bitboard Rank8BB = Rank1BB << (8 * 7);
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square. /// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move { enum Move {
MOVE_NONE = 0, MOVE_NONE,
MOVE_NULL = 65 MOVE_NULL = 65
}; };
enum MoveType { enum MoveType {
NORMAL = 0, NORMAL,
PROMOTION = 1 << 14, PROMOTION = 1 << 14,
ENPASSANT = 2 << 14, ENPASSANT = 2 << 14,
CASTLE = 3 << 14 CASTLE = 3 << 14
}; };
enum CastleRight { // Defined as in PolyGlot book hash key enum CastleRight { // Defined as in PolyGlot book hash key
CASTLES_NONE = 0, CASTLES_NONE,
WHITE_OO = 1, WHITE_OO,
WHITE_OOO = 2, WHITE_OOO = WHITE_OO << 1,
BLACK_OO = 4, BLACK_OO = WHITE_OO << 2,
BLACK_OOO = 8, BLACK_OOO = WHITE_OO << 3,
ALL_CASTLES = 15, ALL_CASTLES = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
CASTLE_RIGHT_NB = 16 CASTLE_RIGHT_NB = 16
}; };
@@ -149,7 +132,7 @@ enum CastlingSide {
}; };
enum Phase { enum Phase {
PHASE_ENDGAME = 0, PHASE_ENDGAME,
PHASE_MIDGAME = 128, PHASE_MIDGAME = 128,
MG = 0, EG = 1, PHASE_NB = 2 MG = 0, EG = 1, PHASE_NB = 2
}; };
@@ -162,9 +145,9 @@ enum ScaleFactor {
}; };
enum Bound { enum Bound {
BOUND_NONE = 0, BOUND_NONE,
BOUND_UPPER = 1, BOUND_UPPER,
BOUND_LOWER = 2, BOUND_LOWER,
BOUND_EXACT = BOUND_UPPER | BOUND_LOWER BOUND_EXACT = BOUND_UPPER | BOUND_LOWER
}; };
@@ -190,15 +173,15 @@ enum Value {
}; };
enum PieceType { enum PieceType {
NO_PIECE_TYPE = 0, ALL_PIECES = 0, NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6, ALL_PIECES = 0,
PIECE_TYPE_NB = 8 PIECE_TYPE_NB = 8
}; };
enum Piece { enum Piece {
NO_PIECE = 0, NO_PIECE,
W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6, W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14, B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
PIECE_NB = 16 PIECE_NB = 16
}; };
@@ -213,7 +196,7 @@ enum Depth {
DEPTH_ZERO = 0 * ONE_PLY, DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY, DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY, DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY, DEPTH_QS_RECAPTURES = -7 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY DEPTH_NONE = -127 * ONE_PLY
}; };
@@ -245,11 +228,11 @@ enum Square {
}; };
enum File { enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB = 8 FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB
}; };
enum Rank { enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB = 8 RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
}; };
@@ -258,7 +241,7 @@ enum Rank {
/// for midgame value. Compiler is free to choose the enum type as long as can /// for midgame value. Compiler is free to choose the enum type as long as can
/// keep its data, so ensure Score to be an integer type. /// keep its data, so ensure Score to be an integer type.
enum Score { enum Score {
SCORE_ZERO = 0, SCORE_ZERO,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX, SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
}; };
@@ -268,7 +251,7 @@ inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
/// Extracting the signed lower and upper 16 bits it not so trivial because /// Extracting the signed lower and upper 16 bits it not so trivial because
/// according to the standard a simple cast to short is implementation defined /// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer. /// and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((s + 32768) & ~0xffff) / 0x10000); } inline Value mg_value(Score s) { return Value(((s + 0x8000) & ~0xffff) / 0x10000); }
/// On Intel 64 bit we have a small speed regression with the standard conforming /// On Intel 64 bit we have a small speed regression with the standard conforming
/// version, so use a faster code in this case that, although not 100% standard /// version, so use a faster code in this case that, although not 100% standard
@@ -325,38 +308,17 @@ inline Score operator/(Score s, int i) {
return make_score(mg_value(s) / i, eg_value(s) / i); return make_score(mg_value(s) / i, eg_value(s) / i);
} }
/// Weight score v by score w trying to prevent overflow
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
#undef ENABLE_OPERATORS_ON #undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON #undef ENABLE_SAFE_OPERATORS_ON
namespace Zobrist {
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
extern Key enpassant[FILE_NB];
extern Key castle[CASTLE_RIGHT_NB];
extern Key side;
extern Key exclusion;
void init();
}
CACHE_LINE_ALIGNMENT
extern Score pieceSquareTable[PIECE_NB][SQUARE_NB];
extern Value PieceValue[PHASE_NB][PIECE_NB]; extern Value PieceValue[PHASE_NB][PIECE_NB];
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
struct MoveStack { struct ExtMove {
Move move; Move move;
int score; int score;
}; };
inline bool operator<(const MoveStack& f, const MoveStack& s) { inline bool operator<(const ExtMove& f, const ExtMove& s) {
return f.score < s.score; return f.score < s.score;
} }
@@ -430,18 +392,6 @@ inline bool opposite_colors(Square s1, Square s2) {
return ((s >> 3) ^ s) & 1; return ((s >> 3) ^ s) & 1;
} }
inline int file_distance(Square s1, Square s2) {
return abs(file_of(s1) - file_of(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(rank_of(s1) - rank_of(s2));
}
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline char file_to_char(File f, bool tolower = true) { inline char file_to_char(File f, bool tolower = true) {
return char(f - FILE_A + (tolower ? 'a' : 'A')); return char(f - FILE_A + (tolower ? 'a' : 'A'));
} }
@@ -490,4 +440,4 @@ inline const std::string square_to_string(Square s) {
return ch; return ch;
} }
#endif // !defined(TYPES_H_INCLUDED) #endif // #ifndef TYPES_H_INCLUDED

View File

@@ -42,8 +42,8 @@ namespace {
// position just before to start searching). Needed by repetition draw detection. // position just before to start searching). Needed by repetition draw detection.
Search::StateStackPtr SetupStates; Search::StateStackPtr SetupStates;
void set_option(istringstream& up); void setoption(istringstream& up);
void set_position(Position& pos, istringstream& up); void position(Position& pos, istringstream& up);
void go(const Position& pos, istringstream& up); void go(const Position& pos, istringstream& up);
} }
@@ -55,7 +55,7 @@ namespace {
void UCI::loop(const string& args) { void UCI::loop(const string& args) {
Position pos(StartFEN, false, Threads.main_thread()); // The root position Position pos(StartFEN, false, Threads.main()); // The root position
string token, cmd = args; string token, cmd = args;
do { do {
@@ -76,7 +76,7 @@ void UCI::loop(const string& args) {
if (token != "ponderhit" || Search::Signals.stopOnPonderhit) if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
{ {
Search::Signals.stop = true; Search::Signals.stop = true;
Threads.main_thread()->notify_one(); // Could be sleeping Threads.main()->notify_one(); // Could be sleeping
} }
else else
Search::Limits.ponder = false; Search::Limits.ponder = false;
@@ -102,15 +102,19 @@ void UCI::loop(const string& args) {
<< "\n" << Options << "\n" << Options
<< "\nuciok" << sync_endl; << "\nuciok" << sync_endl;
else if (token == "eval")
{
Search::RootColor = pos.side_to_move(); // Ensure it is set
sync_cout << Eval::trace(pos) << sync_endl;
}
else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ } else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ }
else if (token == "go") go(pos, is); else if (token == "go") go(pos, is);
else if (token == "position") set_position(pos, is); else if (token == "position") position(pos, is);
else if (token == "setoption") set_option(is); else if (token == "setoption") setoption(is);
else if (token == "flip") pos.flip(); else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is); else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos.pretty() << sync_endl; else if (token == "d") sync_cout << pos.pretty() << sync_endl;
else if (token == "isready") sync_cout << "readyok" << sync_endl; else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else else
sync_cout << "Unknown command: " << cmd << sync_endl; sync_cout << "Unknown command: " << cmd << sync_endl;
@@ -122,12 +126,12 @@ void UCI::loop(const string& args) {
namespace { namespace {
// set_position() is called when engine receives the "position" UCI command. // position() is called when engine receives the "position" UCI command.
// The function sets up the position described in the given fen string ("fen") // The function sets up the position described in the given fen string ("fen")
// or the starting position ("startpos") and then makes the moves given in the // or the starting position ("startpos") and then makes the moves given in the
// following move list ("moves"). // following move list ("moves").
void set_position(Position& pos, istringstream& is) { void position(Position& pos, istringstream& is) {
Move m; Move m;
string token, fen; string token, fen;
@@ -145,7 +149,7 @@ namespace {
else else
return; return;
pos.set(fen, Options["UCI_Chess960"], Threads.main_thread()); pos.set(fen, Options["UCI_Chess960"], Threads.main());
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>()); SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
// Parse move list (if any) // Parse move list (if any)
@@ -157,10 +161,10 @@ namespace {
} }
// set_option() is called when engine receives the "setoption" UCI command. The // setoption() is called when engine receives the "setoption" UCI command. The
// function updates the UCI option ("name") to the given value ("value"). // function updates the UCI option ("name") to the given value ("value").
void set_option(istringstream& is) { void setoption(istringstream& is) {
string token, name, value; string token, name, value;

View File

@@ -51,31 +51,28 @@ 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
/// and initializes the default value of "Threads" and "Min Split Depth"
/// parameters according to the number of CPU cores detected.
void init(OptionsMap& o) { void init(OptionsMap& o) {
int cpus = std::min(cpu_count(), MAX_THREADS); o["Write Debug Log"] = Option(false, on_logger);
int msd = cpus < 8 ? 4 : 7; o["Write Search Log"] = Option(false);
o["Use Debug Log"] = Option(false, on_logger);
o["Use Search Log"] = Option(false);
o["Search Log Filename"] = Option("SearchLog.txt"); o["Search Log Filename"] = Option("SearchLog.txt");
o["Book File"] = Option("book.bin"); o["Book File"] = Option("book.bin");
o["Best Book Move"] = Option(false); o["Best Book Move"] = Option(false);
o["Contempt Factor"] = Option(0, -50, 50); o["Contempt Factor"] = Option(0, -50, 50);
o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval); o["Mobility (Midgame)"] = Option(100, 0, 200, on_eval);
o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval); o["Pawn Structure (Midgame)"] = Option(100, 0, 200, on_eval);
o["Pawn Structure (Endgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Midgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval);
o["Space"] = Option(100, 0, 200, on_eval); o["Space"] = Option(100, 0, 200, on_eval);
o["Aggressiveness"] = Option(100, 0, 200, on_eval); o["Aggressiveness"] = Option(100, 0, 200, on_eval);
o["Cowardice"] = Option(100, 0, 200, on_eval); o["Cowardice"] = Option(100, 0, 200, on_eval);
o["Min Split Depth"] = Option(msd, 4, 12, on_threads); o["Min Split Depth"] = Option(0, 0, 12, on_threads);
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads); o["Threads"] = Option(1, 1, MAX_THREADS, on_threads);
o["Use Sleeping Threads"] = Option(true); o["Idle Threads Sleep"] = Option(true);
o["Hash"] = Option(32, 1, 8192, on_hash_size); o["Hash"] = Option(32, 1, 8192, on_hash_size);
o["Clear Hash"] = Option(on_clear_hash); o["Clear Hash"] = Option(on_clear_hash);
o["Ponder"] = Option(true); o["Ponder"] = Option(true);

View File

@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(UCIOPTION_H_INCLUDED) #ifndef UCIOPTION_H_INCLUDED
#define UCIOPTION_H_INCLUDED #define UCIOPTION_H_INCLUDED
#include <map> #include <map>
@@ -66,4 +66,4 @@ void loop(const std::string&);
extern UCI::OptionsMap Options; extern UCI::OptionsMap Options;
#endif // !defined(UCIOPTION_H_INCLUDED) #endif // #ifndef UCIOPTION_H_INCLUDED