mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-11 16:42:41 +01:00
DroidFish: Updated stockfish to version 2.3.
This commit is contained in:
@@ -4,7 +4,7 @@ include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := stockfish
|
||||
LOCAL_SRC_FILES := \
|
||||
evaluate.cpp move.cpp search.cpp \
|
||||
evaluate.cpp notation.cpp search.cpp \
|
||||
benchmark.cpp movegen.cpp tt.cpp \
|
||||
bitbase.cpp main.cpp movepick.cpp uci.cpp \
|
||||
bitboard.cpp pawns.cpp ucioption.cpp \
|
||||
|
||||
@@ -110,7 +110,8 @@ void benchmark(const Position& current, istream& is) {
|
||||
}
|
||||
|
||||
int64_t nodes = 0;
|
||||
Time time = Time::current_time();
|
||||
Search::StateStackPtr st;
|
||||
Time::point elapsed = Time::now();
|
||||
|
||||
for (size_t i = 0; i < fens.size(); i++)
|
||||
{
|
||||
@@ -120,22 +121,22 @@ void benchmark(const Position& current, istream& is) {
|
||||
|
||||
if (limitType == "perft")
|
||||
{
|
||||
int64_t cnt = Search::perft(pos, limits.depth * ONE_PLY);
|
||||
size_t cnt = Search::perft(pos, limits.depth * ONE_PLY);
|
||||
cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl;
|
||||
nodes += cnt;
|
||||
}
|
||||
else
|
||||
{
|
||||
Threads.start_searching(pos, limits, vector<Move>());
|
||||
Threads.start_searching(pos, limits, vector<Move>(), st);
|
||||
Threads.wait_for_search_finished();
|
||||
nodes += Search::RootPosition.nodes_searched();
|
||||
}
|
||||
}
|
||||
|
||||
int e = time.elapsed();
|
||||
elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero'
|
||||
|
||||
cerr << "\n==========================="
|
||||
<< "\nTotal time (ms) : " << e
|
||||
<< "\nTotal time (ms) : " << elapsed
|
||||
<< "\nNodes searched : " << nodes
|
||||
<< "\nNodes/second : " << int(nodes / (e / 1000.0)) << endl;
|
||||
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl;
|
||||
}
|
||||
|
||||
@@ -62,14 +62,14 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm) {
|
||||
uint32_t Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) {
|
||||
|
||||
int idx = index(wksq, bksq, wpsq, stm);
|
||||
return KPKBitbase[idx / 32] & (1 << (idx & 31));
|
||||
}
|
||||
|
||||
|
||||
void kpk_bitbase_init() {
|
||||
void Bitbases::init_kpk() {
|
||||
|
||||
Result db[IndexMax];
|
||||
KPKPosition pos;
|
||||
@@ -117,7 +117,7 @@ namespace {
|
||||
stm = Color(idx & 1);
|
||||
bksq = Square((idx >> 1) & 63);
|
||||
wksq = Square((idx >> 7) & 63);
|
||||
psq = make_square(File((idx >> 13) & 3), Rank((idx >> 15) + 1));
|
||||
psq = File((idx >> 13) & 3) | Rank((idx >> 15) + 1);
|
||||
}
|
||||
|
||||
Result KPKPosition::classify_leaf(int idx) {
|
||||
@@ -196,8 +196,8 @@ namespace {
|
||||
|
||||
while (b)
|
||||
{
|
||||
r |= Us == WHITE ? db[index(pop_1st_bit(&b), bksq, psq, BLACK)]
|
||||
: db[index(wksq, pop_1st_bit(&b), psq, WHITE)];
|
||||
r |= Us == WHITE ? db[index(pop_lsb(&b), bksq, psq, BLACK)]
|
||||
: db[index(wksq, pop_lsb(&b), psq, WHITE)];
|
||||
|
||||
if (Us == WHITE && (r & WIN))
|
||||
return WIN;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "bitcount.h"
|
||||
#include "misc.h"
|
||||
#include "rkiss.h"
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
@@ -45,22 +46,27 @@ Bitboard ThisAndAdjacentFilesBB[8];
|
||||
Bitboard InFrontBB[2][8];
|
||||
Bitboard StepAttacksBB[16][64];
|
||||
Bitboard BetweenBB[64][64];
|
||||
Bitboard DistanceRingsBB[64][8];
|
||||
Bitboard ForwardBB[2][64];
|
||||
Bitboard PassedPawnMask[2][64];
|
||||
Bitboard AttackSpanMask[2][64];
|
||||
Bitboard PseudoAttacks[6][64];
|
||||
|
||||
uint8_t BitCount8Bit[256];
|
||||
int SquareDistance[64][64];
|
||||
|
||||
namespace {
|
||||
|
||||
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
|
||||
const uint64_t DeBruijn_64 = 0x218A392CD3D5DBFULL;
|
||||
const uint32_t DeBruijn_32 = 0x783A9B23;
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
|
||||
int BSFTable[64];
|
||||
int MS1BTable[256];
|
||||
Bitboard RTable[0x19000]; // Storage space for rook attacks
|
||||
Bitboard BTable[0x1480]; // Storage space for bishop attacks
|
||||
uint8_t BitCount8Bit[256];
|
||||
|
||||
typedef unsigned (Fn)(Square, Bitboard);
|
||||
|
||||
@@ -68,40 +74,35 @@ namespace {
|
||||
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index);
|
||||
}
|
||||
|
||||
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
|
||||
/// pop_1st_bit() finds and clears the least significant nonzero 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.
|
||||
|
||||
#if defined(IS_64BIT) && !defined(USE_BSFQ)
|
||||
#if !defined(USE_BSFQ)
|
||||
|
||||
Square first_1(Bitboard b) {
|
||||
return Square(BSFTable[((b & -b) * 0x218A392CD3D5DBFULL) >> 58]);
|
||||
}
|
||||
Square lsb(Bitboard b) {
|
||||
|
||||
Square pop_1st_bit(Bitboard* b) {
|
||||
Bitboard bb = *b;
|
||||
*b &= (*b - 1);
|
||||
return Square(BSFTable[((bb & -bb) * 0x218A392CD3D5DBFULL) >> 58]);
|
||||
}
|
||||
if (Is64Bit)
|
||||
return Square(BSFTable[((b & -b) * DeBruijn_64) >> 58]);
|
||||
|
||||
#elif !defined(USE_BSFQ)
|
||||
|
||||
Square first_1(Bitboard b) {
|
||||
b ^= (b - 1);
|
||||
uint32_t fold = unsigned(b) ^ unsigned(b >> 32);
|
||||
return Square(BSFTable[(fold * 0x783A9B23) >> 26]);
|
||||
return Square(BSFTable[(fold * DeBruijn_32) >> 26]);
|
||||
}
|
||||
|
||||
Square pop_1st_bit(Bitboard* b) {
|
||||
Square pop_lsb(Bitboard* b) {
|
||||
|
||||
Bitboard bb = *b;
|
||||
*b = bb & (bb - 1);
|
||||
|
||||
if (Is64Bit)
|
||||
return Square(BSFTable[((bb & -bb) * DeBruijn_64) >> 58]);
|
||||
|
||||
bb ^= (bb - 1);
|
||||
uint32_t fold = unsigned(bb) ^ unsigned(bb >> 32);
|
||||
return Square(BSFTable[(fold * 0x783A9B23) >> 26]);
|
||||
return Square(BSFTable[(fold * DeBruijn_32) >> 26]);
|
||||
}
|
||||
|
||||
Square last_1(Bitboard b) {
|
||||
Square msb(Bitboard b) {
|
||||
|
||||
unsigned b32;
|
||||
int result = 0;
|
||||
@@ -137,16 +138,18 @@ Square last_1(Bitboard b) {
|
||||
|
||||
void Bitboards::print(Bitboard b) {
|
||||
|
||||
sync_cout;
|
||||
|
||||
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
|
||||
{
|
||||
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
|
||||
|
||||
for (File file = FILE_A; file <= FILE_H; file++)
|
||||
std::cout << "| " << ((b & make_square(file, rank)) ? "X " : " ");
|
||||
std::cout << "| " << (b & (file | rank) ? "X " : " ");
|
||||
|
||||
std::cout << "|\n";
|
||||
}
|
||||
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
|
||||
std::cout << "+---+---+---+---+---+---+---+---+" << sync_endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -195,16 +198,22 @@ void Bitboards::init() {
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
||||
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
||||
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;
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
if (!Is64Bit) // Matt Taylor's folding trick for 32 bit systems
|
||||
{
|
||||
Bitboard b = 1ULL << i;
|
||||
b ^= b - 1;
|
||||
b ^= b >> 32;
|
||||
BSFTable[(uint32_t)(b * 0x783A9B23) >> 26] = i;
|
||||
BSFTable[(uint32_t)(b * DeBruijn_32) >> 26] = i;
|
||||
}
|
||||
else
|
||||
BSFTable[((1ULL << i) * 0x218A392CD3D5DBFULL) >> 58] = i;
|
||||
BSFTable[((1ULL << i) * DeBruijn_64) >> 58] = i;
|
||||
|
||||
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
|
||||
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
|
||||
@@ -265,25 +274,17 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
Bitboard pick_random(Bitboard mask, RKISS& rk, int booster) {
|
||||
|
||||
Bitboard magic;
|
||||
Bitboard pick_random(RKISS& rk, int booster) {
|
||||
|
||||
// Values s1 and s2 are used to rotate the candidate magic of a
|
||||
// quantity known to be the optimal to quickly find the magics.
|
||||
int s1 = booster & 63, s2 = (booster >> 6) & 63;
|
||||
|
||||
while (true)
|
||||
{
|
||||
magic = rk.rand<Bitboard>();
|
||||
magic = (magic >> s1) | (magic << (64 - s1));
|
||||
magic &= rk.rand<Bitboard>();
|
||||
magic = (magic >> s2) | (magic << (64 - s2));
|
||||
magic &= rk.rand<Bitboard>();
|
||||
|
||||
if (BitCount8Bit[(mask * magic) >> 56] >= 6)
|
||||
return magic;
|
||||
}
|
||||
Bitboard m = rk.rand<Bitboard>();
|
||||
m = (m >> s1) | (m << (64 - s1));
|
||||
m &= rk.rand<Bitboard>();
|
||||
m = (m >> s2) | (m << (64 - s2));
|
||||
return m & rk.rand<Bitboard>();
|
||||
}
|
||||
|
||||
|
||||
@@ -336,7 +337,9 @@ namespace {
|
||||
// Find a magic for square 's' picking up an (almost) random number
|
||||
// until we find the one that passes the verification test.
|
||||
do {
|
||||
magics[s] = pick_random(masks[s], rk, booster);
|
||||
do magics[s] = pick_random(rk, booster);
|
||||
while (BitCount8Bit[(magics[s] * masks[s]) >> 56] < 6);
|
||||
|
||||
memset(attacks[s], 0, size * sizeof(Bitboard));
|
||||
|
||||
// A good magic must map every possible occupancy to an index that
|
||||
@@ -350,6 +353,8 @@ namespace {
|
||||
if (attack && attack != reference[i])
|
||||
break;
|
||||
|
||||
assert(reference[i] != 0);
|
||||
|
||||
attack = reference[i];
|
||||
}
|
||||
} while (i != size);
|
||||
|
||||
@@ -25,8 +25,15 @@
|
||||
|
||||
namespace Bitboards {
|
||||
|
||||
extern void init();
|
||||
extern void print(Bitboard b);
|
||||
void init();
|
||||
void print(Bitboard b);
|
||||
|
||||
}
|
||||
|
||||
namespace Bitbases {
|
||||
|
||||
void init_kpk();
|
||||
uint32_t probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm);
|
||||
|
||||
}
|
||||
|
||||
@@ -50,6 +57,7 @@ extern Bitboard ThisAndAdjacentFilesBB[8];
|
||||
extern Bitboard InFrontBB[2][8];
|
||||
extern Bitboard StepAttacksBB[16][64];
|
||||
extern Bitboard BetweenBB[64][64];
|
||||
extern Bitboard DistanceRingsBB[64][8];
|
||||
extern Bitboard ForwardBB[2][64];
|
||||
extern Bitboard PassedPawnMask[2][64];
|
||||
extern Bitboard AttackSpanMask[2][64];
|
||||
@@ -220,51 +228,52 @@ inline Bitboard attacks_bb(Square s, Bitboard occ) {
|
||||
}
|
||||
|
||||
|
||||
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
|
||||
/// pop_1st_bit() finds and clears the least significant nonzero 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.
|
||||
|
||||
#if defined(USE_BSFQ)
|
||||
|
||||
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||
|
||||
FORCE_INLINE Square first_1(Bitboard b) {
|
||||
FORCE_INLINE Square lsb(Bitboard b) {
|
||||
unsigned long index;
|
||||
_BitScanForward64(&index, b);
|
||||
return (Square) index;
|
||||
}
|
||||
|
||||
FORCE_INLINE Square last_1(Bitboard b) {
|
||||
FORCE_INLINE Square msb(Bitboard b) {
|
||||
unsigned long index;
|
||||
_BitScanReverse64(&index, b);
|
||||
return (Square) index;
|
||||
}
|
||||
|
||||
# else
|
||||
|
||||
FORCE_INLINE Square first_1(Bitboard b) { // Assembly code by Heinz van Saanen
|
||||
Bitboard dummy;
|
||||
__asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) );
|
||||
return (Square) dummy;
|
||||
FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
|
||||
Bitboard index;
|
||||
__asm__("bsfq %1, %0": "=r"(index): "rm"(b) );
|
||||
return (Square) index;
|
||||
}
|
||||
|
||||
FORCE_INLINE Square last_1(Bitboard b) {
|
||||
Bitboard dummy;
|
||||
__asm__("bsrq %1, %0": "=r"(dummy): "rm"(b) );
|
||||
return (Square) dummy;
|
||||
FORCE_INLINE Square msb(Bitboard b) {
|
||||
Bitboard index;
|
||||
__asm__("bsrq %1, %0": "=r"(index): "rm"(b) );
|
||||
return (Square) index;
|
||||
}
|
||||
|
||||
# endif
|
||||
|
||||
FORCE_INLINE Square pop_1st_bit(Bitboard* b) {
|
||||
const Square s = first_1(*b);
|
||||
FORCE_INLINE Square pop_lsb(Bitboard* b) {
|
||||
const Square s = lsb(*b);
|
||||
*b &= ~(1ULL << s);
|
||||
return s;
|
||||
}
|
||||
|
||||
#else // if !defined(USE_BSFQ)
|
||||
|
||||
extern Square first_1(Bitboard b);
|
||||
extern Square last_1(Bitboard b);
|
||||
extern Square pop_1st_bit(Bitboard* b);
|
||||
extern Square msb(Bitboard b);
|
||||
extern Square lsb(Bitboard b);
|
||||
extern Square pop_lsb(Bitboard* b);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
|
||||
#if !defined(USE_POPCNT)
|
||||
|
||||
assert(false);
|
||||
return int(b != 0); // Avoid 'b not used' warning
|
||||
return b != 0; // Avoid 'b not used' warning
|
||||
|
||||
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
|
||||
|
||||
|
||||
@@ -301,10 +301,10 @@ namespace {
|
||||
};
|
||||
|
||||
// Offsets to the PolyGlotRandoms[] array of zobrist keys
|
||||
const Key* ZobPiece = PolyGlotRandoms + 0;
|
||||
const Key* ZobCastle = PolyGlotRandoms + 768;
|
||||
const Key* ZobEnPassant = PolyGlotRandoms + 772;
|
||||
const Key* ZobTurn = PolyGlotRandoms + 780;
|
||||
const Key* ZobPiece = PolyGlotRandoms;
|
||||
const Key* ZobCastle = ZobPiece + 12 * 64; // Pieces * squares
|
||||
const Key* ZobEnPassant = ZobCastle + 4; // Castle flags
|
||||
const Key* ZobTurn = ZobEnPassant + 8; // Number of files
|
||||
|
||||
// book_key() returns the PolyGlot hash key of the given position
|
||||
uint64_t book_key(const Position& pos) {
|
||||
@@ -314,18 +314,18 @@ namespace {
|
||||
|
||||
while (b)
|
||||
{
|
||||
// Piece offset is at 64 * polyPiece where polyPiece is defined as:
|
||||
// In PolyGlotRandoms[] pieces are stored in the following sequence:
|
||||
// BP = 0, WP = 1, BN = 2, WN = 3, ... BK = 10, WK = 11
|
||||
Square s = pop_1st_bit(&b);
|
||||
Square s = pop_lsb(&b);
|
||||
Piece p = pos.piece_on(s);
|
||||
int polyPiece = 2 * (type_of(p) - 1) + (color_of(p) == WHITE);
|
||||
key ^= ZobPiece[64 * polyPiece + s];
|
||||
int pieceOfs = 2 * (type_of(p) - 1) + (color_of(p) == WHITE);
|
||||
key ^= ZobPiece[64 * pieceOfs + s];
|
||||
}
|
||||
|
||||
b = pos.can_castle(ALL_CASTLES);
|
||||
|
||||
while (b)
|
||||
key ^= ZobCastle[pop_1st_bit(&b)];
|
||||
key ^= ZobCastle[pop_lsb(&b)];
|
||||
|
||||
if (pos.ep_square() != SQ_NONE)
|
||||
key ^= ZobEnPassant[file_of(pos.ep_square())];
|
||||
@@ -338,9 +338,9 @@ namespace {
|
||||
|
||||
} // namespace
|
||||
|
||||
Book::Book() : size(0) {
|
||||
Book::Book() {
|
||||
|
||||
for (int i = Time::current_time().msec() % 10000; i > 0; i--)
|
||||
for (int i = Time::now() % 10000; i > 0; i--)
|
||||
RKiss.rand<unsigned>(); // Make random number generation less deterministic
|
||||
}
|
||||
|
||||
@@ -370,30 +370,14 @@ template<> Book& Book::operator>>(BookEntry& e) {
|
||||
|
||||
bool Book::open(const char* fName) {
|
||||
|
||||
fileName = "";
|
||||
|
||||
if (is_open()) // Cannot close an already closed file
|
||||
close();
|
||||
|
||||
ifstream::open(fName, ifstream::in | ifstream::binary | ios::ate);
|
||||
ifstream::open(fName, ifstream::in | ifstream::binary);
|
||||
|
||||
if (!is_open())
|
||||
{
|
||||
clear();
|
||||
return false; // Silently fail if the file is not found
|
||||
}
|
||||
|
||||
// Get the book size in number of entries, we are already at the end of file
|
||||
size = (size_t)tellg() / sizeof(BookEntry);
|
||||
|
||||
if (!good())
|
||||
{
|
||||
cerr << "Failed to open book file " << fName << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fileName = fName; // Set only if successful
|
||||
return true;
|
||||
fileName = is_open() ? fName : "";
|
||||
ifstream::clear(); // Reset any error flag to allow retry ifstream::open()
|
||||
return !fileName.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -403,16 +387,16 @@ bool Book::open(const char* fName) {
|
||||
|
||||
Move Book::probe(const Position& pos, const string& fName, bool pickBest) {
|
||||
|
||||
if (fileName != fName && !open(fName.c_str()))
|
||||
return MOVE_NONE;
|
||||
|
||||
BookEntry e;
|
||||
uint16_t best = 0;
|
||||
unsigned sum = 0;
|
||||
Move move = MOVE_NONE;
|
||||
uint64_t key = book_key(pos);
|
||||
|
||||
if (fileName != fName && !open(fName.c_str()))
|
||||
return MOVE_NONE;
|
||||
|
||||
binary_search(key);
|
||||
seekg(find_first(key) * sizeof(BookEntry), ios_base::beg);
|
||||
|
||||
while (*this >> e, e.key == key && good())
|
||||
{
|
||||
@@ -442,10 +426,10 @@ Move Book::probe(const Position& pos, const string& fName, bool pickBest) {
|
||||
// the special Move's flags (bit 14-15) that are not supported by PolyGlot.
|
||||
int pt = (move >> 12) & 7;
|
||||
if (pt)
|
||||
move = make_promotion(from_sq(move), to_sq(move), PieceType(pt + 1));
|
||||
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
|
||||
|
||||
// Add 'special move' flags and verify it is legal
|
||||
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
|
||||
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
|
||||
if (move == (ml.move() & 0x3FFF))
|
||||
return ml.move();
|
||||
|
||||
@@ -453,18 +437,17 @@ Move Book::probe(const Position& pos, const string& fName, bool pickBest) {
|
||||
}
|
||||
|
||||
|
||||
/// Book::binary_search() takes a book key as input, and does a binary search
|
||||
/// through the book file for the given key. File stream current position is set
|
||||
/// to the leftmost book entry with the same key as the input.
|
||||
/// Book::find_first() takes a book key as input, and does a binary search
|
||||
/// through the book file for the given key. Returns the index of the leftmost
|
||||
/// book entry with the same key as the input.
|
||||
|
||||
void Book::binary_search(uint64_t key) {
|
||||
size_t Book::find_first(uint64_t key) {
|
||||
|
||||
size_t low, high, mid;
|
||||
seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size
|
||||
|
||||
size_t low = 0, mid, high = (size_t)tellg() / sizeof(BookEntry) - 1;
|
||||
BookEntry e;
|
||||
|
||||
low = 0;
|
||||
high = size - 1;
|
||||
|
||||
assert(low <= high);
|
||||
|
||||
while (low < high && good())
|
||||
@@ -484,5 +467,5 @@ void Book::binary_search(uint64_t key) {
|
||||
|
||||
assert(low == high);
|
||||
|
||||
seekg(low * sizeof(BookEntry), ios_base::beg);
|
||||
return low;
|
||||
}
|
||||
|
||||
@@ -48,11 +48,10 @@ private:
|
||||
template<typename T> Book& operator>>(T& n);
|
||||
|
||||
bool open(const char* fName);
|
||||
void binary_search(uint64_t key);
|
||||
size_t find_first(uint64_t key);
|
||||
|
||||
RKISS RKiss;
|
||||
std::string fileName;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#endif // !defined(BOOK_H_INCLUDED)
|
||||
|
||||
@@ -20,14 +20,13 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "bitcount.h"
|
||||
#include "endgame.h"
|
||||
#include "movegen.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm);
|
||||
|
||||
namespace {
|
||||
|
||||
// Table used to drive the defending king towards the edge of the board
|
||||
@@ -134,7 +133,7 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
|
||||
// Stalemate detection with lone king
|
||||
if ( pos.side_to_move() == weakerSide
|
||||
&& !pos.in_check()
|
||||
&& !MoveList<MV_LEGAL>(pos).size()) {
|
||||
&& !MoveList<LEGAL>(pos).size()) {
|
||||
return VALUE_DRAW;
|
||||
}
|
||||
|
||||
@@ -142,7 +141,7 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
|
||||
Square loserKSq = pos.king_square(weakerSide);
|
||||
|
||||
Value result = pos.non_pawn_material(strongerSide)
|
||||
+ pos.piece_count(strongerSide, PAWN) * PawnValueEndgame
|
||||
+ pos.piece_count(strongerSide, PAWN) * PawnValueEg
|
||||
+ MateTable[loserKSq]
|
||||
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
|
||||
|
||||
@@ -163,7 +162,7 @@ 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) == KnightValueMidgame + BishopValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg);
|
||||
assert(pos.piece_count(strongerSide, BISHOP) == 1);
|
||||
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
||||
@@ -223,12 +222,10 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
|
||||
wpsq = mirror(wpsq);
|
||||
}
|
||||
|
||||
if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm))
|
||||
if (!Bitbases::probe_kpk(wksq, wpsq, bksq, stm))
|
||||
return VALUE_DRAW;
|
||||
|
||||
Value result = VALUE_KNOWN_WIN
|
||||
+ PawnValueEndgame
|
||||
+ Value(rank_of(wpsq));
|
||||
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq));
|
||||
|
||||
return strongerSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
@@ -241,7 +238,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
Value Endgame<KRKP>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
||||
assert(pos.non_pawn_material(weakerSide) == 0);
|
||||
assert(pos.piece_count(weakerSide, PAWN) == 1);
|
||||
@@ -262,18 +259,18 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
|
||||
bpsq = ~bpsq;
|
||||
}
|
||||
|
||||
Square queeningSq = make_square(file_of(bpsq), RANK_1);
|
||||
Square queeningSq = file_of(bpsq) | RANK_1;
|
||||
Value result;
|
||||
|
||||
// If the stronger side's king is in front of the pawn, it's a win
|
||||
if (wksq < bpsq && file_of(wksq) == file_of(bpsq))
|
||||
result = RookValueEndgame - Value(square_distance(wksq, bpsq));
|
||||
result = RookValueEg - Value(square_distance(wksq, bpsq));
|
||||
|
||||
// If the weaker side's king is too far from the pawn and the rook,
|
||||
// it's a win
|
||||
else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3
|
||||
&& square_distance(bksq, wrsq) >= 3)
|
||||
result = RookValueEndgame - Value(square_distance(wksq, bpsq));
|
||||
result = RookValueEg - Value(square_distance(wksq, bpsq));
|
||||
|
||||
// If the pawn is far advanced and supported by the defending king,
|
||||
// the position is drawish
|
||||
@@ -298,9 +295,9 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
Value Endgame<KRKB>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
||||
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
|
||||
assert(pos.non_pawn_material(weakerSide) == BishopValueMg);
|
||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||
assert(pos.piece_count(weakerSide, BISHOP) == 1);
|
||||
|
||||
@@ -314,9 +311,9 @@ Value Endgame<KRKB>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
Value Endgame<KRKN>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
||||
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
|
||||
assert(pos.non_pawn_material(weakerSide) == KnightValueMg);
|
||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
|
||||
|
||||
@@ -337,16 +334,16 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
Value Endgame<KQKR>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
||||
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
|
||||
assert(pos.non_pawn_material(weakerSide) == RookValueMg);
|
||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||
|
||||
Square winnerKSq = pos.king_square(strongerSide);
|
||||
Square loserKSq = pos.king_square(weakerSide);
|
||||
|
||||
Value result = QueenValueEndgame
|
||||
- RookValueEndgame
|
||||
Value result = QueenValueEg
|
||||
- RookValueEg
|
||||
+ MateTable[loserKSq]
|
||||
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
|
||||
|
||||
@@ -357,12 +354,12 @@ template<>
|
||||
Value Endgame<KBBKN>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.piece_count(strongerSide, BISHOP) == 2);
|
||||
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMg);
|
||||
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
|
||||
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
|
||||
assert(pos.non_pawn_material(weakerSide) == KnightValueMg);
|
||||
assert(!pos.pieces(PAWN));
|
||||
|
||||
Value result = BishopValueEndgame;
|
||||
Value result = BishopValueEg;
|
||||
Square wksq = pos.king_square(strongerSide);
|
||||
Square bksq = pos.king_square(weakerSide);
|
||||
Square nsq = pos.piece_list(weakerSide, KNIGHT)[0];
|
||||
@@ -399,7 +396,7 @@ Value Endgame<KNNK>::operator()(const Position&) const {
|
||||
template<>
|
||||
ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
|
||||
assert(pos.piece_count(strongerSide, BISHOP) == 1);
|
||||
assert(pos.piece_count(strongerSide, PAWN) >= 1);
|
||||
|
||||
@@ -414,7 +411,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
||||
&& !(pawns & ~file_bb(pawnFile)))
|
||||
{
|
||||
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
|
||||
Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8));
|
||||
Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8);
|
||||
Square kingSq = pos.king_square(weakerSide);
|
||||
|
||||
if ( opposite_colors(queeningSq, bishopSq)
|
||||
@@ -451,7 +448,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
|
||||
assert(pos.piece_count(strongerSide, QUEEN) == 1);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
||||
assert(pos.piece_count(weakerSide, ROOK) == 1);
|
||||
@@ -481,9 +478,9 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 1);
|
||||
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
|
||||
assert(pos.non_pawn_material(weakerSide) == RookValueMg);
|
||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||
|
||||
Square wksq = pos.king_square(strongerSide);
|
||||
@@ -513,7 +510,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
||||
|
||||
File f = file_of(wpsq);
|
||||
Rank r = rank_of(wpsq);
|
||||
Square queeningSq = make_square(f, RANK_8);
|
||||
Square queeningSq = f | RANK_8;
|
||||
int tempo = (pos.side_to_move() == strongerSide);
|
||||
|
||||
// If the pawn is not too far advanced and the defending king defends the
|
||||
@@ -599,9 +596,9 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == RookValueMg);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 2);
|
||||
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
|
||||
assert(pos.non_pawn_material(weakerSide) == RookValueMg);
|
||||
assert(pos.piece_count(weakerSide, PAWN) == 1);
|
||||
|
||||
Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0];
|
||||
@@ -674,10 +671,10 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
|
||||
assert(pos.piece_count(strongerSide, BISHOP) == 1);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 1);
|
||||
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
|
||||
assert(pos.non_pawn_material(weakerSide) == BishopValueMg);
|
||||
assert(pos.piece_count(weakerSide, BISHOP) == 1);
|
||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||
|
||||
@@ -729,10 +726,10 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
|
||||
assert(pos.piece_count(strongerSide, BISHOP) == 1);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 2);
|
||||
assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame);
|
||||
assert(pos.non_pawn_material(weakerSide) == BishopValueMg);
|
||||
assert(pos.piece_count(weakerSide, BISHOP) == 1);
|
||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||
|
||||
@@ -752,12 +749,12 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
||||
if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2))
|
||||
{
|
||||
blockSq1 = psq1 + pawn_push(strongerSide);
|
||||
blockSq2 = make_square(file_of(psq2), rank_of(psq1));
|
||||
blockSq2 = file_of(psq2) | rank_of(psq1);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockSq1 = psq2 + pawn_push(strongerSide);
|
||||
blockSq2 = make_square(file_of(psq1), rank_of(psq2));
|
||||
blockSq2 = file_of(psq1) | rank_of(psq2);
|
||||
}
|
||||
|
||||
switch (file_distance(psq1, psq2))
|
||||
@@ -804,10 +801,10 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
|
||||
assert(pos.piece_count(strongerSide, BISHOP) == 1);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 1);
|
||||
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
|
||||
assert(pos.non_pawn_material(weakerSide) == KnightValueMg);
|
||||
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
|
||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||
|
||||
@@ -831,7 +828,7 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
|
||||
template<>
|
||||
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
|
||||
|
||||
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
|
||||
assert(pos.non_pawn_material(strongerSide) == KnightValueMg);
|
||||
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
|
||||
assert(pos.piece_count(strongerSide, PAWN) == 1);
|
||||
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
|
||||
@@ -893,5 +890,5 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
|
||||
|
||||
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
|
||||
// it's probably at least a draw even with the pawn.
|
||||
return probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
|
||||
return Bitbases::probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
|
||||
}
|
||||
|
||||
@@ -435,8 +435,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
&& sf == SCALE_FACTOR_NORMAL)
|
||||
{
|
||||
// Only the two bishops ?
|
||||
if ( pos.non_pawn_material(WHITE) == BishopValueMidgame
|
||||
&& pos.non_pawn_material(BLACK) == BishopValueMidgame)
|
||||
if ( pos.non_pawn_material(WHITE) == BishopValueMg
|
||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||
{
|
||||
// Check for KBP vs KB with only a single pawn that is almost
|
||||
// certainly a draw or at least two pawns.
|
||||
@@ -492,7 +492,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
|
||||
// Init king safety tables only if we are going to use them
|
||||
if ( pos.piece_count(Us, QUEEN)
|
||||
&& pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame)
|
||||
&& pos.non_pawn_material(Us) >= QueenValueMg + RookValueMg)
|
||||
{
|
||||
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
|
||||
b &= ei.attackedBy[Us][PAWN];
|
||||
@@ -582,7 +582,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
assert(b);
|
||||
|
||||
if (!more_than_one(b) && (b & pos.pieces(Them)))
|
||||
score += ThreatBonus[Piece][type_of(pos.piece_on(first_1(b)))];
|
||||
score += ThreatBonus[Piece][type_of(pos.piece_on(lsb(b)))];
|
||||
}
|
||||
|
||||
// Decrease score if we are attacked by an enemy pawn. Remaining part
|
||||
@@ -870,7 +870,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
return SCORE_ZERO;
|
||||
|
||||
do {
|
||||
Square s = pop_1st_bit(&b);
|
||||
Square s = pop_lsb(&b);
|
||||
|
||||
assert(pos.pawn_is_passed(Us, s));
|
||||
|
||||
@@ -938,7 +938,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
// value if the other side has a rook or queen.
|
||||
if (file_of(s) == FILE_A || file_of(s) == FILE_H)
|
||||
{
|
||||
if (pos.non_pawn_material(Them) <= KnightValueMidgame)
|
||||
if (pos.non_pawn_material(Them) <= KnightValueMg)
|
||||
ebonus += ebonus / 4;
|
||||
else if (pos.pieces(Them, ROOK, QUEEN))
|
||||
ebonus -= ebonus / 4;
|
||||
@@ -976,8 +976,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
|
||||
while (b)
|
||||
{
|
||||
s = pop_1st_bit(&b);
|
||||
queeningSquare = relative_square(c, make_square(file_of(s), RANK_8));
|
||||
s = pop_lsb(&b);
|
||||
queeningSquare = relative_square(c, file_of(s) | RANK_8);
|
||||
queeningPath = forward_bb(c, s);
|
||||
|
||||
// Compute plies to queening and check direct advancement
|
||||
@@ -1017,10 +1017,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
|
||||
while (b)
|
||||
{
|
||||
s = pop_1st_bit(&b);
|
||||
s = pop_lsb(&b);
|
||||
|
||||
// Compute plies from queening
|
||||
queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8));
|
||||
queeningSquare = relative_square(loserSide, file_of(s) | RANK_8);
|
||||
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
|
||||
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
|
||||
|
||||
@@ -1039,12 +1039,12 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
|
||||
while (b)
|
||||
{
|
||||
s = pop_1st_bit(&b);
|
||||
s = pop_lsb(&b);
|
||||
sacptg = blockersCount = 0;
|
||||
minKingDist = kingptg = 256;
|
||||
|
||||
// Compute plies from queening
|
||||
queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8));
|
||||
queeningSquare = relative_square(loserSide, file_of(s) | RANK_8);
|
||||
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
|
||||
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
|
||||
|
||||
@@ -1058,7 +1058,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
// How many plies does it take to remove all the blocking pawns?
|
||||
while (blockers)
|
||||
{
|
||||
blockSq = pop_1st_bit(&blockers);
|
||||
blockSq = pop_lsb(&blockers);
|
||||
movesToGo = 256;
|
||||
|
||||
// Check pawns that can give support to overcome obstacle, for instance
|
||||
@@ -1069,7 +1069,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
|
||||
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
|
||||
{
|
||||
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
|
||||
d = square_distance(blockSq, pop_lsb(&b2)) - 2;
|
||||
movesToGo = std::min(movesToGo, d);
|
||||
}
|
||||
}
|
||||
@@ -1079,7 +1079,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
|
||||
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
|
||||
{
|
||||
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
|
||||
d = square_distance(blockSq, pop_lsb(&b2)) - 2;
|
||||
movesToGo = std::min(movesToGo, d);
|
||||
}
|
||||
|
||||
@@ -1172,7 +1172,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
||||
// A couple of little helpers used by tracing code, to_cp() converts a value to
|
||||
// a double in centipawns scale, trace_add() stores white and black scores.
|
||||
|
||||
double to_cp(Value v) { return double(v) / double(PawnValueMidgame); }
|
||||
double to_cp(Value v) { return double(v) / double(PawnValueMg); }
|
||||
|
||||
void trace_add(int idx, Score wScore, Score bScore) {
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if !defined(LOCK_H_INCLUDED)
|
||||
#define LOCK_H_INCLUDED
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
|
||||
# include <pthread.h>
|
||||
|
||||
typedef pthread_mutex_t Lock;
|
||||
typedef pthread_cond_t WaitCondition;
|
||||
|
||||
# define lock_init(x) pthread_mutex_init(x, NULL)
|
||||
# define lock_grab(x) pthread_mutex_lock(x)
|
||||
# define lock_release(x) pthread_mutex_unlock(x)
|
||||
# define lock_destroy(x) pthread_mutex_destroy(x)
|
||||
# define cond_destroy(x) pthread_cond_destroy(x)
|
||||
# define cond_init(x) pthread_cond_init(x, NULL)
|
||||
# define cond_signal(x) pthread_cond_signal(x)
|
||||
# define cond_wait(x,y) pthread_cond_wait(x,y)
|
||||
# define cond_timedwait(x,y,z) pthread_cond_timedwait(x,y,z)
|
||||
|
||||
#else
|
||||
|
||||
#define NOMINMAX // disable macros min() and max()
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#undef NOMINMAX
|
||||
|
||||
// We use critical sections on Windows to support Windows XP and older versions,
|
||||
// unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject()
|
||||
// but apart from this they have the same speed performance of SRW locks.
|
||||
typedef CRITICAL_SECTION Lock;
|
||||
typedef HANDLE WaitCondition;
|
||||
|
||||
# define lock_init(x) InitializeCriticalSection(x)
|
||||
# define lock_grab(x) EnterCriticalSection(x)
|
||||
# define lock_release(x) LeaveCriticalSection(x)
|
||||
# define lock_destroy(x) DeleteCriticalSection(x)
|
||||
# define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); }
|
||||
# define cond_destroy(x) CloseHandle(*x)
|
||||
# define cond_signal(x) SetEvent(*x)
|
||||
# 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); }
|
||||
|
||||
#endif
|
||||
|
||||
#endif // !defined(LOCK_H_INCLUDED)
|
||||
@@ -28,19 +28,17 @@
|
||||
#include "tt.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
extern void uci_loop(const std::string&);
|
||||
extern void kpk_bitbase_init();
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
std::cout << engine_info() << std::endl;
|
||||
|
||||
UCI::init(Options);
|
||||
Bitboards::init();
|
||||
Position::init();
|
||||
kpk_bitbase_init();
|
||||
Zobrist::init();
|
||||
Bitbases::init_kpk();
|
||||
Search::init();
|
||||
Threads.init();
|
||||
Eval::init();
|
||||
Threads.init();
|
||||
TT.set_size(Options["Hash"]);
|
||||
|
||||
std::string args;
|
||||
@@ -48,5 +46,7 @@ int main(int argc, char* argv[]) {
|
||||
for (int i = 1; i < argc; i++)
|
||||
args += std::string(argv[i]) + " ";
|
||||
|
||||
uci_loop(args);
|
||||
UCI::loop(args);
|
||||
|
||||
Threads.exit();
|
||||
}
|
||||
|
||||
@@ -63,11 +63,11 @@ namespace {
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
return pos.non_pawn_material(Them) == VALUE_ZERO
|
||||
&& pos.piece_count(Them, PAWN) == 0
|
||||
&& pos.non_pawn_material(Us) >= RookValueMidgame;
|
||||
&& pos.non_pawn_material(Us) >= RookValueMg;
|
||||
}
|
||||
|
||||
template<Color Us> bool is_KBPsKs(const Position& pos) {
|
||||
return pos.non_pawn_material(Us) == BishopValueMidgame
|
||||
return pos.non_pawn_material(Us) == BishopValueMg
|
||||
&& pos.piece_count(Us, BISHOP) == 1
|
||||
&& pos.piece_count(Us, PAWN) >= 1;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace {
|
||||
template<Color Us> bool is_KQKRPs(const Position& pos) {
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
return pos.piece_count(Us, PAWN) == 0
|
||||
&& pos.non_pawn_material(Us) == QueenValueMidgame
|
||||
&& pos.non_pawn_material(Us) == QueenValueMg
|
||||
&& pos.piece_count(Us, QUEEN) == 1
|
||||
&& pos.piece_count(Them, ROOK) == 1
|
||||
&& pos.piece_count(Them, PAWN) >= 1;
|
||||
@@ -191,20 +191,20 @@ MaterialEntry* MaterialTable::probe(const Position& pos) {
|
||||
}
|
||||
|
||||
// No pawns makes it difficult to win, even with a material advantage
|
||||
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame)
|
||||
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMg)
|
||||
{
|
||||
e->factor[WHITE] = (uint8_t)
|
||||
(npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
|
||||
(npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
|
||||
}
|
||||
|
||||
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame)
|
||||
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMg)
|
||||
{
|
||||
e->factor[BLACK] = (uint8_t)
|
||||
(npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
|
||||
(npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
|
||||
}
|
||||
|
||||
// Compute the space weight
|
||||
if (npm_w + npm_b >= 2 * QueenValueMidgame + 4 * RookValueMidgame + 2 * KnightValueMidgame)
|
||||
if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
|
||||
{
|
||||
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP)
|
||||
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP);
|
||||
|
||||
@@ -33,7 +33,7 @@ using namespace std;
|
||||
/// Version number. If Version is left empty, then Tag plus current
|
||||
/// date (in the format YYMMDD) is used as a version number.
|
||||
|
||||
static const string Version = "120603";
|
||||
static const string Version = "2.3";
|
||||
static const string Tag = "";
|
||||
|
||||
|
||||
@@ -68,6 +68,13 @@ const string engine_info(bool to_uci) {
|
||||
}
|
||||
|
||||
|
||||
/// 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
|
||||
|
||||
static uint64_t hits[2], means[2];
|
||||
@@ -94,34 +101,34 @@ void dbg_print() {
|
||||
/// usual i/o functionality and without changing a single line of code!
|
||||
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
|
||||
|
||||
class Logger {
|
||||
|
||||
Logger() : in(cin.rdbuf(), file), out(cout.rdbuf(), file) {}
|
||||
~Logger() { start(false); }
|
||||
|
||||
struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout
|
||||
|
||||
Tie(streambuf* b, ofstream& f) : buf(b), file(f) {}
|
||||
Tie(streambuf* b, ofstream* f) : buf(b), file(f) {}
|
||||
|
||||
int sync() { return file.rdbuf()->pubsync(), buf->pubsync(); }
|
||||
int sync() { return file->rdbuf()->pubsync(), buf->pubsync(); }
|
||||
int overflow(int c) { return log(buf->sputc((char)c), "<< "); }
|
||||
int underflow() { return buf->sgetc(); }
|
||||
int uflow() { return log(buf->sbumpc(), ">> "); }
|
||||
|
||||
streambuf* buf;
|
||||
ofstream* file;
|
||||
|
||||
int log(int c, const char* prefix) {
|
||||
|
||||
static int last = '\n';
|
||||
|
||||
if (last == '\n')
|
||||
file.rdbuf()->sputn(prefix, 3);
|
||||
file->rdbuf()->sputn(prefix, 3);
|
||||
|
||||
return last = file.rdbuf()->sputc((char)c);
|
||||
return last = file->rdbuf()->sputc((char)c);
|
||||
}
|
||||
|
||||
streambuf* buf;
|
||||
ofstream& file;
|
||||
};
|
||||
|
||||
class Logger {
|
||||
|
||||
Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {}
|
||||
~Logger() { start(false); }
|
||||
|
||||
ofstream file;
|
||||
Tie in, out;
|
||||
|
||||
@@ -146,6 +153,23 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/// Used to serialize access to std::cout to avoid multiple threads to write at
|
||||
/// the same time.
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
|
||||
|
||||
static Mutex m;
|
||||
|
||||
if (sc == io_lock)
|
||||
m.lock();
|
||||
|
||||
if (sc == io_unlock)
|
||||
m.unlock();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
/// Trampoline helper to avoid moving Logger to misc.h
|
||||
void start_logger(bool b) { Logger::start(b); }
|
||||
|
||||
@@ -184,7 +208,7 @@ void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
|
||||
int tm = msec;
|
||||
#else
|
||||
timespec ts, *tm = &ts;
|
||||
uint64_t ms = Time::current_time().msec() + msec;
|
||||
uint64_t ms = Time::now() + msec;
|
||||
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000LL;
|
||||
|
||||
@@ -37,11 +37,6 @@ extern void dbg_hit_on_c(bool c, bool b);
|
||||
extern void dbg_mean_of(int v);
|
||||
extern void dbg_print();
|
||||
|
||||
class Position;
|
||||
extern Move move_from_uci(const Position& pos, std::string& str);
|
||||
extern const std::string move_to_uci(Move m, bool chess960);
|
||||
extern const std::string move_to_san(Position& pos, Move m);
|
||||
|
||||
|
||||
struct Log : public std::ofstream {
|
||||
Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {}
|
||||
@@ -49,25 +44,26 @@ struct Log : public std::ofstream {
|
||||
};
|
||||
|
||||
|
||||
struct Time {
|
||||
void restart() { system_time(&t); }
|
||||
uint64_t msec() const { return time_to_msec(t); }
|
||||
int elapsed() const { return int(current_time().msec() - time_to_msec(t)); }
|
||||
|
||||
static Time current_time() { Time t; t.restart(); return t; }
|
||||
|
||||
private:
|
||||
sys_time_t t;
|
||||
};
|
||||
namespace Time {
|
||||
typedef int64_t point;
|
||||
point now();
|
||||
}
|
||||
|
||||
|
||||
template<class Entry, int Size>
|
||||
struct HashTable {
|
||||
HashTable() : e(Size, Entry()) { memset(&e[0], 0, sizeof(Entry) * Size); }
|
||||
HashTable() : e(Size, Entry()) {}
|
||||
Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; }
|
||||
|
||||
private:
|
||||
std::vector<Entry> e;
|
||||
};
|
||||
|
||||
|
||||
enum SyncCout { io_lock, io_unlock };
|
||||
std::ostream& operator<<(std::ostream&, SyncCout);
|
||||
|
||||
#define sync_cout std::cout << io_lock
|
||||
#define sync_endl std::endl << io_unlock
|
||||
|
||||
#endif // !defined(MISC_H_INCLUDED)
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
/// move_to_uci() converts a move to a string in coordinate notation
|
||||
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
|
||||
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
|
||||
/// Chess960 mode. Instead internally Move is coded as "king captures rook".
|
||||
|
||||
const string move_to_uci(Move m, bool chess960) {
|
||||
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
string promotion;
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
|
||||
if (m == MOVE_NULL)
|
||||
return "0000";
|
||||
|
||||
if (is_castle(m) && !chess960)
|
||||
to = from + (file_of(to) == FILE_H ? Square(2) : -Square(2));
|
||||
|
||||
if (is_promotion(m))
|
||||
promotion = char(tolower(piece_type_to_char(promotion_type(m))));
|
||||
|
||||
return square_to_string(from) + square_to_string(to) + promotion;
|
||||
}
|
||||
|
||||
|
||||
/// move_from_uci() takes a position and a string representing a move in
|
||||
/// simple coordinate notation and returns an equivalent Move if any.
|
||||
/// Moves are guaranteed to be legal.
|
||||
|
||||
Move move_from_uci(const Position& pos, string& str) {
|
||||
|
||||
if (str.length() == 5) // Junior could send promotion in uppercase
|
||||
str[4] = char(tolower(str[4]));
|
||||
|
||||
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
|
||||
if (str == move_to_uci(ml.move(), pos.is_chess960()))
|
||||
return ml.move();
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
|
||||
/// move_to_san() takes a position and a move as input, where it is assumed
|
||||
/// that the move is a legal move for the position. The return value is
|
||||
/// a string containing the move in short algebraic notation.
|
||||
|
||||
const string move_to_san(Position& pos, Move m) {
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
|
||||
if (m == MOVE_NULL)
|
||||
return "(null)";
|
||||
|
||||
assert(is_ok(m));
|
||||
|
||||
Bitboard attackers;
|
||||
bool ambiguousMove, ambiguousFile, ambiguousRank;
|
||||
Square sq, from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
PieceType pt = type_of(pos.piece_moved(m));
|
||||
string san;
|
||||
|
||||
if (is_castle(m))
|
||||
san = (to_sq(m) < from_sq(m) ? "O-O-O" : "O-O");
|
||||
else
|
||||
{
|
||||
if (pt != PAWN)
|
||||
{
|
||||
san = piece_type_to_char(pt);
|
||||
|
||||
// Disambiguation if we have more then one piece with destination 'to'
|
||||
// note that for pawns is not needed because starting file is explicit.
|
||||
attackers = pos.attackers_to(to) & pos.pieces(pos.side_to_move(), pt);
|
||||
attackers ^= from;
|
||||
ambiguousMove = ambiguousFile = ambiguousRank = false;
|
||||
|
||||
while (attackers)
|
||||
{
|
||||
sq = pop_1st_bit(&attackers);
|
||||
|
||||
// Pinned pieces are not included in the possible sub-set
|
||||
if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces()))
|
||||
continue;
|
||||
|
||||
if (file_of(sq) == file_of(from))
|
||||
ambiguousFile = true;
|
||||
|
||||
if (rank_of(sq) == rank_of(from))
|
||||
ambiguousRank = true;
|
||||
|
||||
ambiguousMove = true;
|
||||
}
|
||||
|
||||
if (ambiguousMove)
|
||||
{
|
||||
if (!ambiguousFile)
|
||||
san += file_to_char(file_of(from));
|
||||
else if (!ambiguousRank)
|
||||
san += rank_to_char(rank_of(from));
|
||||
else
|
||||
san += square_to_string(from);
|
||||
}
|
||||
}
|
||||
|
||||
if (pos.is_capture(m))
|
||||
{
|
||||
if (pt == PAWN)
|
||||
san += file_to_char(file_of(from));
|
||||
|
||||
san += 'x';
|
||||
}
|
||||
|
||||
san += square_to_string(to);
|
||||
|
||||
if (is_promotion(m))
|
||||
{
|
||||
san += '=';
|
||||
san += piece_type_to_char(promotion_type(m));
|
||||
}
|
||||
}
|
||||
|
||||
if (pos.move_gives_check(m, CheckInfo(pos)))
|
||||
{
|
||||
StateInfo st;
|
||||
pos.do_move(m, st);
|
||||
san += MoveList<MV_LEGAL>(pos).size() ? "+" : "#";
|
||||
pos.undo_move(m);
|
||||
}
|
||||
|
||||
return san;
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "movegen.h"
|
||||
@@ -25,14 +24,14 @@
|
||||
|
||||
/// Simple macro to wrap a very common while loop, no facny, no flexibility,
|
||||
/// hardcoded names 'mlist' and 'from'.
|
||||
#define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_1st_bit(&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
|
||||
#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_1st_bit(&b); \
|
||||
#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \
|
||||
(*mlist++).move = make_move(to - (d), to); }
|
||||
namespace {
|
||||
|
||||
template<CastlingSide Side, bool OnlyChecks>
|
||||
template<CastlingSide Side, bool Checks>
|
||||
MoveStack* generate_castle(const Position& pos, MoveStack* mlist, Color us) {
|
||||
|
||||
if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side)))
|
||||
@@ -47,9 +46,8 @@ namespace {
|
||||
|
||||
assert(!pos.in_check());
|
||||
|
||||
for (Square s = std::min(kfrom, kto), e = std::max(kfrom, kto); s <= e; s++)
|
||||
if ( s != kfrom // We are not in check
|
||||
&& (pos.attackers_to(s) & enemies))
|
||||
for (Square s = kto; s != kfrom; s += (Square)(Side == KING_SIDE ? -1 : 1))
|
||||
if (pos.attackers_to(s) & enemies)
|
||||
return mlist;
|
||||
|
||||
// Because we generate only legal castling moves we need to verify that
|
||||
@@ -59,9 +57,9 @@ namespace {
|
||||
&& (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies))
|
||||
return mlist;
|
||||
|
||||
(*mlist++).move = make_castle(kfrom, rfrom);
|
||||
(*mlist++).move = make<CASTLE>(kfrom, rfrom);
|
||||
|
||||
if (OnlyChecks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
|
||||
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
|
||||
mlist--;
|
||||
|
||||
return mlist;
|
||||
@@ -80,39 +78,41 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
template<MoveType Type, Square Delta>
|
||||
inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7, Bitboard target, Square ksq) {
|
||||
template<GenType Type, Square Delta>
|
||||
inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7,
|
||||
Bitboard target, const CheckInfo* ci) {
|
||||
|
||||
Bitboard b = move_pawns<Delta>(pawnsOn7) & target;
|
||||
|
||||
while (b)
|
||||
{
|
||||
Square to = pop_1st_bit(&b);
|
||||
Square to = pop_lsb(&b);
|
||||
|
||||
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
|
||||
(*mlist++).move = make_promotion(to - Delta, to, QUEEN);
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
(*mlist++).move = make<PROMOTION>(to - Delta, to, QUEEN);
|
||||
|
||||
if (Type == MV_QUIET || Type == MV_EVASION || Type == MV_NON_EVASION)
|
||||
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
(*mlist++).move = make_promotion(to - Delta, to, ROOK);
|
||||
(*mlist++).move = make_promotion(to - Delta, to, BISHOP);
|
||||
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
|
||||
(*mlist++).move = make<PROMOTION>(to - Delta, to, ROOK);
|
||||
(*mlist++).move = make<PROMOTION>(to - Delta, to, BISHOP);
|
||||
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||
}
|
||||
|
||||
// Knight-promotion is the only one that can give a direct check not
|
||||
// already included in the queen-promotion.
|
||||
if (Type == MV_QUIET_CHECK && (StepAttacksBB[W_KNIGHT][to] & ksq))
|
||||
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
|
||||
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
|
||||
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||
else
|
||||
(void)ksq; // Silence a warning under MSVC
|
||||
(void)ci; // Silence a warning under MSVC
|
||||
}
|
||||
|
||||
return mlist;
|
||||
}
|
||||
|
||||
|
||||
template<Color Us, MoveType Type>
|
||||
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq = SQ_NONE) {
|
||||
template<Color Us, GenType Type>
|
||||
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist,
|
||||
Bitboard target, const CheckInfo* ci) {
|
||||
|
||||
// Compute our parametrized parameters at compile time, named according to
|
||||
// the point of view of white side.
|
||||
@@ -129,35 +129,35 @@ namespace {
|
||||
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
|
||||
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
|
||||
|
||||
Bitboard enemies = (Type == MV_EVASION ? pos.pieces(Them) & target:
|
||||
Type == MV_CAPTURE ? target : pos.pieces(Them));
|
||||
Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
|
||||
Type == CAPTURES ? target : pos.pieces(Them));
|
||||
|
||||
// Single and double pawn pushes, no promotions
|
||||
if (Type != MV_CAPTURE)
|
||||
if (Type != CAPTURES)
|
||||
{
|
||||
emptySquares = (Type == MV_QUIET ? target : ~pos.pieces());
|
||||
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
|
||||
|
||||
b1 = move_pawns<UP>(pawnsNotOn7) & emptySquares;
|
||||
b2 = move_pawns<UP>(b1 & TRank3BB) & emptySquares;
|
||||
|
||||
if (Type == MV_EVASION) // Consider only blocking squares
|
||||
if (Type == EVASIONS) // Consider only blocking squares
|
||||
{
|
||||
b1 &= target;
|
||||
b2 &= target;
|
||||
}
|
||||
|
||||
if (Type == MV_QUIET_CHECK)
|
||||
if (Type == QUIET_CHECKS)
|
||||
{
|
||||
b1 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
b2 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
b1 &= pos.attacks_from<PAWN>(ci->ksq, Them);
|
||||
b2 &= pos.attacks_from<PAWN>(ci->ksq, Them);
|
||||
|
||||
// Add pawn pushes which give discovered check. This is possible only
|
||||
// if the pawn is not on the same file as the enemy king, because we
|
||||
// don't generate captures. Note that a possible discovery check
|
||||
// promotion has been already generated among captures.
|
||||
if (pawnsNotOn7 & target) // Target is dc bitboard
|
||||
if (pawnsNotOn7 & ci->dcCandidates)
|
||||
{
|
||||
dc1 = move_pawns<UP>(pawnsNotOn7 & target) & emptySquares & ~file_bb(ksq);
|
||||
dc1 = move_pawns<UP>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
|
||||
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
|
||||
|
||||
b1 |= dc1;
|
||||
@@ -170,21 +170,21 @@ namespace {
|
||||
}
|
||||
|
||||
// Promotions and underpromotions
|
||||
if (pawnsOn7 && (Type != MV_EVASION || (target & TRank8BB)))
|
||||
if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB)))
|
||||
{
|
||||
if (Type == MV_CAPTURE)
|
||||
if (Type == CAPTURES)
|
||||
emptySquares = ~pos.pieces();
|
||||
|
||||
if (Type == MV_EVASION)
|
||||
if (Type == EVASIONS)
|
||||
emptySquares &= target;
|
||||
|
||||
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ksq);
|
||||
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ksq);
|
||||
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ksq);
|
||||
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ci);
|
||||
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ci);
|
||||
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ci);
|
||||
}
|
||||
|
||||
// Standard and en-passant captures
|
||||
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
b1 = move_pawns<RIGHT>(pawnsNotOn7) & enemies;
|
||||
b2 = move_pawns<LEFT >(pawnsNotOn7) & enemies;
|
||||
@@ -199,7 +199,7 @@ namespace {
|
||||
// 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 a discovery check and we are forced to do otherwise.
|
||||
if (Type == MV_EVASION && !(target & (pos.ep_square() - UP)))
|
||||
if (Type == EVASIONS && !(target & (pos.ep_square() - UP)))
|
||||
return mlist;
|
||||
|
||||
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
|
||||
@@ -207,7 +207,7 @@ namespace {
|
||||
assert(b1);
|
||||
|
||||
while (b1)
|
||||
(*mlist++).move = make_enpassant(pop_1st_bit(&b1), pos.ep_square());
|
||||
(*mlist++).move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,60 +215,39 @@ namespace {
|
||||
}
|
||||
|
||||
|
||||
template<PieceType Pt>
|
||||
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist,
|
||||
Color us, const CheckInfo& ci) {
|
||||
template<PieceType Pt, bool Checks> FORCE_INLINE
|
||||
MoveStack* generate_moves(const Position& pos, MoveStack* mlist, Color us,
|
||||
Bitboard target, const CheckInfo* ci) {
|
||||
|
||||
assert(Pt != KING && Pt != PAWN);
|
||||
|
||||
Bitboard b, target;
|
||||
Square from;
|
||||
const Square* pl = pos.piece_list(us, Pt);
|
||||
|
||||
if (*pl != SQ_NONE)
|
||||
for (Square from = *pl; from != SQ_NONE; from = *++pl)
|
||||
{
|
||||
if (Checks)
|
||||
{
|
||||
target = ci.checkSq[Pt] & ~pos.pieces(); // Non capture checks only
|
||||
|
||||
do {
|
||||
from = *pl;
|
||||
|
||||
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
|
||||
&& !(PseudoAttacks[Pt][from] & target))
|
||||
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
|
||||
continue;
|
||||
|
||||
if (ci.dcCandidates && (ci.dcCandidates & from))
|
||||
if (ci->dcCandidates && (ci->dcCandidates & from))
|
||||
continue;
|
||||
}
|
||||
|
||||
Bitboard b = pos.attacks_from<Pt>(from) & target;
|
||||
|
||||
if (Checks)
|
||||
b &= ci->checkSq[Pt];
|
||||
|
||||
b = pos.attacks_from<Pt>(from) & target;
|
||||
SERIALIZE(b);
|
||||
} while (*++pl != SQ_NONE);
|
||||
}
|
||||
|
||||
return mlist;
|
||||
}
|
||||
|
||||
|
||||
template<PieceType Pt>
|
||||
FORCE_INLINE MoveStack* generate_moves(const Position& pos, MoveStack* mlist,
|
||||
Color us, Bitboard target) {
|
||||
assert(Pt != KING && Pt != PAWN);
|
||||
|
||||
Bitboard b;
|
||||
Square from;
|
||||
const Square* pl = pos.piece_list(us, Pt);
|
||||
|
||||
if (*pl != SQ_NONE)
|
||||
do {
|
||||
from = *pl;
|
||||
b = pos.attacks_from<Pt>(from) & target;
|
||||
SERIALIZE(b);
|
||||
} while (*++pl != SQ_NONE);
|
||||
|
||||
return mlist;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
FORCE_INLINE MoveStack* generate_moves<KING>(const Position& pos, MoveStack* mlist,
|
||||
FORCE_INLINE MoveStack* generate_king_moves(const Position& pos, MoveStack* mlist,
|
||||
Color us, Bitboard target) {
|
||||
Square from = pos.king_square(us);
|
||||
Bitboard b = pos.attacks_from<KING>(from) & target;
|
||||
@@ -276,64 +255,75 @@ namespace {
|
||||
return mlist;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
template<GenType Type> FORCE_INLINE
|
||||
MoveStack* generate_all_moves(const Position& pos, MoveStack* mlist, Color us,
|
||||
Bitboard target, const CheckInfo* ci = NULL) {
|
||||
|
||||
/// generate<MV_CAPTURE> generates all pseudo-legal captures and queen
|
||||
/// promotions. Returns a pointer to the end of the move list.
|
||||
///
|
||||
/// generate<MV_QUIET> generates all pseudo-legal non-captures and
|
||||
/// underpromotions. Returns a pointer to the end of the move list.
|
||||
///
|
||||
/// generate<MV_NON_EVASION> generates all pseudo-legal captures and
|
||||
/// non-captures. Returns a pointer to the end of the move list.
|
||||
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci)
|
||||
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci));
|
||||
|
||||
template<MoveType Type>
|
||||
MoveStack* generate(const Position& pos, MoveStack* mlist) {
|
||||
mlist = generate_moves<KNIGHT, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
|
||||
mlist = generate_moves<BISHOP, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
|
||||
mlist = generate_moves<ROOK, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
|
||||
mlist = generate_moves<QUEEN, Type == QUIET_CHECKS>(pos, mlist, us, target, ci);
|
||||
|
||||
assert(Type == MV_CAPTURE || Type == MV_QUIET || Type == MV_NON_EVASION);
|
||||
assert(!pos.in_check());
|
||||
if (Type != QUIET_CHECKS && Type != EVASIONS)
|
||||
mlist = generate_king_moves(pos, mlist, us, target);
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard target;
|
||||
|
||||
if (Type == MV_CAPTURE)
|
||||
target = pos.pieces(~us);
|
||||
|
||||
else if (Type == MV_QUIET)
|
||||
target = ~pos.pieces();
|
||||
|
||||
else if (Type == MV_NON_EVASION)
|
||||
target = ~pos.pieces(us);
|
||||
|
||||
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target)
|
||||
: generate_pawn_moves<BLACK, Type>(pos, mlist, target));
|
||||
|
||||
mlist = generate_moves<KNIGHT>(pos, mlist, us, target);
|
||||
mlist = generate_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_moves<ROOK>(pos, mlist, us, target);
|
||||
mlist = generate_moves<QUEEN>(pos, mlist, us, target);
|
||||
mlist = generate_moves<KING>(pos, mlist, us, target);
|
||||
|
||||
if (Type != MV_CAPTURE && pos.can_castle(us))
|
||||
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us))
|
||||
{
|
||||
mlist = generate_castle<KING_SIDE, false>(pos, mlist, us);
|
||||
mlist = generate_castle<QUEEN_SIDE, false>(pos, mlist, us);
|
||||
mlist = generate_castle<KING_SIDE, Type == QUIET_CHECKS>(pos, mlist, us);
|
||||
mlist = generate_castle<QUEEN_SIDE, Type == QUIET_CHECKS>(pos, mlist, us);
|
||||
}
|
||||
|
||||
return mlist;
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
/// generate<CAPTURES> generates all pseudo-legal captures and queen
|
||||
/// promotions. Returns a pointer to the end of the move list.
|
||||
///
|
||||
/// generate<QUIETS> generates all pseudo-legal non-captures and
|
||||
/// underpromotions. Returns a pointer to the end of the move list.
|
||||
///
|
||||
/// generate<NON_EVASIONS> generates all pseudo-legal captures and
|
||||
/// non-captures. Returns a pointer to the end of the move list.
|
||||
|
||||
template<GenType Type>
|
||||
MoveStack* generate(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
|
||||
assert(!pos.in_check());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard target;
|
||||
|
||||
if (Type == CAPTURES)
|
||||
target = pos.pieces(~us);
|
||||
|
||||
else if (Type == QUIETS)
|
||||
target = ~pos.pieces();
|
||||
|
||||
else if (Type == NON_EVASIONS)
|
||||
target = ~pos.pieces(us);
|
||||
|
||||
return generate_all_moves<Type>(pos, mlist, us, target);
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
template MoveStack* generate<MV_CAPTURE>(const Position& pos, MoveStack* mlist);
|
||||
template MoveStack* generate<MV_QUIET>(const Position& pos, MoveStack* mlist);
|
||||
template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist);
|
||||
template MoveStack* generate<CAPTURES>(const Position&, MoveStack*);
|
||||
template MoveStack* generate<QUIETS>(const Position&, MoveStack*);
|
||||
template MoveStack* generate<NON_EVASIONS>(const Position&, MoveStack*);
|
||||
|
||||
|
||||
/// generate<MV_QUIET_CHECK> 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.
|
||||
template<>
|
||||
MoveStack* generate<MV_QUIET_CHECK>(const Position& pos, MoveStack* mlist) {
|
||||
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(!pos.in_check());
|
||||
|
||||
@@ -343,7 +333,7 @@ MoveStack* generate<MV_QUIET_CHECK>(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
while (dc)
|
||||
{
|
||||
Square from = pop_1st_bit(&dc);
|
||||
Square from = pop_lsb(&dc);
|
||||
PieceType pt = type_of(pos.piece_on(from));
|
||||
|
||||
if (pt == PAWN)
|
||||
@@ -357,48 +347,32 @@ MoveStack* generate<MV_QUIET_CHECK>(const Position& pos, MoveStack* mlist) {
|
||||
SERIALIZE(b);
|
||||
}
|
||||
|
||||
mlist = (us == WHITE ? generate_pawn_moves<WHITE, MV_QUIET_CHECK>(pos, mlist, ci.dcCandidates, ci.ksq)
|
||||
: generate_pawn_moves<BLACK, MV_QUIET_CHECK>(pos, mlist, ci.dcCandidates, ci.ksq));
|
||||
|
||||
mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, ci);
|
||||
mlist = generate_direct_checks<BISHOP>(pos, mlist, us, ci);
|
||||
mlist = generate_direct_checks<ROOK>(pos, mlist, us, ci);
|
||||
mlist = generate_direct_checks<QUEEN>(pos, mlist, us, ci);
|
||||
|
||||
if (pos.can_castle(us))
|
||||
{
|
||||
mlist = generate_castle<KING_SIDE, true>(pos, mlist, us);
|
||||
mlist = generate_castle<QUEEN_SIDE, true>(pos, mlist, us);
|
||||
}
|
||||
|
||||
return mlist;
|
||||
return generate_all_moves<QUIET_CHECKS>(pos, mlist, us, ~pos.pieces(), &ci);
|
||||
}
|
||||
|
||||
|
||||
/// generate<MV_EVASION> 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.
|
||||
template<>
|
||||
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
|
||||
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(pos.in_check());
|
||||
|
||||
Bitboard b, target;
|
||||
Square from, checksq;
|
||||
int checkersCnt = 0;
|
||||
Color us = pos.side_to_move();
|
||||
Square ksq = pos.king_square(us);
|
||||
Bitboard sliderAttacks = 0;
|
||||
Bitboard checkers = pos.checkers();
|
||||
Bitboard b = pos.checkers();
|
||||
|
||||
assert(checkers);
|
||||
assert(pos.checkers());
|
||||
|
||||
// Find squares attacked by slider checkers, we will remove them from the king
|
||||
// evasions so to skip known illegal moves avoiding useless legality check later.
|
||||
b = checkers;
|
||||
do
|
||||
{
|
||||
checkersCnt++;
|
||||
checksq = pop_1st_bit(&b);
|
||||
checksq = pop_lsb(&b);
|
||||
|
||||
assert(color_of(pos.piece_on(checksq)) == ~us);
|
||||
|
||||
@@ -429,38 +403,33 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
|
||||
from = ksq;
|
||||
SERIALIZE(b);
|
||||
|
||||
// Generate evasions for other pieces only if not under a double check
|
||||
if (checkersCnt > 1)
|
||||
return mlist;
|
||||
return mlist; // Double check, only a king move can save the day
|
||||
|
||||
// Blocking evasions or captures of the checking piece
|
||||
target = between_bb(checksq, ksq) | checkers;
|
||||
// Generate blocking evasions or captures of the checking piece
|
||||
Bitboard target = between_bb(checksq, ksq) | pos.checkers();
|
||||
|
||||
mlist = (us == WHITE ? generate_pawn_moves<WHITE, MV_EVASION>(pos, mlist, target)
|
||||
: generate_pawn_moves<BLACK, MV_EVASION>(pos, mlist, target));
|
||||
|
||||
mlist = generate_moves<KNIGHT>(pos, mlist, us, target);
|
||||
mlist = generate_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_moves<ROOK>(pos, mlist, us, target);
|
||||
return generate_moves<QUEEN>(pos, mlist, us, target);
|
||||
return generate_all_moves<EVASIONS>(pos, mlist, us, target);
|
||||
}
|
||||
|
||||
|
||||
/// generate<MV_LEGAL> generates all the legal moves in the given position
|
||||
/// generate<LEGAL> generates all the legal moves in the given position
|
||||
|
||||
template<>
|
||||
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
|
||||
MoveStack* generate<LEGAL>(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
MoveStack *last, *cur = mlist;
|
||||
MoveStack *end, *cur = mlist;
|
||||
Bitboard pinned = pos.pinned_pieces();
|
||||
Square ksq = pos.king_square(pos.side_to_move());
|
||||
|
||||
last = pos.in_check() ? generate<MV_EVASION>(pos, mlist)
|
||||
: generate<MV_NON_EVASION>(pos, mlist);
|
||||
while (cur != last)
|
||||
if (!pos.pl_move_is_legal(cur->move, pinned))
|
||||
cur->move = (--last)->move;
|
||||
end = pos.in_check() ? generate<EVASIONS>(pos, mlist)
|
||||
: generate<NON_EVASIONS>(pos, mlist);
|
||||
while (cur != end)
|
||||
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
|
||||
&& !pos.pl_move_is_legal(cur->move, pinned))
|
||||
cur->move = (--end)->move;
|
||||
else
|
||||
cur++;
|
||||
|
||||
return last;
|
||||
return end;
|
||||
}
|
||||
|
||||
@@ -22,30 +22,30 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
enum MoveType {
|
||||
MV_CAPTURE,
|
||||
MV_QUIET,
|
||||
MV_QUIET_CHECK,
|
||||
MV_EVASION,
|
||||
MV_NON_EVASION,
|
||||
MV_LEGAL
|
||||
enum GenType {
|
||||
CAPTURES,
|
||||
QUIETS,
|
||||
QUIET_CHECKS,
|
||||
EVASIONS,
|
||||
NON_EVASIONS,
|
||||
LEGAL
|
||||
};
|
||||
|
||||
class Position;
|
||||
|
||||
template<MoveType>
|
||||
template<GenType>
|
||||
MoveStack* generate(const Position& pos, MoveStack* mlist);
|
||||
|
||||
/// The MoveList struct is a simple wrapper around generate(), sometimes comes
|
||||
/// handy to use this class instead of the low level generate() function.
|
||||
template<MoveType T>
|
||||
template<GenType T>
|
||||
struct MoveList {
|
||||
|
||||
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {}
|
||||
void operator++() { cur++; }
|
||||
bool end() const { return cur == last; }
|
||||
Move move() const { return cur->move; }
|
||||
int size() const { return int(last - mlist); }
|
||||
size_t size() const { return last - mlist; }
|
||||
|
||||
private:
|
||||
MoveStack mlist[MAX_MOVES];
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "movegen.h"
|
||||
#include "movepick.h"
|
||||
#include "thread.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -38,15 +39,15 @@ namespace {
|
||||
|
||||
// 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.
|
||||
inline bool has_positive_score(const MoveStack& move) { return move.score > 0; }
|
||||
inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; }
|
||||
|
||||
// Picks and moves to the front the best move in the range [firstMove, lastMove),
|
||||
// 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
|
||||
// normally are the possible captures.
|
||||
inline MoveStack* pick_best(MoveStack* firstMove, MoveStack* lastMove)
|
||||
inline MoveStack* pick_best(MoveStack* begin, MoveStack* end)
|
||||
{
|
||||
std::swap(*firstMove, *std::max_element(firstMove, lastMove));
|
||||
return firstMove;
|
||||
std::swap(*begin, *std::max_element(begin, end));
|
||||
return begin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,13 +59,14 @@ namespace {
|
||||
/// move ordering is at the current node.
|
||||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
||||
Search::Stack* ss, Value beta) : pos(p), H(h), depth(d) {
|
||||
Search::Stack* s, Value beta) : pos(p), H(h), depth(d) {
|
||||
|
||||
assert(d > DEPTH_ZERO);
|
||||
|
||||
captureThreshold = 0;
|
||||
curMove = lastMove = moves;
|
||||
lastBadCapture = moves + MAX_MOVES - 1;
|
||||
cur = end = moves;
|
||||
endBadCaptures = moves + MAX_MOVES - 1;
|
||||
ss = s;
|
||||
|
||||
if (p.in_check())
|
||||
phase = EVASION;
|
||||
@@ -77,8 +79,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
||||
killers[1].move = ss->killers[1];
|
||||
|
||||
// Consider sligtly negative captures as good if at low depth and far from beta
|
||||
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY)
|
||||
captureThreshold = -PawnValueMidgame;
|
||||
if (ss && ss->eval < beta - PawnValueMg && d < 3 * ONE_PLY)
|
||||
captureThreshold = -PawnValueMg;
|
||||
|
||||
// Consider negative captures as good if still enough to reach beta
|
||||
else if (ss && ss->eval > beta)
|
||||
@@ -86,11 +88,11 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
||||
}
|
||||
|
||||
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||
lastMove += (ttMove != MOVE_NONE);
|
||||
end += (ttMove != MOVE_NONE);
|
||||
}
|
||||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
||||
Square sq) : pos(p), H(h), curMove(moves), lastMove(moves) {
|
||||
Square sq) : pos(p), H(h), cur(moves), end(moves) {
|
||||
|
||||
assert(d <= DEPTH_ZERO);
|
||||
|
||||
@@ -118,24 +120,24 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
||||
}
|
||||
|
||||
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||
lastMove += (ttMove != MOVE_NONE);
|
||||
end += (ttMove != MOVE_NONE);
|
||||
}
|
||||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt)
|
||||
: pos(p), H(h), curMove(moves), lastMove(moves) {
|
||||
: pos(p), H(h), cur(moves), end(moves) {
|
||||
|
||||
assert(!pos.in_check());
|
||||
|
||||
phase = PROBCUT;
|
||||
|
||||
// In ProbCut we generate only captures better than parent's captured piece
|
||||
captureThreshold = PieceValueMidgame[pt];
|
||||
captureThreshold = PieceValue[Mg][pt];
|
||||
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||
|
||||
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
|
||||
ttMove = MOVE_NONE;
|
||||
|
||||
lastMove += (ttMove != MOVE_NONE);
|
||||
end += (ttMove != MOVE_NONE);
|
||||
}
|
||||
|
||||
|
||||
@@ -160,14 +162,14 @@ void MovePicker::score_captures() {
|
||||
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
|
||||
Move m;
|
||||
|
||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
||||
for (MoveStack* it = moves; it != end; ++it)
|
||||
{
|
||||
m = cur->move;
|
||||
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))]
|
||||
m = it->move;
|
||||
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))]
|
||||
- type_of(pos.piece_moved(m));
|
||||
|
||||
if (is_promotion(m))
|
||||
cur->score += PieceValueMidgame[promotion_type(m)];
|
||||
if (type_of(m) == PROMOTION)
|
||||
it->score += PieceValue[Mg][promotion_type(m)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,10 +177,10 @@ void MovePicker::score_noncaptures() {
|
||||
|
||||
Move m;
|
||||
|
||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
||||
for (MoveStack* it = moves; it != end; ++it)
|
||||
{
|
||||
m = cur->move;
|
||||
cur->score = H.value(pos.piece_moved(m), to_sq(m));
|
||||
m = it->move;
|
||||
it->score = H.value(pos.piece_moved(m), to_sq(m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,19 +191,19 @@ void MovePicker::score_evasions() {
|
||||
Move m;
|
||||
int seeScore;
|
||||
|
||||
if (lastMove < moves + 2)
|
||||
if (end < moves + 2)
|
||||
return;
|
||||
|
||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
||||
for (MoveStack* it = moves; it != end; ++it)
|
||||
{
|
||||
m = cur->move;
|
||||
m = it->move;
|
||||
if ((seeScore = pos.see_sign(m)) < 0)
|
||||
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom
|
||||
it->score = seeScore - History::MaxValue; // Be sure we are at the bottom
|
||||
else if (pos.is_capture(m))
|
||||
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))]
|
||||
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))]
|
||||
- type_of(pos.piece_moved(m)) + History::MaxValue;
|
||||
else
|
||||
cur->score = H.value(pos.piece_moved(m), to_sq(m));
|
||||
it->score = H.value(pos.piece_moved(m), to_sq(m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,53 +213,53 @@ void MovePicker::score_evasions() {
|
||||
|
||||
void MovePicker::generate_next() {
|
||||
|
||||
curMove = moves;
|
||||
cur = moves;
|
||||
|
||||
switch (++phase) {
|
||||
|
||||
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
|
||||
lastMove = generate<MV_CAPTURE>(pos, moves);
|
||||
end = generate<CAPTURES>(pos, moves);
|
||||
score_captures();
|
||||
return;
|
||||
|
||||
case KILLERS_S1:
|
||||
curMove = killers;
|
||||
lastMove = curMove + 2;
|
||||
cur = killers;
|
||||
end = cur + 2;
|
||||
return;
|
||||
|
||||
case QUIETS_1_S1:
|
||||
lastQuiet = lastMove = generate<MV_QUIET>(pos, moves);
|
||||
endQuiets = end = generate<QUIETS>(pos, moves);
|
||||
score_noncaptures();
|
||||
lastMove = std::partition(curMove, lastMove, has_positive_score);
|
||||
sort<MoveStack>(curMove, lastMove);
|
||||
end = std::partition(cur, end, has_positive_score);
|
||||
sort<MoveStack>(cur, end);
|
||||
return;
|
||||
|
||||
case QUIETS_2_S1:
|
||||
curMove = lastMove;
|
||||
lastMove = lastQuiet;
|
||||
cur = end;
|
||||
end = endQuiets;
|
||||
if (depth >= 3 * ONE_PLY)
|
||||
sort<MoveStack>(curMove, lastMove);
|
||||
sort<MoveStack>(cur, end);
|
||||
return;
|
||||
|
||||
case BAD_CAPTURES_S1:
|
||||
// Just pick them in reverse order to get MVV/LVA ordering
|
||||
curMove = moves + MAX_MOVES - 1;
|
||||
lastMove = lastBadCapture;
|
||||
cur = moves + MAX_MOVES - 1;
|
||||
end = endBadCaptures;
|
||||
return;
|
||||
|
||||
case EVASIONS_S2:
|
||||
lastMove = generate<MV_EVASION>(pos, moves);
|
||||
end = generate<EVASIONS>(pos, moves);
|
||||
score_evasions();
|
||||
return;
|
||||
|
||||
case QUIET_CHECKS_S3:
|
||||
lastMove = generate<MV_QUIET_CHECK>(pos, moves);
|
||||
end = generate<QUIET_CHECKS>(pos, moves);
|
||||
return;
|
||||
|
||||
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
|
||||
phase = STOP;
|
||||
case STOP:
|
||||
lastMove = curMove + 1; // Avoid another next_phase() call
|
||||
end = cur + 1; // Avoid another next_phase() call
|
||||
return;
|
||||
|
||||
default:
|
||||
@@ -270,26 +272,25 @@ void MovePicker::generate_next() {
|
||||
/// It returns a new pseudo legal move every time it is called, until there
|
||||
/// are no more moves left. It picks the move with the biggest score from a list
|
||||
/// of generated moves taking care not to return the tt move if has already been
|
||||
/// searched previously. Note that this function is not thread safe so should be
|
||||
/// lock protected by caller when accessed through a shared MovePicker object.
|
||||
|
||||
Move MovePicker::next_move() {
|
||||
/// searched previously.
|
||||
template<>
|
||||
Move MovePicker::next_move<false>() {
|
||||
|
||||
Move move;
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (curMove == lastMove)
|
||||
while (cur == end)
|
||||
generate_next();
|
||||
|
||||
switch (phase) {
|
||||
|
||||
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
|
||||
curMove++;
|
||||
cur++;
|
||||
return ttMove;
|
||||
|
||||
case CAPTURES_S1:
|
||||
move = pick_best(curMove++, lastMove)->move;
|
||||
move = pick_best(cur++, end)->move;
|
||||
if (move != ttMove)
|
||||
{
|
||||
assert(captureThreshold <= 0); // Otherwise we cannot use see_sign()
|
||||
@@ -298,12 +299,12 @@ Move MovePicker::next_move() {
|
||||
return move;
|
||||
|
||||
// Losing capture, move it to the tail of the array
|
||||
(lastBadCapture--)->move = move;
|
||||
(endBadCaptures--)->move = move;
|
||||
}
|
||||
break;
|
||||
|
||||
case KILLERS_S1:
|
||||
move = (curMove++)->move;
|
||||
move = (cur++)->move;
|
||||
if ( move != MOVE_NONE
|
||||
&& pos.is_pseudo_legal(move)
|
||||
&& move != ttMove
|
||||
@@ -312,7 +313,7 @@ Move MovePicker::next_move() {
|
||||
break;
|
||||
|
||||
case QUIETS_1_S1: case QUIETS_2_S1:
|
||||
move = (curMove++)->move;
|
||||
move = (cur++)->move;
|
||||
if ( move != ttMove
|
||||
&& move != killers[0].move
|
||||
&& move != killers[1].move)
|
||||
@@ -320,28 +321,28 @@ Move MovePicker::next_move() {
|
||||
break;
|
||||
|
||||
case BAD_CAPTURES_S1:
|
||||
return (curMove--)->move;
|
||||
return (cur--)->move;
|
||||
|
||||
case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4:
|
||||
move = pick_best(curMove++, lastMove)->move;
|
||||
move = pick_best(cur++, end)->move;
|
||||
if (move != ttMove)
|
||||
return move;
|
||||
break;
|
||||
|
||||
case CAPTURES_S5:
|
||||
move = pick_best(curMove++, lastMove)->move;
|
||||
move = pick_best(cur++, end)->move;
|
||||
if (move != ttMove && pos.see(move) > captureThreshold)
|
||||
return move;
|
||||
break;
|
||||
|
||||
case CAPTURES_S6:
|
||||
move = pick_best(curMove++, lastMove)->move;
|
||||
move = pick_best(cur++, end)->move;
|
||||
if (to_sq(move) == recaptureSquare)
|
||||
return move;
|
||||
break;
|
||||
|
||||
case QUIET_CHECKS_S3:
|
||||
move = (curMove++)->move;
|
||||
move = (cur++)->move;
|
||||
if (move != ttMove)
|
||||
return move;
|
||||
break;
|
||||
@@ -354,3 +355,10 @@ Move MovePicker::next_move() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Version of next_move() to use at split point nodes where the move is grabbed
|
||||
/// from the split point's shared MovePicker object. This function is not thread
|
||||
/// safe so should be lock protected by the caller.
|
||||
template<>
|
||||
Move MovePicker::next_move<true>() { return ss->sp->mp->next_move<false>(); }
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value);
|
||||
MovePicker(const Position&, Move, Depth, const History&, Square);
|
||||
MovePicker(const Position&, Move, const History&, PieceType);
|
||||
Move next_move();
|
||||
template<bool SpNode> Move next_move();
|
||||
|
||||
private:
|
||||
void score_captures();
|
||||
@@ -51,12 +51,13 @@ private:
|
||||
|
||||
const Position& pos;
|
||||
const History& H;
|
||||
Search::Stack* ss;
|
||||
Depth depth;
|
||||
Move ttMove;
|
||||
MoveStack killers[2];
|
||||
Square recaptureSquare;
|
||||
int captureThreshold, phase;
|
||||
MoveStack *curMove, *lastMove, *lastQuiet, *lastBadCapture;
|
||||
MoveStack *cur, *end, *endQuiets, *endBadCaptures;
|
||||
MoveStack moves[MAX_MOVES];
|
||||
};
|
||||
|
||||
|
||||
271
DroidFish/jni/stockfish/notation.cpp
Normal file
271
DroidFish/jni/stockfish/notation.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "movegen.h"
|
||||
#include "notation.h"
|
||||
#include "position.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const char* PieceToChar = " PNBRQK pnbrqk";
|
||||
|
||||
|
||||
/// score_to_uci() converts a value to a string suitable for use with the UCI
|
||||
/// protocol specifications:
|
||||
///
|
||||
/// cp <x> The score from the engine's point of view in centipawns.
|
||||
/// mate <y> Mate in y moves, not plies. If the engine is getting mated
|
||||
/// use negative values for y.
|
||||
|
||||
string score_to_uci(Value v, Value alpha, Value beta) {
|
||||
|
||||
stringstream s;
|
||||
|
||||
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
|
||||
s << "cp " << v * 100 / int(PawnValueMg);
|
||||
else
|
||||
s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
|
||||
|
||||
s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
|
||||
/// move_to_uci() converts a move to a string in coordinate notation
|
||||
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print
|
||||
/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960
|
||||
/// mode. Internally castle moves are always coded as "king captures rook".
|
||||
|
||||
const string move_to_uci(Move m, bool chess960) {
|
||||
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
|
||||
if (m == MOVE_NULL)
|
||||
return "0000";
|
||||
|
||||
if (type_of(m) == CASTLE && !chess960)
|
||||
to = (to > from ? FILE_G : FILE_C) | rank_of(from);
|
||||
|
||||
string move = square_to_string(from) + square_to_string(to);
|
||||
|
||||
if (type_of(m) == PROMOTION)
|
||||
move += PieceToChar[make_piece(BLACK, promotion_type(m))]; // Lower case
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
|
||||
/// move_from_uci() takes a position and a string representing a move in
|
||||
/// simple coordinate notation and returns an equivalent legal Move if any.
|
||||
|
||||
Move move_from_uci(const Position& pos, string& str) {
|
||||
|
||||
if (str.length() == 5) // Junior could send promotion piece in uppercase
|
||||
str[4] = char(tolower(str[4]));
|
||||
|
||||
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
|
||||
if (str == move_to_uci(ml.move(), pos.is_chess960()))
|
||||
return ml.move();
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
|
||||
/// move_to_san() takes a position and a legal Move as input and returns its
|
||||
/// short algebraic notation representation.
|
||||
|
||||
const string move_to_san(Position& pos, Move m) {
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
|
||||
if (m == MOVE_NULL)
|
||||
return "(null)";
|
||||
|
||||
assert(pos.move_is_legal(m));
|
||||
|
||||
Bitboard attackers;
|
||||
bool ambiguousMove, ambiguousFile, ambiguousRank;
|
||||
string san;
|
||||
Color us = pos.side_to_move();
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
Piece pc = pos.piece_on(from);
|
||||
PieceType pt = type_of(pc);
|
||||
|
||||
if (type_of(m) == CASTLE)
|
||||
san = to > from ? "O-O" : "O-O-O";
|
||||
else
|
||||
{
|
||||
if (pt != PAWN)
|
||||
{
|
||||
san = PieceToChar[pt]; // Upper case
|
||||
|
||||
// Disambiguation if we have more then one piece with destination 'to'
|
||||
// note that for pawns is not needed because starting file is explicit.
|
||||
ambiguousMove = ambiguousFile = ambiguousRank = false;
|
||||
|
||||
attackers = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
|
||||
|
||||
while (attackers)
|
||||
{
|
||||
Square sq = pop_lsb(&attackers);
|
||||
|
||||
// Pinned pieces are not included in the possible sub-set
|
||||
if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces()))
|
||||
continue;
|
||||
|
||||
ambiguousFile |= file_of(sq) == file_of(from);
|
||||
ambiguousRank |= rank_of(sq) == rank_of(from);
|
||||
ambiguousMove = true;
|
||||
}
|
||||
|
||||
if (ambiguousMove)
|
||||
{
|
||||
if (!ambiguousFile)
|
||||
san += file_to_char(file_of(from));
|
||||
|
||||
else if (!ambiguousRank)
|
||||
san += rank_to_char(rank_of(from));
|
||||
|
||||
else
|
||||
san += square_to_string(from);
|
||||
}
|
||||
}
|
||||
else if (pos.is_capture(m))
|
||||
san = file_to_char(file_of(from));
|
||||
|
||||
if (pos.is_capture(m))
|
||||
san += 'x';
|
||||
|
||||
san += square_to_string(to);
|
||||
|
||||
if (type_of(m) == PROMOTION)
|
||||
san += string("=") + PieceToChar[promotion_type(m)];
|
||||
}
|
||||
|
||||
if (pos.move_gives_check(m, CheckInfo(pos)))
|
||||
{
|
||||
StateInfo st;
|
||||
pos.do_move(m, st);
|
||||
san += MoveList<LEGAL>(pos).size() ? "+" : "#";
|
||||
pos.undo_move(m);
|
||||
}
|
||||
|
||||
return san;
|
||||
}
|
||||
|
||||
|
||||
/// pretty_pv() formats human-readable search information, typically to be
|
||||
/// appended to the search log file. It uses the two helpers below to pretty
|
||||
/// format time and score respectively.
|
||||
|
||||
static string time_to_string(int64_t msecs) {
|
||||
|
||||
const int MSecMinute = 1000 * 60;
|
||||
const int MSecHour = 1000 * 60 * 60;
|
||||
|
||||
int64_t hours = msecs / MSecHour;
|
||||
int64_t minutes = (msecs % MSecHour) / MSecMinute;
|
||||
int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000;
|
||||
|
||||
stringstream s;
|
||||
|
||||
if (hours)
|
||||
s << hours << ':';
|
||||
|
||||
s << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
static string score_to_string(Value v) {
|
||||
|
||||
stringstream s;
|
||||
|
||||
if (v >= VALUE_MATE_IN_MAX_PLY)
|
||||
s << "#" << (VALUE_MATE - v + 1) / 2;
|
||||
|
||||
else if (v <= VALUE_MATED_IN_MAX_PLY)
|
||||
s << "-#" << (VALUE_MATE + v) / 2;
|
||||
|
||||
else
|
||||
s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) {
|
||||
|
||||
const int64_t K = 1000;
|
||||
const int64_t M = 1000000;
|
||||
|
||||
StateInfo state[MAX_PLY_PLUS_2], *st = state;
|
||||
Move* m = pv;
|
||||
string san, padding;
|
||||
size_t length;
|
||||
stringstream s;
|
||||
|
||||
s << setw(2) << depth
|
||||
<< setw(8) << score_to_string(value)
|
||||
<< setw(8) << time_to_string(msecs);
|
||||
|
||||
if (pos.nodes_searched() < M)
|
||||
s << setw(8) << pos.nodes_searched() / 1 << " ";
|
||||
|
||||
else if (pos.nodes_searched() < K * M)
|
||||
s << setw(7) << pos.nodes_searched() / K << "K ";
|
||||
|
||||
else
|
||||
s << setw(7) << pos.nodes_searched() / M << "M ";
|
||||
|
||||
padding = string(s.str().length(), ' ');
|
||||
length = padding.length();
|
||||
|
||||
while (*m != MOVE_NONE)
|
||||
{
|
||||
san = move_to_san(pos, *m);
|
||||
|
||||
if (length + san.length() > 80)
|
||||
{
|
||||
s << "\n" + padding;
|
||||
length = padding.length();
|
||||
}
|
||||
|
||||
s << san << ' ';
|
||||
length += san.length() + 1;
|
||||
|
||||
pos.do_move(*m++, *st++);
|
||||
}
|
||||
|
||||
while (m != pv)
|
||||
pos.undo_move(*--m);
|
||||
|
||||
return s.str();
|
||||
}
|
||||
35
DroidFish/jni/stockfish/notation.h
Normal file
35
DroidFish/jni/stockfish/notation.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if !defined(NOTATION_H_INCLUDED)
|
||||
#define NOTATION_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
class Position;
|
||||
|
||||
std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE);
|
||||
Move move_from_uci(const Position& pos, std::string& str);
|
||||
const std::string move_to_uci(Move m, bool chess960);
|
||||
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[]);
|
||||
|
||||
#endif // !defined(NOTATION_H_INCLUDED)
|
||||
@@ -240,12 +240,12 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) {
|
||||
{
|
||||
// Shelter penalty is higher for the pawn in front of the king
|
||||
b = ourPawns & FileBB[f];
|
||||
rkUs = b ? rank_of(Us == WHITE ? first_1(b) : ~last_1(b)) : RANK_1;
|
||||
rkUs = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
|
||||
safety -= ShelterWeakness[f != kf][rkUs];
|
||||
|
||||
// Storm danger is smaller if enemy pawn is blocked
|
||||
b = theirPawns & FileBB[f];
|
||||
rkThem = b ? rank_of(Us == WHITE ? first_1(b) : ~last_1(b)) : RANK_1;
|
||||
rkThem = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
|
||||
safety -= StormDanger[rkThem == rkUs + 1][rkThem];
|
||||
}
|
||||
|
||||
@@ -261,9 +261,14 @@ Score PawnEntry::update_safety(const Position& pos, Square ksq) {
|
||||
|
||||
kingSquares[Us] = ksq;
|
||||
castleRights[Us] = pos.can_castle(Us);
|
||||
minKPdistance[Us] = 0;
|
||||
|
||||
Bitboard pawns = pos.pieces(Us, PAWN);
|
||||
if (pawns)
|
||||
while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {}
|
||||
|
||||
if (relative_rank(Us, ksq) > RANK_4)
|
||||
return kingSafety[Us] = SCORE_ZERO;
|
||||
return kingSafety[Us] = make_score(0, -16 * minKPdistance[Us]);
|
||||
|
||||
Value bonus = shelter_storm<Us>(pos, ksq);
|
||||
|
||||
@@ -274,7 +279,7 @@ Score PawnEntry::update_safety(const Position& pos, Square ksq) {
|
||||
if (pos.can_castle(make_castle_right(Us, QUEEN_SIDE)))
|
||||
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
|
||||
|
||||
return kingSafety[Us] = make_score(bonus, 0);
|
||||
return kingSafety[Us] = make_score(bonus, -16 * minKPdistance[Us]);
|
||||
}
|
||||
|
||||
// Explicit template instantiation
|
||||
|
||||
@@ -59,6 +59,7 @@ private:
|
||||
Bitboard passedPawns[2];
|
||||
Bitboard pawnAttacks[2];
|
||||
Square kingSquares[2];
|
||||
int minKPdistance[2];
|
||||
int castleRights[2];
|
||||
Score value;
|
||||
int halfOpenFiles[2];
|
||||
|
||||
@@ -48,7 +48,7 @@ typedef unsigned __int64 uint64_t;
|
||||
typedef timeval sys_time_t;
|
||||
|
||||
inline void system_time(sys_time_t* t) { gettimeofday(t, NULL); }
|
||||
inline uint64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; }
|
||||
inline int64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; }
|
||||
|
||||
# include <pthread.h>
|
||||
typedef pthread_mutex_t Lock;
|
||||
@@ -74,7 +74,7 @@ typedef void*(*pt_start_fn)(void*);
|
||||
typedef _timeb sys_time_t;
|
||||
|
||||
inline void system_time(sys_time_t* t) { _ftime(t); }
|
||||
inline uint64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; }
|
||||
inline int64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; }
|
||||
|
||||
#if !defined(NOMINMAX)
|
||||
# define NOMINMAX // disable macros min() and max()
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "bitcount.h"
|
||||
#include "movegen.h"
|
||||
#include "notation.h"
|
||||
#include "position.h"
|
||||
#include "psqtab.h"
|
||||
#include "rkiss.h"
|
||||
@@ -35,36 +36,105 @@ using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
Key Position::zobrist[2][8][64];
|
||||
Key Position::zobEp[8];
|
||||
Key Position::zobCastle[16];
|
||||
Key Position::zobSideToMove;
|
||||
Key Position::zobExclusion;
|
||||
|
||||
Score Position::pieceSquareTable[16][64];
|
||||
|
||||
// Material values arrays, indexed by Piece
|
||||
const Value PieceValueMidgame[17] = {
|
||||
VALUE_ZERO,
|
||||
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame,
|
||||
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
|
||||
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame
|
||||
};
|
||||
|
||||
const Value PieceValueEndgame[17] = {
|
||||
VALUE_ZERO,
|
||||
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
|
||||
RookValueEndgame, QueenValueEndgame,
|
||||
VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
|
||||
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
|
||||
RookValueEndgame, QueenValueEndgame
|
||||
};
|
||||
|
||||
// To convert a Piece to and from a FEN char
|
||||
static const string PieceToChar(" PNBRQK pnbrqk");
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
|
||||
Score pieceSquareTable[16][64]; // [piece][square]
|
||||
Value PieceValue[2][18] = { // [Mg / Eg][piece / pieceType]
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
|
||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
|
||||
|
||||
namespace Zobrist {
|
||||
|
||||
Key psq[2][8][64]; // [color][pieceType][square / piece count]
|
||||
Key enpassant[8]; // [file]
|
||||
Key castle[16]; // [castleRight]
|
||||
Key side;
|
||||
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
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
/// next_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
|
||||
/// from the 'occupied' bitboard and scan for new X-ray attacks behind it.
|
||||
|
||||
template<int Pt> FORCE_INLINE
|
||||
PieceType next_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers,
|
||||
Bitboard& occupied, Bitboard& attackers) {
|
||||
|
||||
if (stmAttackers & bb[Pt])
|
||||
{
|
||||
Bitboard b = stmAttackers & bb[Pt];
|
||||
occupied ^= b & ~(b - 1);
|
||||
|
||||
if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
|
||||
attackers |= attacks_bb<BISHOP>(to, occupied) & (bb[BISHOP] | bb[QUEEN]);
|
||||
|
||||
if (Pt == ROOK || Pt == QUEEN)
|
||||
attackers |= attacks_bb<ROOK>(to, occupied) & (bb[ROOK] | bb[QUEEN]);
|
||||
|
||||
return (PieceType)Pt;
|
||||
}
|
||||
return next_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
|
||||
}
|
||||
|
||||
template<> FORCE_INLINE
|
||||
PieceType next_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) {
|
||||
return KING; // No need to update bitboards, it is the last cycle
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
/// CheckInfo c'tor
|
||||
|
||||
@@ -89,7 +159,7 @@ CheckInfo::CheckInfo(const Position& pos) {
|
||||
/// object do not depend on any external data so we detach state pointer from
|
||||
/// the source one.
|
||||
|
||||
void Position::operator=(const Position& pos) {
|
||||
Position& Position::operator=(const Position& pos) {
|
||||
|
||||
memcpy(this, &pos, sizeof(Position));
|
||||
startState = *st;
|
||||
@@ -97,6 +167,8 @@ void Position::operator=(const Position& pos) {
|
||||
nodes = 0;
|
||||
|
||||
assert(pos_is_ok());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -187,7 +259,7 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
|
||||
for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; rsq++) {}
|
||||
|
||||
else if (token >= 'A' && token <= 'H')
|
||||
rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
|
||||
rsq = File(token - 'A') | relative_rank(c, RANK_1);
|
||||
|
||||
else
|
||||
continue;
|
||||
@@ -199,7 +271,7 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
|
||||
if ( ((fen >> col) && (col >= 'a' && col <= 'h'))
|
||||
&& ((fen >> row) && (row == '3' || row == '6')))
|
||||
{
|
||||
st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
|
||||
st->epSquare = File(col - 'a') | Rank(row - '1');
|
||||
|
||||
if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)))
|
||||
st->epSquare = SQ_NONE;
|
||||
@@ -268,7 +340,7 @@ const string Position::to_fen() const {
|
||||
|
||||
for (File file = FILE_A; file <= FILE_H; file++)
|
||||
{
|
||||
sq = make_square(file, rank);
|
||||
sq = file | rank;
|
||||
|
||||
if (is_empty(sq))
|
||||
emptyCnt++;
|
||||
@@ -325,6 +397,8 @@ void Position::print(Move move) const {
|
||||
|
||||
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
|
||||
|
||||
sync_cout;
|
||||
|
||||
if (move)
|
||||
{
|
||||
Position p(*this);
|
||||
@@ -335,7 +409,7 @@ void Position::print(Move move) const {
|
||||
if (piece_on(sq) != NO_PIECE)
|
||||
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
|
||||
|
||||
cout << brd << "\nFen is: " << to_fen() << "\nKey is: " << st->key << endl;
|
||||
cout << brd << "\nFen is: " << to_fen() << "\nKey is: " << st->key << sync_endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -357,7 +431,7 @@ Bitboard Position::hidden_checkers() const {
|
||||
|
||||
while (pinners)
|
||||
{
|
||||
b = between_bb(ksq, pop_1st_bit(&pinners)) & pieces();
|
||||
b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
|
||||
|
||||
if (b && !more_than_one(b) && (b & pieces(sideToMove)))
|
||||
result |= b;
|
||||
@@ -448,7 +522,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
// En passant captures are a tricky special case. Because they are rather
|
||||
// uncommon, we do it simply by testing whether the king is attacked after
|
||||
// the move is made.
|
||||
if (is_enpassant(m))
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
Color them = ~us;
|
||||
Square to = to_sq(m);
|
||||
@@ -469,7 +543,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
// square is attacked by the opponent. Castling moves are checked
|
||||
// for legality during move generation.
|
||||
if (type_of(piece_on(from)) == KING)
|
||||
return is_castle(m) || !(attackers_to(to_sq(m)) & pieces(~us));
|
||||
return type_of(m) == CASTLE || !(attackers_to(to_sq(m)) & pieces(~us));
|
||||
|
||||
// A non-king move is legal if and only if it is not pinned or it
|
||||
// is moving along the ray towards or away from the king.
|
||||
@@ -485,7 +559,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
|
||||
bool Position::move_is_legal(const Move m) const {
|
||||
|
||||
for (MoveList<MV_LEGAL> ml(*this); !ml.end(); ++ml)
|
||||
for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml)
|
||||
if (ml.move() == m)
|
||||
return true;
|
||||
|
||||
@@ -506,7 +580,7 @@ bool Position::is_pseudo_legal(const Move m) const {
|
||||
Piece pc = piece_moved(m);
|
||||
|
||||
// Use a slower but simpler function for uncommon cases
|
||||
if (is_special(m))
|
||||
if (type_of(m) != NORMAL)
|
||||
return move_is_legal(m);
|
||||
|
||||
// Is not a promotion, so promotion piece must be empty
|
||||
@@ -595,7 +669,7 @@ bool Position::is_pseudo_legal(const Move m) const {
|
||||
if (type_of(pc) != KING)
|
||||
{
|
||||
Bitboard b = checkers();
|
||||
Square checksq = pop_1st_bit(&b);
|
||||
Square checksq = pop_lsb(&b);
|
||||
|
||||
if (b) // double check ? In this case a king move is required
|
||||
return false;
|
||||
@@ -640,23 +714,23 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
|
||||
}
|
||||
|
||||
// Can we skip the ugly special cases ?
|
||||
if (!is_special(m))
|
||||
if (type_of(m) == NORMAL)
|
||||
return false;
|
||||
|
||||
Color us = sideToMove;
|
||||
Square ksq = king_square(~us);
|
||||
|
||||
// Promotion with check ?
|
||||
if (is_promotion(m))
|
||||
if (type_of(m) == PROMOTION)
|
||||
return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
|
||||
|
||||
// En passant capture with check ? We have already handled the case
|
||||
// of direct checks and ordinary discovered check, the only case we
|
||||
// need to handle is the unusual case of a discovered check through
|
||||
// the captured pawn.
|
||||
if (is_enpassant(m))
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
Square capsq = make_square(file_of(to), rank_of(from));
|
||||
Square capsq = file_of(to) | rank_of(from);
|
||||
Bitboard b = (pieces() ^ from ^ capsq) | to;
|
||||
|
||||
return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK))
|
||||
@@ -664,7 +738,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
|
||||
}
|
||||
|
||||
// Castling with check ?
|
||||
if (is_castle(m))
|
||||
if (type_of(m) == CASTLE)
|
||||
{
|
||||
Square kfrom = from;
|
||||
Square rfrom = to; // 'King captures the rook' notation
|
||||
@@ -706,14 +780,14 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
st = &newSt;
|
||||
|
||||
// Update side to move
|
||||
k ^= zobSideToMove;
|
||||
k ^= Zobrist::side;
|
||||
|
||||
// Increment the 50 moves rule draw counter. Resetting it to zero in the
|
||||
// case of a capture or a pawn move is taken care of later.
|
||||
st->rule50++;
|
||||
st->pliesFromNull++;
|
||||
|
||||
if (is_castle(m))
|
||||
if (type_of(m) == CASTLE)
|
||||
{
|
||||
st->key = k;
|
||||
do_castle_move<true>(m);
|
||||
@@ -726,7 +800,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
Square to = to_sq(m);
|
||||
Piece piece = piece_on(from);
|
||||
PieceType pt = type_of(piece);
|
||||
PieceType capture = is_enpassant(m) ? 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(piece_on(to)) != us);
|
||||
@@ -740,7 +814,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
// update non-pawn material.
|
||||
if (capture == PAWN)
|
||||
{
|
||||
if (is_enpassant(m))
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
capsq += pawn_push(them);
|
||||
|
||||
@@ -753,10 +827,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
board[capsq] = NO_PIECE;
|
||||
}
|
||||
|
||||
st->pawnKey ^= zobrist[them][PAWN][capsq];
|
||||
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
|
||||
}
|
||||
else
|
||||
st->npMaterial[them] -= PieceValueMidgame[capture];
|
||||
st->npMaterial[them] -= PieceValue[Mg][capture];
|
||||
|
||||
// Remove the captured piece
|
||||
byTypeBB[ALL_PIECES] ^= capsq;
|
||||
@@ -776,8 +850,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
|
||||
|
||||
// Update hash keys
|
||||
k ^= zobrist[them][capture][capsq];
|
||||
st->materialKey ^= zobrist[them][capture][pieceCount[them][capture]];
|
||||
k ^= Zobrist::psq[them][capture][capsq];
|
||||
st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]];
|
||||
|
||||
// Update incremental scores
|
||||
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq];
|
||||
@@ -787,12 +861,12 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
}
|
||||
|
||||
// Update hash key
|
||||
k ^= zobrist[us][pt][from] ^ zobrist[us][pt][to];
|
||||
k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to];
|
||||
|
||||
// Reset en passant square
|
||||
if (st->epSquare != SQ_NONE)
|
||||
{
|
||||
k ^= zobEp[file_of(st->epSquare)];
|
||||
k ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||
st->epSquare = SQ_NONE;
|
||||
}
|
||||
|
||||
@@ -800,7 +874,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
if (st->castleRights && (castleRightsMask[from] | castleRightsMask[to]))
|
||||
{
|
||||
int cr = castleRightsMask[from] | castleRightsMask[to];
|
||||
k ^= zobCastle[st->castleRights & cr];
|
||||
k ^= Zobrist::castle[st->castleRights & cr];
|
||||
st->castleRights &= ~cr;
|
||||
}
|
||||
|
||||
@@ -829,10 +903,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
&& (attacks_from<PAWN>(from + pawn_push(us), us) & pieces(them, PAWN)))
|
||||
{
|
||||
st->epSquare = Square((from + to) / 2);
|
||||
k ^= zobEp[file_of(st->epSquare)];
|
||||
k ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||
}
|
||||
|
||||
if (is_promotion(m))
|
||||
if (type_of(m) == PROMOTION)
|
||||
{
|
||||
PieceType promotion = promotion_type(m);
|
||||
|
||||
@@ -854,21 +928,21 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
pieceList[us][promotion][index[to]] = to;
|
||||
|
||||
// Update hash keys
|
||||
k ^= zobrist[us][PAWN][to] ^ zobrist[us][promotion][to];
|
||||
st->pawnKey ^= zobrist[us][PAWN][to];
|
||||
st->materialKey ^= zobrist[us][promotion][pieceCount[us][promotion]++]
|
||||
^ zobrist[us][PAWN][pieceCount[us][PAWN]];
|
||||
k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to];
|
||||
st->pawnKey ^= Zobrist::psq[us][PAWN][to];
|
||||
st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]++]
|
||||
^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]];
|
||||
|
||||
// Update incremental score
|
||||
st->psqScore += pieceSquareTable[make_piece(us, promotion)][to]
|
||||
- pieceSquareTable[make_piece(us, PAWN)][to];
|
||||
|
||||
// Update material
|
||||
st->npMaterial[us] += PieceValueMidgame[promotion];
|
||||
st->npMaterial[us] += PieceValue[Mg][promotion];
|
||||
}
|
||||
|
||||
// Update pawn hash key
|
||||
st->pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
|
||||
st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to];
|
||||
|
||||
// Reset rule 50 draw counter
|
||||
st->rule50 = 0;
|
||||
@@ -892,7 +966,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
|
||||
if (moveIsCheck)
|
||||
{
|
||||
if (is_special(m))
|
||||
if (type_of(m) != NORMAL)
|
||||
st->checkersBB = attackers_to(king_square(them)) & pieces(us);
|
||||
else
|
||||
{
|
||||
@@ -927,7 +1001,7 @@ void Position::undo_move(Move m) {
|
||||
|
||||
sideToMove = ~sideToMove;
|
||||
|
||||
if (is_castle(m))
|
||||
if (type_of(m) == CASTLE)
|
||||
{
|
||||
do_castle_move<false>(m);
|
||||
return;
|
||||
@@ -945,7 +1019,7 @@ void Position::undo_move(Move m) {
|
||||
assert(color_of(piece) == us);
|
||||
assert(capture != KING);
|
||||
|
||||
if (is_promotion(m))
|
||||
if (type_of(m) == PROMOTION)
|
||||
{
|
||||
PieceType promotion = promotion_type(m);
|
||||
|
||||
@@ -988,7 +1062,7 @@ void Position::undo_move(Move m) {
|
||||
{
|
||||
Square capsq = to;
|
||||
|
||||
if (is_enpassant(m))
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
capsq -= pawn_push(us);
|
||||
|
||||
@@ -1025,7 +1099,7 @@ template<bool Do>
|
||||
void Position::do_castle_move(Move m) {
|
||||
|
||||
assert(is_ok(m));
|
||||
assert(is_castle(m));
|
||||
assert(type_of(m) == CASTLE);
|
||||
|
||||
Square kto, kfrom, rfrom, rto, kAfter, rAfter;
|
||||
|
||||
@@ -1086,18 +1160,18 @@ void Position::do_castle_move(Move m) {
|
||||
st->psqScore += psq_delta(rook, rfrom, rto);
|
||||
|
||||
// Update hash key
|
||||
st->key ^= zobrist[us][KING][kfrom] ^ zobrist[us][KING][kto];
|
||||
st->key ^= zobrist[us][ROOK][rfrom] ^ zobrist[us][ROOK][rto];
|
||||
st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto];
|
||||
st->key ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
|
||||
|
||||
// Clear en passant square
|
||||
if (st->epSquare != SQ_NONE)
|
||||
{
|
||||
st->key ^= zobEp[file_of(st->epSquare)];
|
||||
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||
st->epSquare = SQ_NONE;
|
||||
}
|
||||
|
||||
// Update castling rights
|
||||
st->key ^= zobCastle[st->castleRights & castleRightsMask[kfrom]];
|
||||
st->key ^= Zobrist::castle[st->castleRights & castleRightsMask[kfrom]];
|
||||
st->castleRights &= ~castleRightsMask[kfrom];
|
||||
|
||||
// Update checkers BB
|
||||
@@ -1138,9 +1212,9 @@ void Position::do_null_move(StateInfo& backupSt) {
|
||||
if (Do)
|
||||
{
|
||||
if (st->epSquare != SQ_NONE)
|
||||
st->key ^= zobEp[file_of(st->epSquare)];
|
||||
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||
|
||||
st->key ^= zobSideToMove;
|
||||
st->key ^= Zobrist::side;
|
||||
prefetch((char*)TT.first_entry(st->key));
|
||||
|
||||
st->epSquare = SQ_NONE;
|
||||
@@ -1169,7 +1243,7 @@ int Position::see_sign(Move m) const {
|
||||
// Early return if SEE cannot be negative because captured piece value
|
||||
// is not less then capturing one. Note that king moves always return
|
||||
// here because king midgame value is set to 0.
|
||||
if (PieceValueMidgame[piece_on(to_sq(m))] >= PieceValueMidgame[piece_moved(m)])
|
||||
if (PieceValue[Mg][piece_on(to_sq(m))] >= PieceValue[Mg][piece_moved(m)])
|
||||
return 1;
|
||||
|
||||
return see(m);
|
||||
@@ -1178,47 +1252,45 @@ int Position::see_sign(Move m) const {
|
||||
int Position::see(Move m) const {
|
||||
|
||||
Square from, to;
|
||||
Bitboard occ, attackers, stmAttackers, b;
|
||||
Bitboard occupied, attackers, stmAttackers;
|
||||
int swapList[32], slIndex = 1;
|
||||
PieceType capturedType, pt;
|
||||
PieceType captured;
|
||||
Color stm;
|
||||
|
||||
assert(is_ok(m));
|
||||
|
||||
// As castle moves are implemented as capturing the rook, they have
|
||||
// SEE == RookValueMidgame most of the times (unless the rook is under
|
||||
// attack).
|
||||
if (is_castle(m))
|
||||
return 0;
|
||||
|
||||
from = from_sq(m);
|
||||
to = to_sq(m);
|
||||
capturedType = type_of(piece_on(to));
|
||||
occ = pieces();
|
||||
captured = type_of(piece_on(to));
|
||||
occupied = pieces() ^ from;
|
||||
|
||||
// Handle en passant moves
|
||||
if (is_enpassant(m))
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
Square capQq = to - pawn_push(sideToMove);
|
||||
|
||||
assert(!capturedType);
|
||||
assert(!captured);
|
||||
assert(type_of(piece_on(capQq)) == PAWN);
|
||||
|
||||
// Remove the captured pawn
|
||||
occ ^= capQq;
|
||||
capturedType = 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
|
||||
// removed, but possibly an X-ray attacker added behind it.
|
||||
occ ^= from;
|
||||
attackers = attackers_to(to, occ);
|
||||
attackers = attackers_to(to, occupied);
|
||||
|
||||
// If the opponent has no attackers we are finished
|
||||
stm = ~color_of(piece_on(from));
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
if (!stmAttackers)
|
||||
return PieceValueMidgame[capturedType];
|
||||
return PieceValue[Mg][captured];
|
||||
|
||||
// The destination square is defended, which makes things rather more
|
||||
// difficult to compute. We proceed by building up a "swap list" containing
|
||||
@@ -1226,43 +1298,32 @@ int Position::see(Move m) const {
|
||||
// destination square, where the sides alternately capture, and always
|
||||
// capture with the least valuable piece. After each capture, we look for
|
||||
// new X-ray attacks from behind the capturing piece.
|
||||
swapList[0] = PieceValueMidgame[capturedType];
|
||||
capturedType = type_of(piece_on(from));
|
||||
swapList[0] = PieceValue[Mg][captured];
|
||||
captured = type_of(piece_on(from));
|
||||
|
||||
do {
|
||||
// Locate the least valuable attacker for the side to move. The loop
|
||||
// below looks like it is potentially infinite, but it isn't. We know
|
||||
// that the side to move still has at least one attacker left.
|
||||
for (pt = PAWN; !(stmAttackers & pieces(pt)); pt++)
|
||||
assert(pt < KING);
|
||||
|
||||
// Remove the attacker we just found from the 'occupied' bitboard,
|
||||
// and scan for new X-ray attacks behind the attacker.
|
||||
b = stmAttackers & pieces(pt);
|
||||
occ ^= (b & (~b + 1));
|
||||
attackers |= (attacks_bb<ROOK>(to, occ) & pieces(ROOK, QUEEN))
|
||||
| (attacks_bb<BISHOP>(to, occ) & pieces(BISHOP, QUEEN));
|
||||
|
||||
attackers &= occ; // Cut out pieces we've already done
|
||||
assert(slIndex < 32);
|
||||
|
||||
// Add the new entry to the swap list
|
||||
assert(slIndex < 32);
|
||||
swapList[slIndex] = -swapList[slIndex - 1] + PieceValueMidgame[capturedType];
|
||||
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[Mg][captured];
|
||||
slIndex++;
|
||||
|
||||
// Remember the value of the capturing piece, and change the side to
|
||||
// move before beginning the next iteration.
|
||||
capturedType = pt;
|
||||
// Locate and remove from 'occupied' the next least valuable attacker
|
||||
captured = next_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
|
||||
|
||||
attackers &= occupied; // Remove the just found attacker
|
||||
stm = ~stm;
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
|
||||
// Stop before processing a king capture
|
||||
if (capturedType == KING && stmAttackers)
|
||||
if (captured == KING)
|
||||
{
|
||||
assert(slIndex < 32);
|
||||
swapList[slIndex++] = QueenValueMidgame*10;
|
||||
// Stop before processing a king capture
|
||||
if (stmAttackers)
|
||||
swapList[slIndex++] = QueenValueMg * 16;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} while (stmAttackers);
|
||||
|
||||
// Having built the swap list, we negamax through it to find the best
|
||||
@@ -1317,19 +1378,19 @@ void Position::put_piece(Piece p, Square s) {
|
||||
|
||||
Key Position::compute_key() const {
|
||||
|
||||
Key k = zobCastle[st->castleRights];
|
||||
Key k = Zobrist::castle[st->castleRights];
|
||||
|
||||
for (Bitboard b = pieces(); b; )
|
||||
{
|
||||
Square s = pop_1st_bit(&b);
|
||||
k ^= zobrist[color_of(piece_on(s))][type_of(piece_on(s))][s];
|
||||
Square s = pop_lsb(&b);
|
||||
k ^= Zobrist::psq[color_of(piece_on(s))][type_of(piece_on(s))][s];
|
||||
}
|
||||
|
||||
if (ep_square() != SQ_NONE)
|
||||
k ^= zobEp[file_of(ep_square())];
|
||||
k ^= Zobrist::enpassant[file_of(ep_square())];
|
||||
|
||||
if (sideToMove == BLACK)
|
||||
k ^= zobSideToMove;
|
||||
k ^= Zobrist::side;
|
||||
|
||||
return k;
|
||||
}
|
||||
@@ -1347,8 +1408,8 @@ Key Position::compute_pawn_key() const {
|
||||
|
||||
for (Bitboard b = pieces(PAWN); b; )
|
||||
{
|
||||
Square s = pop_1st_bit(&b);
|
||||
k ^= zobrist[color_of(piece_on(s))][PAWN][s];
|
||||
Square s = pop_lsb(&b);
|
||||
k ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s];
|
||||
}
|
||||
|
||||
return k;
|
||||
@@ -1368,7 +1429,7 @@ Key Position::compute_material_key() const {
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
for (PieceType pt = PAWN; pt <= QUEEN; pt++)
|
||||
for (int cnt = 0; cnt < piece_count(c, pt); cnt++)
|
||||
k ^= zobrist[c][pt][cnt];
|
||||
k ^= Zobrist::psq[c][pt][cnt];
|
||||
|
||||
return k;
|
||||
}
|
||||
@@ -1384,7 +1445,7 @@ Score Position::compute_psq_score() const {
|
||||
|
||||
for (Bitboard b = pieces(); b; )
|
||||
{
|
||||
Square s = pop_1st_bit(&b);
|
||||
Square s = pop_lsb(&b);
|
||||
score += pieceSquareTable[piece_on(s)][s];
|
||||
}
|
||||
|
||||
@@ -1402,7 +1463,7 @@ Value Position::compute_non_pawn_material(Color c) const {
|
||||
Value value = VALUE_ZERO;
|
||||
|
||||
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
|
||||
value += piece_count(c, pt) * PieceValueMidgame[pt];
|
||||
value += piece_count(c, pt) * PieceValue[Mg][pt];
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -1416,11 +1477,11 @@ bool Position::is_draw() const {
|
||||
|
||||
// Draw by material?
|
||||
if ( !pieces(PAWN)
|
||||
&& (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMidgame))
|
||||
&& (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg))
|
||||
return true;
|
||||
|
||||
// Draw by the 50 moves rule?
|
||||
if (st->rule50 > 99 && (!in_check() || MoveList<MV_LEGAL>(*this).size()))
|
||||
if (st->rule50 > 99 && (!in_check() || MoveList<LEGAL>(*this).size()))
|
||||
return true;
|
||||
|
||||
// Draw by repetition?
|
||||
@@ -1452,50 +1513,6 @@ template bool Position::is_draw<false>() const;
|
||||
template bool Position::is_draw<true>() const;
|
||||
|
||||
|
||||
/// Position::init() is a static member function which 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[c][pt][s] = rk.rand<Key>();
|
||||
|
||||
for (File f = FILE_A; f <= FILE_H; f++)
|
||||
zobEp[f] = rk.rand<Key>();
|
||||
|
||||
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
|
||||
{
|
||||
Bitboard b = cr;
|
||||
while (b)
|
||||
{
|
||||
Key k = zobCastle[1ULL << pop_1st_bit(&b)];
|
||||
zobCastle[cr] ^= k ? k : rk.rand<Key>();
|
||||
}
|
||||
}
|
||||
|
||||
zobSideToMove = rk.rand<Key>();
|
||||
zobExclusion = rk.rand<Key>();
|
||||
|
||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
||||
{
|
||||
Score v = make_score(PieceValueMidgame[pt], PieceValueEndgame[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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Position::flip() flips position with the white and black sides reversed. This
|
||||
/// is only useful for debugging especially for finding evaluation symmetry bugs.
|
||||
|
||||
|
||||
@@ -94,10 +94,9 @@ struct ReducedStateInfo {
|
||||
class Position {
|
||||
public:
|
||||
Position() {}
|
||||
Position(const Position& p) { *this = p; }
|
||||
Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
|
||||
Position(const std::string& f, bool c960, Thread* t) { from_fen(f, c960, t); }
|
||||
void operator=(const Position&);
|
||||
Position& operator=(const Position&);
|
||||
|
||||
// Text input/output
|
||||
void from_fen(const std::string& fen, bool isChess960, Thread* th);
|
||||
@@ -141,6 +140,7 @@ public:
|
||||
// Properties of moves
|
||||
bool move_gives_check(Move m, const CheckInfo& ci) const;
|
||||
bool move_attacks_square(Move m, Square s) const;
|
||||
bool move_is_legal(const Move m) const;
|
||||
bool pl_move_is_legal(Move m, Bitboard pinned) const;
|
||||
bool is_pseudo_legal(const Move m) const;
|
||||
bool is_capture(Move m) const;
|
||||
@@ -189,15 +189,11 @@ public:
|
||||
bool pos_is_ok(int* failedStep = NULL) const;
|
||||
void flip();
|
||||
|
||||
// Global initialization
|
||||
static void init();
|
||||
|
||||
private:
|
||||
// Initialization helpers (used while setting up a position)
|
||||
void clear();
|
||||
void put_piece(Piece p, Square s);
|
||||
void set_castle_right(Color c, Square rfrom);
|
||||
bool move_is_legal(const Move m) const;
|
||||
|
||||
// Helper template functions
|
||||
template<bool Do> void do_castle_move(Move m);
|
||||
@@ -231,14 +227,6 @@ private:
|
||||
Thread* thisThread;
|
||||
StateInfo* st;
|
||||
int chess960;
|
||||
|
||||
// Static variables
|
||||
static Score pieceSquareTable[16][64]; // [piece][square]
|
||||
static Key zobrist[2][8][64]; // [color][pieceType][square]/[piece count]
|
||||
static Key zobEp[8]; // [file]
|
||||
static Key zobCastle[16]; // [castleRight]
|
||||
static Key zobSideToMove;
|
||||
static Key zobExclusion;
|
||||
};
|
||||
|
||||
inline int64_t Position::nodes_searched() const {
|
||||
@@ -367,7 +355,7 @@ inline Key Position::key() const {
|
||||
}
|
||||
|
||||
inline Key Position::exclusion_key() const {
|
||||
return st->key ^ zobExclusion;
|
||||
return st->key ^ Zobrist::exclusion;
|
||||
}
|
||||
|
||||
inline Key Position::pawn_key() const {
|
||||
@@ -424,14 +412,14 @@ inline bool Position::is_chess960() const {
|
||||
inline bool Position::is_capture_or_promotion(Move m) const {
|
||||
|
||||
assert(is_ok(m));
|
||||
return is_special(m) ? !is_castle(m) : !is_empty(to_sq(m));
|
||||
return type_of(m) ? type_of(m) != CASTLE : !is_empty(to_sq(m));
|
||||
}
|
||||
|
||||
inline bool Position::is_capture(Move m) const {
|
||||
|
||||
// Note that castle is coded as "king captures the rook"
|
||||
assert(is_ok(m));
|
||||
return (!is_empty(to_sq(m)) && !is_castle(m)) || is_enpassant(m);
|
||||
return (!is_empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT;
|
||||
}
|
||||
|
||||
inline PieceType Position::captured_piece_type() const {
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
@@ -30,6 +29,7 @@
|
||||
#include "history.h"
|
||||
#include "movegen.h"
|
||||
#include "movepick.h"
|
||||
#include "notation.h"
|
||||
#include "search.h"
|
||||
#include "timeman.h"
|
||||
#include "thread.h"
|
||||
@@ -42,20 +42,14 @@ namespace Search {
|
||||
LimitsType Limits;
|
||||
std::vector<RootMove> RootMoves;
|
||||
Position RootPosition;
|
||||
Time SearchTime;
|
||||
Time::point SearchTime;
|
||||
StateStackPtr SetupStates;
|
||||
}
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using Eval::evaluate;
|
||||
using namespace Search;
|
||||
|
||||
// For some reason argument-dependent lookup (ADL) doesn't work for Android's
|
||||
// STLPort, so explicitly qualify following functions.
|
||||
using std::count;
|
||||
using std::find;
|
||||
|
||||
namespace {
|
||||
|
||||
// Set to true to force running with one thread. Used for debugging
|
||||
@@ -144,48 +138,27 @@ namespace {
|
||||
bool connected_threat(const Position& pos, Move m, Move threat);
|
||||
Value refine_eval(const TTEntry* tte, Value ttValue, Value defaultEval);
|
||||
Move do_skill_level();
|
||||
string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE);
|
||||
void pv_info_to_log(Position& pos, int depth, Value score, int time, Move pv[]);
|
||||
void pv_info_to_uci(const Position& pos, int depth, Value alpha, Value beta);
|
||||
|
||||
// MovePickerExt class template extends MovePicker and allows to choose at
|
||||
// compile time the proper moves source according to the type of node. In the
|
||||
// default case we simply create and use a standard MovePicker object.
|
||||
template<bool SpNode> struct MovePickerExt : public MovePicker {
|
||||
|
||||
MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b)
|
||||
: MovePicker(p, ttm, d, h, ss, b) {}
|
||||
};
|
||||
|
||||
// In case of a SpNode we use split point's shared MovePicker object as moves source
|
||||
template<> struct MovePickerExt<true> : public MovePicker {
|
||||
|
||||
MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b)
|
||||
: MovePicker(p, ttm, d, h, ss, b), mp(ss->sp->mp) {}
|
||||
|
||||
Move next_move() { return mp->next_move(); }
|
||||
MovePicker* mp;
|
||||
};
|
||||
string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
|
||||
|
||||
// is_dangerous() checks whether a move belongs to some classes of known
|
||||
// 'dangerous' moves so that we avoid to prune it.
|
||||
FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) {
|
||||
|
||||
// Test for a pawn pushed to 7th or a passed pawn move
|
||||
if (type_of(pos.piece_moved(m)) == PAWN)
|
||||
{
|
||||
Color c = pos.side_to_move();
|
||||
if ( relative_rank(c, to_sq(m)) == RANK_7
|
||||
|| pos.pawn_is_passed(c, to_sq(m)))
|
||||
// Castle move?
|
||||
if (type_of(m) == CASTLE)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test for a capture that triggers a pawn endgame
|
||||
// Passed pawn move?
|
||||
if ( type_of(pos.piece_moved(m)) == PAWN
|
||||
&& pos.pawn_is_passed(pos.side_to_move(), to_sq(m)))
|
||||
return true;
|
||||
|
||||
// Entering a pawn endgame?
|
||||
if ( captureOrPromotion
|
||||
&& type_of(pos.piece_on(to_sq(m))) != PAWN
|
||||
&& !is_special(m)
|
||||
&& type_of(m) == NORMAL
|
||||
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
|
||||
- PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO))
|
||||
- PieceValue[Mg][pos.piece_on(to_sq(m))] == VALUE_ZERO))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -224,24 +197,23 @@ void Search::init() {
|
||||
/// 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.
|
||||
|
||||
int64_t Search::perft(Position& pos, Depth depth) {
|
||||
size_t Search::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;
|
||||
int64_t cnt = 0;
|
||||
|
||||
MoveList<MV_LEGAL> ml(pos);
|
||||
|
||||
// At the last ply just return the number of moves (leaf nodes)
|
||||
if (depth == ONE_PLY)
|
||||
return ml.size();
|
||||
|
||||
size_t cnt = 0;
|
||||
CheckInfo ci(pos);
|
||||
for ( ; !ml.end(); ++ml)
|
||||
|
||||
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
|
||||
{
|
||||
pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci));
|
||||
cnt += perft(pos, depth - ONE_PLY);
|
||||
pos.undo_move(ml.move());
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
@@ -263,8 +235,8 @@ void Search::think() {
|
||||
|
||||
if (RootMoves.empty())
|
||||
{
|
||||
cout << "info depth 0 score "
|
||||
<< score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl;
|
||||
sync_cout << "info depth 0 score "
|
||||
<< score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << sync_endl;
|
||||
|
||||
RootMoves.push_back(MOVE_NONE);
|
||||
goto finalize;
|
||||
@@ -274,9 +246,9 @@ void Search::think() {
|
||||
{
|
||||
Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]);
|
||||
|
||||
if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove))
|
||||
if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove))
|
||||
{
|
||||
std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove));
|
||||
std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), bookMove));
|
||||
goto finalize;
|
||||
}
|
||||
}
|
||||
@@ -298,7 +270,7 @@ void Search::think() {
|
||||
<< " time: " << Limits.time[pos.side_to_move()]
|
||||
<< " increment: " << Limits.inc[pos.side_to_move()]
|
||||
<< " moves to go: " << Limits.movestogo
|
||||
<< endl;
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
Threads.wake_up();
|
||||
@@ -318,16 +290,16 @@ void Search::think() {
|
||||
|
||||
if (Options["Use Search Log"])
|
||||
{
|
||||
int e = SearchTime.elapsed();
|
||||
Time::point elapsed = Time::now() - SearchTime + 1;
|
||||
|
||||
Log log(Options["Search Log Filename"]);
|
||||
log << "Nodes: " << pos.nodes_searched()
|
||||
<< "\nNodes/second: " << (e > 0 ? pos.nodes_searched() * 1000 / e : 0)
|
||||
<< "\nNodes/second: " << pos.nodes_searched() * 1000 / elapsed
|
||||
<< "\nBest move: " << move_to_san(pos, RootMoves[0].pv[0]);
|
||||
|
||||
StateInfo st;
|
||||
pos.do_move(RootMoves[0].pv[0], st);
|
||||
log << "\nPonder move: " << move_to_san(pos, RootMoves[0].pv[1]) << endl;
|
||||
log << "\nPonder move: " << move_to_san(pos, RootMoves[0].pv[1]) << std::endl;
|
||||
pos.undo_move(RootMoves[0].pv[0]);
|
||||
}
|
||||
|
||||
@@ -340,8 +312,8 @@ finalize:
|
||||
pos.this_thread()->wait_for_stop_or_ponderhit();
|
||||
|
||||
// Best move could be MOVE_NONE when searching on a stalemate position
|
||||
cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960)
|
||||
<< " ponder " << move_to_uci(RootMoves[0].pv[1], Chess960) << endl;
|
||||
sync_cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960)
|
||||
<< " ponder " << move_to_uci(RootMoves[0].pv[1], Chess960) << sync_endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -393,7 +365,8 @@ namespace {
|
||||
|
||||
// Start with a small aspiration window and, in case of fail high/low,
|
||||
// research with bigger window until not failing high/low anymore.
|
||||
do {
|
||||
while (true)
|
||||
{
|
||||
// Search starts from ss+1 to allow referencing (ss-1). This is
|
||||
// needed by update gains and ss copy when splitting at Root.
|
||||
bestValue = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
|
||||
@@ -426,8 +399,8 @@ namespace {
|
||||
|
||||
// Send full PV info to GUI if we are going to leave the loop or
|
||||
// if we have a fail high/low and we are deep in the search.
|
||||
if ((bestValue > alpha && bestValue < beta) || SearchTime.elapsed() > 2000)
|
||||
pv_info_to_uci(pos, depth, alpha, beta);
|
||||
if ((bestValue > alpha && bestValue < beta) || Time::now() - SearchTime > 2000)
|
||||
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
|
||||
|
||||
// In case of failing high/low increase aspiration window and
|
||||
// research, otherwise exit the fail high/low loop.
|
||||
@@ -447,9 +420,15 @@ namespace {
|
||||
else
|
||||
break;
|
||||
|
||||
assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
|
||||
// Search with full window in case we have a win/mate score
|
||||
if (abs(bestValue) >= VALUE_KNOWN_WIN)
|
||||
{
|
||||
alpha = -VALUE_INFINITE;
|
||||
beta = VALUE_INFINITE;
|
||||
}
|
||||
|
||||
} while (abs(bestValue) < VALUE_KNOWN_WIN);
|
||||
assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
// Skills: Do we need to pick now the best move ?
|
||||
@@ -457,7 +436,11 @@ namespace {
|
||||
skillBest = do_skill_level();
|
||||
|
||||
if (!Signals.stop && Options["Use Search Log"])
|
||||
pv_info_to_log(pos, depth, bestValue, SearchTime.elapsed(), &RootMoves[0].pv[0]);
|
||||
{
|
||||
Log log(Options["Search Log Filename"]);
|
||||
log << pretty_pv(pos, depth, bestValue, Time::now() - SearchTime, &RootMoves[0].pv[0])
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Filter out startup noise when monitoring best move stability
|
||||
if (depth > 2 && BestMoveChanges)
|
||||
@@ -475,14 +458,14 @@ namespace {
|
||||
// Stop search if most of available time is already consumed. We
|
||||
// probably don't have enough time to search the first move at the
|
||||
// next iteration anyway.
|
||||
if (SearchTime.elapsed() > (TimeMgr.available_time() * 62) / 100)
|
||||
if (Time::now() - SearchTime > (TimeMgr.available_time() * 62) / 100)
|
||||
stop = true;
|
||||
|
||||
// Stop search early if one move seems to be much better than others
|
||||
if ( depth >= 12
|
||||
&& !stop
|
||||
&& ( (bestMoveNeverChanged && pos.captured_piece_type())
|
||||
|| SearchTime.elapsed() > (TimeMgr.available_time() * 40) / 100))
|
||||
|| Time::now() - SearchTime > (TimeMgr.available_time() * 40) / 100))
|
||||
{
|
||||
Value rBeta = bestValue - EasyMoveMargin;
|
||||
(ss+1)->excludedMove = RootMoves[0].pv[0];
|
||||
@@ -513,7 +496,7 @@ namespace {
|
||||
if (skillBest == MOVE_NONE) // Still unassigned ?
|
||||
skillBest = do_skill_level();
|
||||
|
||||
std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), skillBest));
|
||||
std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), skillBest));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,7 +644,7 @@ namespace {
|
||||
&& (ss-1)->eval != VALUE_NONE
|
||||
&& ss->eval != VALUE_NONE
|
||||
&& !pos.captured_piece_type()
|
||||
&& !is_special(move))
|
||||
&& type_of(move) == NORMAL)
|
||||
{
|
||||
Square to = to_sq(move);
|
||||
H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval);
|
||||
@@ -708,16 +691,16 @@ namespace {
|
||||
ss->currentMove = MOVE_NULL;
|
||||
|
||||
// Null move dynamic reduction based on depth
|
||||
int R = 3 + (depth >= 5 * ONE_PLY ? depth / 8 : 0);
|
||||
Depth R = 3 * ONE_PLY + depth / 4;
|
||||
|
||||
// Null move dynamic reduction based on value
|
||||
if (refinedValue - PawnValueMidgame > beta)
|
||||
R++;
|
||||
if (refinedValue - PawnValueMg > beta)
|
||||
R += ONE_PLY;
|
||||
|
||||
pos.do_null_move<true>(st);
|
||||
(ss+1)->skipNullMove = true;
|
||||
nullValue = depth-R*ONE_PLY < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
|
||||
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R*ONE_PLY);
|
||||
nullValue = depth-R < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
|
||||
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R);
|
||||
(ss+1)->skipNullMove = false;
|
||||
pos.do_null_move<false>(st);
|
||||
|
||||
@@ -732,7 +715,7 @@ namespace {
|
||||
|
||||
// Do verification search at high depths
|
||||
ss->skipNullMove = true;
|
||||
Value v = search<NonPV>(pos, ss, alpha, beta, depth-R*ONE_PLY);
|
||||
Value v = search<NonPV>(pos, ss, alpha, beta, depth-R);
|
||||
ss->skipNullMove = false;
|
||||
|
||||
if (v >= beta)
|
||||
@@ -777,7 +760,7 @@ namespace {
|
||||
MovePicker mp(pos, ttMove, H, pos.captured_piece_type());
|
||||
CheckInfo ci(pos);
|
||||
|
||||
while ((move = mp.next_move()) != MOVE_NONE)
|
||||
while ((move = mp.next_move<false>()) != MOVE_NONE)
|
||||
if (pos.pl_move_is_legal(move, ci.pinned))
|
||||
{
|
||||
ss->currentMove = move;
|
||||
@@ -806,7 +789,7 @@ namespace {
|
||||
|
||||
split_point_start: // At split points actual search starts from here
|
||||
|
||||
MovePickerExt<SpNode> mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta);
|
||||
MovePicker mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta);
|
||||
CheckInfo ci(pos);
|
||||
futilityBase = ss->eval + ss->evalMargin;
|
||||
singularExtensionNode = !RootNode
|
||||
@@ -820,7 +803,7 @@ split_point_start: // At split points actual search starts from here
|
||||
// Step 11. Loop through moves
|
||||
// Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs
|
||||
while ( bestValue < beta
|
||||
&& (move = mp.next_move()) != MOVE_NONE
|
||||
&& (move = mp.next_move<SpNode>()) != MOVE_NONE
|
||||
&& !thisThread->cutoff_occurred()
|
||||
&& !Signals.stop)
|
||||
{
|
||||
@@ -832,7 +815,7 @@ split_point_start: // At split points actual search starts from here
|
||||
// At root obey the "searchmoves" option and skip moves not listed in Root
|
||||
// Move List, as a consequence any illegal move is also skipped. In MultiPV
|
||||
// mode we also skip PV moves which have been already searched.
|
||||
if (RootNode && !count(RootMoves.begin() + PVIdx, RootMoves.end(), move))
|
||||
if (RootNode && !std::count(RootMoves.begin() + PVIdx, RootMoves.end(), move))
|
||||
continue;
|
||||
|
||||
// At PV and SpNode nodes we want all moves to be legal since the beginning
|
||||
@@ -842,7 +825,7 @@ split_point_start: // At split points actual search starts from here
|
||||
if (SpNode)
|
||||
{
|
||||
moveCount = ++sp->moveCount;
|
||||
lock_release(sp->lock);
|
||||
sp->mutex.unlock();
|
||||
}
|
||||
else
|
||||
moveCount++;
|
||||
@@ -851,10 +834,10 @@ split_point_start: // At split points actual search starts from here
|
||||
{
|
||||
Signals.firstRootMove = (moveCount == 1);
|
||||
|
||||
if (thisThread == Threads.main_thread() && SearchTime.elapsed() > 2000)
|
||||
cout << "info depth " << depth / ONE_PLY
|
||||
if (thisThread == Threads.main_thread() && Time::now() - SearchTime > 2000)
|
||||
sync_cout << "info depth " << depth / ONE_PLY
|
||||
<< " currmove " << move_to_uci(move, Chess960)
|
||||
<< " currmovenumber " << moveCount + PVIdx << endl;
|
||||
<< " currmovenumber " << moveCount + PVIdx << sync_endl;
|
||||
}
|
||||
|
||||
isPvMove = (PvNode && moveCount <= 1);
|
||||
@@ -878,9 +861,8 @@ split_point_start: // At split points actual search starts from here
|
||||
if ( singularExtensionNode
|
||||
&& !ext
|
||||
&& move == ttMove
|
||||
&& pos.pl_move_is_legal(move, ci.pinned))
|
||||
{
|
||||
if (abs(ttValue) < VALUE_KNOWN_WIN)
|
||||
&& pos.pl_move_is_legal(move, ci.pinned)
|
||||
&& abs(ttValue) < VALUE_KNOWN_WIN)
|
||||
{
|
||||
Value rBeta = ttValue - int(depth);
|
||||
ss->excludedMove = move;
|
||||
@@ -888,10 +870,10 @@ split_point_start: // At split points actual search starts from here
|
||||
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
|
||||
ss->skipNullMove = false;
|
||||
ss->excludedMove = MOVE_NONE;
|
||||
|
||||
if (value < rBeta)
|
||||
ext = ONE_PLY;
|
||||
}
|
||||
}
|
||||
|
||||
// Update current move (this must be done after singular extension search)
|
||||
newDepth = depth - ONE_PLY + ext;
|
||||
@@ -902,7 +884,6 @@ split_point_start: // At split points actual search starts from here
|
||||
&& !inCheck
|
||||
&& !dangerous
|
||||
&& move != ttMove
|
||||
&& !is_castle(move)
|
||||
&& (bestValue > VALUE_MATED_IN_MAX_PLY || bestValue == -VALUE_INFINITE))
|
||||
{
|
||||
// Move count based pruning
|
||||
@@ -910,7 +891,7 @@ split_point_start: // At split points actual search starts from here
|
||||
&& (!threatMove || !connected_threat(pos, move, threatMove)))
|
||||
{
|
||||
if (SpNode)
|
||||
lock_grab(sp->lock);
|
||||
sp->mutex.lock();
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -925,7 +906,7 @@ split_point_start: // At split points actual search starts from here
|
||||
if (futilityValue < beta)
|
||||
{
|
||||
if (SpNode)
|
||||
lock_grab(sp->lock);
|
||||
sp->mutex.lock();
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -935,7 +916,7 @@ split_point_start: // At split points actual search starts from here
|
||||
&& pos.see_sign(move) < 0)
|
||||
{
|
||||
if (SpNode)
|
||||
lock_grab(sp->lock);
|
||||
sp->mutex.lock();
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -961,7 +942,6 @@ split_point_start: // At split points actual search starts from here
|
||||
&& !isPvMove
|
||||
&& !captureOrPromotion
|
||||
&& !dangerous
|
||||
&& !is_castle(move)
|
||||
&& ss->killers[0] != move
|
||||
&& ss->killers[1] != move)
|
||||
{
|
||||
@@ -1000,7 +980,7 @@ split_point_start: // At split points actual search starts from here
|
||||
// Step 18. Check for new best move
|
||||
if (SpNode)
|
||||
{
|
||||
lock_grab(sp->lock);
|
||||
sp->mutex.lock();
|
||||
bestValue = sp->bestValue;
|
||||
alpha = sp->alpha;
|
||||
}
|
||||
@@ -1011,7 +991,7 @@ split_point_start: // At split points actual search starts from here
|
||||
// be trusted, and we don't update the best move and/or PV.
|
||||
if (RootNode && !Signals.stop)
|
||||
{
|
||||
RootMove& rm = *find(RootMoves.begin(), RootMoves.end(), move);
|
||||
RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move);
|
||||
|
||||
// PV move or new best move ?
|
||||
if (isPvMove || value > alpha)
|
||||
@@ -1202,7 +1182,7 @@ split_point_start: // At split points actual search starts from here
|
||||
alpha = bestValue;
|
||||
|
||||
futilityBase = ss->eval + evalMargin + FutilityMarginQS;
|
||||
enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame;
|
||||
enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMg;
|
||||
}
|
||||
|
||||
// Initialize a MovePicker object for the current position, and prepare
|
||||
@@ -1214,7 +1194,7 @@ split_point_start: // At split points actual search starts from here
|
||||
|
||||
// Loop through the moves until no moves remain or a beta cutoff occurs
|
||||
while ( bestValue < beta
|
||||
&& (move = mp.next_move()) != MOVE_NONE)
|
||||
&& (move = mp.next_move<false>()) != MOVE_NONE)
|
||||
{
|
||||
assert(is_ok(move));
|
||||
|
||||
@@ -1226,12 +1206,12 @@ split_point_start: // At split points actual search starts from here
|
||||
&& !givesCheck
|
||||
&& move != ttMove
|
||||
&& enoughMaterial
|
||||
&& !is_promotion(move)
|
||||
&& type_of(move) != PROMOTION
|
||||
&& !pos.is_passed_pawn_push(move))
|
||||
{
|
||||
futilityValue = futilityBase
|
||||
+ PieceValueEndgame[pos.piece_on(to_sq(move))]
|
||||
+ (is_enpassant(move) ? PawnValueEndgame : VALUE_ZERO);
|
||||
+ PieceValue[Eg][pos.piece_on(to_sq(move))]
|
||||
+ (type_of(move) == ENPASSANT ? PawnValueEg : VALUE_ZERO);
|
||||
|
||||
if (futilityValue < beta)
|
||||
{
|
||||
@@ -1259,7 +1239,7 @@ split_point_start: // At split points actual search starts from here
|
||||
if ( !PvNode
|
||||
&& (!inCheck || evasionPrunable)
|
||||
&& move != ttMove
|
||||
&& !is_promotion(move)
|
||||
&& type_of(move) != PROMOTION
|
||||
&& pos.see_sign(move) < 0)
|
||||
continue;
|
||||
|
||||
@@ -1269,7 +1249,7 @@ split_point_start: // At split points actual search starts from here
|
||||
&& givesCheck
|
||||
&& move != ttMove
|
||||
&& !pos.is_capture_or_promotion(move)
|
||||
&& ss->eval + PawnValueMidgame / 4 < beta
|
||||
&& ss->eval + PawnValueMg / 4 < beta
|
||||
&& !check_is_dangerous(pos, move, futilityBase, beta))
|
||||
continue;
|
||||
|
||||
@@ -1354,7 +1334,7 @@ split_point_start: // At split points actual search starts from here
|
||||
while (b)
|
||||
{
|
||||
// Note that here we generate illegal "double move"!
|
||||
if (futilityBase + PieceValueEndgame[pos.piece_on(pop_1st_bit(&b))] >= beta)
|
||||
if (futilityBase + PieceValue[Eg][pos.piece_on(pop_lsb(&b))] >= beta)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1466,7 +1446,7 @@ split_point_start: // At split points actual search starts from here
|
||||
// Case 2: If the threatened piece has value less than or equal to the
|
||||
// value of the threatening piece, don't prune moves which defend it.
|
||||
if ( pos.is_capture(threat)
|
||||
&& ( PieceValueMidgame[pos.piece_on(tfrom)] >= PieceValueMidgame[pos.piece_on(tto)]
|
||||
&& ( PieceValue[Mg][pos.piece_on(tfrom)] >= PieceValue[Mg][pos.piece_on(tto)]
|
||||
|| type_of(pos.piece_on(tfrom)) == KING)
|
||||
&& pos.move_attacks_square(m, tto))
|
||||
return true;
|
||||
@@ -1511,156 +1491,6 @@ split_point_start: // At split points actual search starts from here
|
||||
}
|
||||
|
||||
|
||||
// score_to_uci() converts a value to a string suitable for use with the UCI
|
||||
// protocol specifications:
|
||||
//
|
||||
// cp <x> The score from the engine's point of view in centipawns.
|
||||
// mate <y> Mate in y moves, not plies. If the engine is getting mated
|
||||
// use negative values for y.
|
||||
|
||||
string score_to_uci(Value v, Value alpha, Value beta) {
|
||||
|
||||
std::stringstream s;
|
||||
|
||||
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
|
||||
s << "cp " << v * 100 / int(PawnValueMidgame);
|
||||
else
|
||||
s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
|
||||
|
||||
s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
|
||||
// pv_info_to_uci() sends search info to GUI. UCI protocol requires to send all
|
||||
// the PV lines also if are still to be searched and so refer to the previous
|
||||
// search score.
|
||||
|
||||
void pv_info_to_uci(const Position& pos, int depth, Value alpha, Value beta) {
|
||||
|
||||
int t = SearchTime.elapsed();
|
||||
int selDepth = 0;
|
||||
|
||||
for (int i = 0; i < Threads.size(); i++)
|
||||
if (Threads[i].maxPly > selDepth)
|
||||
selDepth = Threads[i].maxPly;
|
||||
|
||||
for (size_t i = 0; i < std::min(UCIMultiPV, RootMoves.size()); i++)
|
||||
{
|
||||
bool updated = (i <= PVIdx);
|
||||
|
||||
if (depth == 1 && !updated)
|
||||
continue;
|
||||
|
||||
int d = (updated ? depth : depth - 1);
|
||||
Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore);
|
||||
std::stringstream s;
|
||||
|
||||
for (int j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++)
|
||||
s << " " << move_to_uci(RootMoves[i].pv[j], Chess960);
|
||||
|
||||
cout << "info depth " << d
|
||||
<< " seldepth " << selDepth
|
||||
<< " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v))
|
||||
<< " nodes " << pos.nodes_searched()
|
||||
<< " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0)
|
||||
<< " time " << t
|
||||
<< " multipv " << i + 1
|
||||
<< " pv" << s.str() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// pv_info_to_log() writes human-readable search information to the log file
|
||||
// (which is created when the UCI parameter "Use Search Log" is "true"). It
|
||||
// uses the two below helpers to pretty format time and score respectively.
|
||||
|
||||
string time_to_string(int millisecs) {
|
||||
|
||||
const int MSecMinute = 1000 * 60;
|
||||
const int MSecHour = 1000 * 60 * 60;
|
||||
|
||||
int hours = millisecs / MSecHour;
|
||||
int minutes = (millisecs % MSecHour) / MSecMinute;
|
||||
int seconds = ((millisecs % MSecHour) % MSecMinute) / 1000;
|
||||
|
||||
std::stringstream s;
|
||||
|
||||
if (hours)
|
||||
s << hours << ':';
|
||||
|
||||
s << std::setfill('0') << std::setw(2) << minutes << ':'
|
||||
<< std::setw(2) << seconds;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
string score_to_string(Value v) {
|
||||
|
||||
std::stringstream s;
|
||||
|
||||
if (v >= VALUE_MATE_IN_MAX_PLY)
|
||||
s << "#" << (VALUE_MATE - v + 1) / 2;
|
||||
else if (v <= VALUE_MATED_IN_MAX_PLY)
|
||||
s << "-#" << (VALUE_MATE + v) / 2;
|
||||
else
|
||||
s << std::setprecision(2) << std::fixed << std::showpos
|
||||
<< float(v) / PawnValueMidgame;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
void pv_info_to_log(Position& pos, int depth, Value value, int time, Move pv[]) {
|
||||
|
||||
const int64_t K = 1000;
|
||||
const int64_t M = 1000000;
|
||||
|
||||
StateInfo state[MAX_PLY_PLUS_2], *st = state;
|
||||
Move* m = pv;
|
||||
string san, padding;
|
||||
size_t length;
|
||||
std::stringstream s;
|
||||
|
||||
s << std::setw(2) << depth
|
||||
<< std::setw(8) << score_to_string(value)
|
||||
<< std::setw(8) << time_to_string(time);
|
||||
|
||||
if (pos.nodes_searched() < M)
|
||||
s << std::setw(8) << pos.nodes_searched() / 1 << " ";
|
||||
|
||||
else if (pos.nodes_searched() < K * M)
|
||||
s << std::setw(7) << pos.nodes_searched() / K << "K ";
|
||||
|
||||
else
|
||||
s << std::setw(7) << pos.nodes_searched() / M << "M ";
|
||||
|
||||
padding = string(s.str().length(), ' ');
|
||||
length = padding.length();
|
||||
|
||||
while (*m != MOVE_NONE)
|
||||
{
|
||||
san = move_to_san(pos, *m);
|
||||
|
||||
if (length + san.length() > 80)
|
||||
{
|
||||
s << "\n" + padding;
|
||||
length = padding.length();
|
||||
}
|
||||
|
||||
s << san << ' ';
|
||||
length += san.length() + 1;
|
||||
|
||||
pos.do_move(*m++, *st++);
|
||||
}
|
||||
|
||||
while (m != pv)
|
||||
pos.undo_move(*--m);
|
||||
|
||||
Log l(Options["Search Log Filename"]);
|
||||
l << s.str() << endl;
|
||||
}
|
||||
|
||||
|
||||
// When playing with strength handicap choose best move among the MultiPV set
|
||||
// using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen.
|
||||
|
||||
@@ -1671,12 +1501,12 @@ split_point_start: // At split points actual search starts from here
|
||||
static RKISS rk;
|
||||
|
||||
// PRNG sequence should be not deterministic
|
||||
for (int i = Time::current_time().msec() % 50; i > 0; i--)
|
||||
for (int i = Time::now() % 50; i > 0; i--)
|
||||
rk.rand<unsigned>();
|
||||
|
||||
// RootMoves are already sorted by score in descending order
|
||||
size_t size = std::min(MultiPV, RootMoves.size());
|
||||
int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMidgame);
|
||||
int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMg);
|
||||
int weakness = 120 - 2 * SkillLevel;
|
||||
int max_s = -VALUE_INFINITE;
|
||||
Move best = MOVE_NONE;
|
||||
@@ -1705,6 +1535,50 @@ split_point_start: // At split points actual search starts from here
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
// uci_pv() formats PV information according to UCI protocol. UCI requires
|
||||
// to send all the PV lines also if are still to be searched and so refer to
|
||||
// the previous search score.
|
||||
|
||||
string uci_pv(const Position& pos, int depth, Value alpha, Value beta) {
|
||||
|
||||
std::stringstream s;
|
||||
Time::point elaspsed = Time::now() - SearchTime + 1;
|
||||
int selDepth = 0;
|
||||
|
||||
for (size_t i = 0; i < Threads.size(); i++)
|
||||
if (Threads[i].maxPly > selDepth)
|
||||
selDepth = Threads[i].maxPly;
|
||||
|
||||
for (size_t i = 0; i < std::min(UCIMultiPV, RootMoves.size()); i++)
|
||||
{
|
||||
bool updated = (i <= PVIdx);
|
||||
|
||||
if (depth == 1 && !updated)
|
||||
continue;
|
||||
|
||||
int d = (updated ? depth : depth - 1);
|
||||
Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore);
|
||||
|
||||
if (s.rdbuf()->in_avail())
|
||||
s << "\n";
|
||||
|
||||
s << "info depth " << d
|
||||
<< " seldepth " << selDepth
|
||||
<< " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v))
|
||||
<< " nodes " << pos.nodes_searched()
|
||||
<< " nps " << pos.nodes_searched() * 1000 / elaspsed
|
||||
<< " time " << elaspsed
|
||||
<< " multipv " << i + 1
|
||||
<< " pv";
|
||||
|
||||
for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++)
|
||||
s << " " << move_to_uci(RootMoves[i].pv[j], Chess960);
|
||||
}
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -1775,11 +1649,15 @@ void RootMove::insert_pv_in_tt(Position& pos) {
|
||||
}
|
||||
|
||||
|
||||
/// Thread::idle_loop() is where the thread is parked when it has no work to do.
|
||||
/// The parameter 'master_sp', if non-NULL, is a pointer to an active SplitPoint
|
||||
/// object for which the thread is the master.
|
||||
/// Thread::idle_loop() is where the thread is parked when it has no work to do
|
||||
|
||||
void Thread::idle_loop(SplitPoint* sp_master) {
|
||||
void Thread::idle_loop() {
|
||||
|
||||
// Pointer 'sp_master', if non-NULL, points to the active SplitPoint
|
||||
// object for which the thread is the master.
|
||||
const SplitPoint* sp_master = splitPointsCnt ? curSplitPoint : NULL;
|
||||
|
||||
assert(!sp_master || (sp_master->master == this && is_searching));
|
||||
|
||||
// If this thread is the master of a split point and all slaves have
|
||||
// finished their work at this split point, return from the idle loop.
|
||||
@@ -1798,12 +1676,12 @@ void Thread::idle_loop(SplitPoint* sp_master) {
|
||||
}
|
||||
|
||||
// Grab the lock to avoid races with Thread::wake_up()
|
||||
lock_grab(sleepLock);
|
||||
mutex.lock();
|
||||
|
||||
// If we are master and all slaves have finished don't go to sleep
|
||||
if (sp_master && !sp_master->slavesMask)
|
||||
{
|
||||
lock_release(sleepLock);
|
||||
mutex.unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1812,9 +1690,9 @@ void Thread::idle_loop(SplitPoint* sp_master) {
|
||||
// in the meanwhile, allocated us and sent the wake_up() call before we
|
||||
// had the chance to grab the lock.
|
||||
if (do_sleep || !is_searching)
|
||||
cond_wait(sleepCond, sleepLock);
|
||||
sleepCondition.wait(mutex);
|
||||
|
||||
lock_release(sleepLock);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
// If this thread has been assigned work, launch a search
|
||||
@@ -1822,12 +1700,12 @@ void Thread::idle_loop(SplitPoint* sp_master) {
|
||||
{
|
||||
assert(!do_sleep && !do_exit);
|
||||
|
||||
lock_grab(Threads.splitLock);
|
||||
Threads.mutex.lock();
|
||||
|
||||
assert(is_searching);
|
||||
SplitPoint* sp = curSplitPoint;
|
||||
|
||||
lock_release(Threads.splitLock);
|
||||
Threads.mutex.unlock();
|
||||
|
||||
Stack ss[MAX_PLY_PLUS_2];
|
||||
Position pos(*sp->pos, this);
|
||||
@@ -1835,7 +1713,7 @@ void Thread::idle_loop(SplitPoint* sp_master) {
|
||||
memcpy(ss, sp->ss - 1, 4 * sizeof(Stack));
|
||||
(ss+1)->sp = sp;
|
||||
|
||||
lock_grab(sp->lock);
|
||||
sp->mutex.lock();
|
||||
|
||||
if (sp->nodeType == Root)
|
||||
search<SplitPointRoot>(pos, ss+1, sp->alpha, sp->beta, sp->depth);
|
||||
@@ -1856,14 +1734,17 @@ void Thread::idle_loop(SplitPoint* sp_master) {
|
||||
// case we are the last slave of the split point.
|
||||
if ( Threads.use_sleeping_threads()
|
||||
&& this != sp->master
|
||||
&& !sp->master->is_searching)
|
||||
&& !sp->slavesMask)
|
||||
{
|
||||
assert(!sp->master->is_searching);
|
||||
sp->master->wake_up();
|
||||
}
|
||||
|
||||
// After releasing the lock we cannot access anymore any SplitPoint
|
||||
// related data in a safe way becuase it could have been released under
|
||||
// our feet by the sp master. Also accessing other Thread objects is
|
||||
// unsafe because if we are exiting there is a chance are already freed.
|
||||
lock_release(sp->lock);
|
||||
sp->mutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1875,26 +1756,26 @@ void Thread::idle_loop(SplitPoint* sp_master) {
|
||||
|
||||
void check_time() {
|
||||
|
||||
static Time lastInfoTime = Time::current_time();
|
||||
static Time::point lastInfoTime = Time::now();
|
||||
|
||||
if (lastInfoTime.elapsed() >= 1000)
|
||||
if (Time::now() - lastInfoTime >= 1000)
|
||||
{
|
||||
lastInfoTime.restart();
|
||||
lastInfoTime = Time::now();
|
||||
dbg_print();
|
||||
}
|
||||
|
||||
if (Limits.ponder)
|
||||
return;
|
||||
|
||||
int e = SearchTime.elapsed();
|
||||
Time::point elapsed = Time::now() - SearchTime;
|
||||
bool stillAtFirstMove = Signals.firstRootMove
|
||||
&& !Signals.failedLowAtRoot
|
||||
&& e > TimeMgr.available_time();
|
||||
&& elapsed > TimeMgr.available_time();
|
||||
|
||||
bool noMoreTime = e > TimeMgr.maximum_time() - 2 * TimerResolution
|
||||
bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerResolution
|
||||
|| stillAtFirstMove;
|
||||
|
||||
if ( (Limits.use_time_management() && noMoreTime)
|
||||
|| (Limits.movetime && e >= Limits.movetime))
|
||||
|| (Limits.movetime && elapsed >= Limits.movetime))
|
||||
Signals.stop = true;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,14 @@
|
||||
#define SEARCH_H_INCLUDED
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
#include "misc.h"
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
|
||||
class Position;
|
||||
struct SplitPoint;
|
||||
|
||||
namespace Search {
|
||||
@@ -91,14 +93,17 @@ struct SignalsType {
|
||||
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
|
||||
};
|
||||
|
||||
typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
|
||||
|
||||
extern volatile SignalsType Signals;
|
||||
extern LimitsType Limits;
|
||||
extern std::vector<RootMove> RootMoves;
|
||||
extern Position RootPosition;
|
||||
extern Time SearchTime;
|
||||
extern Time::point SearchTime;
|
||||
extern StateStackPtr SetupStates;
|
||||
|
||||
extern void init();
|
||||
extern int64_t perft(Position& pos, Depth depth);
|
||||
extern size_t perft(Position& pos, Depth depth);
|
||||
extern void think();
|
||||
|
||||
} // namespace Search
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
using namespace Search;
|
||||
|
||||
ThreadsManager Threads; // Global object
|
||||
ThreadPool Threads; // Global object
|
||||
|
||||
namespace { extern "C" {
|
||||
|
||||
@@ -52,12 +52,6 @@ Thread::Thread(Fn fn) {
|
||||
|
||||
do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching()
|
||||
|
||||
lock_init(sleepLock);
|
||||
cond_init(sleepCond);
|
||||
|
||||
for (int j = 0; j < MAX_SPLITPOINTS_PER_THREAD; j++)
|
||||
lock_init(splitPoints[j].lock);
|
||||
|
||||
if (!thread_create(handle, start_routine, this))
|
||||
{
|
||||
std::cerr << "Failed to create thread number " << idx << std::endl;
|
||||
@@ -74,14 +68,7 @@ Thread::~Thread() {
|
||||
|
||||
do_exit = true; // Search must be already finished
|
||||
wake_up();
|
||||
|
||||
thread_join(handle); // Wait for thread termination
|
||||
|
||||
lock_destroy(sleepLock);
|
||||
cond_destroy(sleepCond);
|
||||
|
||||
for (int j = 0; j < MAX_SPLITPOINTS_PER_THREAD; j++)
|
||||
lock_destroy(splitPoints[j].lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,9 +80,9 @@ void Thread::timer_loop() {
|
||||
|
||||
while (!do_exit)
|
||||
{
|
||||
lock_grab(sleepLock);
|
||||
timed_wait(sleepCond, sleepLock, maxPly ? maxPly : INT_MAX);
|
||||
lock_release(sleepLock);
|
||||
mutex.lock();
|
||||
sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX);
|
||||
mutex.unlock();
|
||||
check_time();
|
||||
}
|
||||
}
|
||||
@@ -108,18 +95,18 @@ void Thread::main_loop() {
|
||||
|
||||
while (true)
|
||||
{
|
||||
lock_grab(sleepLock);
|
||||
mutex.lock();
|
||||
|
||||
do_sleep = true; // Always return to sleep after a search
|
||||
is_searching = false;
|
||||
|
||||
while (do_sleep && !do_exit)
|
||||
{
|
||||
cond_signal(Threads.sleepCond); // Wake up UI thread if needed
|
||||
cond_wait(sleepCond, sleepLock);
|
||||
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
|
||||
sleepCondition.wait(mutex);
|
||||
}
|
||||
|
||||
lock_release(sleepLock);
|
||||
mutex.unlock();
|
||||
|
||||
if (do_exit)
|
||||
return;
|
||||
@@ -127,6 +114,8 @@ void Thread::main_loop() {
|
||||
is_searching = true;
|
||||
|
||||
Search::think();
|
||||
|
||||
assert(is_searching);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,9 +125,9 @@ void Thread::main_loop() {
|
||||
|
||||
void Thread::wake_up() {
|
||||
|
||||
lock_grab(sleepLock);
|
||||
cond_signal(sleepCond);
|
||||
lock_release(sleepLock);
|
||||
mutex.lock();
|
||||
sleepCondition.notify_one();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -153,9 +142,9 @@ void Thread::wait_for_stop_or_ponderhit() {
|
||||
|
||||
Signals.stopOnPonderhit = true;
|
||||
|
||||
lock_grab(sleepLock);
|
||||
while (!Signals.stop) cond_wait(sleepCond, sleepLock);
|
||||
lock_release(sleepLock);
|
||||
mutex.lock();
|
||||
while (!Signals.stop) sleepCondition.wait(mutex);;
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -199,26 +188,22 @@ bool Thread::is_available_to(Thread* master) const {
|
||||
// a c'tor becuase Threads is a static object and we need a fully initialized
|
||||
// engine at this point due to allocation of endgames in Thread c'tor.
|
||||
|
||||
void ThreadsManager::init() {
|
||||
void ThreadPool::init() {
|
||||
|
||||
cond_init(sleepCond);
|
||||
lock_init(splitLock);
|
||||
timer = new Thread(&Thread::timer_loop);
|
||||
threads.push_back(new Thread(&Thread::main_loop));
|
||||
read_uci_options();
|
||||
}
|
||||
|
||||
|
||||
// d'tor cleanly terminates the threads when the program exits.
|
||||
// exit() cleanly terminates the threads before the program exits.
|
||||
|
||||
ThreadsManager::~ThreadsManager() {
|
||||
void ThreadPool::exit() {
|
||||
|
||||
for (int i = 0; i < size(); i++)
|
||||
for (size_t i = 0; i < threads.size(); i++)
|
||||
delete threads[i];
|
||||
|
||||
delete timer;
|
||||
lock_destroy(splitLock);
|
||||
cond_destroy(sleepCond);
|
||||
}
|
||||
|
||||
|
||||
@@ -227,19 +212,19 @@ ThreadsManager::~ThreadsManager() {
|
||||
// objects are dynamically allocated to avoid creating in advance all possible
|
||||
// threads, with included pawns and material tables, if only few are used.
|
||||
|
||||
void ThreadsManager::read_uci_options() {
|
||||
void ThreadPool::read_uci_options() {
|
||||
|
||||
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
|
||||
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
|
||||
useSleepingThreads = Options["Use Sleeping Threads"];
|
||||
int requested = Options["Threads"];
|
||||
size_t requested = Options["Threads"];
|
||||
|
||||
assert(requested > 0);
|
||||
|
||||
while (size() < requested)
|
||||
while (threads.size() < requested)
|
||||
threads.push_back(new Thread(&Thread::idle_loop));
|
||||
|
||||
while (size() > requested)
|
||||
while (threads.size() > requested)
|
||||
{
|
||||
delete threads.back();
|
||||
threads.pop_back();
|
||||
@@ -251,9 +236,9 @@ void ThreadsManager::read_uci_options() {
|
||||
// on the sleep condition and to reset maxPly. When useSleepingThreads is set
|
||||
// threads will be woken up at split time.
|
||||
|
||||
void ThreadsManager::wake_up() const {
|
||||
void ThreadPool::wake_up() const {
|
||||
|
||||
for (int i = 0; i < size(); i++)
|
||||
for (size_t i = 0; i < threads.size(); i++)
|
||||
{
|
||||
threads[i]->maxPly = 0;
|
||||
threads[i]->do_sleep = false;
|
||||
@@ -267,19 +252,20 @@ void ThreadsManager::wake_up() const {
|
||||
// sleep() is called after the search finishes to ask all the threads but the
|
||||
// main one to go waiting on a sleep condition.
|
||||
|
||||
void ThreadsManager::sleep() const {
|
||||
void ThreadPool::sleep() const {
|
||||
|
||||
for (int i = 1; i < size(); i++) // Main thread will go to sleep by itself
|
||||
threads[i]->do_sleep = true; // to avoid a race with start_searching()
|
||||
// Main thread will go to sleep by itself to avoid a race with start_searching()
|
||||
for (size_t i = 1; i < threads.size(); i++)
|
||||
threads[i]->do_sleep = true;
|
||||
}
|
||||
|
||||
|
||||
// available_slave_exists() tries to find an idle thread which is available as
|
||||
// a slave for the thread 'master'.
|
||||
|
||||
bool ThreadsManager::available_slave_exists(Thread* master) const {
|
||||
bool ThreadPool::available_slave_exists(Thread* master) const {
|
||||
|
||||
for (int i = 0; i < size(); i++)
|
||||
for (size_t i = 0; i < threads.size(); i++)
|
||||
if (threads[i]->is_available_to(master))
|
||||
return true;
|
||||
|
||||
@@ -297,9 +283,10 @@ bool ThreadsManager::available_slave_exists(Thread* master) const {
|
||||
// search(). When all threads have returned from search() then split() returns.
|
||||
|
||||
template <bool Fake>
|
||||
Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
|
||||
Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta,
|
||||
Value bestValue, Move* bestMove, Depth depth,
|
||||
Move threatMove, int moveCount, MovePicker* mp, int nodeType) {
|
||||
|
||||
assert(pos.pos_is_ok());
|
||||
assert(bestValue > -VALUE_INFINITE);
|
||||
assert(bestValue <= alpha);
|
||||
@@ -313,41 +300,41 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
|
||||
return bestValue;
|
||||
|
||||
// Pick the next available split point from the split point stack
|
||||
SplitPoint* sp = &master->splitPoints[master->splitPointsCnt];
|
||||
SplitPoint& sp = master->splitPoints[master->splitPointsCnt];
|
||||
|
||||
sp->parent = master->curSplitPoint;
|
||||
sp->master = master;
|
||||
sp->cutoff = false;
|
||||
sp->slavesMask = 1ULL << master->idx;
|
||||
sp->depth = depth;
|
||||
sp->bestMove = *bestMove;
|
||||
sp->threatMove = threatMove;
|
||||
sp->alpha = alpha;
|
||||
sp->beta = beta;
|
||||
sp->nodeType = nodeType;
|
||||
sp->bestValue = bestValue;
|
||||
sp->mp = mp;
|
||||
sp->moveCount = moveCount;
|
||||
sp->pos = &pos;
|
||||
sp->nodes = 0;
|
||||
sp->ss = ss;
|
||||
sp.parent = master->curSplitPoint;
|
||||
sp.master = master;
|
||||
sp.cutoff = false;
|
||||
sp.slavesMask = 1ULL << master->idx;
|
||||
sp.depth = depth;
|
||||
sp.bestMove = *bestMove;
|
||||
sp.threatMove = threatMove;
|
||||
sp.alpha = alpha;
|
||||
sp.beta = beta;
|
||||
sp.nodeType = nodeType;
|
||||
sp.bestValue = bestValue;
|
||||
sp.mp = mp;
|
||||
sp.moveCount = moveCount;
|
||||
sp.pos = &pos;
|
||||
sp.nodes = 0;
|
||||
sp.ss = ss;
|
||||
|
||||
assert(master->is_searching);
|
||||
|
||||
master->curSplitPoint = sp;
|
||||
master->curSplitPoint = &sp;
|
||||
int slavesCnt = 0;
|
||||
|
||||
// Try to allocate available threads and ask them to start searching setting
|
||||
// is_searching flag. This must be done under lock protection to avoid concurrent
|
||||
// allocation of the same slave by another master.
|
||||
lock_grab(sp->lock);
|
||||
lock_grab(splitLock);
|
||||
sp.mutex.lock();
|
||||
mutex.lock();
|
||||
|
||||
for (int i = 0; i < size() && !Fake; ++i)
|
||||
for (size_t i = 0; i < threads.size() && !Fake; ++i)
|
||||
if (threads[i]->is_available_to(master))
|
||||
{
|
||||
sp->slavesMask |= 1ULL << i;
|
||||
threads[i]->curSplitPoint = sp;
|
||||
sp.slavesMask |= 1ULL << i;
|
||||
threads[i]->curSplitPoint = &sp;
|
||||
threads[i]->is_searching = true; // Slave leaves idle_loop()
|
||||
|
||||
if (useSleepingThreads)
|
||||
@@ -359,17 +346,16 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
|
||||
|
||||
master->splitPointsCnt++;
|
||||
|
||||
lock_release(splitLock);
|
||||
lock_release(sp->lock);
|
||||
mutex.unlock();
|
||||
sp.mutex.unlock();
|
||||
|
||||
// Everything is set up. The master thread enters the idle loop, from which
|
||||
// it will instantly launch a search, because its is_searching flag is set.
|
||||
// We pass the split point as a parameter to the idle loop, which means that
|
||||
// the thread will return from the idle loop when all slaves have finished
|
||||
// The thread will return from the idle loop when all slaves have finished
|
||||
// their work at this split point.
|
||||
if (slavesCnt || Fake)
|
||||
{
|
||||
master->idle_loop(sp);
|
||||
master->idle_loop();
|
||||
|
||||
// In helpful master concept a master can help only a sub-tree of its split
|
||||
// point, and because here is all finished is not possible master is booked.
|
||||
@@ -379,68 +365,69 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
|
||||
// We have returned from the idle loop, which means that all threads are
|
||||
// finished. Note that setting is_searching and decreasing splitPointsCnt is
|
||||
// done under lock protection to avoid a race with Thread::is_available_to().
|
||||
lock_grab(sp->lock); // To protect sp->nodes
|
||||
lock_grab(splitLock);
|
||||
sp.mutex.lock(); // To protect sp.nodes
|
||||
mutex.lock();
|
||||
|
||||
master->is_searching = true;
|
||||
master->splitPointsCnt--;
|
||||
master->curSplitPoint = sp->parent;
|
||||
pos.set_nodes_searched(pos.nodes_searched() + sp->nodes);
|
||||
*bestMove = sp->bestMove;
|
||||
master->curSplitPoint = sp.parent;
|
||||
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
|
||||
*bestMove = sp.bestMove;
|
||||
|
||||
lock_release(splitLock);
|
||||
lock_release(sp->lock);
|
||||
mutex.unlock();
|
||||
sp.mutex.unlock();
|
||||
|
||||
return sp->bestValue;
|
||||
return sp.bestValue;
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
template Value ThreadsManager::split<false>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
|
||||
template Value ThreadsManager::split<true>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
|
||||
template Value ThreadPool::split<false>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
|
||||
template Value ThreadPool::split<true>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
|
||||
|
||||
|
||||
// ThreadsManager::set_timer() is used to set the timer to trigger after msec
|
||||
// milliseconds. If msec is 0 then timer is stopped.
|
||||
// set_timer() is used to set the timer to trigger after msec milliseconds.
|
||||
// If msec is 0 then timer is stopped.
|
||||
|
||||
void ThreadsManager::set_timer(int msec) {
|
||||
void ThreadPool::set_timer(int msec) {
|
||||
|
||||
lock_grab(timer->sleepLock);
|
||||
timer->mutex.lock();
|
||||
timer->maxPly = msec;
|
||||
cond_signal(timer->sleepCond); // Wake up and restart the timer
|
||||
lock_release(timer->sleepLock);
|
||||
timer->sleepCondition.notify_one(); // Wake up and restart the timer
|
||||
timer->mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
// ThreadsManager::wait_for_search_finished() waits for main thread to go to
|
||||
// sleep, this means search is finished. Then returns.
|
||||
// wait_for_search_finished() waits for main thread to go to sleep, this means
|
||||
// search is finished. Then returns.
|
||||
|
||||
void ThreadsManager::wait_for_search_finished() {
|
||||
void ThreadPool::wait_for_search_finished() {
|
||||
|
||||
Thread* t = main_thread();
|
||||
lock_grab(t->sleepLock);
|
||||
cond_signal(t->sleepCond); // In case is waiting for stop or ponderhit
|
||||
while (!t->do_sleep) cond_wait(sleepCond, t->sleepLock);
|
||||
lock_release(t->sleepLock);
|
||||
t->mutex.lock();
|
||||
t->sleepCondition.notify_one(); // In case is waiting for stop or ponderhit
|
||||
while (!t->do_sleep) sleepCondition.wait(t->mutex);
|
||||
t->mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
// ThreadsManager::start_searching() wakes up the main thread sleeping in
|
||||
// main_loop() so to start a new search, then returns immediately.
|
||||
// start_searching() wakes up the main thread sleeping in main_loop() so to start
|
||||
// a new search, then returns immediately.
|
||||
|
||||
void ThreadsManager::start_searching(const Position& pos, const LimitsType& limits,
|
||||
const std::vector<Move>& searchMoves) {
|
||||
void ThreadPool::start_searching(const Position& pos, const LimitsType& limits,
|
||||
const std::vector<Move>& searchMoves, StateStackPtr& states) {
|
||||
wait_for_search_finished();
|
||||
|
||||
SearchTime.restart(); // As early as possible
|
||||
SearchTime = Time::now(); // As early as possible
|
||||
|
||||
Signals.stopOnPonderhit = Signals.firstRootMove = false;
|
||||
Signals.stop = Signals.failedLowAtRoot = false;
|
||||
|
||||
RootPosition = pos;
|
||||
Limits = limits;
|
||||
SetupStates = states; // Ownership transfer here
|
||||
RootMoves.clear();
|
||||
|
||||
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
|
||||
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
|
||||
if (searchMoves.empty() || std::count(searchMoves.begin(), searchMoves.end(), ml.move()))
|
||||
RootMoves.push_back(RootMove(ml.move()));
|
||||
|
||||
|
||||
@@ -31,6 +31,31 @@
|
||||
const int MAX_THREADS = 32;
|
||||
const int MAX_SPLITPOINTS_PER_THREAD = 8;
|
||||
|
||||
struct Mutex {
|
||||
Mutex() { lock_init(l); }
|
||||
~Mutex() { lock_destroy(l); }
|
||||
|
||||
void lock() { lock_grab(l); }
|
||||
void unlock() { lock_release(l); }
|
||||
|
||||
private:
|
||||
friend struct ConditionVariable;
|
||||
|
||||
Lock l;
|
||||
};
|
||||
|
||||
struct ConditionVariable {
|
||||
ConditionVariable() { cond_init(c); }
|
||||
~ConditionVariable() { cond_destroy(c); }
|
||||
|
||||
void wait(Mutex& m) { cond_wait(c, m.l); }
|
||||
void wait_for(Mutex& m, int ms) { timed_wait(c, m.l, ms); }
|
||||
void notify_one() { cond_signal(c); }
|
||||
|
||||
private:
|
||||
WaitCondition c;
|
||||
};
|
||||
|
||||
class Thread;
|
||||
|
||||
struct SplitPoint {
|
||||
@@ -49,7 +74,7 @@ struct SplitPoint {
|
||||
SplitPoint* parent;
|
||||
|
||||
// Shared data
|
||||
Lock lock;
|
||||
Mutex mutex;
|
||||
volatile uint64_t slavesMask;
|
||||
volatile int64_t nodes;
|
||||
volatile Value alpha;
|
||||
@@ -67,10 +92,7 @@ struct SplitPoint {
|
||||
|
||||
class Thread {
|
||||
|
||||
Thread(const Thread&); // Only declared to disable the default ones
|
||||
Thread& operator=(const Thread&); // that are not suitable in this case.
|
||||
|
||||
typedef void (Thread::* Fn) ();
|
||||
typedef void (Thread::* Fn) (); // Pointer to member function
|
||||
|
||||
public:
|
||||
Thread(Fn fn);
|
||||
@@ -79,8 +101,7 @@ public:
|
||||
void wake_up();
|
||||
bool cutoff_occurred() const;
|
||||
bool is_available_to(Thread* master) const;
|
||||
void idle_loop(SplitPoint* sp_master);
|
||||
void idle_loop() { idle_loop(NULL); } // Hack to allow storing in start_fn
|
||||
void idle_loop();
|
||||
void main_loop();
|
||||
void timer_loop();
|
||||
void wait_for_stop_or_ponderhit();
|
||||
@@ -88,10 +109,10 @@ public:
|
||||
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
|
||||
MaterialTable materialTable;
|
||||
PawnTable pawnTable;
|
||||
int idx;
|
||||
size_t idx;
|
||||
int maxPly;
|
||||
Lock sleepLock;
|
||||
WaitCondition sleepCond;
|
||||
Mutex mutex;
|
||||
ConditionVariable sleepCondition;
|
||||
NativeHandle handle;
|
||||
Fn start_fn;
|
||||
SplitPoint* volatile curSplitPoint;
|
||||
@@ -102,23 +123,20 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/// ThreadsManager class handles all the threads related stuff like init, starting,
|
||||
/// ThreadPool class handles all the threads related stuff like init, starting,
|
||||
/// parking and, the most important, launching a slave thread at a split point.
|
||||
/// All the access to shared thread data is done through this class.
|
||||
|
||||
class ThreadsManager {
|
||||
/* As long as the single ThreadsManager object is defined as a global we don't
|
||||
need to explicitly initialize to zero its data members because variables with
|
||||
static storage duration are automatically set to zero before enter main()
|
||||
*/
|
||||
public:
|
||||
void init(); // No c'tor becuase Threads is static and we need engine initialized
|
||||
~ThreadsManager();
|
||||
class ThreadPool {
|
||||
|
||||
Thread& operator[](int id) { return *threads[id]; }
|
||||
public:
|
||||
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.
|
||||
|
||||
Thread& operator[](size_t id) { return *threads[id]; }
|
||||
bool use_sleeping_threads() const { return useSleepingThreads; }
|
||||
int min_split_depth() const { return minimumSplitDepth; }
|
||||
int size() const { return (int)threads.size(); }
|
||||
size_t size() const { return threads.size(); }
|
||||
Thread* main_thread() { return threads[0]; }
|
||||
|
||||
void wake_up() const;
|
||||
@@ -127,8 +145,8 @@ public:
|
||||
bool available_slave_exists(Thread* master) const;
|
||||
void set_timer(int msec);
|
||||
void wait_for_search_finished();
|
||||
void start_searching(const Position& pos, const Search::LimitsType& limits,
|
||||
const std::vector<Move>& searchMoves);
|
||||
void start_searching(const Position&, const Search::LimitsType&,
|
||||
const std::vector<Move>&, Search::StateStackPtr&);
|
||||
|
||||
template <bool Fake>
|
||||
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove,
|
||||
@@ -138,13 +156,13 @@ private:
|
||||
|
||||
std::vector<Thread*> threads;
|
||||
Thread* timer;
|
||||
Lock splitLock;
|
||||
WaitCondition sleepCond;
|
||||
Mutex mutex;
|
||||
ConditionVariable sleepCondition;
|
||||
Depth minimumSplitDepth;
|
||||
int maxThreadsPerSplitPoint;
|
||||
bool useSleepingThreads;
|
||||
};
|
||||
|
||||
extern ThreadsManager Threads;
|
||||
extern ThreadPool Threads;
|
||||
|
||||
#endif // !defined(THREAD_H_INCLUDED)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "tt.h"
|
||||
|
||||
TranspositionTable TT; // Our global transposition table
|
||||
@@ -37,18 +38,13 @@ TranspositionTable::~TranspositionTable() {
|
||||
|
||||
|
||||
/// TranspositionTable::set_size() sets the size of the transposition table,
|
||||
/// measured in megabytes.
|
||||
/// measured in megabytes. Transposition table consists of a power of 2 number of
|
||||
/// TTCluster and each cluster consists of ClusterSize number of TTEntries. Each
|
||||
/// non-empty entry contains information of exactly one position.
|
||||
|
||||
void TranspositionTable::set_size(size_t mbSize) {
|
||||
|
||||
size_t newSize = 1024;
|
||||
|
||||
// Transposition table consists of clusters and each cluster consists
|
||||
// of ClusterSize number of TTEntries. Each non-empty entry contains
|
||||
// information of exactly one position and newSize is the number of
|
||||
// clusters we are going to allocate.
|
||||
while (2ULL * newSize * sizeof(TTCluster) <= (mbSize << 20))
|
||||
newSize *= 2;
|
||||
size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTCluster));
|
||||
|
||||
if (newSize == size)
|
||||
return;
|
||||
@@ -56,13 +52,15 @@ void TranspositionTable::set_size(size_t mbSize) {
|
||||
size = newSize;
|
||||
delete [] entries;
|
||||
entries = new (std::nothrow) TTCluster[size];
|
||||
|
||||
if (!entries)
|
||||
{
|
||||
std::cerr << "Failed to allocate " << mbSize
|
||||
<< "MB for transposition table." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
clear();
|
||||
|
||||
clear(); // Operator new is not guaranteed to initialize memory to zero
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -119,15 +119,13 @@ enum Move {
|
||||
MOVE_NULL = 65
|
||||
};
|
||||
|
||||
struct MoveStack {
|
||||
Move move;
|
||||
int score;
|
||||
enum MoveType {
|
||||
NORMAL = 0,
|
||||
PROMOTION = 1 << 14,
|
||||
ENPASSANT = 2 << 14,
|
||||
CASTLE = 3 << 14
|
||||
};
|
||||
|
||||
inline bool operator<(const MoveStack& f, const MoveStack& s) {
|
||||
return f.score < s.score;
|
||||
}
|
||||
|
||||
enum CastleRight { // Defined as in PolyGlot book hash key
|
||||
CASTLES_NONE = 0,
|
||||
WHITE_OO = 1,
|
||||
@@ -168,7 +166,15 @@ enum Value {
|
||||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
|
||||
|
||||
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
|
||||
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN
|
||||
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
|
||||
|
||||
Mg = 0, Eg = 1,
|
||||
|
||||
PawnValueMg = 198, PawnValueEg = 258,
|
||||
KnightValueMg = 817, KnightValueEg = 846,
|
||||
BishopValueMg = 836, BishopValueEg = 857,
|
||||
RookValueMg = 1270, RookValueEg = 1278,
|
||||
QueenValueMg = 2521, QueenValueEg = 2558
|
||||
};
|
||||
|
||||
enum PieceType {
|
||||
@@ -312,20 +318,31 @@ inline Score apply_weight(Score v, Score w) {
|
||||
#undef ENABLE_OPERATORS_ON
|
||||
#undef ENABLE_SAFE_OPERATORS_ON
|
||||
|
||||
const Value PawnValueMidgame = Value(198);
|
||||
const Value PawnValueEndgame = Value(258);
|
||||
const Value KnightValueMidgame = Value(817);
|
||||
const Value KnightValueEndgame = Value(846);
|
||||
const Value BishopValueMidgame = Value(836);
|
||||
const Value BishopValueEndgame = Value(857);
|
||||
const Value RookValueMidgame = Value(1270);
|
||||
const Value RookValueEndgame = Value(1278);
|
||||
const Value QueenValueMidgame = Value(2521);
|
||||
const Value QueenValueEndgame = Value(2558);
|
||||
namespace Zobrist {
|
||||
|
||||
extern const Value PieceValueMidgame[17]; // Indexed by Piece or PieceType
|
||||
extern const Value PieceValueEndgame[17];
|
||||
extern int SquareDistance[64][64];
|
||||
extern Key psq[2][8][64]; // [color][pieceType][square / piece count]
|
||||
extern Key enpassant[8]; // [file]
|
||||
extern Key castle[16]; // [castleRight]
|
||||
extern Key side;
|
||||
extern Key exclusion;
|
||||
|
||||
void init();
|
||||
}
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
|
||||
extern Score pieceSquareTable[16][64]; // [piece][square]
|
||||
extern Value PieceValue[2][18]; // [Mg / Eg][piece / pieceType]
|
||||
extern int SquareDistance[64][64]; // [square][square]
|
||||
|
||||
struct MoveStack {
|
||||
Move move;
|
||||
int score;
|
||||
};
|
||||
|
||||
inline bool operator<(const MoveStack& f, const MoveStack& s) {
|
||||
return f.score < s.score;
|
||||
}
|
||||
|
||||
inline Color operator~(Color c) {
|
||||
return Color(c ^ 1);
|
||||
@@ -335,6 +352,10 @@ inline Square operator~(Square s) {
|
||||
return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8
|
||||
}
|
||||
|
||||
inline Square operator|(File f, Rank r) {
|
||||
return Square((r << 3) | f);
|
||||
}
|
||||
|
||||
inline Value mate_in(int ply) {
|
||||
return VALUE_MATE - ply;
|
||||
}
|
||||
@@ -359,10 +380,6 @@ inline Color color_of(Piece p) {
|
||||
return Color(p >> 3);
|
||||
}
|
||||
|
||||
inline Square make_square(File f, Rank r) {
|
||||
return Square((r << 3) | f);
|
||||
}
|
||||
|
||||
inline bool is_ok(Square s) {
|
||||
return s >= SQ_A1 && s <= SQ_H8;
|
||||
}
|
||||
@@ -408,10 +425,6 @@ inline int square_distance(Square s1, Square s2) {
|
||||
return SquareDistance[s1][s2];
|
||||
}
|
||||
|
||||
inline char piece_type_to_char(PieceType pt) {
|
||||
return " PNBRQK"[pt];
|
||||
}
|
||||
|
||||
inline char file_to_char(File f) {
|
||||
return char(f - FILE_A + int('a'));
|
||||
}
|
||||
@@ -432,20 +445,8 @@ inline Square to_sq(Move m) {
|
||||
return Square(m & 0x3F);
|
||||
}
|
||||
|
||||
inline bool is_special(Move m) {
|
||||
return m & (3 << 14);
|
||||
}
|
||||
|
||||
inline bool is_promotion(Move m) {
|
||||
return (m & (3 << 14)) == (1 << 14);
|
||||
}
|
||||
|
||||
inline int is_enpassant(Move m) {
|
||||
return (m & (3 << 14)) == (2 << 14);
|
||||
}
|
||||
|
||||
inline int is_castle(Move m) {
|
||||
return (m & (3 << 14)) == (3 << 14);
|
||||
inline MoveType type_of(Move m) {
|
||||
return MoveType(m & (3 << 14));
|
||||
}
|
||||
|
||||
inline PieceType promotion_type(Move m) {
|
||||
@@ -456,16 +457,9 @@ inline Move make_move(Square from, Square to) {
|
||||
return Move(to | (from << 6));
|
||||
}
|
||||
|
||||
inline Move make_promotion(Square from, Square to, PieceType pt) {
|
||||
return Move(to | (from << 6) | (1 << 14) | ((pt - 2) << 12)) ;
|
||||
}
|
||||
|
||||
inline Move make_enpassant(Square from, Square to) {
|
||||
return Move(to | (from << 6) | (2 << 14));
|
||||
}
|
||||
|
||||
inline Move make_castle(Square from, Square to) {
|
||||
return Move(to | (from << 6) | (3 << 14));
|
||||
template<MoveType T>
|
||||
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
|
||||
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)) ;
|
||||
}
|
||||
|
||||
inline bool is_ok(Move m) {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "evaluate.h"
|
||||
#include "notation.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
@@ -37,9 +38,8 @@ namespace {
|
||||
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
// Keep track of position keys along the setup moves (from start position to the
|
||||
// position just before to start searching). This is needed by draw detection
|
||||
// where, due to 50 moves rule, we need to check at most 100 plies back.
|
||||
StateInfo StateRingBuf[102], *SetupState = StateRingBuf;
|
||||
// position just before to start searching). Needed by repetition draw detection.
|
||||
Search::StateStackPtr SetupStates;
|
||||
|
||||
void set_option(istringstream& up);
|
||||
void set_position(Position& pos, istringstream& up);
|
||||
@@ -52,7 +52,7 @@ namespace {
|
||||
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
|
||||
/// commands, the function also supports a few debug commands.
|
||||
|
||||
void uci_loop(const string& args) {
|
||||
void UCI::loop(const string& args) {
|
||||
|
||||
Position pos(StartFEN, false, Threads.main_thread()); // The root position
|
||||
string cmd, token;
|
||||
@@ -96,7 +96,7 @@ void uci_loop(const string& args) {
|
||||
{ /* Avoid returning "Unknown command" */ }
|
||||
|
||||
else if (token == "isready")
|
||||
cout << "readyok" << endl;
|
||||
sync_cout << "readyok" << sync_endl;
|
||||
|
||||
else if (token == "position")
|
||||
set_position(pos, is);
|
||||
@@ -111,20 +111,20 @@ void uci_loop(const string& args) {
|
||||
pos.flip();
|
||||
|
||||
else if (token == "eval")
|
||||
cout << Eval::trace(pos) << endl;
|
||||
sync_cout << Eval::trace(pos) << sync_endl;
|
||||
|
||||
else if (token == "bench")
|
||||
benchmark(pos, is);
|
||||
|
||||
else if (token == "key")
|
||||
cout << "key: " << hex << pos.key()
|
||||
sync_cout << "key: " << hex << pos.key()
|
||||
<< "\nmaterial key: " << pos.material_key()
|
||||
<< "\npawn key: " << pos.pawn_key() << endl;
|
||||
<< "\npawn key: " << pos.pawn_key() << sync_endl;
|
||||
|
||||
else if (token == "uci")
|
||||
cout << "id name " << engine_info(true)
|
||||
sync_cout << "id name " << engine_info(true)
|
||||
<< "\n" << Options
|
||||
<< "\nuciok" << endl;
|
||||
<< "\nuciok" << sync_endl;
|
||||
|
||||
else if (token == "perft" && (is >> token)) // Read depth
|
||||
{
|
||||
@@ -137,7 +137,7 @@ void uci_loop(const string& args) {
|
||||
}
|
||||
|
||||
else
|
||||
cout << "Unknown command: " << cmd << endl;
|
||||
sync_cout << "Unknown command: " << cmd << sync_endl;
|
||||
|
||||
if (!args.empty()) // Command line arguments have one-shot behaviour
|
||||
{
|
||||
@@ -150,10 +150,10 @@ void uci_loop(const string& args) {
|
||||
|
||||
namespace {
|
||||
|
||||
// set_position() is called when engine receives the "position" UCI
|
||||
// command. 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 following move list ("moves").
|
||||
// set_position() is called when engine receives the "position" UCI command.
|
||||
// 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
|
||||
// following move list ("moves").
|
||||
|
||||
void set_position(Position& pos, istringstream& is) {
|
||||
|
||||
@@ -174,15 +174,13 @@ namespace {
|
||||
return;
|
||||
|
||||
pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread());
|
||||
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
|
||||
|
||||
// Parse move list (if any)
|
||||
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
|
||||
{
|
||||
pos.do_move(m, *SetupState);
|
||||
|
||||
// Increment pointer to StateRingBuf circular buffer
|
||||
if (++SetupState - StateRingBuf >= 102)
|
||||
SetupState = StateRingBuf;
|
||||
SetupStates->push(StateInfo());
|
||||
pos.do_move(m, SetupStates->top());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +205,7 @@ namespace {
|
||||
if (Options.count(name))
|
||||
Options[name] = value;
|
||||
else
|
||||
cout << "No such option: " << name << endl;
|
||||
sync_cout << "No such option: " << name << sync_endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -248,6 +246,6 @@ namespace {
|
||||
searchMoves.push_back(move_from_uci(pos, token));
|
||||
}
|
||||
|
||||
Threads.start_searching(pos, limits, searchMoves);
|
||||
Threads.start_searching(pos, limits, searchMoves, SetupStates);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#include "evaluate.h"
|
||||
@@ -28,71 +30,69 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
OptionsMap Options; // Global object
|
||||
UCI::OptionsMap Options; // Global object
|
||||
|
||||
namespace {
|
||||
namespace UCI {
|
||||
|
||||
/// 'On change' actions, triggered by an option's value change
|
||||
void on_logger(const UCIOption& opt) { start_logger(opt); }
|
||||
void on_eval(const UCIOption&) { Eval::init(); }
|
||||
void on_threads(const UCIOption&) { Threads.read_uci_options(); }
|
||||
void on_hash_size(const UCIOption& opt) { TT.set_size(opt); }
|
||||
void on_clear_hash(const UCIOption&) { TT.clear(); }
|
||||
void on_logger(const Option& o) { start_logger(o); }
|
||||
void on_eval(const Option&) { Eval::init(); }
|
||||
void on_threads(const Option&) { Threads.read_uci_options(); }
|
||||
void on_hash_size(const Option& o) { TT.set_size(o); }
|
||||
void on_clear_hash(const Option&) { TT.clear(); }
|
||||
|
||||
|
||||
/// Our case insensitive less() function as required by UCI protocol
|
||||
bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
|
||||
|
||||
}
|
||||
|
||||
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
||||
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
|
||||
}
|
||||
|
||||
|
||||
/// OptionsMap c'tor initializes the UCI options to their hard coded default
|
||||
/// values and initializes the default value of "Threads" and "Min Split Depth"
|
||||
/// 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.
|
||||
|
||||
OptionsMap::OptionsMap() {
|
||||
void init(OptionsMap& o) {
|
||||
|
||||
int cpus = std::min(cpu_count(), MAX_THREADS);
|
||||
int msd = cpus < 8 ? 4 : 7;
|
||||
OptionsMap& o = *this;
|
||||
|
||||
o["Use Debug Log"] = UCIOption(false, on_logger);
|
||||
o["Use Search Log"] = UCIOption(false);
|
||||
o["Search Log Filename"] = UCIOption("SearchLog.txt");
|
||||
o["Book File"] = UCIOption("book.bin");
|
||||
o["Best Book Move"] = UCIOption(false);
|
||||
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200, on_eval);
|
||||
o["Mobility (Endgame)"] = UCIOption(100, 0, 200, on_eval);
|
||||
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200, on_eval);
|
||||
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200, on_eval);
|
||||
o["Space"] = UCIOption(100, 0, 200, on_eval);
|
||||
o["Aggressiveness"] = UCIOption(100, 0, 200, on_eval);
|
||||
o["Cowardice"] = UCIOption(100, 0, 200, on_eval);
|
||||
o["Min Split Depth"] = UCIOption(msd, 4, 7, on_threads);
|
||||
o["Max Threads per Split Point"] = UCIOption(5, 4, 8, on_threads);
|
||||
o["Threads"] = UCIOption(cpus, 1, MAX_THREADS, on_threads);
|
||||
o["Use Sleeping Threads"] = UCIOption(true, on_threads);
|
||||
o["Hash"] = UCIOption(32, 4, 8192, on_hash_size);
|
||||
o["Clear Hash"] = UCIOption(on_clear_hash);
|
||||
o["Ponder"] = UCIOption(true);
|
||||
o["OwnBook"] = UCIOption(false);
|
||||
o["MultiPV"] = UCIOption(1, 1, 500);
|
||||
o["Skill Level"] = UCIOption(20, 0, 20);
|
||||
o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
|
||||
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
|
||||
o["Emergency Move Time"] = UCIOption(70, 0, 5000);
|
||||
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
|
||||
o["Slow Mover"] = UCIOption(100, 10, 1000);
|
||||
o["UCI_Chess960"] = UCIOption(false);
|
||||
o["UCI_AnalyseMode"] = UCIOption(false, on_eval);
|
||||
o["Use Debug Log"] = Option(false, on_logger);
|
||||
o["Use Search Log"] = Option(false);
|
||||
o["Search Log Filename"] = Option("SearchLog.txt");
|
||||
o["Book File"] = Option("book.bin");
|
||||
o["Best Book Move"] = Option(false);
|
||||
o["Mobility (Middle Game)"] = 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["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval);
|
||||
o["Space"] = Option(100, 0, 200, on_eval);
|
||||
o["Aggressiveness"] = Option(100, 0, 200, on_eval);
|
||||
o["Cowardice"] = Option(100, 0, 200, on_eval);
|
||||
o["Min Split Depth"] = Option(msd, 4, 7, on_threads);
|
||||
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
|
||||
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads);
|
||||
o["Use Sleeping Threads"] = Option(true, on_threads);
|
||||
o["Hash"] = Option(32, 4, 8192, on_hash_size);
|
||||
o["Clear Hash"] = Option(on_clear_hash);
|
||||
o["Ponder"] = Option(true);
|
||||
o["OwnBook"] = Option(false);
|
||||
o["MultiPV"] = Option(1, 1, 500);
|
||||
o["Skill Level"] = Option(20, 0, 20);
|
||||
o["Emergency Move Horizon"] = Option(40, 0, 50);
|
||||
o["Emergency Base Time"] = Option(200, 0, 30000);
|
||||
o["Emergency Move Time"] = Option(70, 0, 5000);
|
||||
o["Minimum Thinking Time"] = Option(20, 0, 5000);
|
||||
o["Slow Mover"] = Option(100, 10, 1000);
|
||||
o["UCI_Chess960"] = Option(false);
|
||||
o["UCI_AnalyseMode"] = Option(false, on_eval);
|
||||
}
|
||||
|
||||
|
||||
/// operator<<() is used to output all the UCI options in chronological insertion
|
||||
/// order (the idx field) and in the format defined by the UCI protocol.
|
||||
/// operator<<() is used to print all the options default values in chronological
|
||||
/// insertion order (the idx field) and in the format defined by the UCI protocol.
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
||||
|
||||
@@ -100,7 +100,7 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
||||
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
|
||||
if (it->second.idx == idx)
|
||||
{
|
||||
const UCIOption& o = it->second;
|
||||
const Option& o = it->second;
|
||||
os << "\noption name " << it->first << " type " << o.type;
|
||||
|
||||
if (o.type != "button")
|
||||
@@ -115,37 +115,52 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
||||
}
|
||||
|
||||
|
||||
/// UCIOption class c'tors
|
||||
/// Option c'tors and conversion operators
|
||||
|
||||
UCIOption::UCIOption(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f)
|
||||
Option::Option(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f)
|
||||
{ defaultValue = currentValue = v; }
|
||||
|
||||
UCIOption::UCIOption(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f)
|
||||
Option::Option(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f)
|
||||
{ defaultValue = currentValue = (v ? "true" : "false"); }
|
||||
|
||||
UCIOption::UCIOption(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f)
|
||||
Option::Option(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f)
|
||||
{}
|
||||
|
||||
UCIOption::UCIOption(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f)
|
||||
Option::Option(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f)
|
||||
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
|
||||
|
||||
|
||||
/// UCIOption::operator=() updates currentValue. Normally it's up to the GUI to
|
||||
/// check for option's limits, but we could receive the new value directly from
|
||||
Option::operator int() const {
|
||||
assert(type == "check" || type == "spin");
|
||||
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
|
||||
}
|
||||
|
||||
Option::operator std::string() const {
|
||||
assert(type == "string");
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
|
||||
/// operator=() updates currentValue and triggers on_change() action. It's up to
|
||||
/// the GUI to check for option's limits, but we could receive the new value from
|
||||
/// the user by console window, so let's check the bounds anyway.
|
||||
|
||||
void UCIOption::operator=(const string& v) {
|
||||
Option& Option::operator=(const string& v) {
|
||||
|
||||
assert(!type.empty());
|
||||
|
||||
if ( (type == "button" || !v.empty())
|
||||
&& (type != "check" || (v == "true" || v == "false"))
|
||||
&& (type != "spin" || (atoi(v.c_str()) >= min && atoi(v.c_str()) <= max)))
|
||||
{
|
||||
if ( (type != "button" && v.empty())
|
||||
|| (type == "check" && v != "true" && v != "false")
|
||||
|| (type == "spin" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max)))
|
||||
return *this;
|
||||
|
||||
if (type != "button")
|
||||
currentValue = v;
|
||||
|
||||
if (on_change)
|
||||
(*on_change)(*this);
|
||||
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UCI
|
||||
|
||||
@@ -20,35 +20,35 @@
|
||||
#if !defined(UCIOPTION_H_INCLUDED)
|
||||
#define UCIOPTION_H_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
struct OptionsMap;
|
||||
namespace UCI {
|
||||
|
||||
/// UCIOption class implements an option as defined by UCI protocol
|
||||
class UCIOption {
|
||||
class Option;
|
||||
|
||||
typedef void (Fn)(const UCIOption&);
|
||||
/// Custom comparator because UCI options should be case insensitive
|
||||
struct CaseInsensitiveLess {
|
||||
bool operator() (const std::string&, const std::string&) const;
|
||||
};
|
||||
|
||||
/// Our options container is actually a std::map
|
||||
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
|
||||
|
||||
/// Option class implements an option as defined by UCI protocol
|
||||
class Option {
|
||||
|
||||
typedef void (Fn)(const Option&);
|
||||
|
||||
public:
|
||||
UCIOption(Fn* = NULL);
|
||||
UCIOption(bool v, Fn* = NULL);
|
||||
UCIOption(const char* v, Fn* = NULL);
|
||||
UCIOption(int v, int min, int max, Fn* = NULL);
|
||||
Option(Fn* = NULL);
|
||||
Option(bool v, Fn* = NULL);
|
||||
Option(const char* v, Fn* = NULL);
|
||||
Option(int v, int min, int max, Fn* = NULL);
|
||||
|
||||
void operator=(const std::string& v);
|
||||
|
||||
operator int() const {
|
||||
assert(type == "check" || type == "spin");
|
||||
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
|
||||
}
|
||||
|
||||
operator std::string() const {
|
||||
assert(type == "string");
|
||||
return currentValue;
|
||||
}
|
||||
Option& operator=(const std::string& v);
|
||||
operator int() const;
|
||||
operator std::string() const;
|
||||
|
||||
private:
|
||||
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
|
||||
@@ -59,19 +59,11 @@ private:
|
||||
Fn* on_change;
|
||||
};
|
||||
|
||||
void init(OptionsMap&);
|
||||
void loop(const std::string&);
|
||||
|
||||
/// Custom comparator because UCI options should be case insensitive
|
||||
struct CaseInsensitiveLess {
|
||||
bool operator() (const std::string&, const std::string&) const;
|
||||
};
|
||||
} // namespace UCI
|
||||
|
||||
|
||||
/// Our options container is actually a map with a customized c'tor
|
||||
struct OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
|
||||
OptionsMap();
|
||||
};
|
||||
|
||||
extern std::ostream& operator<<(std::ostream&, const OptionsMap&);
|
||||
extern OptionsMap Options;
|
||||
extern UCI::OptionsMap Options;
|
||||
|
||||
#endif // !defined(UCIOPTION_H_INCLUDED)
|
||||
|
||||
Reference in New Issue
Block a user