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_MODULE := stockfish
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
evaluate.cpp move.cpp search.cpp \
|
evaluate.cpp notation.cpp search.cpp \
|
||||||
benchmark.cpp movegen.cpp tt.cpp \
|
benchmark.cpp movegen.cpp tt.cpp \
|
||||||
bitbase.cpp main.cpp movepick.cpp uci.cpp \
|
bitbase.cpp main.cpp movepick.cpp uci.cpp \
|
||||||
bitboard.cpp pawns.cpp ucioption.cpp \
|
bitboard.cpp pawns.cpp ucioption.cpp \
|
||||||
|
|||||||
@@ -110,7 +110,8 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t nodes = 0;
|
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++)
|
for (size_t i = 0; i < fens.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -120,22 +121,22 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
|
|
||||||
if (limitType == "perft")
|
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;
|
cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl;
|
||||||
nodes += cnt;
|
nodes += cnt;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Threads.start_searching(pos, limits, vector<Move>());
|
Threads.start_searching(pos, limits, vector<Move>(), st);
|
||||||
Threads.wait_for_search_finished();
|
Threads.wait_for_search_finished();
|
||||||
nodes += Search::RootPosition.nodes_searched();
|
nodes += Search::RootPosition.nodes_searched();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int e = time.elapsed();
|
elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero'
|
||||||
|
|
||||||
cerr << "\n==========================="
|
cerr << "\n==========================="
|
||||||
<< "\nTotal time (ms) : " << e
|
<< "\nTotal time (ms) : " << elapsed
|
||||||
<< "\nNodes searched : " << nodes
|
<< "\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);
|
int idx = index(wksq, bksq, wpsq, stm);
|
||||||
return KPKBitbase[idx / 32] & (1 << (idx & 31));
|
return KPKBitbase[idx / 32] & (1 << (idx & 31));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void kpk_bitbase_init() {
|
void Bitbases::init_kpk() {
|
||||||
|
|
||||||
Result db[IndexMax];
|
Result db[IndexMax];
|
||||||
KPKPosition pos;
|
KPKPosition pos;
|
||||||
@@ -117,7 +117,7 @@ namespace {
|
|||||||
stm = Color(idx & 1);
|
stm = Color(idx & 1);
|
||||||
bksq = Square((idx >> 1) & 63);
|
bksq = Square((idx >> 1) & 63);
|
||||||
wksq = Square((idx >> 7) & 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) {
|
Result KPKPosition::classify_leaf(int idx) {
|
||||||
@@ -196,8 +196,8 @@ namespace {
|
|||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
{
|
{
|
||||||
r |= Us == WHITE ? db[index(pop_1st_bit(&b), bksq, psq, BLACK)]
|
r |= Us == WHITE ? db[index(pop_lsb(&b), bksq, psq, BLACK)]
|
||||||
: db[index(wksq, pop_1st_bit(&b), psq, WHITE)];
|
: db[index(wksq, pop_lsb(&b), psq, WHITE)];
|
||||||
|
|
||||||
if (Us == WHITE && (r & WIN))
|
if (Us == WHITE && (r & WIN))
|
||||||
return WIN;
|
return WIN;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "bitboard.h"
|
#include "bitboard.h"
|
||||||
#include "bitcount.h"
|
#include "bitcount.h"
|
||||||
|
#include "misc.h"
|
||||||
#include "rkiss.h"
|
#include "rkiss.h"
|
||||||
|
|
||||||
CACHE_LINE_ALIGNMENT
|
CACHE_LINE_ALIGNMENT
|
||||||
@@ -45,22 +46,27 @@ Bitboard ThisAndAdjacentFilesBB[8];
|
|||||||
Bitboard InFrontBB[2][8];
|
Bitboard InFrontBB[2][8];
|
||||||
Bitboard StepAttacksBB[16][64];
|
Bitboard StepAttacksBB[16][64];
|
||||||
Bitboard BetweenBB[64][64];
|
Bitboard BetweenBB[64][64];
|
||||||
|
Bitboard DistanceRingsBB[64][8];
|
||||||
Bitboard ForwardBB[2][64];
|
Bitboard ForwardBB[2][64];
|
||||||
Bitboard PassedPawnMask[2][64];
|
Bitboard PassedPawnMask[2][64];
|
||||||
Bitboard AttackSpanMask[2][64];
|
Bitboard AttackSpanMask[2][64];
|
||||||
Bitboard PseudoAttacks[6][64];
|
Bitboard PseudoAttacks[6][64];
|
||||||
|
|
||||||
uint8_t BitCount8Bit[256];
|
|
||||||
int SquareDistance[64][64];
|
int SquareDistance[64][64];
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
|
||||||
|
const uint64_t DeBruijn_64 = 0x218A392CD3D5DBFULL;
|
||||||
|
const uint32_t DeBruijn_32 = 0x783A9B23;
|
||||||
|
|
||||||
CACHE_LINE_ALIGNMENT
|
CACHE_LINE_ALIGNMENT
|
||||||
|
|
||||||
int BSFTable[64];
|
int BSFTable[64];
|
||||||
int MS1BTable[256];
|
int MS1BTable[256];
|
||||||
Bitboard RTable[0x19000]; // Storage space for rook attacks
|
Bitboard RTable[0x19000]; // Storage space for rook attacks
|
||||||
Bitboard BTable[0x1480]; // Storage space for bishop attacks
|
Bitboard BTable[0x1480]; // Storage space for bishop attacks
|
||||||
|
uint8_t BitCount8Bit[256];
|
||||||
|
|
||||||
typedef unsigned (Fn)(Square, Bitboard);
|
typedef unsigned (Fn)(Square, Bitboard);
|
||||||
|
|
||||||
@@ -68,40 +74,35 @@ namespace {
|
|||||||
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index);
|
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
|
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
|
||||||
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
|
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
|
||||||
/// nonzero bitboard.
|
|
||||||
|
|
||||||
#if defined(IS_64BIT) && !defined(USE_BSFQ)
|
#if !defined(USE_BSFQ)
|
||||||
|
|
||||||
Square first_1(Bitboard b) {
|
Square lsb(Bitboard b) {
|
||||||
return Square(BSFTable[((b & -b) * 0x218A392CD3D5DBFULL) >> 58]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Square pop_1st_bit(Bitboard* b) {
|
if (Is64Bit)
|
||||||
Bitboard bb = *b;
|
return Square(BSFTable[((b & -b) * DeBruijn_64) >> 58]);
|
||||||
*b &= (*b - 1);
|
|
||||||
return Square(BSFTable[((bb & -bb) * 0x218A392CD3D5DBFULL) >> 58]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif !defined(USE_BSFQ)
|
|
||||||
|
|
||||||
Square first_1(Bitboard b) {
|
|
||||||
b ^= (b - 1);
|
b ^= (b - 1);
|
||||||
uint32_t fold = unsigned(b) ^ unsigned(b >> 32);
|
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;
|
Bitboard bb = *b;
|
||||||
*b = bb & (bb - 1);
|
*b = bb & (bb - 1);
|
||||||
|
|
||||||
|
if (Is64Bit)
|
||||||
|
return Square(BSFTable[((bb & -bb) * DeBruijn_64) >> 58]);
|
||||||
|
|
||||||
bb ^= (bb - 1);
|
bb ^= (bb - 1);
|
||||||
uint32_t fold = unsigned(bb) ^ unsigned(bb >> 32);
|
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;
|
unsigned b32;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
@@ -137,16 +138,18 @@ Square last_1(Bitboard b) {
|
|||||||
|
|
||||||
void Bitboards::print(Bitboard b) {
|
void Bitboards::print(Bitboard b) {
|
||||||
|
|
||||||
|
sync_cout;
|
||||||
|
|
||||||
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
|
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
|
||||||
{
|
{
|
||||||
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
|
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
|
||||||
|
|
||||||
for (File file = FILE_A; file <= FILE_H; file++)
|
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 << "|\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++)
|
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
||||||
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
|
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
|
||||||
|
|
||||||
|
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++)
|
for (int i = 0; i < 64; i++)
|
||||||
if (!Is64Bit) // Matt Taylor's folding trick for 32 bit systems
|
if (!Is64Bit) // Matt Taylor's folding trick for 32 bit systems
|
||||||
{
|
{
|
||||||
Bitboard b = 1ULL << i;
|
Bitboard b = 1ULL << i;
|
||||||
b ^= b - 1;
|
b ^= b - 1;
|
||||||
b ^= b >> 32;
|
b ^= b >> 32;
|
||||||
BSFTable[(uint32_t)(b * 0x783A9B23) >> 26] = i;
|
BSFTable[(uint32_t)(b * DeBruijn_32) >> 26] = i;
|
||||||
}
|
}
|
||||||
else
|
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 },
|
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
|
||||||
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
|
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
|
||||||
@@ -265,25 +274,17 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Bitboard pick_random(Bitboard mask, RKISS& rk, int booster) {
|
Bitboard pick_random(RKISS& rk, int booster) {
|
||||||
|
|
||||||
Bitboard magic;
|
|
||||||
|
|
||||||
// Values s1 and s2 are used to rotate the candidate magic of a
|
// Values s1 and s2 are used to rotate the candidate magic of a
|
||||||
// quantity known to be the optimal to quickly find the magics.
|
// quantity known to be the optimal to quickly find the magics.
|
||||||
int s1 = booster & 63, s2 = (booster >> 6) & 63;
|
int s1 = booster & 63, s2 = (booster >> 6) & 63;
|
||||||
|
|
||||||
while (true)
|
Bitboard m = rk.rand<Bitboard>();
|
||||||
{
|
m = (m >> s1) | (m << (64 - s1));
|
||||||
magic = rk.rand<Bitboard>();
|
m &= rk.rand<Bitboard>();
|
||||||
magic = (magic >> s1) | (magic << (64 - s1));
|
m = (m >> s2) | (m << (64 - s2));
|
||||||
magic &= rk.rand<Bitboard>();
|
return m & rk.rand<Bitboard>();
|
||||||
magic = (magic >> s2) | (magic << (64 - s2));
|
|
||||||
magic &= rk.rand<Bitboard>();
|
|
||||||
|
|
||||||
if (BitCount8Bit[(mask * magic) >> 56] >= 6)
|
|
||||||
return magic;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -336,7 +337,9 @@ namespace {
|
|||||||
// Find a magic for square 's' picking up an (almost) random number
|
// Find a magic for square 's' picking up an (almost) random number
|
||||||
// until we find the one that passes the verification test.
|
// until we find the one that passes the verification test.
|
||||||
do {
|
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));
|
memset(attacks[s], 0, size * sizeof(Bitboard));
|
||||||
|
|
||||||
// A good magic must map every possible occupancy to an index that
|
// A good magic must map every possible occupancy to an index that
|
||||||
@@ -350,6 +353,8 @@ namespace {
|
|||||||
if (attack && attack != reference[i])
|
if (attack && attack != reference[i])
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
assert(reference[i] != 0);
|
||||||
|
|
||||||
attack = reference[i];
|
attack = reference[i];
|
||||||
}
|
}
|
||||||
} while (i != size);
|
} while (i != size);
|
||||||
|
|||||||
@@ -25,8 +25,15 @@
|
|||||||
|
|
||||||
namespace Bitboards {
|
namespace Bitboards {
|
||||||
|
|
||||||
extern void init();
|
void init();
|
||||||
extern void print(Bitboard b);
|
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 InFrontBB[2][8];
|
||||||
extern Bitboard StepAttacksBB[16][64];
|
extern Bitboard StepAttacksBB[16][64];
|
||||||
extern Bitboard BetweenBB[64][64];
|
extern Bitboard BetweenBB[64][64];
|
||||||
|
extern Bitboard DistanceRingsBB[64][8];
|
||||||
extern Bitboard ForwardBB[2][64];
|
extern Bitboard ForwardBB[2][64];
|
||||||
extern Bitboard PassedPawnMask[2][64];
|
extern Bitboard PassedPawnMask[2][64];
|
||||||
extern Bitboard AttackSpanMask[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.
|
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
|
||||||
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
|
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
|
||||||
/// nonzero bitboard.
|
|
||||||
|
|
||||||
#if defined(USE_BSFQ)
|
#if defined(USE_BSFQ)
|
||||||
|
|
||||||
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
|
||||||
|
|
||||||
FORCE_INLINE Square first_1(Bitboard b) {
|
FORCE_INLINE Square lsb(Bitboard b) {
|
||||||
unsigned long index;
|
unsigned long index;
|
||||||
_BitScanForward64(&index, b);
|
_BitScanForward64(&index, b);
|
||||||
return (Square) index;
|
return (Square) index;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE Square last_1(Bitboard b) {
|
FORCE_INLINE Square msb(Bitboard b) {
|
||||||
unsigned long index;
|
unsigned long index;
|
||||||
_BitScanReverse64(&index, b);
|
_BitScanReverse64(&index, b);
|
||||||
return (Square) index;
|
return (Square) index;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
|
|
||||||
FORCE_INLINE Square first_1(Bitboard b) { // Assembly code by Heinz van Saanen
|
# else
|
||||||
Bitboard dummy;
|
|
||||||
__asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) );
|
FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
|
||||||
return (Square) dummy;
|
Bitboard index;
|
||||||
|
__asm__("bsfq %1, %0": "=r"(index): "rm"(b) );
|
||||||
|
return (Square) index;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE Square last_1(Bitboard b) {
|
FORCE_INLINE Square msb(Bitboard b) {
|
||||||
Bitboard dummy;
|
Bitboard index;
|
||||||
__asm__("bsrq %1, %0": "=r"(dummy): "rm"(b) );
|
__asm__("bsrq %1, %0": "=r"(index): "rm"(b) );
|
||||||
return (Square) dummy;
|
return (Square) index;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
FORCE_INLINE Square pop_1st_bit(Bitboard* b) {
|
# endif
|
||||||
const Square s = first_1(*b);
|
|
||||||
*b &= ~(1ULL<<s);
|
FORCE_INLINE Square pop_lsb(Bitboard* b) {
|
||||||
|
const Square s = lsb(*b);
|
||||||
|
*b &= ~(1ULL << s);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // if !defined(USE_BSFQ)
|
#else // if !defined(USE_BSFQ)
|
||||||
|
|
||||||
extern Square first_1(Bitboard b);
|
extern Square msb(Bitboard b);
|
||||||
extern Square last_1(Bitboard b);
|
extern Square lsb(Bitboard b);
|
||||||
extern Square pop_1st_bit(Bitboard* b);
|
extern Square pop_lsb(Bitboard* b);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
|
|||||||
#if !defined(USE_POPCNT)
|
#if !defined(USE_POPCNT)
|
||||||
|
|
||||||
assert(false);
|
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)
|
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
|
||||||
|
|
||||||
|
|||||||
@@ -301,10 +301,10 @@ namespace {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Offsets to the PolyGlotRandoms[] array of zobrist keys
|
// Offsets to the PolyGlotRandoms[] array of zobrist keys
|
||||||
const Key* ZobPiece = PolyGlotRandoms + 0;
|
const Key* ZobPiece = PolyGlotRandoms;
|
||||||
const Key* ZobCastle = PolyGlotRandoms + 768;
|
const Key* ZobCastle = ZobPiece + 12 * 64; // Pieces * squares
|
||||||
const Key* ZobEnPassant = PolyGlotRandoms + 772;
|
const Key* ZobEnPassant = ZobCastle + 4; // Castle flags
|
||||||
const Key* ZobTurn = PolyGlotRandoms + 780;
|
const Key* ZobTurn = ZobEnPassant + 8; // Number of files
|
||||||
|
|
||||||
// book_key() returns the PolyGlot hash key of the given position
|
// book_key() returns the PolyGlot hash key of the given position
|
||||||
uint64_t book_key(const Position& pos) {
|
uint64_t book_key(const Position& pos) {
|
||||||
@@ -314,18 +314,18 @@ namespace {
|
|||||||
|
|
||||||
while (b)
|
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
|
// 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);
|
Piece p = pos.piece_on(s);
|
||||||
int polyPiece = 2 * (type_of(p) - 1) + (color_of(p) == WHITE);
|
int pieceOfs = 2 * (type_of(p) - 1) + (color_of(p) == WHITE);
|
||||||
key ^= ZobPiece[64 * polyPiece + s];
|
key ^= ZobPiece[64 * pieceOfs + s];
|
||||||
}
|
}
|
||||||
|
|
||||||
b = pos.can_castle(ALL_CASTLES);
|
b = pos.can_castle(ALL_CASTLES);
|
||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
key ^= ZobCastle[pop_1st_bit(&b)];
|
key ^= ZobCastle[pop_lsb(&b)];
|
||||||
|
|
||||||
if (pos.ep_square() != SQ_NONE)
|
if (pos.ep_square() != SQ_NONE)
|
||||||
key ^= ZobEnPassant[file_of(pos.ep_square())];
|
key ^= ZobEnPassant[file_of(pos.ep_square())];
|
||||||
@@ -338,9 +338,9 @@ namespace {
|
|||||||
|
|
||||||
} // 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
|
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) {
|
bool Book::open(const char* fName) {
|
||||||
|
|
||||||
fileName = "";
|
|
||||||
|
|
||||||
if (is_open()) // Cannot close an already closed file
|
if (is_open()) // Cannot close an already closed file
|
||||||
close();
|
close();
|
||||||
|
|
||||||
ifstream::open(fName, ifstream::in | ifstream::binary | ios::ate);
|
ifstream::open(fName, ifstream::in | ifstream::binary);
|
||||||
|
|
||||||
if (!is_open())
|
fileName = is_open() ? fName : "";
|
||||||
{
|
ifstream::clear(); // Reset any error flag to allow retry ifstream::open()
|
||||||
clear();
|
return !fileName.empty();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -403,16 +387,16 @@ bool Book::open(const char* fName) {
|
|||||||
|
|
||||||
Move Book::probe(const Position& pos, const string& fName, bool pickBest) {
|
Move Book::probe(const Position& pos, const string& fName, bool pickBest) {
|
||||||
|
|
||||||
|
if (fileName != fName && !open(fName.c_str()))
|
||||||
|
return MOVE_NONE;
|
||||||
|
|
||||||
BookEntry e;
|
BookEntry e;
|
||||||
uint16_t best = 0;
|
uint16_t best = 0;
|
||||||
unsigned sum = 0;
|
unsigned sum = 0;
|
||||||
Move move = MOVE_NONE;
|
Move move = MOVE_NONE;
|
||||||
uint64_t key = book_key(pos);
|
uint64_t key = book_key(pos);
|
||||||
|
|
||||||
if (fileName != fName && !open(fName.c_str()))
|
seekg(find_first(key) * sizeof(BookEntry), ios_base::beg);
|
||||||
return MOVE_NONE;
|
|
||||||
|
|
||||||
binary_search(key);
|
|
||||||
|
|
||||||
while (*this >> e, e.key == key && good())
|
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.
|
// the special Move's flags (bit 14-15) that are not supported by PolyGlot.
|
||||||
int pt = (move >> 12) & 7;
|
int pt = (move >> 12) & 7;
|
||||||
if (pt)
|
if (pt)
|
||||||
move = make_promotion(from_sq(move), to_sq(move), PieceType(pt + 1));
|
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
|
||||||
|
|
||||||
// Add 'special move' flags and verify it is legal
|
// 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))
|
if (move == (ml.move() & 0x3FFF))
|
||||||
return ml.move();
|
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
|
/// Book::find_first() 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
|
/// through the book file for the given key. Returns the index of the leftmost
|
||||||
/// to the leftmost book entry with the same key as the input.
|
/// 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;
|
BookEntry e;
|
||||||
|
|
||||||
low = 0;
|
|
||||||
high = size - 1;
|
|
||||||
|
|
||||||
assert(low <= high);
|
assert(low <= high);
|
||||||
|
|
||||||
while (low < high && good())
|
while (low < high && good())
|
||||||
@@ -484,5 +467,5 @@ void Book::binary_search(uint64_t key) {
|
|||||||
|
|
||||||
assert(low == high);
|
assert(low == high);
|
||||||
|
|
||||||
seekg(low * sizeof(BookEntry), ios_base::beg);
|
return low;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,11 +48,10 @@ private:
|
|||||||
template<typename T> Book& operator>>(T& n);
|
template<typename T> Book& operator>>(T& n);
|
||||||
|
|
||||||
bool open(const char* fName);
|
bool open(const char* fName);
|
||||||
void binary_search(uint64_t key);
|
size_t find_first(uint64_t key);
|
||||||
|
|
||||||
RKISS RKiss;
|
RKISS RKiss;
|
||||||
std::string fileName;
|
std::string fileName;
|
||||||
size_t size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // !defined(BOOK_H_INCLUDED)
|
#endif // !defined(BOOK_H_INCLUDED)
|
||||||
|
|||||||
@@ -20,14 +20,13 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "bitboard.h"
|
||||||
#include "bitcount.h"
|
#include "bitcount.h"
|
||||||
#include "endgame.h"
|
#include "endgame.h"
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm);
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Table used to drive the defending king towards the edge of the board
|
// 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
|
// Stalemate detection with lone king
|
||||||
if ( pos.side_to_move() == weakerSide
|
if ( pos.side_to_move() == weakerSide
|
||||||
&& !pos.in_check()
|
&& !pos.in_check()
|
||||||
&& !MoveList<MV_LEGAL>(pos).size()) {
|
&& !MoveList<LEGAL>(pos).size()) {
|
||||||
return VALUE_DRAW;
|
return VALUE_DRAW;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +141,7 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
|
|||||||
Square loserKSq = pos.king_square(weakerSide);
|
Square loserKSq = pos.king_square(weakerSide);
|
||||||
|
|
||||||
Value result = pos.non_pawn_material(strongerSide)
|
Value result = pos.non_pawn_material(strongerSide)
|
||||||
+ pos.piece_count(strongerSide, PAWN) * PawnValueEndgame
|
+ pos.piece_count(strongerSide, PAWN) * PawnValueEg
|
||||||
+ MateTable[loserKSq]
|
+ MateTable[loserKSq]
|
||||||
+ DistanceBonus[square_distance(winnerKSq, 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.non_pawn_material(weakerSide) == VALUE_ZERO);
|
||||||
assert(pos.piece_count(weakerSide, PAWN) == 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, BISHOP) == 1);
|
||||||
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
|
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
|
||||||
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
||||||
@@ -223,12 +222,10 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
|
|||||||
wpsq = mirror(wpsq);
|
wpsq = mirror(wpsq);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm))
|
if (!Bitbases::probe_kpk(wksq, wpsq, bksq, stm))
|
||||||
return VALUE_DRAW;
|
return VALUE_DRAW;
|
||||||
|
|
||||||
Value result = VALUE_KNOWN_WIN
|
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq));
|
||||||
+ PawnValueEndgame
|
|
||||||
+ Value(rank_of(wpsq));
|
|
||||||
|
|
||||||
return strongerSide == pos.side_to_move() ? result : -result;
|
return strongerSide == pos.side_to_move() ? result : -result;
|
||||||
}
|
}
|
||||||
@@ -241,7 +238,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
Value Endgame<KRKP>::operator()(const Position& pos) const {
|
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.piece_count(strongerSide, PAWN) == 0);
|
||||||
assert(pos.non_pawn_material(weakerSide) == 0);
|
assert(pos.non_pawn_material(weakerSide) == 0);
|
||||||
assert(pos.piece_count(weakerSide, PAWN) == 1);
|
assert(pos.piece_count(weakerSide, PAWN) == 1);
|
||||||
@@ -262,18 +259,18 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
|
|||||||
bpsq = ~bpsq;
|
bpsq = ~bpsq;
|
||||||
}
|
}
|
||||||
|
|
||||||
Square queeningSq = make_square(file_of(bpsq), RANK_1);
|
Square queeningSq = file_of(bpsq) | RANK_1;
|
||||||
Value result;
|
Value result;
|
||||||
|
|
||||||
// If the stronger side's king is in front of the pawn, it's a win
|
// 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))
|
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,
|
// If the weaker side's king is too far from the pawn and the rook,
|
||||||
// it's a win
|
// it's a win
|
||||||
else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3
|
else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3
|
||||||
&& square_distance(bksq, wrsq) >= 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,
|
// If the pawn is far advanced and supported by the defending king,
|
||||||
// the position is drawish
|
// the position is drawish
|
||||||
@@ -298,9 +295,9 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
Value Endgame<KRKB>::operator()(const Position& pos) const {
|
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.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, PAWN) == 0);
|
||||||
assert(pos.piece_count(weakerSide, BISHOP) == 1);
|
assert(pos.piece_count(weakerSide, BISHOP) == 1);
|
||||||
|
|
||||||
@@ -314,9 +311,9 @@ Value Endgame<KRKB>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
Value Endgame<KRKN>::operator()(const Position& pos) const {
|
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.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, PAWN) == 0);
|
||||||
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
|
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
|
||||||
|
|
||||||
@@ -337,16 +334,16 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
Value Endgame<KQKR>::operator()(const Position& pos) const {
|
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.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);
|
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||||
|
|
||||||
Square winnerKSq = pos.king_square(strongerSide);
|
Square winnerKSq = pos.king_square(strongerSide);
|
||||||
Square loserKSq = pos.king_square(weakerSide);
|
Square loserKSq = pos.king_square(weakerSide);
|
||||||
|
|
||||||
Value result = QueenValueEndgame
|
Value result = QueenValueEg
|
||||||
- RookValueEndgame
|
- RookValueEg
|
||||||
+ MateTable[loserKSq]
|
+ MateTable[loserKSq]
|
||||||
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
|
+ DistanceBonus[square_distance(winnerKSq, loserKSq)];
|
||||||
|
|
||||||
@@ -357,12 +354,12 @@ template<>
|
|||||||
Value Endgame<KBBKN>::operator()(const Position& pos) const {
|
Value Endgame<KBBKN>::operator()(const Position& pos) const {
|
||||||
|
|
||||||
assert(pos.piece_count(strongerSide, BISHOP) == 2);
|
assert(pos.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.piece_count(weakerSide, KNIGHT) == 1);
|
||||||
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
|
assert(pos.non_pawn_material(weakerSide) == KnightValueMg);
|
||||||
assert(!pos.pieces(PAWN));
|
assert(!pos.pieces(PAWN));
|
||||||
|
|
||||||
Value result = BishopValueEndgame;
|
Value result = BishopValueEg;
|
||||||
Square wksq = pos.king_square(strongerSide);
|
Square wksq = pos.king_square(strongerSide);
|
||||||
Square bksq = pos.king_square(weakerSide);
|
Square bksq = pos.king_square(weakerSide);
|
||||||
Square nsq = pos.piece_list(weakerSide, KNIGHT)[0];
|
Square nsq = pos.piece_list(weakerSide, KNIGHT)[0];
|
||||||
@@ -399,7 +396,7 @@ Value Endgame<KNNK>::operator()(const Position&) const {
|
|||||||
template<>
|
template<>
|
||||||
ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
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, BISHOP) == 1);
|
||||||
assert(pos.piece_count(strongerSide, PAWN) >= 1);
|
assert(pos.piece_count(strongerSide, PAWN) >= 1);
|
||||||
|
|
||||||
@@ -414,7 +411,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
|||||||
&& !(pawns & ~file_bb(pawnFile)))
|
&& !(pawns & ~file_bb(pawnFile)))
|
||||||
{
|
{
|
||||||
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
|
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);
|
Square kingSq = pos.king_square(weakerSide);
|
||||||
|
|
||||||
if ( opposite_colors(queeningSq, bishopSq)
|
if ( opposite_colors(queeningSq, bishopSq)
|
||||||
@@ -451,7 +448,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
|
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, QUEEN) == 1);
|
||||||
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
assert(pos.piece_count(strongerSide, PAWN) == 0);
|
||||||
assert(pos.piece_count(weakerSide, ROOK) == 1);
|
assert(pos.piece_count(weakerSide, ROOK) == 1);
|
||||||
@@ -481,9 +478,9 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
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.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);
|
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||||
|
|
||||||
Square wksq = pos.king_square(strongerSide);
|
Square wksq = pos.king_square(strongerSide);
|
||||||
@@ -513,7 +510,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
|
|||||||
|
|
||||||
File f = file_of(wpsq);
|
File f = file_of(wpsq);
|
||||||
Rank r = rank_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);
|
int tempo = (pos.side_to_move() == strongerSide);
|
||||||
|
|
||||||
// If the pawn is not too far advanced and the defending king defends the
|
// 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<>
|
template<>
|
||||||
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
|
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.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);
|
assert(pos.piece_count(weakerSide, PAWN) == 1);
|
||||||
|
|
||||||
Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0];
|
Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0];
|
||||||
@@ -674,10 +671,10 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
|
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, BISHOP) == 1);
|
||||||
assert(pos.piece_count(strongerSide, PAWN) == 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, BISHOP) == 1);
|
||||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||||
|
|
||||||
@@ -729,10 +726,10 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
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, BISHOP) == 1);
|
||||||
assert(pos.piece_count(strongerSide, PAWN) == 2);
|
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, BISHOP) == 1);
|
||||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
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))
|
if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2))
|
||||||
{
|
{
|
||||||
blockSq1 = psq1 + pawn_push(strongerSide);
|
blockSq1 = psq1 + pawn_push(strongerSide);
|
||||||
blockSq2 = make_square(file_of(psq2), rank_of(psq1));
|
blockSq2 = file_of(psq2) | rank_of(psq1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
blockSq1 = psq2 + pawn_push(strongerSide);
|
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))
|
switch (file_distance(psq1, psq2))
|
||||||
@@ -804,10 +801,10 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
|
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, BISHOP) == 1);
|
||||||
assert(pos.piece_count(strongerSide, PAWN) == 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, KNIGHT) == 1);
|
||||||
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
assert(pos.piece_count(weakerSide, PAWN) == 0);
|
||||||
|
|
||||||
@@ -831,7 +828,7 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
|
|||||||
template<>
|
template<>
|
||||||
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
|
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, KNIGHT) == 1);
|
||||||
assert(pos.piece_count(strongerSide, PAWN) == 1);
|
assert(pos.piece_count(strongerSide, PAWN) == 1);
|
||||||
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
|
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,
|
// 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.
|
// 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)
|
&& sf == SCALE_FACTOR_NORMAL)
|
||||||
{
|
{
|
||||||
// Only the two bishops ?
|
// Only the two bishops ?
|
||||||
if ( pos.non_pawn_material(WHITE) == BishopValueMidgame
|
if ( pos.non_pawn_material(WHITE) == BishopValueMg
|
||||||
&& pos.non_pawn_material(BLACK) == BishopValueMidgame)
|
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||||
{
|
{
|
||||||
// Check for KBP vs KB with only a single pawn that is almost
|
// Check for KBP vs KB with only a single pawn that is almost
|
||||||
// certainly a draw or at least two pawns.
|
// 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
|
// Init king safety tables only if we are going to use them
|
||||||
if ( pos.piece_count(Us, QUEEN)
|
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));
|
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
|
||||||
b &= ei.attackedBy[Us][PAWN];
|
b &= ei.attackedBy[Us][PAWN];
|
||||||
@@ -582,7 +582,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
assert(b);
|
assert(b);
|
||||||
|
|
||||||
if (!more_than_one(b) && (b & pos.pieces(Them)))
|
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
|
// 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;
|
return SCORE_ZERO;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
Square s = pop_1st_bit(&b);
|
Square s = pop_lsb(&b);
|
||||||
|
|
||||||
assert(pos.pawn_is_passed(Us, s));
|
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.
|
// value if the other side has a rook or queen.
|
||||||
if (file_of(s) == FILE_A || file_of(s) == FILE_H)
|
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;
|
ebonus += ebonus / 4;
|
||||||
else if (pos.pieces(Them, ROOK, QUEEN))
|
else if (pos.pieces(Them, ROOK, QUEEN))
|
||||||
ebonus -= ebonus / 4;
|
ebonus -= ebonus / 4;
|
||||||
@@ -976,8 +976,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
{
|
{
|
||||||
s = pop_1st_bit(&b);
|
s = pop_lsb(&b);
|
||||||
queeningSquare = relative_square(c, make_square(file_of(s), RANK_8));
|
queeningSquare = relative_square(c, file_of(s) | RANK_8);
|
||||||
queeningPath = forward_bb(c, s);
|
queeningPath = forward_bb(c, s);
|
||||||
|
|
||||||
// Compute plies to queening and check direct advancement
|
// Compute plies to queening and check direct advancement
|
||||||
@@ -1017,10 +1017,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
{
|
{
|
||||||
s = pop_1st_bit(&b);
|
s = pop_lsb(&b);
|
||||||
|
|
||||||
// Compute plies from queening
|
// 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);
|
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
|
||||||
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
|
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
|
||||||
|
|
||||||
@@ -1039,12 +1039,12 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
{
|
{
|
||||||
s = pop_1st_bit(&b);
|
s = pop_lsb(&b);
|
||||||
sacptg = blockersCount = 0;
|
sacptg = blockersCount = 0;
|
||||||
minKingDist = kingptg = 256;
|
minKingDist = kingptg = 256;
|
||||||
|
|
||||||
// Compute plies from queening
|
// 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);
|
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
|
||||||
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
|
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?
|
// How many plies does it take to remove all the blocking pawns?
|
||||||
while (blockers)
|
while (blockers)
|
||||||
{
|
{
|
||||||
blockSq = pop_1st_bit(&blockers);
|
blockSq = pop_lsb(&blockers);
|
||||||
movesToGo = 256;
|
movesToGo = 256;
|
||||||
|
|
||||||
// Check pawns that can give support to overcome obstacle, for instance
|
// 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)
|
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);
|
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)
|
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);
|
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 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.
|
// 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) {
|
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 "tt.h"
|
||||||
#include "ucioption.h"
|
#include "ucioption.h"
|
||||||
|
|
||||||
extern void uci_loop(const std::string&);
|
|
||||||
extern void kpk_bitbase_init();
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
std::cout << engine_info() << std::endl;
|
std::cout << engine_info() << std::endl;
|
||||||
|
|
||||||
|
UCI::init(Options);
|
||||||
Bitboards::init();
|
Bitboards::init();
|
||||||
Position::init();
|
Zobrist::init();
|
||||||
kpk_bitbase_init();
|
Bitbases::init_kpk();
|
||||||
Search::init();
|
Search::init();
|
||||||
Threads.init();
|
|
||||||
Eval::init();
|
Eval::init();
|
||||||
|
Threads.init();
|
||||||
TT.set_size(Options["Hash"]);
|
TT.set_size(Options["Hash"]);
|
||||||
|
|
||||||
std::string args;
|
std::string args;
|
||||||
@@ -48,5 +46,7 @@ int main(int argc, char* argv[]) {
|
|||||||
for (int i = 1; i < argc; i++)
|
for (int i = 1; i < argc; i++)
|
||||||
args += std::string(argv[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);
|
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||||
return pos.non_pawn_material(Them) == VALUE_ZERO
|
return pos.non_pawn_material(Them) == VALUE_ZERO
|
||||||
&& pos.piece_count(Them, PAWN) == 0
|
&& 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) {
|
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, BISHOP) == 1
|
||||||
&& pos.piece_count(Us, PAWN) >= 1;
|
&& pos.piece_count(Us, PAWN) >= 1;
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ namespace {
|
|||||||
template<Color Us> bool is_KQKRPs(const Position& pos) {
|
template<Color Us> bool is_KQKRPs(const Position& pos) {
|
||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||||
return pos.piece_count(Us, PAWN) == 0
|
return pos.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(Us, QUEEN) == 1
|
||||||
&& pos.piece_count(Them, ROOK) == 1
|
&& pos.piece_count(Them, ROOK) == 1
|
||||||
&& pos.piece_count(Them, PAWN) >= 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
|
// 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)
|
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)
|
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
|
// 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)
|
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP)
|
||||||
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, 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
|
/// Version number. If Version is left empty, then Tag plus current
|
||||||
/// date (in the format YYMMDD) is used as a version number.
|
/// 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 = "";
|
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
|
/// Debug functions used mainly to collect run-time statistics
|
||||||
|
|
||||||
static uint64_t hits[2], means[2];
|
static uint64_t hits[2], means[2];
|
||||||
@@ -94,33 +101,33 @@ void dbg_print() {
|
|||||||
/// usual i/o functionality and without changing a single line of code!
|
/// usual i/o functionality and without changing a single line of code!
|
||||||
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
|
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
|
||||||
|
|
||||||
|
struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout
|
||||||
|
|
||||||
|
Tie(streambuf* b, ofstream* f) : buf(b), file(f) {}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
return last = file->rdbuf()->sputc((char)c);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
|
|
||||||
Logger() : in(cin.rdbuf(), file), out(cout.rdbuf(), file) {}
|
Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {}
|
||||||
~Logger() { start(false); }
|
~Logger() { start(false); }
|
||||||
|
|
||||||
struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout
|
|
||||||
|
|
||||||
Tie(streambuf* b, ofstream& f) : buf(b), file(f) {}
|
|
||||||
|
|
||||||
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(), ">> "); }
|
|
||||||
|
|
||||||
int log(int c, const char* prefix) {
|
|
||||||
|
|
||||||
static int last = '\n';
|
|
||||||
|
|
||||||
if (last == '\n')
|
|
||||||
file.rdbuf()->sputn(prefix, 3);
|
|
||||||
|
|
||||||
return last = file.rdbuf()->sputc((char)c);
|
|
||||||
}
|
|
||||||
|
|
||||||
streambuf* buf;
|
|
||||||
ofstream& file;
|
|
||||||
};
|
|
||||||
|
|
||||||
ofstream file;
|
ofstream file;
|
||||||
Tie in, out;
|
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
|
/// Trampoline helper to avoid moving Logger to misc.h
|
||||||
void start_logger(bool b) { Logger::start(b); }
|
void start_logger(bool b) { Logger::start(b); }
|
||||||
|
|
||||||
@@ -184,7 +208,7 @@ void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
|
|||||||
int tm = msec;
|
int tm = msec;
|
||||||
#else
|
#else
|
||||||
timespec ts, *tm = &ts;
|
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_sec = ms / 1000;
|
||||||
ts.tv_nsec = (ms % 1000) * 1000000LL;
|
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_mean_of(int v);
|
||||||
extern void dbg_print();
|
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 {
|
struct Log : public std::ofstream {
|
||||||
Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {}
|
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 {
|
namespace Time {
|
||||||
void restart() { system_time(&t); }
|
typedef int64_t point;
|
||||||
uint64_t msec() const { return time_to_msec(t); }
|
point now();
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<class Entry, int Size>
|
template<class Entry, int Size>
|
||||||
struct HashTable {
|
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)]; }
|
Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Entry> e;
|
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)
|
#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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
@@ -25,14 +24,14 @@
|
|||||||
|
|
||||||
/// Simple macro to wrap a very common while loop, no facny, no flexibility,
|
/// Simple macro to wrap a very common while loop, no facny, no flexibility,
|
||||||
/// hardcoded names 'mlist' and 'from'.
|
/// hardcoded names 'mlist' and 'from'.
|
||||||
#define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_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
|
/// 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); }
|
(*mlist++).move = make_move(to - (d), to); }
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template<CastlingSide Side, bool OnlyChecks>
|
template<CastlingSide Side, bool Checks>
|
||||||
MoveStack* generate_castle(const Position& pos, MoveStack* mlist, Color us) {
|
MoveStack* generate_castle(const Position& pos, MoveStack* mlist, Color us) {
|
||||||
|
|
||||||
if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side)))
|
if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side)))
|
||||||
@@ -47,9 +46,8 @@ namespace {
|
|||||||
|
|
||||||
assert(!pos.in_check());
|
assert(!pos.in_check());
|
||||||
|
|
||||||
for (Square s = std::min(kfrom, kto), e = std::max(kfrom, kto); s <= e; s++)
|
for (Square s = kto; s != kfrom; s += (Square)(Side == KING_SIDE ? -1 : 1))
|
||||||
if ( s != kfrom // We are not in check
|
if (pos.attackers_to(s) & enemies)
|
||||||
&& (pos.attackers_to(s) & enemies))
|
|
||||||
return mlist;
|
return mlist;
|
||||||
|
|
||||||
// Because we generate only legal castling moves we need to verify that
|
// 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))
|
&& (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies))
|
||||||
return mlist;
|
return mlist;
|
||||||
|
|
||||||
(*mlist++).move = make_castle(kfrom, rfrom);
|
(*mlist++).move = make<CASTLE>(kfrom, rfrom);
|
||||||
|
|
||||||
if (OnlyChecks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
|
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
|
||||||
mlist--;
|
mlist--;
|
||||||
|
|
||||||
return mlist;
|
return mlist;
|
||||||
@@ -80,39 +78,41 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<MoveType Type, Square Delta>
|
template<GenType Type, Square Delta>
|
||||||
inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7, Bitboard target, Square ksq) {
|
inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7,
|
||||||
|
Bitboard target, const CheckInfo* ci) {
|
||||||
|
|
||||||
Bitboard b = move_pawns<Delta>(pawnsOn7) & target;
|
Bitboard b = move_pawns<Delta>(pawnsOn7) & target;
|
||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
{
|
{
|
||||||
Square to = pop_1st_bit(&b);
|
Square to = pop_lsb(&b);
|
||||||
|
|
||||||
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
|
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||||
(*mlist++).move = make_promotion(to - Delta, to, QUEEN);
|
(*mlist++).move = make<PROMOTION>(to - Delta, to, QUEEN);
|
||||||
|
|
||||||
if (Type == 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, ROOK);
|
||||||
(*mlist++).move = make_promotion(to - Delta, to, BISHOP);
|
(*mlist++).move = make<PROMOTION>(to - Delta, to, BISHOP);
|
||||||
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
|
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Knight-promotion is the only one that can give a direct check not
|
// Knight-promotion is the only one that can give a direct check not
|
||||||
// already included in the queen-promotion.
|
// already included in the queen-promotion.
|
||||||
if (Type == MV_QUIET_CHECK && (StepAttacksBB[W_KNIGHT][to] & ksq))
|
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
|
||||||
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
|
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
||||||
else
|
else
|
||||||
(void)ksq; // Silence a warning under MSVC
|
(void)ci; // Silence a warning under MSVC
|
||||||
}
|
}
|
||||||
|
|
||||||
return mlist;
|
return mlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<Color Us, MoveType Type>
|
template<Color Us, GenType Type>
|
||||||
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq = SQ_NONE) {
|
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist,
|
||||||
|
Bitboard target, const CheckInfo* ci) {
|
||||||
|
|
||||||
// Compute our parametrized parameters at compile time, named according to
|
// Compute our parametrized parameters at compile time, named according to
|
||||||
// the point of view of white side.
|
// the point of view of white side.
|
||||||
@@ -129,35 +129,35 @@ namespace {
|
|||||||
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
|
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
|
||||||
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
|
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
|
||||||
|
|
||||||
Bitboard enemies = (Type == MV_EVASION ? pos.pieces(Them) & target:
|
Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
|
||||||
Type == MV_CAPTURE ? target : pos.pieces(Them));
|
Type == CAPTURES ? target : pos.pieces(Them));
|
||||||
|
|
||||||
// Single and double pawn pushes, no promotions
|
// 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;
|
b1 = move_pawns<UP>(pawnsNotOn7) & emptySquares;
|
||||||
b2 = move_pawns<UP>(b1 & TRank3BB) & 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;
|
b1 &= target;
|
||||||
b2 &= target;
|
b2 &= target;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Type == MV_QUIET_CHECK)
|
if (Type == QUIET_CHECKS)
|
||||||
{
|
{
|
||||||
b1 &= pos.attacks_from<PAWN>(ksq, Them);
|
b1 &= pos.attacks_from<PAWN>(ci->ksq, Them);
|
||||||
b2 &= pos.attacks_from<PAWN>(ksq, Them);
|
b2 &= pos.attacks_from<PAWN>(ci->ksq, Them);
|
||||||
|
|
||||||
// Add pawn pushes which give discovered check. This is possible only
|
// Add pawn pushes which give discovered check. This is possible only
|
||||||
// if the pawn is not on the same file as the enemy king, because we
|
// if the pawn is not on the same file as the enemy king, because we
|
||||||
// don't generate captures. Note that a possible discovery check
|
// don't generate captures. Note that a possible discovery check
|
||||||
// promotion has been already generated among captures.
|
// promotion has been already generated 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;
|
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
|
||||||
|
|
||||||
b1 |= dc1;
|
b1 |= dc1;
|
||||||
@@ -170,21 +170,21 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Promotions and underpromotions
|
// 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();
|
emptySquares = ~pos.pieces();
|
||||||
|
|
||||||
if (Type == MV_EVASION)
|
if (Type == EVASIONS)
|
||||||
emptySquares &= target;
|
emptySquares &= target;
|
||||||
|
|
||||||
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ksq);
|
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ci);
|
||||||
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ksq);
|
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ci);
|
||||||
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ksq);
|
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard and en-passant captures
|
// 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;
|
b1 = move_pawns<RIGHT>(pawnsNotOn7) & enemies;
|
||||||
b2 = move_pawns<LEFT >(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
|
// An en passant capture can be an evasion only if the checking piece
|
||||||
// is the double pushed pawn and so is in the target. Otherwise this
|
// is the double pushed pawn and so is in the target. Otherwise this
|
||||||
// is a discovery check and we are forced to do otherwise.
|
// is a discovery check and we are forced to do otherwise.
|
||||||
if (Type == MV_EVASION && !(target & (pos.ep_square() - UP)))
|
if (Type == EVASIONS && !(target & (pos.ep_square() - UP)))
|
||||||
return mlist;
|
return mlist;
|
||||||
|
|
||||||
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
|
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
|
||||||
@@ -207,7 +207,7 @@ namespace {
|
|||||||
assert(b1);
|
assert(b1);
|
||||||
|
|
||||||
while (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,125 +215,115 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<PieceType Pt>
|
template<PieceType Pt, bool Checks> FORCE_INLINE
|
||||||
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist,
|
MoveStack* generate_moves(const Position& pos, MoveStack* mlist, Color us,
|
||||||
Color us, const CheckInfo& ci) {
|
Bitboard target, const CheckInfo* ci) {
|
||||||
|
|
||||||
assert(Pt != KING && Pt != PAWN);
|
assert(Pt != KING && Pt != PAWN);
|
||||||
|
|
||||||
Bitboard b, target;
|
|
||||||
Square from;
|
|
||||||
const Square* pl = pos.piece_list(us, Pt);
|
const Square* pl = pos.piece_list(us, Pt);
|
||||||
|
|
||||||
if (*pl != SQ_NONE)
|
for (Square from = *pl; from != SQ_NONE; from = *++pl)
|
||||||
{
|
{
|
||||||
target = ci.checkSq[Pt] & ~pos.pieces(); // Non capture checks only
|
if (Checks)
|
||||||
|
{
|
||||||
do {
|
|
||||||
from = *pl;
|
|
||||||
|
|
||||||
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
|
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
|
||||||
&& !(PseudoAttacks[Pt][from] & target))
|
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ci.dcCandidates && (ci.dcCandidates & from))
|
if (ci->dcCandidates && (ci->dcCandidates & from))
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
b = pos.attacks_from<Pt>(from) & target;
|
Bitboard b = pos.attacks_from<Pt>(from) & target;
|
||||||
SERIALIZE(b);
|
|
||||||
} while (*++pl != SQ_NONE);
|
if (Checks)
|
||||||
|
b &= ci->checkSq[Pt];
|
||||||
|
|
||||||
|
SERIALIZE(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mlist;
|
return mlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<PieceType Pt>
|
FORCE_INLINE MoveStack* generate_king_moves(const Position& pos, MoveStack* mlist,
|
||||||
FORCE_INLINE MoveStack* generate_moves(const Position& pos, MoveStack* mlist,
|
Color us, Bitboard target) {
|
||||||
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,
|
|
||||||
Color us, Bitboard target) {
|
|
||||||
Square from = pos.king_square(us);
|
Square from = pos.king_square(us);
|
||||||
Bitboard b = pos.attacks_from<KING>(from) & target;
|
Bitboard b = pos.attacks_from<KING>(from) & target;
|
||||||
SERIALIZE(b);
|
SERIALIZE(b);
|
||||||
return mlist;
|
return mlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<GenType Type> FORCE_INLINE
|
||||||
|
MoveStack* generate_all_moves(const Position& pos, MoveStack* mlist, Color us,
|
||||||
|
Bitboard target, const CheckInfo* ci = NULL) {
|
||||||
|
|
||||||
|
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci)
|
||||||
|
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (Type != QUIET_CHECKS && Type != EVASIONS)
|
||||||
|
mlist = generate_king_moves(pos, mlist, us, target);
|
||||||
|
|
||||||
|
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(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
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// generate<MV_CAPTURE> generates all pseudo-legal captures and queen
|
/// generate<CAPTURES> generates all pseudo-legal captures and queen
|
||||||
/// promotions. Returns a pointer to the end of the move list.
|
/// promotions. Returns a pointer to the end of the move list.
|
||||||
///
|
///
|
||||||
/// generate<MV_QUIET> generates all pseudo-legal non-captures and
|
/// generate<QUIETS> generates all pseudo-legal non-captures and
|
||||||
/// underpromotions. Returns a pointer to the end of the move list.
|
/// underpromotions. Returns a pointer to the end of the move list.
|
||||||
///
|
///
|
||||||
/// generate<MV_NON_EVASION> generates all pseudo-legal captures and
|
/// generate<NON_EVASIONS> generates all pseudo-legal captures and
|
||||||
/// non-captures. Returns a pointer to the end of the move list.
|
/// non-captures. Returns a pointer to the end of the move list.
|
||||||
|
|
||||||
template<MoveType Type>
|
template<GenType Type>
|
||||||
MoveStack* generate(const Position& pos, MoveStack* mlist) {
|
MoveStack* generate(const Position& pos, MoveStack* mlist) {
|
||||||
|
|
||||||
assert(Type == MV_CAPTURE || Type == MV_QUIET || Type == MV_NON_EVASION);
|
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
|
||||||
assert(!pos.in_check());
|
assert(!pos.in_check());
|
||||||
|
|
||||||
Color us = pos.side_to_move();
|
Color us = pos.side_to_move();
|
||||||
Bitboard target;
|
Bitboard target;
|
||||||
|
|
||||||
if (Type == MV_CAPTURE)
|
if (Type == CAPTURES)
|
||||||
target = pos.pieces(~us);
|
target = pos.pieces(~us);
|
||||||
|
|
||||||
else if (Type == MV_QUIET)
|
else if (Type == QUIETS)
|
||||||
target = ~pos.pieces();
|
target = ~pos.pieces();
|
||||||
|
|
||||||
else if (Type == MV_NON_EVASION)
|
else if (Type == NON_EVASIONS)
|
||||||
target = ~pos.pieces(us);
|
target = ~pos.pieces(us);
|
||||||
|
|
||||||
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target)
|
return generate_all_moves<Type>(pos, mlist, us, 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))
|
|
||||||
{
|
|
||||||
mlist = generate_castle<KING_SIDE, false>(pos, mlist, us);
|
|
||||||
mlist = generate_castle<QUEEN_SIDE, false>(pos, mlist, us);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mlist;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiations
|
// Explicit template instantiations
|
||||||
template MoveStack* generate<MV_CAPTURE>(const Position& pos, MoveStack* mlist);
|
template MoveStack* generate<CAPTURES>(const Position&, MoveStack*);
|
||||||
template MoveStack* generate<MV_QUIET>(const Position& pos, MoveStack* mlist);
|
template MoveStack* generate<QUIETS>(const Position&, MoveStack*);
|
||||||
template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist);
|
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.
|
/// underpromotions that give check. Returns a pointer to the end of the move list.
|
||||||
template<>
|
template<>
|
||||||
MoveStack* generate<MV_QUIET_CHECK>(const Position& pos, MoveStack* mlist) {
|
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
|
||||||
|
|
||||||
assert(!pos.in_check());
|
assert(!pos.in_check());
|
||||||
|
|
||||||
@@ -343,7 +333,7 @@ MoveStack* generate<MV_QUIET_CHECK>(const Position& pos, MoveStack* mlist) {
|
|||||||
|
|
||||||
while (dc)
|
while (dc)
|
||||||
{
|
{
|
||||||
Square from = pop_1st_bit(&dc);
|
Square from = pop_lsb(&dc);
|
||||||
PieceType pt = type_of(pos.piece_on(from));
|
PieceType pt = type_of(pos.piece_on(from));
|
||||||
|
|
||||||
if (pt == PAWN)
|
if (pt == PAWN)
|
||||||
@@ -357,48 +347,32 @@ MoveStack* generate<MV_QUIET_CHECK>(const Position& pos, MoveStack* mlist) {
|
|||||||
SERIALIZE(b);
|
SERIALIZE(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
mlist = (us == WHITE ? generate_pawn_moves<WHITE, MV_QUIET_CHECK>(pos, mlist, ci.dcCandidates, ci.ksq)
|
return generate_all_moves<QUIET_CHECKS>(pos, mlist, us, ~pos.pieces(), &ci);
|
||||||
: 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// 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.
|
/// to move is in check. Returns a pointer to the end of the move list.
|
||||||
template<>
|
template<>
|
||||||
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
|
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
|
||||||
|
|
||||||
assert(pos.in_check());
|
assert(pos.in_check());
|
||||||
|
|
||||||
Bitboard b, target;
|
|
||||||
Square from, checksq;
|
Square from, checksq;
|
||||||
int checkersCnt = 0;
|
int checkersCnt = 0;
|
||||||
Color us = pos.side_to_move();
|
Color us = pos.side_to_move();
|
||||||
Square ksq = pos.king_square(us);
|
Square ksq = pos.king_square(us);
|
||||||
Bitboard sliderAttacks = 0;
|
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
|
// 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.
|
// evasions so to skip known illegal moves avoiding useless legality check later.
|
||||||
b = checkers;
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
checkersCnt++;
|
checkersCnt++;
|
||||||
checksq = pop_1st_bit(&b);
|
checksq = pop_lsb(&b);
|
||||||
|
|
||||||
assert(color_of(pos.piece_on(checksq)) == ~us);
|
assert(color_of(pos.piece_on(checksq)) == ~us);
|
||||||
|
|
||||||
@@ -429,38 +403,33 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
|
|||||||
from = ksq;
|
from = ksq;
|
||||||
SERIALIZE(b);
|
SERIALIZE(b);
|
||||||
|
|
||||||
// Generate evasions for other pieces only if not under a double check
|
|
||||||
if (checkersCnt > 1)
|
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
|
// Generate blocking evasions or captures of the checking piece
|
||||||
target = between_bb(checksq, ksq) | checkers;
|
Bitboard target = between_bb(checksq, ksq) | pos.checkers();
|
||||||
|
|
||||||
mlist = (us == WHITE ? generate_pawn_moves<WHITE, MV_EVASION>(pos, mlist, target)
|
return generate_all_moves<EVASIONS>(pos, mlist, us, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// generate<MV_LEGAL> generates all the legal moves in the given position
|
/// generate<LEGAL> generates all the legal moves in the given position
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
MoveStack* generate<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();
|
Bitboard pinned = pos.pinned_pieces();
|
||||||
|
Square ksq = pos.king_square(pos.side_to_move());
|
||||||
|
|
||||||
last = pos.in_check() ? generate<MV_EVASION>(pos, mlist)
|
end = pos.in_check() ? generate<EVASIONS>(pos, mlist)
|
||||||
: generate<MV_NON_EVASION>(pos, mlist);
|
: generate<NON_EVASIONS>(pos, mlist);
|
||||||
while (cur != last)
|
while (cur != end)
|
||||||
if (!pos.pl_move_is_legal(cur->move, pinned))
|
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
|
||||||
cur->move = (--last)->move;
|
&& !pos.pl_move_is_legal(cur->move, pinned))
|
||||||
|
cur->move = (--end)->move;
|
||||||
else
|
else
|
||||||
cur++;
|
cur++;
|
||||||
|
|
||||||
return last;
|
return end;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,30 +22,30 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
enum MoveType {
|
enum GenType {
|
||||||
MV_CAPTURE,
|
CAPTURES,
|
||||||
MV_QUIET,
|
QUIETS,
|
||||||
MV_QUIET_CHECK,
|
QUIET_CHECKS,
|
||||||
MV_EVASION,
|
EVASIONS,
|
||||||
MV_NON_EVASION,
|
NON_EVASIONS,
|
||||||
MV_LEGAL
|
LEGAL
|
||||||
};
|
};
|
||||||
|
|
||||||
class Position;
|
class Position;
|
||||||
|
|
||||||
template<MoveType>
|
template<GenType>
|
||||||
MoveStack* generate(const Position& pos, MoveStack* mlist);
|
MoveStack* generate(const Position& pos, MoveStack* mlist);
|
||||||
|
|
||||||
/// The MoveList struct is a simple wrapper around generate(), sometimes comes
|
/// The MoveList struct is a simple wrapper around generate(), sometimes comes
|
||||||
/// handy to use this class instead of the low level generate() function.
|
/// handy to use this class instead of the low level generate() function.
|
||||||
template<MoveType T>
|
template<GenType T>
|
||||||
struct MoveList {
|
struct MoveList {
|
||||||
|
|
||||||
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {}
|
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {}
|
||||||
void operator++() { cur++; }
|
void operator++() { cur++; }
|
||||||
bool end() const { return cur == last; }
|
bool end() const { return cur == last; }
|
||||||
Move move() const { return cur->move; }
|
Move move() const { return cur->move; }
|
||||||
int size() const { return int(last - mlist); }
|
size_t size() const { return last - mlist; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MoveStack mlist[MAX_MOVES];
|
MoveStack mlist[MAX_MOVES];
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
#include "movepick.h"
|
#include "movepick.h"
|
||||||
|
#include "thread.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -38,15 +39,15 @@ namespace {
|
|||||||
|
|
||||||
// Unary predicate used by std::partition to split positive scores from remaining
|
// Unary predicate used by std::partition to split positive scores from remaining
|
||||||
// ones so to sort separately the two sets, and with the second sort delayed.
|
// ones so to sort separately the two sets, and with the second sort delayed.
|
||||||
inline bool has_positive_score(const MoveStack& 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
|
// it is faster than sorting all the moves in advance when moves are few, as
|
||||||
// normally are the possible captures.
|
// normally are the possible captures.
|
||||||
inline MoveStack* pick_best(MoveStack* firstMove, MoveStack* lastMove)
|
inline MoveStack* pick_best(MoveStack* begin, MoveStack* end)
|
||||||
{
|
{
|
||||||
std::swap(*firstMove, *std::max_element(firstMove, lastMove));
|
std::swap(*begin, *std::max_element(begin, end));
|
||||||
return firstMove;
|
return begin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,13 +59,14 @@ namespace {
|
|||||||
/// move ordering is at the current node.
|
/// move ordering is at the current node.
|
||||||
|
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const 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);
|
assert(d > DEPTH_ZERO);
|
||||||
|
|
||||||
captureThreshold = 0;
|
captureThreshold = 0;
|
||||||
curMove = lastMove = moves;
|
cur = end = moves;
|
||||||
lastBadCapture = moves + MAX_MOVES - 1;
|
endBadCaptures = moves + MAX_MOVES - 1;
|
||||||
|
ss = s;
|
||||||
|
|
||||||
if (p.in_check())
|
if (p.in_check())
|
||||||
phase = EVASION;
|
phase = EVASION;
|
||||||
@@ -77,8 +79,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
|
|||||||
killers[1].move = ss->killers[1];
|
killers[1].move = ss->killers[1];
|
||||||
|
|
||||||
// Consider sligtly negative captures as good if at low depth and far from beta
|
// Consider sligtly negative captures as good if at low depth and far from beta
|
||||||
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY)
|
if (ss && ss->eval < beta - PawnValueMg && d < 3 * ONE_PLY)
|
||||||
captureThreshold = -PawnValueMidgame;
|
captureThreshold = -PawnValueMg;
|
||||||
|
|
||||||
// Consider negative captures as good if still enough to reach beta
|
// Consider negative captures as good if still enough to reach beta
|
||||||
else if (ss && ss->eval > 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);
|
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,
|
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);
|
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);
|
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)
|
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());
|
assert(!pos.in_check());
|
||||||
|
|
||||||
phase = PROBCUT;
|
phase = PROBCUT;
|
||||||
|
|
||||||
// In ProbCut we generate only captures better than parent's captured piece
|
// In ProbCut we generate only captures better than parent's captured piece
|
||||||
captureThreshold = PieceValueMidgame[pt];
|
captureThreshold = PieceValue[Mg][pt];
|
||||||
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||||
|
|
||||||
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
|
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
|
||||||
ttMove = MOVE_NONE;
|
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).
|
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
|
||||||
Move m;
|
Move m;
|
||||||
|
|
||||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
for (MoveStack* it = moves; it != end; ++it)
|
||||||
{
|
{
|
||||||
m = cur->move;
|
m = it->move;
|
||||||
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));
|
- type_of(pos.piece_moved(m));
|
||||||
|
|
||||||
if (is_promotion(m))
|
if (type_of(m) == PROMOTION)
|
||||||
cur->score += PieceValueMidgame[promotion_type(m)];
|
it->score += PieceValue[Mg][promotion_type(m)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,10 +177,10 @@ void MovePicker::score_noncaptures() {
|
|||||||
|
|
||||||
Move m;
|
Move m;
|
||||||
|
|
||||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
for (MoveStack* it = moves; it != end; ++it)
|
||||||
{
|
{
|
||||||
m = cur->move;
|
m = it->move;
|
||||||
cur->score = H.value(pos.piece_moved(m), to_sq(m));
|
it->score = H.value(pos.piece_moved(m), to_sq(m));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,19 +191,19 @@ void MovePicker::score_evasions() {
|
|||||||
Move m;
|
Move m;
|
||||||
int seeScore;
|
int seeScore;
|
||||||
|
|
||||||
if (lastMove < moves + 2)
|
if (end < moves + 2)
|
||||||
return;
|
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)
|
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))
|
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;
|
- type_of(pos.piece_moved(m)) + History::MaxValue;
|
||||||
else
|
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() {
|
void MovePicker::generate_next() {
|
||||||
|
|
||||||
curMove = moves;
|
cur = moves;
|
||||||
|
|
||||||
switch (++phase) {
|
switch (++phase) {
|
||||||
|
|
||||||
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
|
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
|
||||||
lastMove = generate<MV_CAPTURE>(pos, moves);
|
end = generate<CAPTURES>(pos, moves);
|
||||||
score_captures();
|
score_captures();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case KILLERS_S1:
|
case KILLERS_S1:
|
||||||
curMove = killers;
|
cur = killers;
|
||||||
lastMove = curMove + 2;
|
end = cur + 2;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case QUIETS_1_S1:
|
case QUIETS_1_S1:
|
||||||
lastQuiet = lastMove = generate<MV_QUIET>(pos, moves);
|
endQuiets = end = generate<QUIETS>(pos, moves);
|
||||||
score_noncaptures();
|
score_noncaptures();
|
||||||
lastMove = std::partition(curMove, lastMove, has_positive_score);
|
end = std::partition(cur, end, has_positive_score);
|
||||||
sort<MoveStack>(curMove, lastMove);
|
sort<MoveStack>(cur, end);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case QUIETS_2_S1:
|
case QUIETS_2_S1:
|
||||||
curMove = lastMove;
|
cur = end;
|
||||||
lastMove = lastQuiet;
|
end = endQuiets;
|
||||||
if (depth >= 3 * ONE_PLY)
|
if (depth >= 3 * ONE_PLY)
|
||||||
sort<MoveStack>(curMove, lastMove);
|
sort<MoveStack>(cur, end);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case BAD_CAPTURES_S1:
|
case BAD_CAPTURES_S1:
|
||||||
// Just pick them in reverse order to get MVV/LVA ordering
|
// Just pick them in reverse order to get MVV/LVA ordering
|
||||||
curMove = moves + MAX_MOVES - 1;
|
cur = moves + MAX_MOVES - 1;
|
||||||
lastMove = lastBadCapture;
|
end = endBadCaptures;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case EVASIONS_S2:
|
case EVASIONS_S2:
|
||||||
lastMove = generate<MV_EVASION>(pos, moves);
|
end = generate<EVASIONS>(pos, moves);
|
||||||
score_evasions();
|
score_evasions();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case QUIET_CHECKS_S3:
|
case QUIET_CHECKS_S3:
|
||||||
lastMove = generate<MV_QUIET_CHECK>(pos, moves);
|
end = generate<QUIET_CHECKS>(pos, moves);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
|
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
|
||||||
phase = STOP;
|
phase = STOP;
|
||||||
case STOP:
|
case STOP:
|
||||||
lastMove = curMove + 1; // Avoid another next_phase() call
|
end = cur + 1; // Avoid another next_phase() call
|
||||||
return;
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -270,26 +272,25 @@ void MovePicker::generate_next() {
|
|||||||
/// It returns a new pseudo legal move every time it is called, until there
|
/// 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
|
/// 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
|
/// 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
|
/// searched previously.
|
||||||
/// lock protected by caller when accessed through a shared MovePicker object.
|
template<>
|
||||||
|
Move MovePicker::next_move<false>() {
|
||||||
Move MovePicker::next_move() {
|
|
||||||
|
|
||||||
Move move;
|
Move move;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
while (curMove == lastMove)
|
while (cur == end)
|
||||||
generate_next();
|
generate_next();
|
||||||
|
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
|
|
||||||
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
|
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
|
||||||
curMove++;
|
cur++;
|
||||||
return ttMove;
|
return ttMove;
|
||||||
|
|
||||||
case CAPTURES_S1:
|
case CAPTURES_S1:
|
||||||
move = pick_best(curMove++, lastMove)->move;
|
move = pick_best(cur++, end)->move;
|
||||||
if (move != ttMove)
|
if (move != ttMove)
|
||||||
{
|
{
|
||||||
assert(captureThreshold <= 0); // Otherwise we cannot use see_sign()
|
assert(captureThreshold <= 0); // Otherwise we cannot use see_sign()
|
||||||
@@ -298,12 +299,12 @@ Move MovePicker::next_move() {
|
|||||||
return move;
|
return move;
|
||||||
|
|
||||||
// Losing capture, move it to the tail of the array
|
// Losing capture, move it to the tail of the array
|
||||||
(lastBadCapture--)->move = move;
|
(endBadCaptures--)->move = move;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KILLERS_S1:
|
case KILLERS_S1:
|
||||||
move = (curMove++)->move;
|
move = (cur++)->move;
|
||||||
if ( move != MOVE_NONE
|
if ( move != MOVE_NONE
|
||||||
&& pos.is_pseudo_legal(move)
|
&& pos.is_pseudo_legal(move)
|
||||||
&& move != ttMove
|
&& move != ttMove
|
||||||
@@ -312,7 +313,7 @@ Move MovePicker::next_move() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case QUIETS_1_S1: case QUIETS_2_S1:
|
case QUIETS_1_S1: case QUIETS_2_S1:
|
||||||
move = (curMove++)->move;
|
move = (cur++)->move;
|
||||||
if ( move != ttMove
|
if ( move != ttMove
|
||||||
&& move != killers[0].move
|
&& move != killers[0].move
|
||||||
&& move != killers[1].move)
|
&& move != killers[1].move)
|
||||||
@@ -320,28 +321,28 @@ Move MovePicker::next_move() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BAD_CAPTURES_S1:
|
case BAD_CAPTURES_S1:
|
||||||
return (curMove--)->move;
|
return (cur--)->move;
|
||||||
|
|
||||||
case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4:
|
case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4:
|
||||||
move = pick_best(curMove++, lastMove)->move;
|
move = pick_best(cur++, end)->move;
|
||||||
if (move != ttMove)
|
if (move != ttMove)
|
||||||
return move;
|
return move;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CAPTURES_S5:
|
case CAPTURES_S5:
|
||||||
move = pick_best(curMove++, lastMove)->move;
|
move = pick_best(cur++, end)->move;
|
||||||
if (move != ttMove && pos.see(move) > captureThreshold)
|
if (move != ttMove && pos.see(move) > captureThreshold)
|
||||||
return move;
|
return move;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CAPTURES_S6:
|
case CAPTURES_S6:
|
||||||
move = pick_best(curMove++, lastMove)->move;
|
move = pick_best(cur++, end)->move;
|
||||||
if (to_sq(move) == recaptureSquare)
|
if (to_sq(move) == recaptureSquare)
|
||||||
return move;
|
return move;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QUIET_CHECKS_S3:
|
case QUIET_CHECKS_S3:
|
||||||
move = (curMove++)->move;
|
move = (cur++)->move;
|
||||||
if (move != ttMove)
|
if (move != ttMove)
|
||||||
return move;
|
return move;
|
||||||
break;
|
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&, Search::Stack*, Value);
|
||||||
MovePicker(const Position&, Move, Depth, const History&, Square);
|
MovePicker(const Position&, Move, Depth, const History&, Square);
|
||||||
MovePicker(const Position&, Move, const History&, PieceType);
|
MovePicker(const Position&, Move, const History&, PieceType);
|
||||||
Move next_move();
|
template<bool SpNode> Move next_move();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void score_captures();
|
void score_captures();
|
||||||
@@ -51,12 +51,13 @@ private:
|
|||||||
|
|
||||||
const Position& pos;
|
const Position& pos;
|
||||||
const History& H;
|
const History& H;
|
||||||
|
Search::Stack* ss;
|
||||||
Depth depth;
|
Depth depth;
|
||||||
Move ttMove;
|
Move ttMove;
|
||||||
MoveStack killers[2];
|
MoveStack killers[2];
|
||||||
Square recaptureSquare;
|
Square recaptureSquare;
|
||||||
int captureThreshold, phase;
|
int captureThreshold, phase;
|
||||||
MoveStack *curMove, *lastMove, *lastQuiet, *lastBadCapture;
|
MoveStack *cur, *end, *endQuiets, *endBadCaptures;
|
||||||
MoveStack moves[MAX_MOVES];
|
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
|
// Shelter penalty is higher for the pawn in front of the king
|
||||||
b = ourPawns & FileBB[f];
|
b = ourPawns & FileBB[f];
|
||||||
rkUs = b ? rank_of(Us == WHITE ? 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];
|
safety -= ShelterWeakness[f != kf][rkUs];
|
||||||
|
|
||||||
// Storm danger is smaller if enemy pawn is blocked
|
// Storm danger is smaller if enemy pawn is blocked
|
||||||
b = theirPawns & FileBB[f];
|
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];
|
safety -= StormDanger[rkThem == rkUs + 1][rkThem];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,9 +261,14 @@ Score PawnEntry::update_safety(const Position& pos, Square ksq) {
|
|||||||
|
|
||||||
kingSquares[Us] = ksq;
|
kingSquares[Us] = ksq;
|
||||||
castleRights[Us] = pos.can_castle(Us);
|
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)
|
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);
|
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)))
|
if (pos.can_castle(make_castle_right(Us, QUEEN_SIDE)))
|
||||||
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
|
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
|
// Explicit template instantiation
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ private:
|
|||||||
Bitboard passedPawns[2];
|
Bitboard passedPawns[2];
|
||||||
Bitboard pawnAttacks[2];
|
Bitboard pawnAttacks[2];
|
||||||
Square kingSquares[2];
|
Square kingSquares[2];
|
||||||
|
int minKPdistance[2];
|
||||||
int castleRights[2];
|
int castleRights[2];
|
||||||
Score value;
|
Score value;
|
||||||
int halfOpenFiles[2];
|
int halfOpenFiles[2];
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ typedef unsigned __int64 uint64_t;
|
|||||||
typedef timeval sys_time_t;
|
typedef timeval sys_time_t;
|
||||||
|
|
||||||
inline void system_time(sys_time_t* t) { gettimeofday(t, NULL); }
|
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>
|
# include <pthread.h>
|
||||||
typedef pthread_mutex_t Lock;
|
typedef pthread_mutex_t Lock;
|
||||||
@@ -74,7 +74,7 @@ typedef void*(*pt_start_fn)(void*);
|
|||||||
typedef _timeb sys_time_t;
|
typedef _timeb sys_time_t;
|
||||||
|
|
||||||
inline void system_time(sys_time_t* t) { _ftime(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)
|
#if !defined(NOMINMAX)
|
||||||
# define NOMINMAX // disable macros min() and max()
|
# define NOMINMAX // disable macros min() and max()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "bitcount.h"
|
#include "bitcount.h"
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
|
#include "notation.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "psqtab.h"
|
#include "psqtab.h"
|
||||||
#include "rkiss.h"
|
#include "rkiss.h"
|
||||||
@@ -35,36 +36,105 @@ using std::string;
|
|||||||
using std::cout;
|
using std::cout;
|
||||||
using std::endl;
|
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");
|
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
|
/// 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
|
/// object do not depend on any external data so we detach state pointer from
|
||||||
/// the source one.
|
/// the source one.
|
||||||
|
|
||||||
void Position::operator=(const Position& pos) {
|
Position& Position::operator=(const Position& pos) {
|
||||||
|
|
||||||
memcpy(this, &pos, sizeof(Position));
|
memcpy(this, &pos, sizeof(Position));
|
||||||
startState = *st;
|
startState = *st;
|
||||||
@@ -97,6 +167,8 @@ void Position::operator=(const Position& pos) {
|
|||||||
nodes = 0;
|
nodes = 0;
|
||||||
|
|
||||||
assert(pos_is_ok());
|
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++) {}
|
for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; rsq++) {}
|
||||||
|
|
||||||
else if (token >= 'A' && token <= 'H')
|
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
|
else
|
||||||
continue;
|
continue;
|
||||||
@@ -199,7 +271,7 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) {
|
|||||||
if ( ((fen >> col) && (col >= 'a' && col <= 'h'))
|
if ( ((fen >> col) && (col >= 'a' && col <= 'h'))
|
||||||
&& ((fen >> row) && (row == '3' || row == '6')))
|
&& ((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)))
|
if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)))
|
||||||
st->epSquare = SQ_NONE;
|
st->epSquare = SQ_NONE;
|
||||||
@@ -268,7 +340,7 @@ const string Position::to_fen() const {
|
|||||||
|
|
||||||
for (File file = FILE_A; file <= FILE_H; file++)
|
for (File file = FILE_A; file <= FILE_H; file++)
|
||||||
{
|
{
|
||||||
sq = make_square(file, rank);
|
sq = file | rank;
|
||||||
|
|
||||||
if (is_empty(sq))
|
if (is_empty(sq))
|
||||||
emptyCnt++;
|
emptyCnt++;
|
||||||
@@ -325,6 +397,8 @@ void Position::print(Move move) const {
|
|||||||
|
|
||||||
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
|
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
|
||||||
|
|
||||||
|
sync_cout;
|
||||||
|
|
||||||
if (move)
|
if (move)
|
||||||
{
|
{
|
||||||
Position p(*this);
|
Position p(*this);
|
||||||
@@ -335,7 +409,7 @@ void Position::print(Move move) const {
|
|||||||
if (piece_on(sq) != NO_PIECE)
|
if (piece_on(sq) != NO_PIECE)
|
||||||
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
|
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)
|
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)))
|
if (b && !more_than_one(b) && (b & pieces(sideToMove)))
|
||||||
result |= b;
|
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
|
// 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
|
// uncommon, we do it simply by testing whether the king is attacked after
|
||||||
// the move is made.
|
// the move is made.
|
||||||
if (is_enpassant(m))
|
if (type_of(m) == ENPASSANT)
|
||||||
{
|
{
|
||||||
Color them = ~us;
|
Color them = ~us;
|
||||||
Square to = to_sq(m);
|
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
|
// square is attacked by the opponent. Castling moves are checked
|
||||||
// for legality during move generation.
|
// for legality during move generation.
|
||||||
if (type_of(piece_on(from)) == KING)
|
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
|
// 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.
|
// 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 {
|
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)
|
if (ml.move() == m)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -506,7 +580,7 @@ bool Position::is_pseudo_legal(const Move m) const {
|
|||||||
Piece pc = piece_moved(m);
|
Piece pc = piece_moved(m);
|
||||||
|
|
||||||
// Use a slower but simpler function for uncommon cases
|
// Use a slower but simpler function for uncommon cases
|
||||||
if (is_special(m))
|
if (type_of(m) != NORMAL)
|
||||||
return move_is_legal(m);
|
return move_is_legal(m);
|
||||||
|
|
||||||
// Is not a promotion, so promotion piece must be empty
|
// 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)
|
if (type_of(pc) != KING)
|
||||||
{
|
{
|
||||||
Bitboard b = checkers();
|
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
|
if (b) // double check ? In this case a king move is required
|
||||||
return false;
|
return false;
|
||||||
@@ -640,23 +714,23 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Can we skip the ugly special cases ?
|
// Can we skip the ugly special cases ?
|
||||||
if (!is_special(m))
|
if (type_of(m) == NORMAL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Square ksq = king_square(~us);
|
Square ksq = king_square(~us);
|
||||||
|
|
||||||
// Promotion with check ?
|
// Promotion with check ?
|
||||||
if (is_promotion(m))
|
if (type_of(m) == PROMOTION)
|
||||||
return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
|
return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
|
||||||
|
|
||||||
// En passant capture with check ? We have already handled the case
|
// En passant capture with check ? We have already handled the case
|
||||||
// of direct checks and ordinary discovered check, the only case we
|
// of direct checks and ordinary discovered check, the only case we
|
||||||
// need to handle is the unusual case of a discovered check through
|
// need to handle is the unusual case of a discovered check through
|
||||||
// the captured pawn.
|
// the captured pawn.
|
||||||
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;
|
Bitboard b = (pieces() ^ from ^ capsq) | to;
|
||||||
|
|
||||||
return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK))
|
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 ?
|
// Castling with check ?
|
||||||
if (is_castle(m))
|
if (type_of(m) == CASTLE)
|
||||||
{
|
{
|
||||||
Square kfrom = from;
|
Square kfrom = from;
|
||||||
Square rfrom = to; // 'King captures the rook' notation
|
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;
|
st = &newSt;
|
||||||
|
|
||||||
// Update side to move
|
// Update side to move
|
||||||
k ^= zobSideToMove;
|
k ^= Zobrist::side;
|
||||||
|
|
||||||
// Increment the 50 moves rule draw counter. Resetting it to zero in the
|
// 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.
|
// case of a capture or a pawn move is taken care of later.
|
||||||
st->rule50++;
|
st->rule50++;
|
||||||
st->pliesFromNull++;
|
st->pliesFromNull++;
|
||||||
|
|
||||||
if (is_castle(m))
|
if (type_of(m) == CASTLE)
|
||||||
{
|
{
|
||||||
st->key = k;
|
st->key = k;
|
||||||
do_castle_move<true>(m);
|
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);
|
Square to = to_sq(m);
|
||||||
Piece piece = piece_on(from);
|
Piece piece = piece_on(from);
|
||||||
PieceType pt = type_of(piece);
|
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) == us);
|
||||||
assert(color_of(piece_on(to)) != 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.
|
// update non-pawn material.
|
||||||
if (capture == PAWN)
|
if (capture == PAWN)
|
||||||
{
|
{
|
||||||
if (is_enpassant(m))
|
if (type_of(m) == ENPASSANT)
|
||||||
{
|
{
|
||||||
capsq += pawn_push(them);
|
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;
|
board[capsq] = NO_PIECE;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->pawnKey ^= zobrist[them][PAWN][capsq];
|
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
st->npMaterial[them] -= PieceValueMidgame[capture];
|
st->npMaterial[them] -= PieceValue[Mg][capture];
|
||||||
|
|
||||||
// Remove the captured piece
|
// Remove the captured piece
|
||||||
byTypeBB[ALL_PIECES] ^= capsq;
|
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;
|
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
|
||||||
|
|
||||||
// Update hash keys
|
// Update hash keys
|
||||||
k ^= zobrist[them][capture][capsq];
|
k ^= Zobrist::psq[them][capture][capsq];
|
||||||
st->materialKey ^= zobrist[them][capture][pieceCount[them][capture]];
|
st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]];
|
||||||
|
|
||||||
// Update incremental scores
|
// Update incremental scores
|
||||||
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq];
|
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
|
// 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
|
// Reset en passant square
|
||||||
if (st->epSquare != SQ_NONE)
|
if (st->epSquare != SQ_NONE)
|
||||||
{
|
{
|
||||||
k ^= zobEp[file_of(st->epSquare)];
|
k ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||||
st->epSquare = SQ_NONE;
|
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]))
|
if (st->castleRights && (castleRightsMask[from] | castleRightsMask[to]))
|
||||||
{
|
{
|
||||||
int cr = castleRightsMask[from] | castleRightsMask[to];
|
int cr = castleRightsMask[from] | castleRightsMask[to];
|
||||||
k ^= zobCastle[st->castleRights & cr];
|
k ^= Zobrist::castle[st->castleRights & cr];
|
||||||
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)))
|
&& (attacks_from<PAWN>(from + pawn_push(us), us) & pieces(them, PAWN)))
|
||||||
{
|
{
|
||||||
st->epSquare = Square((from + to) / 2);
|
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);
|
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;
|
pieceList[us][promotion][index[to]] = to;
|
||||||
|
|
||||||
// Update hash keys
|
// Update hash keys
|
||||||
k ^= zobrist[us][PAWN][to] ^ zobrist[us][promotion][to];
|
k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to];
|
||||||
st->pawnKey ^= zobrist[us][PAWN][to];
|
st->pawnKey ^= Zobrist::psq[us][PAWN][to];
|
||||||
st->materialKey ^= zobrist[us][promotion][pieceCount[us][promotion]++]
|
st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]++]
|
||||||
^ zobrist[us][PAWN][pieceCount[us][PAWN]];
|
^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]];
|
||||||
|
|
||||||
// Update incremental score
|
// Update incremental score
|
||||||
st->psqScore += pieceSquareTable[make_piece(us, promotion)][to]
|
st->psqScore += pieceSquareTable[make_piece(us, promotion)][to]
|
||||||
- pieceSquareTable[make_piece(us, PAWN)][to];
|
- pieceSquareTable[make_piece(us, PAWN)][to];
|
||||||
|
|
||||||
// Update material
|
// Update material
|
||||||
st->npMaterial[us] += PieceValueMidgame[promotion];
|
st->npMaterial[us] += PieceValue[Mg][promotion];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update pawn hash key
|
// 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
|
// Reset rule 50 draw counter
|
||||||
st->rule50 = 0;
|
st->rule50 = 0;
|
||||||
@@ -892,7 +966,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
|
|
||||||
if (moveIsCheck)
|
if (moveIsCheck)
|
||||||
{
|
{
|
||||||
if (is_special(m))
|
if (type_of(m) != NORMAL)
|
||||||
st->checkersBB = attackers_to(king_square(them)) & pieces(us);
|
st->checkersBB = attackers_to(king_square(them)) & pieces(us);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -927,7 +1001,7 @@ void Position::undo_move(Move m) {
|
|||||||
|
|
||||||
sideToMove = ~sideToMove;
|
sideToMove = ~sideToMove;
|
||||||
|
|
||||||
if (is_castle(m))
|
if (type_of(m) == CASTLE)
|
||||||
{
|
{
|
||||||
do_castle_move<false>(m);
|
do_castle_move<false>(m);
|
||||||
return;
|
return;
|
||||||
@@ -945,7 +1019,7 @@ void Position::undo_move(Move m) {
|
|||||||
assert(color_of(piece) == us);
|
assert(color_of(piece) == us);
|
||||||
assert(capture != KING);
|
assert(capture != KING);
|
||||||
|
|
||||||
if (is_promotion(m))
|
if (type_of(m) == PROMOTION)
|
||||||
{
|
{
|
||||||
PieceType promotion = promotion_type(m);
|
PieceType promotion = promotion_type(m);
|
||||||
|
|
||||||
@@ -988,7 +1062,7 @@ void Position::undo_move(Move m) {
|
|||||||
{
|
{
|
||||||
Square capsq = to;
|
Square capsq = to;
|
||||||
|
|
||||||
if (is_enpassant(m))
|
if (type_of(m) == ENPASSANT)
|
||||||
{
|
{
|
||||||
capsq -= pawn_push(us);
|
capsq -= pawn_push(us);
|
||||||
|
|
||||||
@@ -1025,7 +1099,7 @@ template<bool Do>
|
|||||||
void Position::do_castle_move(Move m) {
|
void Position::do_castle_move(Move m) {
|
||||||
|
|
||||||
assert(is_ok(m));
|
assert(is_ok(m));
|
||||||
assert(is_castle(m));
|
assert(type_of(m) == CASTLE);
|
||||||
|
|
||||||
Square kto, kfrom, rfrom, rto, kAfter, rAfter;
|
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);
|
st->psqScore += psq_delta(rook, rfrom, rto);
|
||||||
|
|
||||||
// Update hash key
|
// Update hash key
|
||||||
st->key ^= zobrist[us][KING][kfrom] ^ zobrist[us][KING][kto];
|
st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto];
|
||||||
st->key ^= zobrist[us][ROOK][rfrom] ^ zobrist[us][ROOK][rto];
|
st->key ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
|
||||||
|
|
||||||
// Clear en passant square
|
// Clear en passant square
|
||||||
if (st->epSquare != SQ_NONE)
|
if (st->epSquare != SQ_NONE)
|
||||||
{
|
{
|
||||||
st->key ^= zobEp[file_of(st->epSquare)];
|
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||||
st->epSquare = SQ_NONE;
|
st->epSquare = SQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update castling rights
|
// Update castling rights
|
||||||
st->key ^= zobCastle[st->castleRights & castleRightsMask[kfrom]];
|
st->key ^= Zobrist::castle[st->castleRights & castleRightsMask[kfrom]];
|
||||||
st->castleRights &= ~castleRightsMask[kfrom];
|
st->castleRights &= ~castleRightsMask[kfrom];
|
||||||
|
|
||||||
// Update checkers BB
|
// Update checkers BB
|
||||||
@@ -1138,9 +1212,9 @@ void Position::do_null_move(StateInfo& backupSt) {
|
|||||||
if (Do)
|
if (Do)
|
||||||
{
|
{
|
||||||
if (st->epSquare != SQ_NONE)
|
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));
|
prefetch((char*)TT.first_entry(st->key));
|
||||||
|
|
||||||
st->epSquare = SQ_NONE;
|
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
|
// Early return if SEE cannot be negative because captured piece value
|
||||||
// is not less then capturing one. Note that king moves always return
|
// is not less then capturing one. Note that king moves always return
|
||||||
// here because king midgame value is set to 0.
|
// here because king midgame value is set to 0.
|
||||||
if (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 1;
|
||||||
|
|
||||||
return see(m);
|
return see(m);
|
||||||
@@ -1178,47 +1252,45 @@ int Position::see_sign(Move m) const {
|
|||||||
int Position::see(Move m) const {
|
int Position::see(Move m) const {
|
||||||
|
|
||||||
Square from, to;
|
Square from, to;
|
||||||
Bitboard occ, attackers, stmAttackers, b;
|
Bitboard occupied, attackers, stmAttackers;
|
||||||
int swapList[32], slIndex = 1;
|
int swapList[32], slIndex = 1;
|
||||||
PieceType capturedType, pt;
|
PieceType captured;
|
||||||
Color stm;
|
Color stm;
|
||||||
|
|
||||||
assert(is_ok(m));
|
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);
|
from = from_sq(m);
|
||||||
to = to_sq(m);
|
to = to_sq(m);
|
||||||
capturedType = type_of(piece_on(to));
|
captured = type_of(piece_on(to));
|
||||||
occ = pieces();
|
occupied = pieces() ^ from;
|
||||||
|
|
||||||
// Handle en passant moves
|
// Handle en passant moves
|
||||||
if (is_enpassant(m))
|
if (type_of(m) == ENPASSANT)
|
||||||
{
|
{
|
||||||
Square capQq = to - pawn_push(sideToMove);
|
Square capQq = to - pawn_push(sideToMove);
|
||||||
|
|
||||||
assert(!capturedType);
|
assert(!captured);
|
||||||
assert(type_of(piece_on(capQq)) == PAWN);
|
assert(type_of(piece_on(capQq)) == PAWN);
|
||||||
|
|
||||||
// Remove the captured pawn
|
// Remove the captured pawn
|
||||||
occ ^= capQq;
|
occupied ^= capQq;
|
||||||
capturedType = PAWN;
|
captured = PAWN;
|
||||||
}
|
}
|
||||||
|
else if (type_of(m) == CASTLE)
|
||||||
|
// Castle moves are implemented as king capturing the rook so cannot be
|
||||||
|
// handled correctly. Simply return 0 that is always the correct value
|
||||||
|
// unless the rook is ends up under attack.
|
||||||
|
return 0;
|
||||||
|
|
||||||
// Find all attackers to the destination square, with the moving piece
|
// Find all attackers to the destination square, with the moving piece
|
||||||
// removed, but possibly an X-ray attacker added behind it.
|
// removed, but possibly an X-ray attacker added behind it.
|
||||||
occ ^= from;
|
attackers = attackers_to(to, occupied);
|
||||||
attackers = attackers_to(to, occ);
|
|
||||||
|
|
||||||
// If the opponent has no attackers we are finished
|
// If the opponent has no attackers we are finished
|
||||||
stm = ~color_of(piece_on(from));
|
stm = ~color_of(piece_on(from));
|
||||||
stmAttackers = attackers & pieces(stm);
|
stmAttackers = attackers & pieces(stm);
|
||||||
if (!stmAttackers)
|
if (!stmAttackers)
|
||||||
return PieceValueMidgame[capturedType];
|
return PieceValue[Mg][captured];
|
||||||
|
|
||||||
// The destination square is defended, which makes things rather more
|
// The destination square is defended, which makes things rather more
|
||||||
// difficult to compute. We proceed by building up a "swap list" containing
|
// difficult to compute. We proceed by building up a "swap list" containing
|
||||||
@@ -1226,43 +1298,32 @@ int Position::see(Move m) const {
|
|||||||
// destination square, where the sides alternately capture, and always
|
// destination square, where the sides alternately capture, and always
|
||||||
// capture with the least valuable piece. After each capture, we look for
|
// capture with the least valuable piece. After each capture, we look for
|
||||||
// new X-ray attacks from behind the capturing piece.
|
// new X-ray attacks from behind the capturing piece.
|
||||||
swapList[0] = PieceValueMidgame[capturedType];
|
swapList[0] = PieceValue[Mg][captured];
|
||||||
capturedType = type_of(piece_on(from));
|
captured = type_of(piece_on(from));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Locate the least valuable attacker for the side to move. The loop
|
assert(slIndex < 32);
|
||||||
// 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
|
|
||||||
|
|
||||||
// Add the new entry to the swap list
|
// Add the new entry to the swap list
|
||||||
assert(slIndex < 32);
|
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[Mg][captured];
|
||||||
swapList[slIndex] = -swapList[slIndex - 1] + PieceValueMidgame[capturedType];
|
|
||||||
slIndex++;
|
slIndex++;
|
||||||
|
|
||||||
// Remember the value of the capturing piece, and change the side to
|
// Locate and remove from 'occupied' the next least valuable attacker
|
||||||
// move before beginning the next iteration.
|
captured = next_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
|
||||||
capturedType = pt;
|
|
||||||
|
attackers &= occupied; // Remove the just found attacker
|
||||||
stm = ~stm;
|
stm = ~stm;
|
||||||
stmAttackers = attackers & pieces(stm);
|
stmAttackers = attackers & pieces(stm);
|
||||||
|
|
||||||
// Stop before processing a king capture
|
if (captured == KING)
|
||||||
if (capturedType == KING && stmAttackers)
|
|
||||||
{
|
{
|
||||||
assert(slIndex < 32);
|
// Stop before processing a king capture
|
||||||
swapList[slIndex++] = QueenValueMidgame*10;
|
if (stmAttackers)
|
||||||
|
swapList[slIndex++] = QueenValueMg * 16;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (stmAttackers);
|
} while (stmAttackers);
|
||||||
|
|
||||||
// Having built the swap list, we negamax through it to find the best
|
// 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 Position::compute_key() const {
|
||||||
|
|
||||||
Key k = zobCastle[st->castleRights];
|
Key k = Zobrist::castle[st->castleRights];
|
||||||
|
|
||||||
for (Bitboard b = pieces(); b; )
|
for (Bitboard b = pieces(); b; )
|
||||||
{
|
{
|
||||||
Square s = pop_1st_bit(&b);
|
Square s = pop_lsb(&b);
|
||||||
k ^= zobrist[color_of(piece_on(s))][type_of(piece_on(s))][s];
|
k ^= Zobrist::psq[color_of(piece_on(s))][type_of(piece_on(s))][s];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep_square() != SQ_NONE)
|
if (ep_square() != SQ_NONE)
|
||||||
k ^= zobEp[file_of(ep_square())];
|
k ^= Zobrist::enpassant[file_of(ep_square())];
|
||||||
|
|
||||||
if (sideToMove == BLACK)
|
if (sideToMove == BLACK)
|
||||||
k ^= zobSideToMove;
|
k ^= Zobrist::side;
|
||||||
|
|
||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
@@ -1347,8 +1408,8 @@ Key Position::compute_pawn_key() const {
|
|||||||
|
|
||||||
for (Bitboard b = pieces(PAWN); b; )
|
for (Bitboard b = pieces(PAWN); b; )
|
||||||
{
|
{
|
||||||
Square s = pop_1st_bit(&b);
|
Square s = pop_lsb(&b);
|
||||||
k ^= zobrist[color_of(piece_on(s))][PAWN][s];
|
k ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s];
|
||||||
}
|
}
|
||||||
|
|
||||||
return k;
|
return k;
|
||||||
@@ -1368,7 +1429,7 @@ Key Position::compute_material_key() const {
|
|||||||
for (Color c = WHITE; c <= BLACK; c++)
|
for (Color c = WHITE; c <= BLACK; c++)
|
||||||
for (PieceType pt = PAWN; pt <= QUEEN; pt++)
|
for (PieceType pt = PAWN; pt <= QUEEN; pt++)
|
||||||
for (int cnt = 0; cnt < piece_count(c, pt); cnt++)
|
for (int cnt = 0; cnt < piece_count(c, pt); cnt++)
|
||||||
k ^= zobrist[c][pt][cnt];
|
k ^= Zobrist::psq[c][pt][cnt];
|
||||||
|
|
||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
@@ -1384,7 +1445,7 @@ Score Position::compute_psq_score() const {
|
|||||||
|
|
||||||
for (Bitboard b = pieces(); b; )
|
for (Bitboard b = pieces(); b; )
|
||||||
{
|
{
|
||||||
Square s = pop_1st_bit(&b);
|
Square s = pop_lsb(&b);
|
||||||
score += pieceSquareTable[piece_on(s)][s];
|
score += pieceSquareTable[piece_on(s)][s];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1402,7 +1463,7 @@ Value Position::compute_non_pawn_material(Color c) const {
|
|||||||
Value value = VALUE_ZERO;
|
Value value = VALUE_ZERO;
|
||||||
|
|
||||||
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
|
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
|
||||||
value += piece_count(c, pt) * PieceValueMidgame[pt];
|
value += piece_count(c, pt) * PieceValue[Mg][pt];
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -1416,11 +1477,11 @@ bool Position::is_draw() const {
|
|||||||
|
|
||||||
// Draw by material?
|
// Draw by material?
|
||||||
if ( !pieces(PAWN)
|
if ( !pieces(PAWN)
|
||||||
&& (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMidgame))
|
&& (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Draw by the 50 moves rule?
|
// 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;
|
return true;
|
||||||
|
|
||||||
// Draw by repetition?
|
// Draw by repetition?
|
||||||
@@ -1452,50 +1513,6 @@ template bool Position::is_draw<false>() const;
|
|||||||
template bool Position::is_draw<true>() 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
|
/// Position::flip() flips position with the white and black sides reversed. This
|
||||||
/// is only useful for debugging especially for finding evaluation symmetry bugs.
|
/// is only useful for debugging especially for finding evaluation symmetry bugs.
|
||||||
|
|
||||||
|
|||||||
@@ -94,10 +94,9 @@ struct ReducedStateInfo {
|
|||||||
class Position {
|
class Position {
|
||||||
public:
|
public:
|
||||||
Position() {}
|
Position() {}
|
||||||
Position(const Position& p) { *this = p; }
|
|
||||||
Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
|
Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
|
||||||
Position(const std::string& f, bool c960, Thread* t) { from_fen(f, c960, 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
|
// Text input/output
|
||||||
void from_fen(const std::string& fen, bool isChess960, Thread* th);
|
void from_fen(const std::string& fen, bool isChess960, Thread* th);
|
||||||
@@ -141,6 +140,7 @@ public:
|
|||||||
// Properties of moves
|
// Properties of moves
|
||||||
bool move_gives_check(Move m, const CheckInfo& ci) const;
|
bool move_gives_check(Move m, const CheckInfo& ci) const;
|
||||||
bool move_attacks_square(Move m, Square s) 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 pl_move_is_legal(Move m, Bitboard pinned) const;
|
||||||
bool is_pseudo_legal(const Move m) const;
|
bool is_pseudo_legal(const Move m) const;
|
||||||
bool is_capture(Move m) const;
|
bool is_capture(Move m) const;
|
||||||
@@ -189,15 +189,11 @@ public:
|
|||||||
bool pos_is_ok(int* failedStep = NULL) const;
|
bool pos_is_ok(int* failedStep = NULL) const;
|
||||||
void flip();
|
void flip();
|
||||||
|
|
||||||
// Global initialization
|
|
||||||
static void init();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Initialization helpers (used while setting up a position)
|
// Initialization helpers (used while setting up a position)
|
||||||
void clear();
|
void clear();
|
||||||
void put_piece(Piece p, Square s);
|
void put_piece(Piece p, Square s);
|
||||||
void set_castle_right(Color c, Square rfrom);
|
void set_castle_right(Color c, Square rfrom);
|
||||||
bool move_is_legal(const Move m) const;
|
|
||||||
|
|
||||||
// Helper template functions
|
// Helper template functions
|
||||||
template<bool Do> void do_castle_move(Move m);
|
template<bool Do> void do_castle_move(Move m);
|
||||||
@@ -231,14 +227,6 @@ private:
|
|||||||
Thread* thisThread;
|
Thread* thisThread;
|
||||||
StateInfo* st;
|
StateInfo* st;
|
||||||
int chess960;
|
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 {
|
inline int64_t Position::nodes_searched() const {
|
||||||
@@ -367,7 +355,7 @@ inline Key Position::key() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Key Position::exclusion_key() const {
|
inline Key Position::exclusion_key() const {
|
||||||
return st->key ^ zobExclusion;
|
return st->key ^ Zobrist::exclusion;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Key Position::pawn_key() const {
|
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 {
|
inline bool Position::is_capture_or_promotion(Move m) const {
|
||||||
|
|
||||||
assert(is_ok(m));
|
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 {
|
inline bool Position::is_capture(Move m) const {
|
||||||
|
|
||||||
// Note that castle is coded as "king captures the rook"
|
// Note that castle is coded as "king captures the rook"
|
||||||
assert(is_ok(m));
|
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 {
|
inline PieceType Position::captured_piece_type() const {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iomanip>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@@ -30,6 +29,7 @@
|
|||||||
#include "history.h"
|
#include "history.h"
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
#include "movepick.h"
|
#include "movepick.h"
|
||||||
|
#include "notation.h"
|
||||||
#include "search.h"
|
#include "search.h"
|
||||||
#include "timeman.h"
|
#include "timeman.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
@@ -42,20 +42,14 @@ namespace Search {
|
|||||||
LimitsType Limits;
|
LimitsType Limits;
|
||||||
std::vector<RootMove> RootMoves;
|
std::vector<RootMove> RootMoves;
|
||||||
Position RootPosition;
|
Position RootPosition;
|
||||||
Time SearchTime;
|
Time::point SearchTime;
|
||||||
|
StateStackPtr SetupStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::cout;
|
|
||||||
using std::endl;
|
|
||||||
using Eval::evaluate;
|
using Eval::evaluate;
|
||||||
using namespace Search;
|
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 {
|
namespace {
|
||||||
|
|
||||||
// Set to true to force running with one thread. Used for debugging
|
// 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);
|
bool connected_threat(const Position& pos, Move m, Move threat);
|
||||||
Value refine_eval(const TTEntry* tte, Value ttValue, Value defaultEval);
|
Value refine_eval(const TTEntry* tte, Value ttValue, Value defaultEval);
|
||||||
Move do_skill_level();
|
Move do_skill_level();
|
||||||
string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE);
|
string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
// is_dangerous() checks whether a move belongs to some classes of known
|
// is_dangerous() checks whether a move belongs to some classes of known
|
||||||
// 'dangerous' moves so that we avoid to prune it.
|
// 'dangerous' moves so that we avoid to prune it.
|
||||||
FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) {
|
FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) {
|
||||||
|
|
||||||
// Test for a pawn pushed to 7th or a passed pawn move
|
// Castle move?
|
||||||
if (type_of(pos.piece_moved(m)) == PAWN)
|
if (type_of(m) == CASTLE)
|
||||||
{
|
return true;
|
||||||
Color c = pos.side_to_move();
|
|
||||||
if ( relative_rank(c, to_sq(m)) == RANK_7
|
|
||||||
|| pos.pawn_is_passed(c, to_sq(m)))
|
|
||||||
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
|
if ( captureOrPromotion
|
||||||
&& type_of(pos.piece_on(to_sq(m))) != PAWN
|
&& 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)
|
&& ( 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 true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -224,24 +197,23 @@ void Search::init() {
|
|||||||
/// Search::perft() is our utility to verify move generation. All the leaf nodes
|
/// Search::perft() is our utility to verify move generation. All the leaf nodes
|
||||||
/// up to the given depth are generated and counted and the sum returned.
|
/// up to the given depth are generated and counted and the sum returned.
|
||||||
|
|
||||||
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;
|
StateInfo st;
|
||||||
int64_t cnt = 0;
|
size_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();
|
|
||||||
|
|
||||||
CheckInfo ci(pos);
|
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));
|
pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci));
|
||||||
cnt += perft(pos, depth - ONE_PLY);
|
cnt += perft(pos, depth - ONE_PLY);
|
||||||
pos.undo_move(ml.move());
|
pos.undo_move(ml.move());
|
||||||
}
|
}
|
||||||
|
|
||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,8 +235,8 @@ void Search::think() {
|
|||||||
|
|
||||||
if (RootMoves.empty())
|
if (RootMoves.empty())
|
||||||
{
|
{
|
||||||
cout << "info depth 0 score "
|
sync_cout << "info depth 0 score "
|
||||||
<< score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl;
|
<< score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << sync_endl;
|
||||||
|
|
||||||
RootMoves.push_back(MOVE_NONE);
|
RootMoves.push_back(MOVE_NONE);
|
||||||
goto finalize;
|
goto finalize;
|
||||||
@@ -274,9 +246,9 @@ void Search::think() {
|
|||||||
{
|
{
|
||||||
Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]);
|
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;
|
goto finalize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,7 +270,7 @@ void Search::think() {
|
|||||||
<< " time: " << Limits.time[pos.side_to_move()]
|
<< " time: " << Limits.time[pos.side_to_move()]
|
||||||
<< " increment: " << Limits.inc[pos.side_to_move()]
|
<< " increment: " << Limits.inc[pos.side_to_move()]
|
||||||
<< " moves to go: " << Limits.movestogo
|
<< " moves to go: " << Limits.movestogo
|
||||||
<< endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
Threads.wake_up();
|
Threads.wake_up();
|
||||||
@@ -318,16 +290,16 @@ void Search::think() {
|
|||||||
|
|
||||||
if (Options["Use Search Log"])
|
if (Options["Use Search Log"])
|
||||||
{
|
{
|
||||||
int e = SearchTime.elapsed();
|
Time::point elapsed = Time::now() - SearchTime + 1;
|
||||||
|
|
||||||
Log log(Options["Search Log Filename"]);
|
Log log(Options["Search Log Filename"]);
|
||||||
log << "Nodes: " << pos.nodes_searched()
|
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]);
|
<< "\nBest move: " << move_to_san(pos, RootMoves[0].pv[0]);
|
||||||
|
|
||||||
StateInfo st;
|
StateInfo st;
|
||||||
pos.do_move(RootMoves[0].pv[0], 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]);
|
pos.undo_move(RootMoves[0].pv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,8 +312,8 @@ finalize:
|
|||||||
pos.this_thread()->wait_for_stop_or_ponderhit();
|
pos.this_thread()->wait_for_stop_or_ponderhit();
|
||||||
|
|
||||||
// Best move could be MOVE_NONE when searching on a stalemate position
|
// Best move could be MOVE_NONE when searching on a stalemate position
|
||||||
cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960)
|
sync_cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960)
|
||||||
<< " ponder " << move_to_uci(RootMoves[0].pv[1], Chess960) << endl;
|
<< " 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,
|
// Start with a small aspiration window and, in case of fail high/low,
|
||||||
// research with bigger window until not failing high/low anymore.
|
// research with bigger window until not failing high/low anymore.
|
||||||
do {
|
while (true)
|
||||||
|
{
|
||||||
// Search starts from ss+1 to allow referencing (ss-1). This is
|
// Search starts from ss+1 to allow referencing (ss-1). This is
|
||||||
// needed by update gains and ss copy when splitting at Root.
|
// needed by update gains and ss copy when splitting at Root.
|
||||||
bestValue = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
|
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
|
// 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 we have a fail high/low and we are deep in the search.
|
||||||
if ((bestValue > alpha && bestValue < beta) || SearchTime.elapsed() > 2000)
|
if ((bestValue > alpha && bestValue < beta) || Time::now() - SearchTime > 2000)
|
||||||
pv_info_to_uci(pos, depth, alpha, beta);
|
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
|
||||||
|
|
||||||
// In case of failing high/low increase aspiration window and
|
// In case of failing high/low increase aspiration window and
|
||||||
// research, otherwise exit the fail high/low loop.
|
// research, otherwise exit the fail high/low loop.
|
||||||
@@ -447,9 +420,15 @@ namespace {
|
|||||||
else
|
else
|
||||||
break;
|
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 ?
|
// Skills: Do we need to pick now the best move ?
|
||||||
@@ -457,7 +436,11 @@ namespace {
|
|||||||
skillBest = do_skill_level();
|
skillBest = do_skill_level();
|
||||||
|
|
||||||
if (!Signals.stop && Options["Use Search Log"])
|
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
|
// Filter out startup noise when monitoring best move stability
|
||||||
if (depth > 2 && BestMoveChanges)
|
if (depth > 2 && BestMoveChanges)
|
||||||
@@ -475,14 +458,14 @@ namespace {
|
|||||||
// Stop search if most of available time is already consumed. We
|
// Stop search if most of available time is already consumed. We
|
||||||
// probably don't have enough time to search the first move at the
|
// probably don't have enough time to search the first move at the
|
||||||
// next iteration anyway.
|
// next iteration anyway.
|
||||||
if (SearchTime.elapsed() > (TimeMgr.available_time() * 62) / 100)
|
if (Time::now() - SearchTime > (TimeMgr.available_time() * 62) / 100)
|
||||||
stop = true;
|
stop = true;
|
||||||
|
|
||||||
// Stop search early if one move seems to be much better than others
|
// Stop search early if one move seems to be much better than others
|
||||||
if ( depth >= 12
|
if ( depth >= 12
|
||||||
&& !stop
|
&& !stop
|
||||||
&& ( (bestMoveNeverChanged && pos.captured_piece_type())
|
&& ( (bestMoveNeverChanged && pos.captured_piece_type())
|
||||||
|| SearchTime.elapsed() > (TimeMgr.available_time() * 40) / 100))
|
|| Time::now() - SearchTime > (TimeMgr.available_time() * 40) / 100))
|
||||||
{
|
{
|
||||||
Value rBeta = bestValue - EasyMoveMargin;
|
Value rBeta = bestValue - EasyMoveMargin;
|
||||||
(ss+1)->excludedMove = RootMoves[0].pv[0];
|
(ss+1)->excludedMove = RootMoves[0].pv[0];
|
||||||
@@ -513,7 +496,7 @@ namespace {
|
|||||||
if (skillBest == MOVE_NONE) // Still unassigned ?
|
if (skillBest == MOVE_NONE) // Still unassigned ?
|
||||||
skillBest = do_skill_level();
|
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-1)->eval != VALUE_NONE
|
||||||
&& ss->eval != VALUE_NONE
|
&& ss->eval != VALUE_NONE
|
||||||
&& !pos.captured_piece_type()
|
&& !pos.captured_piece_type()
|
||||||
&& !is_special(move))
|
&& type_of(move) == NORMAL)
|
||||||
{
|
{
|
||||||
Square to = to_sq(move);
|
Square to = to_sq(move);
|
||||||
H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval);
|
H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval);
|
||||||
@@ -708,16 +691,16 @@ namespace {
|
|||||||
ss->currentMove = MOVE_NULL;
|
ss->currentMove = MOVE_NULL;
|
||||||
|
|
||||||
// Null move dynamic reduction based on depth
|
// 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
|
// Null move dynamic reduction based on value
|
||||||
if (refinedValue - PawnValueMidgame > beta)
|
if (refinedValue - PawnValueMg > beta)
|
||||||
R++;
|
R += ONE_PLY;
|
||||||
|
|
||||||
pos.do_null_move<true>(st);
|
pos.do_null_move<true>(st);
|
||||||
(ss+1)->skipNullMove = true;
|
(ss+1)->skipNullMove = true;
|
||||||
nullValue = depth-R*ONE_PLY < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
|
nullValue = depth-R < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
|
||||||
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R*ONE_PLY);
|
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R);
|
||||||
(ss+1)->skipNullMove = false;
|
(ss+1)->skipNullMove = false;
|
||||||
pos.do_null_move<false>(st);
|
pos.do_null_move<false>(st);
|
||||||
|
|
||||||
@@ -732,7 +715,7 @@ namespace {
|
|||||||
|
|
||||||
// Do verification search at high depths
|
// Do verification search at high depths
|
||||||
ss->skipNullMove = true;
|
ss->skipNullMove = true;
|
||||||
Value v = search<NonPV>(pos, ss, alpha, beta, depth-R*ONE_PLY);
|
Value v = search<NonPV>(pos, ss, alpha, beta, depth-R);
|
||||||
ss->skipNullMove = false;
|
ss->skipNullMove = false;
|
||||||
|
|
||||||
if (v >= beta)
|
if (v >= beta)
|
||||||
@@ -777,7 +760,7 @@ namespace {
|
|||||||
MovePicker mp(pos, ttMove, H, pos.captured_piece_type());
|
MovePicker mp(pos, ttMove, H, pos.captured_piece_type());
|
||||||
CheckInfo ci(pos);
|
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))
|
if (pos.pl_move_is_legal(move, ci.pinned))
|
||||||
{
|
{
|
||||||
ss->currentMove = move;
|
ss->currentMove = move;
|
||||||
@@ -806,7 +789,7 @@ namespace {
|
|||||||
|
|
||||||
split_point_start: // At split points actual search starts from here
|
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);
|
CheckInfo ci(pos);
|
||||||
futilityBase = ss->eval + ss->evalMargin;
|
futilityBase = ss->eval + ss->evalMargin;
|
||||||
singularExtensionNode = !RootNode
|
singularExtensionNode = !RootNode
|
||||||
@@ -820,7 +803,7 @@ split_point_start: // At split points actual search starts from here
|
|||||||
// Step 11. Loop through moves
|
// Step 11. Loop through moves
|
||||||
// Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs
|
// Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs
|
||||||
while ( bestValue < beta
|
while ( bestValue < beta
|
||||||
&& (move = mp.next_move()) != MOVE_NONE
|
&& (move = mp.next_move<SpNode>()) != MOVE_NONE
|
||||||
&& !thisThread->cutoff_occurred()
|
&& !thisThread->cutoff_occurred()
|
||||||
&& !Signals.stop)
|
&& !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
|
// 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
|
// Move List, as a consequence any illegal move is also skipped. In MultiPV
|
||||||
// mode we also skip PV moves which have been already searched.
|
// 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;
|
continue;
|
||||||
|
|
||||||
// At PV and SpNode nodes we want all moves to be legal since the beginning
|
// 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)
|
if (SpNode)
|
||||||
{
|
{
|
||||||
moveCount = ++sp->moveCount;
|
moveCount = ++sp->moveCount;
|
||||||
lock_release(sp->lock);
|
sp->mutex.unlock();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
moveCount++;
|
moveCount++;
|
||||||
@@ -851,10 +834,10 @@ split_point_start: // At split points actual search starts from here
|
|||||||
{
|
{
|
||||||
Signals.firstRootMove = (moveCount == 1);
|
Signals.firstRootMove = (moveCount == 1);
|
||||||
|
|
||||||
if (thisThread == Threads.main_thread() && SearchTime.elapsed() > 2000)
|
if (thisThread == Threads.main_thread() && Time::now() - SearchTime > 2000)
|
||||||
cout << "info depth " << depth / ONE_PLY
|
sync_cout << "info depth " << depth / ONE_PLY
|
||||||
<< " currmove " << move_to_uci(move, Chess960)
|
<< " currmove " << move_to_uci(move, Chess960)
|
||||||
<< " currmovenumber " << moveCount + PVIdx << endl;
|
<< " currmovenumber " << moveCount + PVIdx << sync_endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPvMove = (PvNode && moveCount <= 1);
|
isPvMove = (PvNode && moveCount <= 1);
|
||||||
@@ -878,19 +861,18 @@ split_point_start: // At split points actual search starts from here
|
|||||||
if ( singularExtensionNode
|
if ( singularExtensionNode
|
||||||
&& !ext
|
&& !ext
|
||||||
&& move == ttMove
|
&& move == ttMove
|
||||||
&& pos.pl_move_is_legal(move, ci.pinned))
|
&& pos.pl_move_is_legal(move, ci.pinned)
|
||||||
|
&& abs(ttValue) < VALUE_KNOWN_WIN)
|
||||||
{
|
{
|
||||||
if (abs(ttValue) < VALUE_KNOWN_WIN)
|
Value rBeta = ttValue - int(depth);
|
||||||
{
|
ss->excludedMove = move;
|
||||||
Value rBeta = ttValue - int(depth);
|
ss->skipNullMove = true;
|
||||||
ss->excludedMove = move;
|
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
|
||||||
ss->skipNullMove = true;
|
ss->skipNullMove = false;
|
||||||
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
|
ss->excludedMove = MOVE_NONE;
|
||||||
ss->skipNullMove = false;
|
|
||||||
ss->excludedMove = MOVE_NONE;
|
if (value < rBeta)
|
||||||
if (value < rBeta)
|
ext = ONE_PLY;
|
||||||
ext = ONE_PLY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update current move (this must be done after singular extension search)
|
// Update current move (this must be done after singular extension search)
|
||||||
@@ -902,7 +884,6 @@ split_point_start: // At split points actual search starts from here
|
|||||||
&& !inCheck
|
&& !inCheck
|
||||||
&& !dangerous
|
&& !dangerous
|
||||||
&& move != ttMove
|
&& move != ttMove
|
||||||
&& !is_castle(move)
|
|
||||||
&& (bestValue > VALUE_MATED_IN_MAX_PLY || bestValue == -VALUE_INFINITE))
|
&& (bestValue > VALUE_MATED_IN_MAX_PLY || bestValue == -VALUE_INFINITE))
|
||||||
{
|
{
|
||||||
// Move count based pruning
|
// 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)))
|
&& (!threatMove || !connected_threat(pos, move, threatMove)))
|
||||||
{
|
{
|
||||||
if (SpNode)
|
if (SpNode)
|
||||||
lock_grab(sp->lock);
|
sp->mutex.lock();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -925,7 +906,7 @@ split_point_start: // At split points actual search starts from here
|
|||||||
if (futilityValue < beta)
|
if (futilityValue < beta)
|
||||||
{
|
{
|
||||||
if (SpNode)
|
if (SpNode)
|
||||||
lock_grab(sp->lock);
|
sp->mutex.lock();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -935,7 +916,7 @@ split_point_start: // At split points actual search starts from here
|
|||||||
&& pos.see_sign(move) < 0)
|
&& pos.see_sign(move) < 0)
|
||||||
{
|
{
|
||||||
if (SpNode)
|
if (SpNode)
|
||||||
lock_grab(sp->lock);
|
sp->mutex.lock();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -961,7 +942,6 @@ split_point_start: // At split points actual search starts from here
|
|||||||
&& !isPvMove
|
&& !isPvMove
|
||||||
&& !captureOrPromotion
|
&& !captureOrPromotion
|
||||||
&& !dangerous
|
&& !dangerous
|
||||||
&& !is_castle(move)
|
|
||||||
&& ss->killers[0] != move
|
&& ss->killers[0] != move
|
||||||
&& ss->killers[1] != 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
|
// Step 18. Check for new best move
|
||||||
if (SpNode)
|
if (SpNode)
|
||||||
{
|
{
|
||||||
lock_grab(sp->lock);
|
sp->mutex.lock();
|
||||||
bestValue = sp->bestValue;
|
bestValue = sp->bestValue;
|
||||||
alpha = sp->alpha;
|
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.
|
// be trusted, and we don't update the best move and/or PV.
|
||||||
if (RootNode && !Signals.stop)
|
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 ?
|
// PV move or new best move ?
|
||||||
if (isPvMove || value > alpha)
|
if (isPvMove || value > alpha)
|
||||||
@@ -1202,7 +1182,7 @@ split_point_start: // At split points actual search starts from here
|
|||||||
alpha = bestValue;
|
alpha = bestValue;
|
||||||
|
|
||||||
futilityBase = ss->eval + evalMargin + FutilityMarginQS;
|
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
|
// 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
|
// Loop through the moves until no moves remain or a beta cutoff occurs
|
||||||
while ( bestValue < beta
|
while ( bestValue < beta
|
||||||
&& (move = mp.next_move()) != MOVE_NONE)
|
&& (move = mp.next_move<false>()) != MOVE_NONE)
|
||||||
{
|
{
|
||||||
assert(is_ok(move));
|
assert(is_ok(move));
|
||||||
|
|
||||||
@@ -1226,12 +1206,12 @@ split_point_start: // At split points actual search starts from here
|
|||||||
&& !givesCheck
|
&& !givesCheck
|
||||||
&& move != ttMove
|
&& move != ttMove
|
||||||
&& enoughMaterial
|
&& enoughMaterial
|
||||||
&& !is_promotion(move)
|
&& type_of(move) != PROMOTION
|
||||||
&& !pos.is_passed_pawn_push(move))
|
&& !pos.is_passed_pawn_push(move))
|
||||||
{
|
{
|
||||||
futilityValue = futilityBase
|
futilityValue = futilityBase
|
||||||
+ PieceValueEndgame[pos.piece_on(to_sq(move))]
|
+ PieceValue[Eg][pos.piece_on(to_sq(move))]
|
||||||
+ (is_enpassant(move) ? PawnValueEndgame : VALUE_ZERO);
|
+ (type_of(move) == ENPASSANT ? PawnValueEg : VALUE_ZERO);
|
||||||
|
|
||||||
if (futilityValue < beta)
|
if (futilityValue < beta)
|
||||||
{
|
{
|
||||||
@@ -1259,7 +1239,7 @@ split_point_start: // At split points actual search starts from here
|
|||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& (!inCheck || evasionPrunable)
|
&& (!inCheck || evasionPrunable)
|
||||||
&& move != ttMove
|
&& move != ttMove
|
||||||
&& !is_promotion(move)
|
&& type_of(move) != PROMOTION
|
||||||
&& pos.see_sign(move) < 0)
|
&& pos.see_sign(move) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -1269,7 +1249,7 @@ split_point_start: // At split points actual search starts from here
|
|||||||
&& givesCheck
|
&& givesCheck
|
||||||
&& move != ttMove
|
&& move != ttMove
|
||||||
&& !pos.is_capture_or_promotion(move)
|
&& !pos.is_capture_or_promotion(move)
|
||||||
&& ss->eval + PawnValueMidgame / 4 < beta
|
&& ss->eval + PawnValueMg / 4 < beta
|
||||||
&& !check_is_dangerous(pos, move, futilityBase, beta))
|
&& !check_is_dangerous(pos, move, futilityBase, beta))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -1354,7 +1334,7 @@ split_point_start: // At split points actual search starts from here
|
|||||||
while (b)
|
while (b)
|
||||||
{
|
{
|
||||||
// Note that here we generate illegal "double move"!
|
// 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;
|
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
|
// 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.
|
// value of the threatening piece, don't prune moves which defend it.
|
||||||
if ( pos.is_capture(threat)
|
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)
|
|| type_of(pos.piece_on(tfrom)) == KING)
|
||||||
&& pos.move_attacks_square(m, tto))
|
&& pos.move_attacks_square(m, tto))
|
||||||
return true;
|
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
|
// When playing with strength handicap choose best move among the MultiPV set
|
||||||
// using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen.
|
// 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;
|
static RKISS rk;
|
||||||
|
|
||||||
// PRNG sequence should be not deterministic
|
// 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>();
|
rk.rand<unsigned>();
|
||||||
|
|
||||||
// RootMoves are already sorted by score in descending order
|
// RootMoves are already sorted by score in descending order
|
||||||
size_t size = std::min(MultiPV, RootMoves.size());
|
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 weakness = 120 - 2 * SkillLevel;
|
||||||
int max_s = -VALUE_INFINITE;
|
int max_s = -VALUE_INFINITE;
|
||||||
Move best = MOVE_NONE;
|
Move best = MOVE_NONE;
|
||||||
@@ -1705,6 +1535,50 @@ split_point_start: // At split points actual search starts from here
|
|||||||
return best;
|
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
|
} // 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.
|
/// 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.
|
|
||||||
|
|
||||||
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
|
// 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.
|
// 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()
|
// 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 we are master and all slaves have finished don't go to sleep
|
||||||
if (sp_master && !sp_master->slavesMask)
|
if (sp_master && !sp_master->slavesMask)
|
||||||
{
|
{
|
||||||
lock_release(sleepLock);
|
mutex.unlock();
|
||||||
break;
|
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
|
// in the meanwhile, allocated us and sent the wake_up() call before we
|
||||||
// had the chance to grab the lock.
|
// had the chance to grab the lock.
|
||||||
if (do_sleep || !is_searching)
|
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
|
// 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);
|
assert(!do_sleep && !do_exit);
|
||||||
|
|
||||||
lock_grab(Threads.splitLock);
|
Threads.mutex.lock();
|
||||||
|
|
||||||
assert(is_searching);
|
assert(is_searching);
|
||||||
SplitPoint* sp = curSplitPoint;
|
SplitPoint* sp = curSplitPoint;
|
||||||
|
|
||||||
lock_release(Threads.splitLock);
|
Threads.mutex.unlock();
|
||||||
|
|
||||||
Stack ss[MAX_PLY_PLUS_2];
|
Stack ss[MAX_PLY_PLUS_2];
|
||||||
Position pos(*sp->pos, this);
|
Position pos(*sp->pos, this);
|
||||||
@@ -1835,7 +1713,7 @@ void Thread::idle_loop(SplitPoint* sp_master) {
|
|||||||
memcpy(ss, sp->ss - 1, 4 * sizeof(Stack));
|
memcpy(ss, sp->ss - 1, 4 * sizeof(Stack));
|
||||||
(ss+1)->sp = sp;
|
(ss+1)->sp = sp;
|
||||||
|
|
||||||
lock_grab(sp->lock);
|
sp->mutex.lock();
|
||||||
|
|
||||||
if (sp->nodeType == Root)
|
if (sp->nodeType == Root)
|
||||||
search<SplitPointRoot>(pos, ss+1, sp->alpha, sp->beta, sp->depth);
|
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.
|
// case we are the last slave of the split point.
|
||||||
if ( Threads.use_sleeping_threads()
|
if ( Threads.use_sleeping_threads()
|
||||||
&& this != sp->master
|
&& this != sp->master
|
||||||
&& !sp->master->is_searching)
|
&& !sp->slavesMask)
|
||||||
|
{
|
||||||
|
assert(!sp->master->is_searching);
|
||||||
sp->master->wake_up();
|
sp->master->wake_up();
|
||||||
|
}
|
||||||
|
|
||||||
// After releasing the lock we cannot access anymore any SplitPoint
|
// After releasing the lock we cannot access anymore any SplitPoint
|
||||||
// related data in a safe way becuase it could have been released under
|
// 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
|
// 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.
|
// 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() {
|
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();
|
dbg_print();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Limits.ponder)
|
if (Limits.ponder)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int e = SearchTime.elapsed();
|
Time::point elapsed = Time::now() - SearchTime;
|
||||||
bool stillAtFirstMove = Signals.firstRootMove
|
bool stillAtFirstMove = Signals.firstRootMove
|
||||||
&& !Signals.failedLowAtRoot
|
&& !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;
|
|| stillAtFirstMove;
|
||||||
|
|
||||||
if ( (Limits.use_time_management() && noMoreTime)
|
if ( (Limits.use_time_management() && noMoreTime)
|
||||||
|| (Limits.movetime && e >= Limits.movetime))
|
|| (Limits.movetime && elapsed >= Limits.movetime))
|
||||||
Signals.stop = true;
|
Signals.stop = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,14 @@
|
|||||||
#define SEARCH_H_INCLUDED
|
#define SEARCH_H_INCLUDED
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
#include <stack>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "position.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
class Position;
|
|
||||||
struct SplitPoint;
|
struct SplitPoint;
|
||||||
|
|
||||||
namespace Search {
|
namespace Search {
|
||||||
@@ -91,14 +93,17 @@ struct SignalsType {
|
|||||||
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
|
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
|
||||||
|
|
||||||
extern volatile SignalsType Signals;
|
extern volatile SignalsType Signals;
|
||||||
extern LimitsType Limits;
|
extern LimitsType Limits;
|
||||||
extern std::vector<RootMove> RootMoves;
|
extern std::vector<RootMove> RootMoves;
|
||||||
extern Position RootPosition;
|
extern Position RootPosition;
|
||||||
extern Time SearchTime;
|
extern Time::point SearchTime;
|
||||||
|
extern StateStackPtr SetupStates;
|
||||||
|
|
||||||
extern void init();
|
extern void init();
|
||||||
extern int64_t perft(Position& pos, Depth depth);
|
extern size_t perft(Position& pos, Depth depth);
|
||||||
extern void think();
|
extern void think();
|
||||||
|
|
||||||
} // namespace Search
|
} // namespace Search
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
using namespace Search;
|
using namespace Search;
|
||||||
|
|
||||||
ThreadsManager Threads; // Global object
|
ThreadPool Threads; // Global object
|
||||||
|
|
||||||
namespace { extern "C" {
|
namespace { extern "C" {
|
||||||
|
|
||||||
@@ -52,12 +52,6 @@ Thread::Thread(Fn fn) {
|
|||||||
|
|
||||||
do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching()
|
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))
|
if (!thread_create(handle, start_routine, this))
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to create thread number " << idx << std::endl;
|
std::cerr << "Failed to create thread number " << idx << std::endl;
|
||||||
@@ -74,14 +68,7 @@ Thread::~Thread() {
|
|||||||
|
|
||||||
do_exit = true; // Search must be already finished
|
do_exit = true; // Search must be already finished
|
||||||
wake_up();
|
wake_up();
|
||||||
|
|
||||||
thread_join(handle); // Wait for thread termination
|
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)
|
while (!do_exit)
|
||||||
{
|
{
|
||||||
lock_grab(sleepLock);
|
mutex.lock();
|
||||||
timed_wait(sleepCond, sleepLock, maxPly ? maxPly : INT_MAX);
|
sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX);
|
||||||
lock_release(sleepLock);
|
mutex.unlock();
|
||||||
check_time();
|
check_time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,18 +95,18 @@ void Thread::main_loop() {
|
|||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
lock_grab(sleepLock);
|
mutex.lock();
|
||||||
|
|
||||||
do_sleep = true; // Always return to sleep after a search
|
do_sleep = true; // Always return to sleep after a search
|
||||||
is_searching = false;
|
is_searching = false;
|
||||||
|
|
||||||
while (do_sleep && !do_exit)
|
while (do_sleep && !do_exit)
|
||||||
{
|
{
|
||||||
cond_signal(Threads.sleepCond); // Wake up UI thread if needed
|
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
|
||||||
cond_wait(sleepCond, sleepLock);
|
sleepCondition.wait(mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
lock_release(sleepLock);
|
mutex.unlock();
|
||||||
|
|
||||||
if (do_exit)
|
if (do_exit)
|
||||||
return;
|
return;
|
||||||
@@ -127,6 +114,8 @@ void Thread::main_loop() {
|
|||||||
is_searching = true;
|
is_searching = true;
|
||||||
|
|
||||||
Search::think();
|
Search::think();
|
||||||
|
|
||||||
|
assert(is_searching);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,9 +125,9 @@ void Thread::main_loop() {
|
|||||||
|
|
||||||
void Thread::wake_up() {
|
void Thread::wake_up() {
|
||||||
|
|
||||||
lock_grab(sleepLock);
|
mutex.lock();
|
||||||
cond_signal(sleepCond);
|
sleepCondition.notify_one();
|
||||||
lock_release(sleepLock);
|
mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -153,9 +142,9 @@ void Thread::wait_for_stop_or_ponderhit() {
|
|||||||
|
|
||||||
Signals.stopOnPonderhit = true;
|
Signals.stopOnPonderhit = true;
|
||||||
|
|
||||||
lock_grab(sleepLock);
|
mutex.lock();
|
||||||
while (!Signals.stop) cond_wait(sleepCond, sleepLock);
|
while (!Signals.stop) sleepCondition.wait(mutex);;
|
||||||
lock_release(sleepLock);
|
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
|
// 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.
|
// 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);
|
timer = new Thread(&Thread::timer_loop);
|
||||||
threads.push_back(new Thread(&Thread::main_loop));
|
threads.push_back(new Thread(&Thread::main_loop));
|
||||||
read_uci_options();
|
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 threads[i];
|
||||||
|
|
||||||
delete timer;
|
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
|
// objects are dynamically allocated to avoid creating in advance all possible
|
||||||
// threads, with included pawns and material tables, if only few are used.
|
// 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"];
|
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
|
||||||
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
|
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
|
||||||
useSleepingThreads = Options["Use Sleeping Threads"];
|
useSleepingThreads = Options["Use Sleeping Threads"];
|
||||||
int requested = Options["Threads"];
|
size_t requested = Options["Threads"];
|
||||||
|
|
||||||
assert(requested > 0);
|
assert(requested > 0);
|
||||||
|
|
||||||
while (size() < requested)
|
while (threads.size() < requested)
|
||||||
threads.push_back(new Thread(&Thread::idle_loop));
|
threads.push_back(new Thread(&Thread::idle_loop));
|
||||||
|
|
||||||
while (size() > requested)
|
while (threads.size() > requested)
|
||||||
{
|
{
|
||||||
delete threads.back();
|
delete threads.back();
|
||||||
threads.pop_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
|
// on the sleep condition and to reset maxPly. When useSleepingThreads is set
|
||||||
// threads will be woken up at split time.
|
// 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]->maxPly = 0;
|
||||||
threads[i]->do_sleep = false;
|
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
|
// sleep() is called after the search finishes to ask all the threads but the
|
||||||
// main one to go waiting on a sleep condition.
|
// 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
|
// Main thread will go to sleep by itself to avoid a race with start_searching()
|
||||||
threads[i]->do_sleep = true; // 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
|
// available_slave_exists() tries to find an idle thread which is available as
|
||||||
// a slave for the thread 'master'.
|
// 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))
|
if (threads[i]->is_available_to(master))
|
||||||
return true;
|
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.
|
// search(). When all threads have returned from search() then split() returns.
|
||||||
|
|
||||||
template <bool Fake>
|
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,
|
Value bestValue, Move* bestMove, Depth depth,
|
||||||
Move threatMove, int moveCount, MovePicker* mp, int nodeType) {
|
Move threatMove, int moveCount, MovePicker* mp, int nodeType) {
|
||||||
|
|
||||||
assert(pos.pos_is_ok());
|
assert(pos.pos_is_ok());
|
||||||
assert(bestValue > -VALUE_INFINITE);
|
assert(bestValue > -VALUE_INFINITE);
|
||||||
assert(bestValue <= alpha);
|
assert(bestValue <= alpha);
|
||||||
@@ -313,41 +300,41 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
|
|||||||
return bestValue;
|
return bestValue;
|
||||||
|
|
||||||
// Pick the next available split point from the split point stack
|
// 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.parent = master->curSplitPoint;
|
||||||
sp->master = master;
|
sp.master = master;
|
||||||
sp->cutoff = false;
|
sp.cutoff = false;
|
||||||
sp->slavesMask = 1ULL << master->idx;
|
sp.slavesMask = 1ULL << master->idx;
|
||||||
sp->depth = depth;
|
sp.depth = depth;
|
||||||
sp->bestMove = *bestMove;
|
sp.bestMove = *bestMove;
|
||||||
sp->threatMove = threatMove;
|
sp.threatMove = threatMove;
|
||||||
sp->alpha = alpha;
|
sp.alpha = alpha;
|
||||||
sp->beta = beta;
|
sp.beta = beta;
|
||||||
sp->nodeType = nodeType;
|
sp.nodeType = nodeType;
|
||||||
sp->bestValue = bestValue;
|
sp.bestValue = bestValue;
|
||||||
sp->mp = mp;
|
sp.mp = mp;
|
||||||
sp->moveCount = moveCount;
|
sp.moveCount = moveCount;
|
||||||
sp->pos = &pos;
|
sp.pos = &pos;
|
||||||
sp->nodes = 0;
|
sp.nodes = 0;
|
||||||
sp->ss = ss;
|
sp.ss = ss;
|
||||||
|
|
||||||
assert(master->is_searching);
|
assert(master->is_searching);
|
||||||
|
|
||||||
master->curSplitPoint = sp;
|
master->curSplitPoint = &sp;
|
||||||
int slavesCnt = 0;
|
int slavesCnt = 0;
|
||||||
|
|
||||||
// Try to allocate available threads and ask them to start searching setting
|
// 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
|
// is_searching flag. This must be done under lock protection to avoid concurrent
|
||||||
// allocation of the same slave by another master.
|
// allocation of the same slave by another master.
|
||||||
lock_grab(sp->lock);
|
sp.mutex.lock();
|
||||||
lock_grab(splitLock);
|
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))
|
if (threads[i]->is_available_to(master))
|
||||||
{
|
{
|
||||||
sp->slavesMask |= 1ULL << i;
|
sp.slavesMask |= 1ULL << i;
|
||||||
threads[i]->curSplitPoint = sp;
|
threads[i]->curSplitPoint = &sp;
|
||||||
threads[i]->is_searching = true; // Slave leaves idle_loop()
|
threads[i]->is_searching = true; // Slave leaves idle_loop()
|
||||||
|
|
||||||
if (useSleepingThreads)
|
if (useSleepingThreads)
|
||||||
@@ -359,17 +346,16 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
|
|||||||
|
|
||||||
master->splitPointsCnt++;
|
master->splitPointsCnt++;
|
||||||
|
|
||||||
lock_release(splitLock);
|
mutex.unlock();
|
||||||
lock_release(sp->lock);
|
sp.mutex.unlock();
|
||||||
|
|
||||||
// Everything is set up. The master thread enters the idle loop, from which
|
// 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.
|
// 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.
|
// their work at this split point.
|
||||||
if (slavesCnt || Fake)
|
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
|
// 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.
|
// 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
|
// We have returned from the idle loop, which means that all threads are
|
||||||
// finished. Note that setting is_searching and decreasing splitPointsCnt is
|
// finished. Note that setting is_searching and decreasing splitPointsCnt is
|
||||||
// done under lock protection to avoid a race with Thread::is_available_to().
|
// done under lock protection to avoid a race with Thread::is_available_to().
|
||||||
lock_grab(sp->lock); // To protect sp->nodes
|
sp.mutex.lock(); // To protect sp.nodes
|
||||||
lock_grab(splitLock);
|
mutex.lock();
|
||||||
|
|
||||||
master->is_searching = true;
|
master->is_searching = true;
|
||||||
master->splitPointsCnt--;
|
master->splitPointsCnt--;
|
||||||
master->curSplitPoint = sp->parent;
|
master->curSplitPoint = sp.parent;
|
||||||
pos.set_nodes_searched(pos.nodes_searched() + sp->nodes);
|
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
|
||||||
*bestMove = sp->bestMove;
|
*bestMove = sp.bestMove;
|
||||||
|
|
||||||
lock_release(splitLock);
|
mutex.unlock();
|
||||||
lock_release(sp->lock);
|
sp.mutex.unlock();
|
||||||
|
|
||||||
return sp->bestValue;
|
return sp.bestValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiations
|
// Explicit template instantiations
|
||||||
template Value ThreadsManager::split<false>(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 ThreadsManager::split<true>(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
|
// set_timer() is used to set the timer to trigger after msec milliseconds.
|
||||||
// milliseconds. If msec is 0 then timer is stopped.
|
// 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;
|
timer->maxPly = msec;
|
||||||
cond_signal(timer->sleepCond); // Wake up and restart the timer
|
timer->sleepCondition.notify_one(); // Wake up and restart the timer
|
||||||
lock_release(timer->sleepLock);
|
timer->mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ThreadsManager::wait_for_search_finished() waits for main thread to go to
|
// wait_for_search_finished() waits for main thread to go to sleep, this means
|
||||||
// sleep, this means search is finished. Then returns.
|
// search is finished. Then returns.
|
||||||
|
|
||||||
void ThreadsManager::wait_for_search_finished() {
|
void ThreadPool::wait_for_search_finished() {
|
||||||
|
|
||||||
Thread* t = main_thread();
|
Thread* t = main_thread();
|
||||||
lock_grab(t->sleepLock);
|
t->mutex.lock();
|
||||||
cond_signal(t->sleepCond); // In case is waiting for stop or ponderhit
|
t->sleepCondition.notify_one(); // In case is waiting for stop or ponderhit
|
||||||
while (!t->do_sleep) cond_wait(sleepCond, t->sleepLock);
|
while (!t->do_sleep) sleepCondition.wait(t->mutex);
|
||||||
lock_release(t->sleepLock);
|
t->mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ThreadsManager::start_searching() wakes up the main thread sleeping in
|
// start_searching() wakes up the main thread sleeping in main_loop() so to start
|
||||||
// main_loop() so to start a new search, then returns immediately.
|
// a new search, then returns immediately.
|
||||||
|
|
||||||
void ThreadsManager::start_searching(const Position& pos, const LimitsType& limits,
|
void ThreadPool::start_searching(const Position& pos, const LimitsType& limits,
|
||||||
const std::vector<Move>& searchMoves) {
|
const std::vector<Move>& searchMoves, StateStackPtr& states) {
|
||||||
wait_for_search_finished();
|
wait_for_search_finished();
|
||||||
|
|
||||||
SearchTime.restart(); // As early as possible
|
SearchTime = Time::now(); // As early as possible
|
||||||
|
|
||||||
Signals.stopOnPonderhit = Signals.firstRootMove = false;
|
Signals.stopOnPonderhit = Signals.firstRootMove = false;
|
||||||
Signals.stop = Signals.failedLowAtRoot = false;
|
Signals.stop = Signals.failedLowAtRoot = false;
|
||||||
|
|
||||||
RootPosition = pos;
|
RootPosition = pos;
|
||||||
Limits = limits;
|
Limits = limits;
|
||||||
|
SetupStates = states; // Ownership transfer here
|
||||||
RootMoves.clear();
|
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()))
|
if (searchMoves.empty() || std::count(searchMoves.begin(), searchMoves.end(), ml.move()))
|
||||||
RootMoves.push_back(RootMove(ml.move()));
|
RootMoves.push_back(RootMove(ml.move()));
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,31 @@
|
|||||||
const int MAX_THREADS = 32;
|
const int MAX_THREADS = 32;
|
||||||
const int MAX_SPLITPOINTS_PER_THREAD = 8;
|
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;
|
class Thread;
|
||||||
|
|
||||||
struct SplitPoint {
|
struct SplitPoint {
|
||||||
@@ -49,7 +74,7 @@ struct SplitPoint {
|
|||||||
SplitPoint* parent;
|
SplitPoint* parent;
|
||||||
|
|
||||||
// Shared data
|
// Shared data
|
||||||
Lock lock;
|
Mutex mutex;
|
||||||
volatile uint64_t slavesMask;
|
volatile uint64_t slavesMask;
|
||||||
volatile int64_t nodes;
|
volatile int64_t nodes;
|
||||||
volatile Value alpha;
|
volatile Value alpha;
|
||||||
@@ -67,20 +92,16 @@ struct SplitPoint {
|
|||||||
|
|
||||||
class Thread {
|
class Thread {
|
||||||
|
|
||||||
Thread(const Thread&); // Only declared to disable the default ones
|
typedef void (Thread::* Fn) (); // Pointer to member function
|
||||||
Thread& operator=(const Thread&); // that are not suitable in this case.
|
|
||||||
|
|
||||||
typedef void (Thread::* Fn) ();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Thread(Fn fn);
|
Thread(Fn fn);
|
||||||
~Thread();
|
~Thread();
|
||||||
|
|
||||||
void wake_up();
|
void wake_up();
|
||||||
bool cutoff_occurred() const;
|
bool cutoff_occurred() const;
|
||||||
bool is_available_to(Thread* master) const;
|
bool is_available_to(Thread* master) const;
|
||||||
void idle_loop(SplitPoint* sp_master);
|
void idle_loop();
|
||||||
void idle_loop() { idle_loop(NULL); } // Hack to allow storing in start_fn
|
|
||||||
void main_loop();
|
void main_loop();
|
||||||
void timer_loop();
|
void timer_loop();
|
||||||
void wait_for_stop_or_ponderhit();
|
void wait_for_stop_or_ponderhit();
|
||||||
@@ -88,10 +109,10 @@ public:
|
|||||||
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
|
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
|
||||||
MaterialTable materialTable;
|
MaterialTable materialTable;
|
||||||
PawnTable pawnTable;
|
PawnTable pawnTable;
|
||||||
int idx;
|
size_t idx;
|
||||||
int maxPly;
|
int maxPly;
|
||||||
Lock sleepLock;
|
Mutex mutex;
|
||||||
WaitCondition sleepCond;
|
ConditionVariable sleepCondition;
|
||||||
NativeHandle handle;
|
NativeHandle handle;
|
||||||
Fn start_fn;
|
Fn start_fn;
|
||||||
SplitPoint* volatile curSplitPoint;
|
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.
|
/// 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.
|
/// All the access to shared thread data is done through this class.
|
||||||
|
|
||||||
class ThreadsManager {
|
class ThreadPool {
|
||||||
/* 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();
|
|
||||||
|
|
||||||
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; }
|
bool use_sleeping_threads() const { return useSleepingThreads; }
|
||||||
int min_split_depth() const { return minimumSplitDepth; }
|
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]; }
|
Thread* main_thread() { return threads[0]; }
|
||||||
|
|
||||||
void wake_up() const;
|
void wake_up() const;
|
||||||
@@ -127,8 +145,8 @@ public:
|
|||||||
bool available_slave_exists(Thread* master) const;
|
bool available_slave_exists(Thread* master) const;
|
||||||
void set_timer(int msec);
|
void set_timer(int msec);
|
||||||
void wait_for_search_finished();
|
void wait_for_search_finished();
|
||||||
void start_searching(const Position& pos, const Search::LimitsType& limits,
|
void start_searching(const Position&, const Search::LimitsType&,
|
||||||
const std::vector<Move>& searchMoves);
|
const std::vector<Move>&, Search::StateStackPtr&);
|
||||||
|
|
||||||
template <bool Fake>
|
template <bool Fake>
|
||||||
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove,
|
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove,
|
||||||
@@ -138,13 +156,13 @@ private:
|
|||||||
|
|
||||||
std::vector<Thread*> threads;
|
std::vector<Thread*> threads;
|
||||||
Thread* timer;
|
Thread* timer;
|
||||||
Lock splitLock;
|
Mutex mutex;
|
||||||
WaitCondition sleepCond;
|
ConditionVariable sleepCondition;
|
||||||
Depth minimumSplitDepth;
|
Depth minimumSplitDepth;
|
||||||
int maxThreadsPerSplitPoint;
|
int maxThreadsPerSplitPoint;
|
||||||
bool useSleepingThreads;
|
bool useSleepingThreads;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ThreadsManager Threads;
|
extern ThreadPool Threads;
|
||||||
|
|
||||||
#endif // !defined(THREAD_H_INCLUDED)
|
#endif // !defined(THREAD_H_INCLUDED)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "bitboard.h"
|
||||||
#include "tt.h"
|
#include "tt.h"
|
||||||
|
|
||||||
TranspositionTable TT; // Our global transposition table
|
TranspositionTable TT; // Our global transposition table
|
||||||
@@ -37,18 +38,13 @@ TranspositionTable::~TranspositionTable() {
|
|||||||
|
|
||||||
|
|
||||||
/// TranspositionTable::set_size() sets the size of the transposition table,
|
/// 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) {
|
void TranspositionTable::set_size(size_t mbSize) {
|
||||||
|
|
||||||
size_t newSize = 1024;
|
size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTCluster));
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
if (newSize == size)
|
if (newSize == size)
|
||||||
return;
|
return;
|
||||||
@@ -56,13 +52,15 @@ void TranspositionTable::set_size(size_t mbSize) {
|
|||||||
size = newSize;
|
size = newSize;
|
||||||
delete [] entries;
|
delete [] entries;
|
||||||
entries = new (std::nothrow) TTCluster[size];
|
entries = new (std::nothrow) TTCluster[size];
|
||||||
|
|
||||||
if (!entries)
|
if (!entries)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to allocate " << mbSize
|
std::cerr << "Failed to allocate " << mbSize
|
||||||
<< "MB for transposition table." << std::endl;
|
<< "MB for transposition table." << std::endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
clear();
|
|
||||||
|
clear(); // Operator new is not guaranteed to initialize memory to zero
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -119,15 +119,13 @@ enum Move {
|
|||||||
MOVE_NULL = 65
|
MOVE_NULL = 65
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MoveStack {
|
enum MoveType {
|
||||||
Move move;
|
NORMAL = 0,
|
||||||
int score;
|
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
|
enum CastleRight { // Defined as in PolyGlot book hash key
|
||||||
CASTLES_NONE = 0,
|
CASTLES_NONE = 0,
|
||||||
WHITE_OO = 1,
|
WHITE_OO = 1,
|
||||||
@@ -168,7 +166,15 @@ enum Value {
|
|||||||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
|
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
|
||||||
|
|
||||||
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
|
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 {
|
enum PieceType {
|
||||||
@@ -312,20 +318,31 @@ inline Score apply_weight(Score v, Score w) {
|
|||||||
#undef ENABLE_OPERATORS_ON
|
#undef ENABLE_OPERATORS_ON
|
||||||
#undef ENABLE_SAFE_OPERATORS_ON
|
#undef ENABLE_SAFE_OPERATORS_ON
|
||||||
|
|
||||||
const Value PawnValueMidgame = Value(198);
|
namespace Zobrist {
|
||||||
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);
|
|
||||||
|
|
||||||
extern const Value PieceValueMidgame[17]; // Indexed by Piece or PieceType
|
extern Key psq[2][8][64]; // [color][pieceType][square / piece count]
|
||||||
extern const Value PieceValueEndgame[17];
|
extern Key enpassant[8]; // [file]
|
||||||
extern int SquareDistance[64][64];
|
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) {
|
inline Color operator~(Color c) {
|
||||||
return Color(c ^ 1);
|
return Color(c ^ 1);
|
||||||
@@ -335,6 +352,10 @@ inline Square operator~(Square s) {
|
|||||||
return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8
|
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) {
|
inline Value mate_in(int ply) {
|
||||||
return VALUE_MATE - ply;
|
return VALUE_MATE - ply;
|
||||||
}
|
}
|
||||||
@@ -359,10 +380,6 @@ inline Color color_of(Piece p) {
|
|||||||
return Color(p >> 3);
|
return Color(p >> 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Square make_square(File f, Rank r) {
|
|
||||||
return Square((r << 3) | f);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool is_ok(Square s) {
|
inline bool is_ok(Square s) {
|
||||||
return s >= SQ_A1 && s <= SQ_H8;
|
return s >= SQ_A1 && s <= SQ_H8;
|
||||||
}
|
}
|
||||||
@@ -408,10 +425,6 @@ inline int square_distance(Square s1, Square s2) {
|
|||||||
return SquareDistance[s1][s2];
|
return SquareDistance[s1][s2];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char piece_type_to_char(PieceType pt) {
|
|
||||||
return " PNBRQK"[pt];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline char file_to_char(File f) {
|
inline char file_to_char(File f) {
|
||||||
return char(f - FILE_A + int('a'));
|
return char(f - FILE_A + int('a'));
|
||||||
}
|
}
|
||||||
@@ -432,20 +445,8 @@ inline Square to_sq(Move m) {
|
|||||||
return Square(m & 0x3F);
|
return Square(m & 0x3F);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_special(Move m) {
|
inline MoveType type_of(Move m) {
|
||||||
return m & (3 << 14);
|
return MoveType(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 PieceType promotion_type(Move m) {
|
inline PieceType promotion_type(Move m) {
|
||||||
@@ -456,16 +457,9 @@ inline Move make_move(Square from, Square to) {
|
|||||||
return Move(to | (from << 6));
|
return Move(to | (from << 6));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Move make_promotion(Square from, Square to, PieceType pt) {
|
template<MoveType T>
|
||||||
return Move(to | (from << 6) | (1 << 14) | ((pt - 2) << 12)) ;
|
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
|
||||||
}
|
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_ok(Move m) {
|
inline bool is_ok(Move m) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "evaluate.h"
|
#include "evaluate.h"
|
||||||
|
#include "notation.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "search.h"
|
#include "search.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
@@ -37,9 +38,8 @@ namespace {
|
|||||||
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||||
|
|
||||||
// Keep track of position keys along the setup moves (from start position to the
|
// Keep 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
|
// position just before to start searching). Needed by repetition draw detection.
|
||||||
// where, due to 50 moves rule, we need to check at most 100 plies back.
|
Search::StateStackPtr SetupStates;
|
||||||
StateInfo StateRingBuf[102], *SetupState = StateRingBuf;
|
|
||||||
|
|
||||||
void set_option(istringstream& up);
|
void set_option(istringstream& up);
|
||||||
void set_position(Position& pos, 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
|
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
|
||||||
/// commands, the function also supports a few debug commands.
|
/// 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
|
Position pos(StartFEN, false, Threads.main_thread()); // The root position
|
||||||
string cmd, token;
|
string cmd, token;
|
||||||
@@ -96,7 +96,7 @@ void uci_loop(const string& args) {
|
|||||||
{ /* Avoid returning "Unknown command" */ }
|
{ /* Avoid returning "Unknown command" */ }
|
||||||
|
|
||||||
else if (token == "isready")
|
else if (token == "isready")
|
||||||
cout << "readyok" << endl;
|
sync_cout << "readyok" << sync_endl;
|
||||||
|
|
||||||
else if (token == "position")
|
else if (token == "position")
|
||||||
set_position(pos, is);
|
set_position(pos, is);
|
||||||
@@ -111,20 +111,20 @@ void uci_loop(const string& args) {
|
|||||||
pos.flip();
|
pos.flip();
|
||||||
|
|
||||||
else if (token == "eval")
|
else if (token == "eval")
|
||||||
cout << Eval::trace(pos) << endl;
|
sync_cout << Eval::trace(pos) << sync_endl;
|
||||||
|
|
||||||
else if (token == "bench")
|
else if (token == "bench")
|
||||||
benchmark(pos, is);
|
benchmark(pos, is);
|
||||||
|
|
||||||
else if (token == "key")
|
else if (token == "key")
|
||||||
cout << "key: " << hex << pos.key()
|
sync_cout << "key: " << hex << pos.key()
|
||||||
<< "\nmaterial key: " << pos.material_key()
|
<< "\nmaterial key: " << pos.material_key()
|
||||||
<< "\npawn key: " << pos.pawn_key() << endl;
|
<< "\npawn key: " << pos.pawn_key() << sync_endl;
|
||||||
|
|
||||||
else if (token == "uci")
|
else if (token == "uci")
|
||||||
cout << "id name " << engine_info(true)
|
sync_cout << "id name " << engine_info(true)
|
||||||
<< "\n" << Options
|
<< "\n" << Options
|
||||||
<< "\nuciok" << endl;
|
<< "\nuciok" << sync_endl;
|
||||||
|
|
||||||
else if (token == "perft" && (is >> token)) // Read depth
|
else if (token == "perft" && (is >> token)) // Read depth
|
||||||
{
|
{
|
||||||
@@ -137,7 +137,7 @@ void uci_loop(const string& args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
cout << "Unknown command: " << cmd << endl;
|
sync_cout << "Unknown command: " << cmd << sync_endl;
|
||||||
|
|
||||||
if (!args.empty()) // Command line arguments have one-shot behaviour
|
if (!args.empty()) // Command line arguments have one-shot behaviour
|
||||||
{
|
{
|
||||||
@@ -150,10 +150,10 @@ void uci_loop(const string& args) {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// set_position() is called when engine receives the "position" UCI
|
// set_position() is called when engine receives the "position" UCI command.
|
||||||
// command. The function sets up the position described in the given
|
// The function sets up the position described in the given fen string ("fen")
|
||||||
// fen string ("fen") or the starting position ("startpos") and then
|
// or the starting position ("startpos") and then makes the moves given in the
|
||||||
// makes the moves given in the following move list ("moves").
|
// following move list ("moves").
|
||||||
|
|
||||||
void set_position(Position& pos, istringstream& is) {
|
void set_position(Position& pos, istringstream& is) {
|
||||||
|
|
||||||
@@ -174,15 +174,13 @@ namespace {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread());
|
pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread());
|
||||||
|
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
|
||||||
|
|
||||||
// Parse move list (if any)
|
// Parse move list (if any)
|
||||||
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
|
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
|
||||||
{
|
{
|
||||||
pos.do_move(m, *SetupState);
|
SetupStates->push(StateInfo());
|
||||||
|
pos.do_move(m, SetupStates->top());
|
||||||
// Increment pointer to StateRingBuf circular buffer
|
|
||||||
if (++SetupState - StateRingBuf >= 102)
|
|
||||||
SetupState = StateRingBuf;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +205,7 @@ namespace {
|
|||||||
if (Options.count(name))
|
if (Options.count(name))
|
||||||
Options[name] = value;
|
Options[name] = value;
|
||||||
else
|
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));
|
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 <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "evaluate.h"
|
#include "evaluate.h"
|
||||||
@@ -28,71 +30,69 @@
|
|||||||
|
|
||||||
using std::string;
|
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
|
/// 'On change' actions, triggered by an option's value change
|
||||||
void on_logger(const UCIOption& opt) { start_logger(opt); }
|
void on_logger(const Option& o) { start_logger(o); }
|
||||||
void on_eval(const UCIOption&) { Eval::init(); }
|
void on_eval(const Option&) { Eval::init(); }
|
||||||
void on_threads(const UCIOption&) { Threads.read_uci_options(); }
|
void on_threads(const Option&) { Threads.read_uci_options(); }
|
||||||
void on_hash_size(const UCIOption& opt) { TT.set_size(opt); }
|
void on_hash_size(const Option& o) { TT.set_size(o); }
|
||||||
void on_clear_hash(const UCIOption&) { TT.clear(); }
|
void on_clear_hash(const Option&) { TT.clear(); }
|
||||||
|
|
||||||
|
|
||||||
/// Our case insensitive less() function as required by UCI protocol
|
/// Our case insensitive less() function as required by UCI protocol
|
||||||
bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
|
bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
||||||
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
|
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
|
/// init() initializes the UCI options to their hard coded default values
|
||||||
/// values and initializes the default value of "Threads" and "Min Split Depth"
|
/// and initializes the default value of "Threads" and "Min Split Depth"
|
||||||
/// parameters according to the number of CPU cores detected.
|
/// parameters according to the number of CPU cores detected.
|
||||||
|
|
||||||
OptionsMap::OptionsMap() {
|
void init(OptionsMap& o) {
|
||||||
|
|
||||||
int cpus = std::min(cpu_count(), MAX_THREADS);
|
int cpus = std::min(cpu_count(), MAX_THREADS);
|
||||||
int msd = cpus < 8 ? 4 : 7;
|
int msd = cpus < 8 ? 4 : 7;
|
||||||
OptionsMap& o = *this;
|
|
||||||
|
|
||||||
o["Use Debug Log"] = UCIOption(false, on_logger);
|
o["Use Debug Log"] = Option(false, on_logger);
|
||||||
o["Use Search Log"] = UCIOption(false);
|
o["Use Search Log"] = Option(false);
|
||||||
o["Search Log Filename"] = UCIOption("SearchLog.txt");
|
o["Search Log Filename"] = Option("SearchLog.txt");
|
||||||
o["Book File"] = UCIOption("book.bin");
|
o["Book File"] = Option("book.bin");
|
||||||
o["Best Book Move"] = UCIOption(false);
|
o["Best Book Move"] = Option(false);
|
||||||
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200, on_eval);
|
o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval);
|
||||||
o["Mobility (Endgame)"] = UCIOption(100, 0, 200, on_eval);
|
o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval);
|
||||||
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200, on_eval);
|
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval);
|
||||||
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200, on_eval);
|
o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval);
|
||||||
o["Space"] = UCIOption(100, 0, 200, on_eval);
|
o["Space"] = Option(100, 0, 200, on_eval);
|
||||||
o["Aggressiveness"] = UCIOption(100, 0, 200, on_eval);
|
o["Aggressiveness"] = Option(100, 0, 200, on_eval);
|
||||||
o["Cowardice"] = UCIOption(100, 0, 200, on_eval);
|
o["Cowardice"] = Option(100, 0, 200, on_eval);
|
||||||
o["Min Split Depth"] = UCIOption(msd, 4, 7, on_threads);
|
o["Min Split Depth"] = Option(msd, 4, 7, on_threads);
|
||||||
o["Max Threads per Split Point"] = UCIOption(5, 4, 8, on_threads);
|
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
|
||||||
o["Threads"] = UCIOption(cpus, 1, MAX_THREADS, on_threads);
|
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads);
|
||||||
o["Use Sleeping Threads"] = UCIOption(true, on_threads);
|
o["Use Sleeping Threads"] = Option(true, on_threads);
|
||||||
o["Hash"] = UCIOption(32, 4, 8192, on_hash_size);
|
o["Hash"] = Option(32, 4, 8192, on_hash_size);
|
||||||
o["Clear Hash"] = UCIOption(on_clear_hash);
|
o["Clear Hash"] = Option(on_clear_hash);
|
||||||
o["Ponder"] = UCIOption(true);
|
o["Ponder"] = Option(true);
|
||||||
o["OwnBook"] = UCIOption(false);
|
o["OwnBook"] = Option(false);
|
||||||
o["MultiPV"] = UCIOption(1, 1, 500);
|
o["MultiPV"] = Option(1, 1, 500);
|
||||||
o["Skill Level"] = UCIOption(20, 0, 20);
|
o["Skill Level"] = Option(20, 0, 20);
|
||||||
o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
|
o["Emergency Move Horizon"] = Option(40, 0, 50);
|
||||||
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
|
o["Emergency Base Time"] = Option(200, 0, 30000);
|
||||||
o["Emergency Move Time"] = UCIOption(70, 0, 5000);
|
o["Emergency Move Time"] = Option(70, 0, 5000);
|
||||||
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
|
o["Minimum Thinking Time"] = Option(20, 0, 5000);
|
||||||
o["Slow Mover"] = UCIOption(100, 10, 1000);
|
o["Slow Mover"] = Option(100, 10, 1000);
|
||||||
o["UCI_Chess960"] = UCIOption(false);
|
o["UCI_Chess960"] = Option(false);
|
||||||
o["UCI_AnalyseMode"] = UCIOption(false, on_eval);
|
o["UCI_AnalyseMode"] = Option(false, on_eval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// operator<<() is used to output all the UCI options in chronological insertion
|
/// operator<<() is used to print all the options default values in chronological
|
||||||
/// order (the idx field) and in the format defined by the UCI protocol.
|
/// insertion order (the idx field) and in the format defined by the UCI protocol.
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
|||||||
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
|
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
|
||||||
if (it->second.idx == idx)
|
if (it->second.idx == idx)
|
||||||
{
|
{
|
||||||
const UCIOption& o = it->second;
|
const Option& o = it->second;
|
||||||
os << "\noption name " << it->first << " type " << o.type;
|
os << "\noption name " << it->first << " type " << o.type;
|
||||||
|
|
||||||
if (o.type != "button")
|
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; }
|
{ 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"); }
|
{ 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(); }
|
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
|
||||||
|
|
||||||
|
|
||||||
/// UCIOption::operator=() updates currentValue. Normally it's up to the GUI to
|
Option::operator int() const {
|
||||||
/// check for option's limits, but we could receive the new value directly from
|
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.
|
/// 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());
|
assert(!type.empty());
|
||||||
|
|
||||||
if ( (type == "button" || !v.empty())
|
if ( (type != "button" && v.empty())
|
||||||
&& (type != "check" || (v == "true" || v == "false"))
|
|| (type == "check" && v != "true" && v != "false")
|
||||||
&& (type != "spin" || (atoi(v.c_str()) >= min && atoi(v.c_str()) <= max)))
|
|| (type == "spin" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max)))
|
||||||
{
|
return *this;
|
||||||
if (type != "button")
|
|
||||||
currentValue = v;
|
|
||||||
|
|
||||||
if (on_change)
|
if (type != "button")
|
||||||
(*on_change)(*this);
|
currentValue = v;
|
||||||
}
|
|
||||||
|
if (on_change)
|
||||||
|
(*on_change)(*this);
|
||||||
|
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace UCI
|
||||||
|
|||||||
@@ -20,35 +20,35 @@
|
|||||||
#if !defined(UCIOPTION_H_INCLUDED)
|
#if !defined(UCIOPTION_H_INCLUDED)
|
||||||
#define UCIOPTION_H_INCLUDED
|
#define UCIOPTION_H_INCLUDED
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
struct OptionsMap;
|
namespace UCI {
|
||||||
|
|
||||||
/// UCIOption class implements an option as defined by UCI protocol
|
class Option;
|
||||||
class UCIOption {
|
|
||||||
|
|
||||||
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:
|
public:
|
||||||
UCIOption(Fn* = NULL);
|
Option(Fn* = NULL);
|
||||||
UCIOption(bool v, Fn* = NULL);
|
Option(bool v, Fn* = NULL);
|
||||||
UCIOption(const char* v, Fn* = NULL);
|
Option(const char* v, Fn* = NULL);
|
||||||
UCIOption(int v, int min, int max, Fn* = NULL);
|
Option(int v, int min, int max, Fn* = NULL);
|
||||||
|
|
||||||
void operator=(const std::string& v);
|
Option& operator=(const std::string& v);
|
||||||
|
operator int() const;
|
||||||
operator int() const {
|
operator std::string() const;
|
||||||
assert(type == "check" || type == "spin");
|
|
||||||
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
operator std::string() const {
|
|
||||||
assert(type == "string");
|
|
||||||
return currentValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
|
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
|
||||||
@@ -59,19 +59,11 @@ private:
|
|||||||
Fn* on_change;
|
Fn* on_change;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void init(OptionsMap&);
|
||||||
|
void loop(const std::string&);
|
||||||
|
|
||||||
/// Custom comparator because UCI options should be case insensitive
|
} // namespace UCI
|
||||||
struct CaseInsensitiveLess {
|
|
||||||
bool operator() (const std::string&, const std::string&) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
extern UCI::OptionsMap Options;
|
||||||
/// 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;
|
|
||||||
|
|
||||||
#endif // !defined(UCIOPTION_H_INCLUDED)
|
#endif // !defined(UCIOPTION_H_INCLUDED)
|
||||||
|
|||||||
Reference in New Issue
Block a user