mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-13 09:32:39 +01:00
DroidFish: Updated stockfish engine to version DD.
This commit is contained in:
@@ -47,7 +47,21 @@ static const char* Defaults[] = {
|
|||||||
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
|
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
|
||||||
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
|
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
|
||||||
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
|
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
|
||||||
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26"
|
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
|
||||||
|
"6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1",
|
||||||
|
"3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1",
|
||||||
|
"2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1",
|
||||||
|
"8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1",
|
||||||
|
"7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1",
|
||||||
|
"8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1",
|
||||||
|
"8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1",
|
||||||
|
"8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1",
|
||||||
|
"8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1",
|
||||||
|
"5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1",
|
||||||
|
"6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
|
||||||
|
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
|
||||||
|
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
|
||||||
|
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -68,7 +82,7 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
// Assign default values to missing arguments
|
// Assign default values to missing arguments
|
||||||
string ttSize = (is >> token) ? token : "32";
|
string ttSize = (is >> token) ? token : "32";
|
||||||
string threads = (is >> token) ? token : "1";
|
string threads = (is >> token) ? token : "1";
|
||||||
string limit = (is >> token) ? token : "12";
|
string limit = (is >> token) ? token : "13";
|
||||||
string fenFile = (is >> token) ? token : "default";
|
string fenFile = (is >> token) ? token : "default";
|
||||||
string limitType = (is >> token) ? token : "depth";
|
string limitType = (is >> token) ? token : "depth";
|
||||||
|
|
||||||
@@ -89,7 +103,7 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
limits.depth = atoi(limit.c_str());
|
limits.depth = atoi(limit.c_str());
|
||||||
|
|
||||||
if (fenFile == "default")
|
if (fenFile == "default")
|
||||||
fens.assign(Defaults, Defaults + 16);
|
fens.assign(Defaults, Defaults + 30);
|
||||||
|
|
||||||
else if (fenFile == "current")
|
else if (fenFile == "current")
|
||||||
fens.push_back(current.fen());
|
fens.push_back(current.fen());
|
||||||
@@ -116,7 +130,7 @@ void benchmark(const Position& current, istream& is) {
|
|||||||
Search::StateStackPtr st;
|
Search::StateStackPtr st;
|
||||||
Time::point elapsed = Time::now();
|
Time::point elapsed = Time::now();
|
||||||
|
|
||||||
for (size_t i = 0; i < fens.size(); i++)
|
for (size_t i = 0; i < fens.size(); ++i)
|
||||||
{
|
{
|
||||||
Position pos(fens[i], Options["UCI_Chess960"], Threads.main());
|
Position pos(fens[i], Options["UCI_Chess960"], Threads.main());
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ namespace {
|
|||||||
|
|
||||||
struct KPKPosition {
|
struct KPKPosition {
|
||||||
|
|
||||||
operator Result() const { return res; }
|
KPKPosition(unsigned idx);
|
||||||
Result classify_leaf(unsigned idx);
|
operator Result() const { return result; }
|
||||||
Result classify(const std::vector<KPKPosition>& db)
|
Result classify(const std::vector<KPKPosition>& db)
|
||||||
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
|
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ namespace {
|
|||||||
|
|
||||||
Color us;
|
Color us;
|
||||||
Square bksq, wksq, psq;
|
Square bksq, wksq, psq;
|
||||||
Result res;
|
Result result;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -83,21 +83,21 @@ bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
|
|||||||
void Bitbases::init_kpk() {
|
void Bitbases::init_kpk() {
|
||||||
|
|
||||||
unsigned idx, repeat = 1;
|
unsigned idx, repeat = 1;
|
||||||
std::vector<KPKPosition> db(IndexMax);
|
std::vector<KPKPosition> db;
|
||||||
|
db.reserve(IndexMax);
|
||||||
|
|
||||||
// Initialize db with known win / draw positions
|
// Initialize db with known win / draw positions
|
||||||
for (idx = 0; idx < IndexMax; idx++)
|
for (idx = 0; idx < IndexMax; ++idx)
|
||||||
db[idx].classify_leaf(idx);
|
db.push_back(KPKPosition(idx));
|
||||||
|
|
||||||
// Iterate through the positions until no more of the unknown positions can be
|
// Iterate through the positions until no more of the unknown positions can be
|
||||||
// changed to either wins or draws (15 cycles needed).
|
// changed to either wins or draws (15 cycles needed).
|
||||||
while (repeat)
|
while (repeat)
|
||||||
for (repeat = idx = 0; idx < IndexMax; idx++)
|
for (repeat = idx = 0; idx < IndexMax; ++idx)
|
||||||
if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN)
|
repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
|
||||||
repeat = 1;
|
|
||||||
|
|
||||||
// Map 32 results into one KPKBitbase[] entry
|
// Map 32 results into one KPKBitbase[] entry
|
||||||
for (idx = 0; idx < IndexMax; idx++)
|
for (idx = 0; idx < IndexMax; ++idx)
|
||||||
if (db[idx] == WIN)
|
if (db[idx] == WIN)
|
||||||
KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
|
KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
|
||||||
}
|
}
|
||||||
@@ -105,34 +105,32 @@ void Bitbases::init_kpk() {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Result KPKPosition::classify_leaf(unsigned idx) {
|
KPKPosition::KPKPosition(unsigned idx) {
|
||||||
|
|
||||||
wksq = Square((idx >> 0) & 0x3F);
|
wksq = Square((idx >> 0) & 0x3F);
|
||||||
bksq = Square((idx >> 6) & 0x3F);
|
bksq = Square((idx >> 6) & 0x3F);
|
||||||
us = Color ((idx >> 12) & 0x01);
|
us = Color ((idx >> 12) & 0x01);
|
||||||
psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15));
|
psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15));
|
||||||
|
result = UNKNOWN;
|
||||||
|
|
||||||
// Check if two pieces are on the same square or if a king can be captured
|
// Check if two pieces are on the same square or if a king can be captured
|
||||||
if ( wksq == psq || wksq == bksq || bksq == psq
|
if ( square_distance(wksq, bksq) <= 1 || wksq == psq || bksq == psq
|
||||||
|| (StepAttacksBB[KING][wksq] & bksq)
|
|
||||||
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
|
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
|
||||||
return res = INVALID;
|
result = INVALID;
|
||||||
|
|
||||||
if (us == WHITE)
|
else if (us == WHITE)
|
||||||
{
|
{
|
||||||
// Immediate win if pawn can be promoted without getting captured
|
// Immediate win if pawn can be promoted without getting captured
|
||||||
if ( rank_of(psq) == RANK_7
|
if ( rank_of(psq) == RANK_7
|
||||||
&& wksq != psq + DELTA_N
|
&& wksq != psq + DELTA_N
|
||||||
&& ( square_distance(bksq, psq + DELTA_N) > 1
|
&& ( square_distance(bksq, psq + DELTA_N) > 1
|
||||||
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
|
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
|
||||||
return res = WIN;
|
result = WIN;
|
||||||
}
|
}
|
||||||
// Immediate draw if is stalemate or king captures undefended pawn
|
// Immediate draw if is stalemate or king captures undefended pawn
|
||||||
else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq]))
|
else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq]))
|
||||||
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq]))
|
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq]))
|
||||||
return res = DRAW;
|
result = DRAW;
|
||||||
|
|
||||||
return res = UNKNOWN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
@@ -162,14 +160,14 @@ namespace {
|
|||||||
Square s = psq + DELTA_N;
|
Square s = psq + DELTA_N;
|
||||||
r |= db[index(BLACK, bksq, wksq, s)]; // Single push
|
r |= db[index(BLACK, bksq, wksq, s)]; // Single push
|
||||||
|
|
||||||
if (rank_of(s) == RANK_3 && s != wksq && s != bksq)
|
if (rank_of(psq) == RANK_2 && s != wksq && s != bksq)
|
||||||
r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push
|
r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Us == WHITE)
|
if (Us == WHITE)
|
||||||
return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW;
|
return result = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW;
|
||||||
else
|
else
|
||||||
return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN;
|
return result = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ Bitboard AdjacentFilesBB[FILE_NB];
|
|||||||
Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
||||||
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
|
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
|
||||||
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||||
|
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||||
Bitboard DistanceRingsBB[SQUARE_NB][8];
|
Bitboard DistanceRingsBB[SQUARE_NB][8];
|
||||||
Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
|
Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
|
||||||
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
|
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
|
||||||
@@ -132,11 +133,11 @@ void Bitboards::print(Bitboard b) {
|
|||||||
|
|
||||||
sync_cout;
|
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 & (file | rank) ? "X " : " ");
|
std::cout << "| " << (b & (file | rank) ? "X " : " ");
|
||||||
|
|
||||||
std::cout << "|\n";
|
std::cout << "|\n";
|
||||||
@@ -150,41 +151,41 @@ void Bitboards::print(Bitboard b) {
|
|||||||
|
|
||||||
void Bitboards::init() {
|
void Bitboards::init() {
|
||||||
|
|
||||||
for (int k = 0, i = 0; i < 8; i++)
|
for (int k = 0, i = 0; i < 8; ++i)
|
||||||
while (k < (2 << i))
|
while (k < (2 << i))
|
||||||
MS1BTable[k++] = i;
|
MS1BTable[k++] = i;
|
||||||
|
|
||||||
for (int i = 0; i < 64; i++)
|
for (int i = 0; i < 64; ++i)
|
||||||
BSFTable[bsf_index(1ULL << i)] = Square(i);
|
BSFTable[bsf_index(1ULL << i)] = Square(i);
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
SquareBB[s] = 1ULL << s;
|
SquareBB[s] = 1ULL << s;
|
||||||
|
|
||||||
FileBB[FILE_A] = FileABB;
|
FileBB[FILE_A] = FileABB;
|
||||||
RankBB[RANK_1] = Rank1BB;
|
RankBB[RANK_1] = Rank1BB;
|
||||||
|
|
||||||
for (int i = 1; i < 8; i++)
|
for (int i = 1; i < 8; ++i)
|
||||||
{
|
{
|
||||||
FileBB[i] = FileBB[i - 1] << 1;
|
FileBB[i] = FileBB[i - 1] << 1;
|
||||||
RankBB[i] = RankBB[i - 1] << 8;
|
RankBB[i] = RankBB[i - 1] << 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (File f = FILE_A; f <= FILE_H; f++)
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||||
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
|
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
|
||||||
|
|
||||||
for (Rank r = RANK_1; r < RANK_8; r++)
|
for (Rank r = RANK_1; r < RANK_8; ++r)
|
||||||
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
|
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
|
||||||
|
|
||||||
for (Color c = WHITE; c <= BLACK; c++)
|
for (Color c = WHITE; c <= BLACK; ++c)
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
{
|
{
|
||||||
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
|
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
|
||||||
PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
|
PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
|
||||||
PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s];
|
PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||||
{
|
{
|
||||||
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
|
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
|
||||||
if (s1 != s2)
|
if (s1 != s2)
|
||||||
@@ -194,10 +195,10 @@ void Bitboards::init() {
|
|||||||
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 } };
|
||||||
|
|
||||||
for (Color c = WHITE; c <= BLACK; c++)
|
for (Color c = WHITE; c <= BLACK; ++c)
|
||||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
for (int k = 0; steps[pt][k]; k++)
|
for (int k = 0; steps[pt][k]; ++k)
|
||||||
{
|
{
|
||||||
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]);
|
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]);
|
||||||
|
|
||||||
@@ -211,20 +212,23 @@ void Bitboards::init() {
|
|||||||
init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>);
|
init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>);
|
||||||
init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>);
|
init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>);
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
{
|
{
|
||||||
PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb<BISHOP>(s, 0);
|
PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb<BISHOP>(s, 0);
|
||||||
PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0);
|
PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||||
if (PseudoAttacks[QUEEN][s1] & s2)
|
if (PseudoAttacks[QUEEN][s1] & s2)
|
||||||
{
|
{
|
||||||
Square delta = (s2 - s1) / square_distance(s1, s2);
|
Square delta = (s2 - s1) / square_distance(s1, s2);
|
||||||
|
|
||||||
for (Square s = s1 + delta; s != s2; s += delta)
|
for (Square s = s1 + delta; s != s2; s += delta)
|
||||||
BetweenBB[s1][s2] |= s;
|
BetweenBB[s1][s2] |= s;
|
||||||
|
|
||||||
|
PieceType pt = (PseudoAttacks[BISHOP][s1] & s2) ? BISHOP : ROOK;
|
||||||
|
LineBB[s1][s2] = (PseudoAttacks[pt][s1] & PseudoAttacks[pt][s2]) | s1 | s2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +239,7 @@ namespace {
|
|||||||
|
|
||||||
Bitboard attack = 0;
|
Bitboard attack = 0;
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; ++i)
|
||||||
for (Square s = sq + deltas[i];
|
for (Square s = sq + deltas[i];
|
||||||
is_ok(s) && square_distance(s, s - deltas[i]) == 1;
|
is_ok(s) && square_distance(s, s - deltas[i]) == 1;
|
||||||
s += deltas[i])
|
s += deltas[i])
|
||||||
@@ -281,7 +285,7 @@ namespace {
|
|||||||
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
|
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
|
||||||
attacks[SQ_A1] = table;
|
attacks[SQ_A1] = table;
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
{
|
{
|
||||||
// Board edges are not considered in the relevant occupancies
|
// Board edges are not considered in the relevant occupancies
|
||||||
edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
|
edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
|
||||||
@@ -322,7 +326,7 @@ namespace {
|
|||||||
// looks up the correct sliding attack in the attacks[s] database.
|
// looks up the correct sliding attack in the attacks[s] database.
|
||||||
// Note that we build up the database for square 's' as a side
|
// Note that we build up the database for square 's' as a side
|
||||||
// effect of verifying the magic.
|
// effect of verifying the magic.
|
||||||
for (i = 0; i < size; i++)
|
for (i = 0; i < size; ++i)
|
||||||
{
|
{
|
||||||
Bitboard& attack = attacks[s][index(s, occupancy[i])];
|
Bitboard& attack = attacks[s][index(s, occupancy[i])];
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ extern Bitboard AdjacentFilesBB[FILE_NB];
|
|||||||
extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
||||||
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
|
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
|
||||||
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||||
|
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||||
extern Bitboard DistanceRingsBB[SQUARE_NB][8];
|
extern Bitboard DistanceRingsBB[SQUARE_NB][8];
|
||||||
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
|
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
|
||||||
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
|
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
|
||||||
@@ -222,12 +223,11 @@ inline Bitboard squares_of_color(Square s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// squares_aligned() returns true if the squares s1, s2 and s3 are aligned
|
/// aligned() returns true if the squares s1, s2 and s3 are aligned
|
||||||
/// either on a straight or on a diagonal line.
|
/// either on a straight or on a diagonal line.
|
||||||
|
|
||||||
inline bool squares_aligned(Square s1, Square s2, Square s3) {
|
inline bool aligned(Square s1, Square s2, Square s3) {
|
||||||
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3])
|
return LineBB[s1][s2] & s3;
|
||||||
& ( SquareBB[s1] | SquareBB[s2] | SquareBB[s3]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -319,4 +319,10 @@ extern Square pop_lsb(Bitboard* b);
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// frontmost_sq() and backmost_sq() find the square corresponding to the
|
||||||
|
/// most/least advanced bit relative to the given color.
|
||||||
|
|
||||||
|
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
|
||||||
|
inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
|
||||||
|
|
||||||
#endif // #ifndef BITBOARD_H_INCLUDED
|
#endif // #ifndef BITBOARD_H_INCLUDED
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
|
|||||||
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
|
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
|
||||||
|
|
||||||
n = 0;
|
n = 0;
|
||||||
for (size_t i = 0; i < sizeof(T); i++)
|
for (size_t i = 0; i < sizeof(T); ++i)
|
||||||
n = T((n << 8) + ifstream::get());
|
n = T((n << 8) + ifstream::get());
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@@ -413,7 +413,7 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
|
|||||||
// Choose book move according to its score. If a move has a very
|
// Choose book move according to its score. If a move has a very
|
||||||
// high score it has higher probability to be choosen than a move
|
// high score it has higher probability to be choosen than a move
|
||||||
// with lower score. Note that first entry is always chosen.
|
// with lower score. Note that first entry is always chosen.
|
||||||
if ( (sum && rkiss.rand<unsigned>() % sum < e.count)
|
if ( (!pickBest && sum && rkiss.rand<unsigned>() % sum < e.count)
|
||||||
|| (pickBest && e.count == best))
|
|| (pickBest && e.count == best))
|
||||||
move = Move(e.move);
|
move = Move(e.move);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,9 +33,6 @@ enum EndgameType {
|
|||||||
|
|
||||||
// Evaluation functions
|
// Evaluation functions
|
||||||
|
|
||||||
KK, // K vs K
|
|
||||||
KBK, // KB vs K
|
|
||||||
KNK, // KN vs K
|
|
||||||
KNNK, // KNN vs K
|
KNNK, // KNN vs K
|
||||||
KXK, // Generic "mate lone king" eval
|
KXK, // Generic "mate lone king" eval
|
||||||
KBNK, // KBN vs K
|
KBNK, // KBN vs K
|
||||||
@@ -55,6 +52,7 @@ enum EndgameType {
|
|||||||
KBPsK, // KB+pawns vs K
|
KBPsK, // KB+pawns vs K
|
||||||
KQKRPs, // KQ vs KR+pawns
|
KQKRPs, // KQ vs KR+pawns
|
||||||
KRPKR, // KRP vs KR
|
KRPKR, // KRP vs KR
|
||||||
|
KRPKB, // KRP vs KB
|
||||||
KRPPKRP, // KRPP vs KRP
|
KRPPKRP, // KRPP vs KRP
|
||||||
KPsK, // King and pawns vs king
|
KPsK, // King and pawns vs king
|
||||||
KBPKB, // KBP vs KB
|
KBPKB, // KBP vs KB
|
||||||
@@ -88,12 +86,12 @@ struct EndgameBase {
|
|||||||
template<EndgameType E, typename T = typename eg_fun<(E > SCALE_FUNS)>::type>
|
template<EndgameType E, typename T = typename eg_fun<(E > SCALE_FUNS)>::type>
|
||||||
struct Endgame : public EndgameBase<T> {
|
struct Endgame : public EndgameBase<T> {
|
||||||
|
|
||||||
explicit Endgame(Color c) : strongerSide(c), weakerSide(~c) {}
|
explicit Endgame(Color c) : strongSide(c), weakSide(~c) {}
|
||||||
Color color() const { return strongerSide; }
|
Color color() const { return strongSide; }
|
||||||
T operator()(const Position&) const;
|
T operator()(const Position&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Color strongerSide, weakerSide;
|
const Color strongSide, weakSide;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum ExtendedPieceType { // Used for tracing
|
enum ExtendedPieceType { // Used for tracing
|
||||||
PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, UNSTOPPABLE, SPACE, TOTAL
|
PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Tracing {
|
namespace Tracing {
|
||||||
@@ -83,6 +83,8 @@ namespace {
|
|||||||
// king is on g8 and there's a white knight on g5, this knight adds
|
// king is on g8 and there's a white knight on g5, this knight adds
|
||||||
// 2 to kingAdjacentZoneAttacksCount[BLACK].
|
// 2 to kingAdjacentZoneAttacksCount[BLACK].
|
||||||
int kingAdjacentZoneAttacksCount[COLOR_NB];
|
int kingAdjacentZoneAttacksCount[COLOR_NB];
|
||||||
|
|
||||||
|
Bitboard pinnedPieces[COLOR_NB];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Evaluation grain size, must be a power of 2
|
// Evaluation grain size, must be a power of 2
|
||||||
@@ -114,16 +116,15 @@ namespace {
|
|||||||
S( 37, 28), S( 42, 31), S(44, 33) },
|
S( 37, 28), S( 42, 31), S(44, 33) },
|
||||||
{ S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops
|
{ S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops
|
||||||
S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77),
|
S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77),
|
||||||
S( 84, 79), S( 86, 81), S(87, 82), S(87, 82) },
|
S( 84, 79), S( 86, 81) },
|
||||||
{ S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks
|
{ S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks
|
||||||
S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119),
|
S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119),
|
||||||
S( 35,122), S( 36,123), S(37,124), S(38,124) },
|
S( 35,122), S( 36,123), S(37,124) },
|
||||||
{ S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens
|
{ S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens
|
||||||
S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40),
|
S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40),
|
||||||
S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41),
|
S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41),
|
||||||
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
|
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
|
||||||
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
|
S( 25, 41), S( 25, 41), S(25, 41), S(25, 41) }
|
||||||
S( 25, 41), S( 25, 41) }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Outpost[PieceType][Square] contains bonuses of knights and bishops, indexed
|
// Outpost[PieceType][Square] contains bonuses of knights and bishops, indexed
|
||||||
@@ -173,9 +174,11 @@ namespace {
|
|||||||
const Score RookOpenFile = make_score(43, 21);
|
const Score RookOpenFile = make_score(43, 21);
|
||||||
const Score RookSemiopenFile = make_score(19, 10);
|
const Score RookSemiopenFile = make_score(19, 10);
|
||||||
const Score BishopPawns = make_score( 8, 12);
|
const Score BishopPawns = make_score( 8, 12);
|
||||||
|
const Score KnightPawns = make_score( 8, 4);
|
||||||
const Score MinorBehindPawn = make_score(16, 0);
|
const Score MinorBehindPawn = make_score(16, 0);
|
||||||
const Score UndefendedMinor = make_score(25, 10);
|
const Score UndefendedMinor = make_score(25, 10);
|
||||||
const Score TrappedRook = make_score(90, 0);
|
const Score TrappedRook = make_score(90, 0);
|
||||||
|
const Score Unstoppable = make_score( 0, 20);
|
||||||
|
|
||||||
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
|
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
|
||||||
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
|
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
|
||||||
@@ -187,12 +190,8 @@ namespace {
|
|||||||
// based on how many squares inside this area are safe and available for
|
// based on how many squares inside this area are safe and available for
|
||||||
// friendly minor pieces.
|
// friendly minor pieces.
|
||||||
const Bitboard SpaceMask[] = {
|
const Bitboard SpaceMask[] = {
|
||||||
(1ULL << SQ_C2) | (1ULL << SQ_D2) | (1ULL << SQ_E2) | (1ULL << SQ_F2) |
|
(FileCBB | FileDBB | FileEBB | FileFBB) & (Rank2BB | Rank3BB | Rank4BB),
|
||||||
(1ULL << SQ_C3) | (1ULL << SQ_D3) | (1ULL << SQ_E3) | (1ULL << SQ_F3) |
|
(FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB)
|
||||||
(1ULL << SQ_C4) | (1ULL << SQ_D4) | (1ULL << SQ_E4) | (1ULL << SQ_F4),
|
|
||||||
(1ULL << SQ_C7) | (1ULL << SQ_D7) | (1ULL << SQ_E7) | (1ULL << SQ_F7) |
|
|
||||||
(1ULL << SQ_C6) | (1ULL << SQ_D6) | (1ULL << SQ_E6) | (1ULL << SQ_F6) |
|
|
||||||
(1ULL << SQ_C5) | (1ULL << SQ_D5) | (1ULL << SQ_E5) | (1ULL << SQ_F5)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// King danger constants and variables. The king danger scores are taken
|
// King danger constants and variables. The king danger scores are taken
|
||||||
@@ -204,12 +203,12 @@ namespace {
|
|||||||
const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 };
|
const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 };
|
||||||
|
|
||||||
// Bonuses for enemy's safe checks
|
// Bonuses for enemy's safe checks
|
||||||
const int QueenContactCheck = 6;
|
const int QueenContactCheck = 24;
|
||||||
const int RookContactCheck = 4;
|
const int RookContactCheck = 16;
|
||||||
const int QueenCheck = 3;
|
const int QueenCheck = 12;
|
||||||
const int RookCheck = 2;
|
const int RookCheck = 8;
|
||||||
const int BishopCheck = 1;
|
const int BishopCheck = 2;
|
||||||
const int KnightCheck = 1;
|
const int KnightCheck = 3;
|
||||||
|
|
||||||
// KingExposed[Square] contains penalties based on the position of the
|
// KingExposed[Square] contains penalties based on the position of the
|
||||||
// defending king, indexed by king's square (from white's point of view).
|
// defending king, indexed by king's square (from white's point of view).
|
||||||
@@ -230,16 +229,16 @@ namespace {
|
|||||||
|
|
||||||
// Function prototypes
|
// Function prototypes
|
||||||
template<bool Trace>
|
template<bool Trace>
|
||||||
Value do_evaluate(const Position& pos, Value& margin);
|
Value do_evaluate(const Position& pos);
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
void init_eval_info(const Position& pos, EvalInfo& ei);
|
void init_eval_info(const Position& pos, EvalInfo& ei);
|
||||||
|
|
||||||
template<Color Us, bool Trace>
|
template<Color Us, bool Trace>
|
||||||
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
|
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score* mobility);
|
||||||
|
|
||||||
template<Color Us, bool Trace>
|
template<Color Us, bool Trace>
|
||||||
Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]);
|
Score evaluate_king(const Position& pos, const EvalInfo& ei);
|
||||||
|
|
||||||
template<Color Us, bool Trace>
|
template<Color Us, bool Trace>
|
||||||
Score evaluate_threats(const Position& pos, const EvalInfo& ei);
|
Score evaluate_threats(const Position& pos, const EvalInfo& ei);
|
||||||
@@ -250,7 +249,7 @@ namespace {
|
|||||||
template<Color Us>
|
template<Color Us>
|
||||||
int evaluate_space(const Position& pos, const EvalInfo& ei);
|
int evaluate_space(const Position& pos, const EvalInfo& ei);
|
||||||
|
|
||||||
Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei);
|
Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei);
|
||||||
|
|
||||||
Value interpolate(const Score& v, Phase ph, ScaleFactor sf);
|
Value interpolate(const Score& v, Phase ph, ScaleFactor sf);
|
||||||
Score apply_weight(Score v, Score w);
|
Score apply_weight(Score v, Score w);
|
||||||
@@ -265,8 +264,8 @@ namespace Eval {
|
|||||||
/// values, an endgame score and a middle game score, and interpolates
|
/// values, an endgame score and a middle game score, and interpolates
|
||||||
/// between them based on the remaining material.
|
/// between them based on the remaining material.
|
||||||
|
|
||||||
Value evaluate(const Position& pos, Value& margin) {
|
Value evaluate(const Position& pos) {
|
||||||
return do_evaluate<false>(pos, margin);
|
return do_evaluate<false>(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -293,7 +292,7 @@ namespace Eval {
|
|||||||
const int MaxSlope = 30;
|
const int MaxSlope = 30;
|
||||||
const int Peak = 1280;
|
const int Peak = 1280;
|
||||||
|
|
||||||
for (int t = 0, i = 1; i < 100; i++)
|
for (int t = 0, i = 1; i < 100; ++i)
|
||||||
{
|
{
|
||||||
t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope));
|
t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope));
|
||||||
|
|
||||||
@@ -308,19 +307,14 @@ namespace Eval {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template<bool Trace>
|
template<bool Trace>
|
||||||
Value do_evaluate(const Position& pos, Value& margin) {
|
Value do_evaluate(const Position& pos) {
|
||||||
|
|
||||||
assert(!pos.checkers());
|
assert(!pos.checkers());
|
||||||
|
|
||||||
EvalInfo ei;
|
EvalInfo ei;
|
||||||
Value margins[COLOR_NB];
|
Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO };
|
||||||
Score score, mobilityWhite, mobilityBlack;
|
|
||||||
Thread* th = pos.this_thread();
|
Thread* th = pos.this_thread();
|
||||||
|
|
||||||
// margins[] store the uncertainty estimation of position's evaluation
|
|
||||||
// that typically is used by the search for pruning decisions.
|
|
||||||
margins[WHITE] = margins[BLACK] = VALUE_ZERO;
|
|
||||||
|
|
||||||
// Initialize score by reading the incrementally updated scores included
|
// Initialize score by reading the incrementally updated scores included
|
||||||
// in the position object (material + piece square tables) and adding
|
// in the position object (material + piece square tables) and adding
|
||||||
// Tempo bonus. Score is computed from the point of view of white.
|
// Tempo bonus. Score is computed from the point of view of white.
|
||||||
@@ -333,10 +327,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
// If we have a specialized evaluation function for the current material
|
// If we have a specialized evaluation function for the current material
|
||||||
// configuration, call it and return.
|
// configuration, call it and return.
|
||||||
if (ei.mi->specialized_eval_exists())
|
if (ei.mi->specialized_eval_exists())
|
||||||
{
|
|
||||||
margin = VALUE_ZERO;
|
|
||||||
return ei.mi->evaluate(pos);
|
return ei.mi->evaluate(pos);
|
||||||
}
|
|
||||||
|
|
||||||
// Probe the pawn hash table
|
// Probe the pawn hash table
|
||||||
ei.pi = Pawns::probe(pos, th->pawnsTable);
|
ei.pi = Pawns::probe(pos, th->pawnsTable);
|
||||||
@@ -347,15 +338,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
init_eval_info<BLACK>(pos, ei);
|
init_eval_info<BLACK>(pos, ei);
|
||||||
|
|
||||||
// Evaluate pieces and mobility
|
// Evaluate pieces and mobility
|
||||||
score += evaluate_pieces_of_color<WHITE, Trace>(pos, ei, mobilityWhite)
|
score += evaluate_pieces_of_color<WHITE, Trace>(pos, ei, mobility)
|
||||||
- evaluate_pieces_of_color<BLACK, Trace>(pos, ei, mobilityBlack);
|
- evaluate_pieces_of_color<BLACK, Trace>(pos, ei, mobility);
|
||||||
|
|
||||||
score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]);
|
score += apply_weight(mobility[WHITE] - mobility[BLACK], Weights[Mobility]);
|
||||||
|
|
||||||
// Evaluate kings after all other pieces because we need complete attack
|
// Evaluate kings after all other pieces because we need complete attack
|
||||||
// information when computing the king safety evaluation.
|
// information when computing the king safety evaluation.
|
||||||
score += evaluate_king<WHITE, Trace>(pos, ei, margins)
|
score += evaluate_king<WHITE, Trace>(pos, ei)
|
||||||
- evaluate_king<BLACK, Trace>(pos, ei, margins);
|
- evaluate_king<BLACK, Trace>(pos, ei);
|
||||||
|
|
||||||
// Evaluate tactical threats, we need full attack information including king
|
// Evaluate tactical threats, we need full attack information including king
|
||||||
score += evaluate_threats<WHITE, Trace>(pos, ei)
|
score += evaluate_threats<WHITE, Trace>(pos, ei)
|
||||||
@@ -365,9 +356,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
|
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
|
||||||
- evaluate_passed_pawns<BLACK, Trace>(pos, ei);
|
- evaluate_passed_pawns<BLACK, Trace>(pos, ei);
|
||||||
|
|
||||||
// If one side has only a king, check whether exists any unstoppable passed pawn
|
// If one side has only a king, score for potential unstoppable pawns
|
||||||
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
|
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
|
||||||
score += evaluate_unstoppable_pawns(pos, ei);
|
score += evaluate_unstoppable_pawns(pos, WHITE, ei)
|
||||||
|
- evaluate_unstoppable_pawns(pos, BLACK, ei);
|
||||||
|
|
||||||
// Evaluate space for both sides, only in middle-game.
|
// Evaluate space for both sides, only in middle-game.
|
||||||
if (ei.mi->space_weight())
|
if (ei.mi->space_weight())
|
||||||
@@ -401,7 +393,6 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
sf = ScaleFactor(50);
|
sf = ScaleFactor(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
margin = margins[pos.side_to_move()];
|
|
||||||
Value v = interpolate(score, ei.mi->game_phase(), sf);
|
Value v = interpolate(score, ei.mi->game_phase(), sf);
|
||||||
|
|
||||||
// In case of tracing add all single evaluation contributions for both white and black
|
// In case of tracing add all single evaluation contributions for both white and black
|
||||||
@@ -410,14 +401,11 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
Tracing::add(PST, pos.psq_score());
|
Tracing::add(PST, pos.psq_score());
|
||||||
Tracing::add(IMBALANCE, ei.mi->material_value());
|
Tracing::add(IMBALANCE, ei.mi->material_value());
|
||||||
Tracing::add(PAWN, ei.pi->pawns_value());
|
Tracing::add(PAWN, ei.pi->pawns_value());
|
||||||
Tracing::add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei));
|
|
||||||
Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
|
Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
|
||||||
Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
|
Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
|
||||||
Tracing::add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
|
Tracing::add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
|
||||||
Tracing::add(TOTAL, score);
|
Tracing::add(TOTAL, score);
|
||||||
Tracing::stream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
|
Tracing::stream << "\nScaling: " << std::noshowpos
|
||||||
<< ", Black: " << to_cp(margins[BLACK])
|
|
||||||
<< "\nScaling: " << std::noshowpos
|
|
||||||
<< std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, "
|
<< std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, "
|
||||||
<< std::setw(6) << 100.0 * (1.0 - ei.mi->game_phase() / 128.0) << "% * "
|
<< std::setw(6) << 100.0 * (1.0 - ei.mi->game_phase() / 128.0) << "% * "
|
||||||
<< std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n"
|
<< std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n"
|
||||||
@@ -437,6 +425,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||||
const Square Down = (Us == WHITE ? DELTA_S : DELTA_N);
|
const Square Down = (Us == WHITE ? DELTA_S : DELTA_N);
|
||||||
|
|
||||||
|
ei.pinnedPieces[Us] = pos.pinned_pieces(Us);
|
||||||
|
|
||||||
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
|
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
|
||||||
ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
|
ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
|
||||||
|
|
||||||
@@ -447,7 +437,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
b &= ei.attackedBy[Us][PAWN];
|
b &= ei.attackedBy[Us][PAWN];
|
||||||
ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0;
|
ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0;
|
||||||
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
|
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0;
|
ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,14 +465,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
else
|
else
|
||||||
bonus += bonus / 2;
|
bonus += bonus / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return make_score(bonus, bonus);
|
return make_score(bonus, bonus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color
|
// evaluate_pieces() assigns bonuses and penalties to the pieces of a given color
|
||||||
|
|
||||||
template<PieceType Piece, Color Us, bool Trace>
|
template<PieceType Piece, Color Us, bool Trace>
|
||||||
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) {
|
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, Bitboard mobilityArea) {
|
||||||
|
|
||||||
Bitboard b;
|
Bitboard b;
|
||||||
Square s;
|
Square s;
|
||||||
@@ -499,13 +491,16 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
: Piece == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN))
|
: Piece == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN))
|
||||||
: pos.attacks_from<Piece>(s);
|
: pos.attacks_from<Piece>(s);
|
||||||
|
|
||||||
|
if (ei.pinnedPieces[Us] & s)
|
||||||
|
b &= LineBB[pos.king_square(Us)][s];
|
||||||
|
|
||||||
ei.attackedBy[Us][Piece] |= b;
|
ei.attackedBy[Us][Piece] |= b;
|
||||||
|
|
||||||
if (b & ei.kingRing[Them])
|
if (b & ei.kingRing[Them])
|
||||||
{
|
{
|
||||||
ei.kingAttackersCount[Us]++;
|
ei.kingAttackersCount[Us]++;
|
||||||
ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
|
ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
|
||||||
Bitboard bb = (b & ei.attackedBy[Them][KING]);
|
Bitboard bb = b & ei.attackedBy[Them][KING];
|
||||||
if (bb)
|
if (bb)
|
||||||
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
|
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
|
||||||
}
|
}
|
||||||
@@ -513,7 +508,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
int mob = Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
|
int mob = Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
|
||||||
: popcount<Full >(b & mobilityArea);
|
: popcount<Full >(b & mobilityArea);
|
||||||
|
|
||||||
mobility += MobilityBonus[Piece][mob];
|
mobility[Us] += MobilityBonus[Piece][mob];
|
||||||
|
|
||||||
// Decrease score if we are attacked by an enemy pawn. Remaining part
|
// Decrease score if we are attacked by an enemy pawn. Remaining part
|
||||||
// of threat evaluation must be done later when we have full attack info.
|
// of threat evaluation must be done later when we have full attack info.
|
||||||
@@ -531,6 +526,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
if (Piece == BISHOP)
|
if (Piece == BISHOP)
|
||||||
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
|
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
|
||||||
|
|
||||||
|
// Penalty for knight when there are few enemy pawns
|
||||||
|
if (Piece == KNIGHT)
|
||||||
|
score -= KnightPawns * std::max(5 - pos.count<PAWN>(Them), 0);
|
||||||
|
|
||||||
if (Piece == BISHOP || Piece == KNIGHT)
|
if (Piece == BISHOP || Piece == KNIGHT)
|
||||||
{
|
{
|
||||||
// Bishop and knight outposts squares
|
// Bishop and knight outposts squares
|
||||||
@@ -587,9 +586,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
const enum Piece P = make_piece(Us, PAWN);
|
const enum Piece P = make_piece(Us, PAWN);
|
||||||
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
|
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
|
||||||
if (pos.piece_on(s + d) == P)
|
if (pos.piece_on(s + d) == P)
|
||||||
score -= !pos.is_empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
|
score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
|
||||||
: pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2
|
: pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2
|
||||||
: TrappedBishopA1H1;
|
: TrappedBishopA1H1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,82 +599,37 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// evaluate_threats<>() assigns bonuses according to the type of attacking piece
|
// evaluate_pieces_of_color() assigns bonuses and penalties to all the
|
||||||
// and the type of attacked one.
|
|
||||||
|
|
||||||
template<Color Us, bool Trace>
|
|
||||||
Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
|
|
||||||
|
|
||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
|
||||||
|
|
||||||
Bitboard b, undefendedMinors, weakEnemies;
|
|
||||||
Score score = SCORE_ZERO;
|
|
||||||
|
|
||||||
// Undefended minors get penalized even if not under attack
|
|
||||||
undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT)
|
|
||||||
& ~ei.attackedBy[Them][ALL_PIECES];
|
|
||||||
|
|
||||||
if (undefendedMinors)
|
|
||||||
score += UndefendedMinor;
|
|
||||||
|
|
||||||
// Enemy pieces not defended by a pawn and under our attack
|
|
||||||
weakEnemies = pos.pieces(Them)
|
|
||||||
& ~ei.attackedBy[Them][PAWN]
|
|
||||||
& ei.attackedBy[Us][ALL_PIECES];
|
|
||||||
|
|
||||||
// Add bonus according to type of attacked enemy piece and to the
|
|
||||||
// type of attacking piece, from knights to queens. Kings are not
|
|
||||||
// considered because are already handled in king evaluation.
|
|
||||||
if (weakEnemies)
|
|
||||||
for (PieceType pt1 = KNIGHT; pt1 < KING; pt1++)
|
|
||||||
{
|
|
||||||
b = ei.attackedBy[Us][pt1] & weakEnemies;
|
|
||||||
if (b)
|
|
||||||
for (PieceType pt2 = PAWN; pt2 < KING; pt2++)
|
|
||||||
if (b & pos.pieces(pt2))
|
|
||||||
score += Threat[pt1][pt2];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Trace)
|
|
||||||
Tracing::scores[Us][THREAT] = score;
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// evaluate_pieces_of_color<>() assigns bonuses and penalties to all the
|
|
||||||
// pieces of a given color.
|
// pieces of a given color.
|
||||||
|
|
||||||
template<Color Us, bool Trace>
|
template<Color Us, bool Trace>
|
||||||
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) {
|
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score* mobility) {
|
||||||
|
|
||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||||
|
|
||||||
Score score = mobility = SCORE_ZERO;
|
|
||||||
|
|
||||||
// Do not include in mobility squares protected by enemy pawns or occupied by our pieces
|
// Do not include in mobility squares protected by enemy pawns or occupied by our pieces
|
||||||
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us, PAWN, KING));
|
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us, PAWN, KING));
|
||||||
|
|
||||||
score += evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea);
|
Score score = evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea)
|
||||||
score += evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea);
|
+ evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea)
|
||||||
score += evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea);
|
+ evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea)
|
||||||
score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
|
+ evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
|
||||||
|
|
||||||
// Sum up all attacked squares
|
// Sum up all attacked squares (updated in evaluate_pieces)
|
||||||
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
||||||
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
||||||
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
|
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
|
||||||
if (Trace)
|
if (Trace)
|
||||||
Tracing::scores[Us][MOBILITY] = apply_weight(mobility, Weights[Mobility]);
|
Tracing::scores[Us][MOBILITY] = apply_weight(mobility[Us], Weights[Mobility]);
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// evaluate_king<>() assigns bonuses and penalties to a king of a given color
|
// evaluate_king() assigns bonuses and penalties to a king of a given color
|
||||||
|
|
||||||
template<Color Us, bool Trace>
|
template<Color Us, bool Trace>
|
||||||
Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]) {
|
Score evaluate_king(const Position& pos, const EvalInfo& ei) {
|
||||||
|
|
||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||||
|
|
||||||
@@ -686,15 +640,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
// King shelter and enemy pawns storm
|
// King shelter and enemy pawns storm
|
||||||
Score score = ei.pi->king_safety<Us>(pos, ksq);
|
Score score = ei.pi->king_safety<Us>(pos, ksq);
|
||||||
|
|
||||||
// King safety. This is quite complicated, and is almost certainly far
|
// Main king safety evaluation
|
||||||
// from optimally tuned.
|
|
||||||
if ( ei.kingAttackersCount[Them] >= 2
|
if ( ei.kingAttackersCount[Them] >= 2
|
||||||
&& ei.kingAdjacentZoneAttacksCount[Them])
|
&& ei.kingAdjacentZoneAttacksCount[Them])
|
||||||
{
|
{
|
||||||
// Find the attacked squares around the king which has no defenders
|
// Find the attacked squares around the king which has no defenders
|
||||||
// apart from the king itself
|
// apart from the king itself
|
||||||
undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING];
|
undefended = ei.attackedBy[Them][ALL_PIECES]
|
||||||
undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
& ei.attackedBy[Us][KING]
|
||||||
|
& ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
||||||
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
||||||
| ei.attackedBy[Us][QUEEN]);
|
| ei.attackedBy[Us][QUEEN]);
|
||||||
|
|
||||||
@@ -703,7 +657,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
// number and types of the enemy's attacking pieces, the number of
|
// number and types of the enemy's attacking pieces, the number of
|
||||||
// attacked and undefended squares around our king, the square of the
|
// attacked and undefended squares around our king, the square of the
|
||||||
// king, and the quality of the pawn shelter.
|
// king, and the quality of the pawn shelter.
|
||||||
attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
|
attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
|
||||||
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
|
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
|
||||||
+ KingExposed[relative_square(Us, ksq)]
|
+ KingExposed[relative_square(Us, ksq)]
|
||||||
- mg_value(score) / 32;
|
- mg_value(score) / 32;
|
||||||
@@ -770,12 +724,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
attackUnits = std::min(99, std::max(0, attackUnits));
|
attackUnits = std::min(99, std::max(0, attackUnits));
|
||||||
|
|
||||||
// Finally, extract the king danger score from the KingDanger[]
|
// Finally, extract the king danger score from the KingDanger[]
|
||||||
// array and subtract the score from evaluation. Set also margins[]
|
// array and subtract the score from evaluation.
|
||||||
// value that will be used for pruning because this value can sometimes
|
|
||||||
// be very big, and so capturing a single attacking piece can therefore
|
|
||||||
// result in a score change far bigger than the value of the captured piece.
|
|
||||||
score -= KingDanger[Us == Search::RootColor][attackUnits];
|
score -= KingDanger[Us == Search::RootColor][attackUnits];
|
||||||
margins[Us] += mg_value(KingDanger[Us == Search::RootColor][attackUnits]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Trace)
|
if (Trace)
|
||||||
@@ -785,7 +735,50 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// evaluate_passed_pawns<>() evaluates the passed pawns of the given color
|
// evaluate_threats() assigns bonuses according to the type of attacking piece
|
||||||
|
// and the type of attacked one.
|
||||||
|
|
||||||
|
template<Color Us, bool Trace>
|
||||||
|
Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
|
||||||
|
|
||||||
|
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||||
|
|
||||||
|
Bitboard b, undefendedMinors, weakEnemies;
|
||||||
|
Score score = SCORE_ZERO;
|
||||||
|
|
||||||
|
// Undefended minors get penalized even if not under attack
|
||||||
|
undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT)
|
||||||
|
& ~ei.attackedBy[Them][ALL_PIECES];
|
||||||
|
|
||||||
|
if (undefendedMinors)
|
||||||
|
score += UndefendedMinor;
|
||||||
|
|
||||||
|
// Enemy pieces not defended by a pawn and under our attack
|
||||||
|
weakEnemies = pos.pieces(Them)
|
||||||
|
& ~ei.attackedBy[Them][PAWN]
|
||||||
|
& ei.attackedBy[Us][ALL_PIECES];
|
||||||
|
|
||||||
|
// Add bonus according to type of attacked enemy piece and to the
|
||||||
|
// type of attacking piece, from knights to queens. Kings are not
|
||||||
|
// considered because are already handled in king evaluation.
|
||||||
|
if (weakEnemies)
|
||||||
|
for (PieceType pt1 = KNIGHT; pt1 < KING; ++pt1)
|
||||||
|
{
|
||||||
|
b = ei.attackedBy[Us][pt1] & weakEnemies;
|
||||||
|
if (b)
|
||||||
|
for (PieceType pt2 = PAWN; pt2 < KING; ++pt2)
|
||||||
|
if (b & pos.pieces(pt2))
|
||||||
|
score += Threat[pt1][pt2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Trace)
|
||||||
|
Tracing::scores[Us][THREAT] = score;
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// evaluate_passed_pawns() evaluates the passed pawns of the given color
|
||||||
|
|
||||||
template<Color Us, bool Trace>
|
template<Color Us, bool Trace>
|
||||||
Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) {
|
Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) {
|
||||||
@@ -801,7 +794,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
{
|
{
|
||||||
Square s = pop_lsb(&b);
|
Square s = pop_lsb(&b);
|
||||||
|
|
||||||
assert(pos.pawn_is_passed(Us, s));
|
assert(pos.pawn_passed(Us, s));
|
||||||
|
|
||||||
int r = int(relative_rank(Us, s) - RANK_2);
|
int r = int(relative_rank(Us, s) - RANK_2);
|
||||||
int rr = r * (r - 1);
|
int rr = r * (r - 1);
|
||||||
@@ -815,18 +808,17 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
Square blockSq = s + pawn_push(Us);
|
Square blockSq = s + pawn_push(Us);
|
||||||
|
|
||||||
// Adjust bonus based on kings proximity
|
// Adjust bonus based on kings proximity
|
||||||
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr);
|
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr)
|
||||||
ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 2 * rr);
|
- Value(square_distance(pos.king_square(Us ), blockSq) * 2 * rr);
|
||||||
|
|
||||||
// If blockSq is not the queening square then consider also a second push
|
// If blockSq is not the queening square then consider also a second push
|
||||||
if (rank_of(blockSq) != (Us == WHITE ? RANK_8 : RANK_1))
|
if (relative_rank(Us, blockSq) != RANK_8)
|
||||||
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
|
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
|
||||||
|
|
||||||
// If the pawn is free to advance, increase bonus
|
// If the pawn is free to advance, increase bonus
|
||||||
if (pos.is_empty(blockSq))
|
if (pos.empty(blockSq))
|
||||||
{
|
{
|
||||||
squaresToQueen = forward_bb(Us, s);
|
squaresToQueen = forward_bb(Us, s);
|
||||||
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
|
|
||||||
|
|
||||||
// If there is an enemy rook or queen attacking the pawn from behind,
|
// If there is an enemy rook or queen attacking the pawn from behind,
|
||||||
// add all X-ray attacks by the rook or queen. Otherwise consider only
|
// add all X-ray attacks by the rook or queen. Otherwise consider only
|
||||||
@@ -837,6 +829,12 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
else
|
else
|
||||||
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
|
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
|
||||||
|
|
||||||
|
if ( unlikely(forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN))
|
||||||
|
&& (forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
|
||||||
|
defendedSquares = squaresToQueen;
|
||||||
|
else
|
||||||
|
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
|
||||||
|
|
||||||
// If there aren't enemy attacks huge bonus, a bit smaller if at
|
// If there aren't enemy attacks huge bonus, a bit smaller if at
|
||||||
// least block square is not attacked, otherwise smallest bonus.
|
// least block square is not attacked, otherwise smallest bonus.
|
||||||
int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3;
|
int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3;
|
||||||
@@ -872,9 +870,14 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
{
|
{
|
||||||
if (pos.non_pawn_material(Them) <= KnightValueMg)
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them))
|
||||||
|
ebonus += ebonus / 4;
|
||||||
|
|
||||||
score += make_score(mbonus, ebonus);
|
score += make_score(mbonus, ebonus);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -887,164 +890,18 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite
|
// evaluate_unstoppable_pawns() scores the most advanced among the passed and
|
||||||
// conservative and returns a winning score only when we are very sure that the pawn is winning.
|
// candidate pawns. In case opponent has no pieces but pawns, this is somewhat
|
||||||
|
// related to the possibility pawns are unstoppable.
|
||||||
|
|
||||||
Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei) {
|
Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) {
|
||||||
|
|
||||||
Bitboard b, b2, blockers, supporters, queeningPath, candidates;
|
Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us);
|
||||||
Square s, blockSq, queeningSquare;
|
|
||||||
Color c, winnerSide, loserSide;
|
|
||||||
bool pathDefended, opposed;
|
|
||||||
int pliesToGo, movesToGo, oppMovesToGo, sacptg, blockersCount, minKingDist, kingptg, d;
|
|
||||||
int pliesToQueen[] = { 256, 256 };
|
|
||||||
|
|
||||||
// Step 1. Hunt for unstoppable passed pawns. If we find at least one,
|
if (!b || pos.non_pawn_material(~us))
|
||||||
// record how many plies are required for promotion.
|
|
||||||
for (c = WHITE; c <= BLACK; c++)
|
|
||||||
{
|
|
||||||
// Skip if other side has non-pawn pieces
|
|
||||||
if (pos.non_pawn_material(~c))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
b = ei.pi->passed_pawns(c);
|
|
||||||
|
|
||||||
while (b)
|
|
||||||
{
|
|
||||||
s = pop_lsb(&b);
|
|
||||||
queeningSquare = relative_square(c, file_of(s) | RANK_8);
|
|
||||||
queeningPath = forward_bb(c, s);
|
|
||||||
|
|
||||||
// Compute plies to queening and check direct advancement
|
|
||||||
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
|
|
||||||
oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move());
|
|
||||||
pathDefended = ((ei.attackedBy[c][ALL_PIECES] & queeningPath) == queeningPath);
|
|
||||||
|
|
||||||
if (movesToGo >= oppMovesToGo && !pathDefended)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Opponent king cannot block because path is defended and position
|
|
||||||
// is not in check. So only friendly pieces can be blockers.
|
|
||||||
assert(!pos.checkers());
|
|
||||||
assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c)));
|
|
||||||
|
|
||||||
// Add moves needed to free the path from friendly pieces and retest condition
|
|
||||||
movesToGo += popcount<Max15>(queeningPath & pos.pieces(c));
|
|
||||||
|
|
||||||
if (movesToGo >= oppMovesToGo && !pathDefended)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
pliesToGo = 2 * movesToGo - int(c == pos.side_to_move());
|
|
||||||
pliesToQueen[c] = std::min(pliesToQueen[c], pliesToGo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2. If either side cannot promote at least three plies before the other side then situation
|
|
||||||
// becomes too complex and we give up. Otherwise we determine the possibly "winning side"
|
|
||||||
if (abs(pliesToQueen[WHITE] - pliesToQueen[BLACK]) < 3)
|
|
||||||
return SCORE_ZERO;
|
return SCORE_ZERO;
|
||||||
|
|
||||||
winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK);
|
return Unstoppable * int(relative_rank(us, frontmost_sq(us, b)));
|
||||||
loserSide = ~winnerSide;
|
|
||||||
|
|
||||||
// Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
|
|
||||||
b = candidates = pos.pieces(loserSide, PAWN);
|
|
||||||
|
|
||||||
while (b)
|
|
||||||
{
|
|
||||||
s = pop_lsb(&b);
|
|
||||||
|
|
||||||
// Compute plies from queening
|
|
||||||
queeningSquare = relative_square(loserSide, file_of(s) | RANK_8);
|
|
||||||
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
|
|
||||||
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
|
|
||||||
|
|
||||||
// Check if (without even considering any obstacles) we're too far away or doubled
|
|
||||||
if ( pliesToQueen[winnerSide] + 3 <= pliesToGo
|
|
||||||
|| (forward_bb(loserSide, s) & pos.pieces(loserSide, PAWN)))
|
|
||||||
candidates ^= s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If any candidate is already a passed pawn it _may_ promote in time. We give up.
|
|
||||||
if (candidates & ei.pi->passed_pawns(loserSide))
|
|
||||||
return SCORE_ZERO;
|
|
||||||
|
|
||||||
// Step 4. Check new passed pawn creation through king capturing and pawn sacrifices
|
|
||||||
b = candidates;
|
|
||||||
|
|
||||||
while (b)
|
|
||||||
{
|
|
||||||
s = pop_lsb(&b);
|
|
||||||
sacptg = blockersCount = 0;
|
|
||||||
minKingDist = kingptg = 256;
|
|
||||||
|
|
||||||
// Compute plies from queening
|
|
||||||
queeningSquare = relative_square(loserSide, file_of(s) | RANK_8);
|
|
||||||
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
|
|
||||||
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
|
|
||||||
|
|
||||||
// Generate list of blocking pawns and supporters
|
|
||||||
supporters = adjacent_files_bb(file_of(s)) & candidates;
|
|
||||||
opposed = forward_bb(loserSide, s) & pos.pieces(winnerSide, PAWN);
|
|
||||||
blockers = passed_pawn_mask(loserSide, s) & pos.pieces(winnerSide, PAWN);
|
|
||||||
|
|
||||||
assert(blockers);
|
|
||||||
|
|
||||||
// How many plies does it take to remove all the blocking pawns?
|
|
||||||
while (blockers)
|
|
||||||
{
|
|
||||||
blockSq = pop_lsb(&blockers);
|
|
||||||
movesToGo = 256;
|
|
||||||
|
|
||||||
// Check pawns that can give support to overcome obstacle, for instance
|
|
||||||
// black pawns: a4, b4 white: b2 then pawn in b4 is giving support.
|
|
||||||
if (!opposed)
|
|
||||||
{
|
|
||||||
b2 = supporters & in_front_bb(winnerSide, rank_of(blockSq + pawn_push(winnerSide)));
|
|
||||||
|
|
||||||
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
|
|
||||||
{
|
|
||||||
d = square_distance(blockSq, pop_lsb(&b2)) - 2;
|
|
||||||
movesToGo = std::min(movesToGo, d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check pawns that can be sacrificed against the blocking pawn
|
|
||||||
b2 = pawn_attack_span(winnerSide, blockSq) & candidates & ~(1ULL << s);
|
|
||||||
|
|
||||||
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
|
|
||||||
{
|
|
||||||
d = square_distance(blockSq, pop_lsb(&b2)) - 2;
|
|
||||||
movesToGo = std::min(movesToGo, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If obstacle can be destroyed with an immediate pawn exchange / sacrifice,
|
|
||||||
// it's not a real obstacle and we have nothing to add to pliesToGo.
|
|
||||||
if (movesToGo <= 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Plies needed to sacrifice against all the blocking pawns
|
|
||||||
sacptg += movesToGo * 2;
|
|
||||||
blockersCount++;
|
|
||||||
|
|
||||||
// Plies needed for the king to capture all the blocking pawns
|
|
||||||
d = square_distance(pos.king_square(loserSide), blockSq);
|
|
||||||
minKingDist = std::min(minKingDist, d);
|
|
||||||
kingptg = (minKingDist + blockersCount) * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if pawn sacrifice plan _may_ save the day
|
|
||||||
if (pliesToQueen[winnerSide] + 3 > pliesToGo + sacptg)
|
|
||||||
return SCORE_ZERO;
|
|
||||||
|
|
||||||
// Check if king capture plan _may_ save the day (contains some false positives)
|
|
||||||
if (pliesToQueen[winnerSide] + 3 > pliesToGo + kingptg)
|
|
||||||
return SCORE_ZERO;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Winning pawn is unstoppable and will promote as first, return big score
|
|
||||||
Score score = make_score(0, (Value) 1280 - 32 * pliesToQueen[winnerSide]);
|
|
||||||
return winnerSide == WHITE ? score : -score;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1089,9 +946,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE);
|
assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE);
|
||||||
assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME);
|
assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME);
|
||||||
|
|
||||||
int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
|
int e = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
|
||||||
int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128;
|
int r = (mg_value(v) * int(ph) + e * int(PHASE_MIDGAME - ph)) / PHASE_MIDGAME;
|
||||||
return Value((result / GrainSize) * GrainSize); // Sign independent
|
return Value((r / GrainSize) * GrainSize); // Sign independent
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply_weight() weights score v by score w trying to prevent overflow
|
// apply_weight() weights score v by score w trying to prevent overflow
|
||||||
@@ -1129,7 +986,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
Score bScore = scores[BLACK][idx];
|
Score bScore = scores[BLACK][idx];
|
||||||
|
|
||||||
switch (idx) {
|
switch (idx) {
|
||||||
case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL:
|
case PST: case IMBALANCE: case PAWN: case TOTAL:
|
||||||
stream << std::setw(20) << name << " | --- --- | --- --- | "
|
stream << std::setw(20) << name << " | --- --- | --- --- | "
|
||||||
<< std::setw(6) << to_cp(mg_value(wScore)) << " "
|
<< std::setw(6) << to_cp(mg_value(wScore)) << " "
|
||||||
<< std::setw(6) << to_cp(eg_value(wScore)) << " \n";
|
<< std::setw(6) << to_cp(eg_value(wScore)) << " \n";
|
||||||
@@ -1152,8 +1009,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
|
stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
|
||||||
std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score));
|
std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score));
|
||||||
|
|
||||||
Value margin;
|
do_evaluate<true>(pos);
|
||||||
do_evaluate<true>(pos, margin);
|
|
||||||
|
|
||||||
std::string totals = stream.str();
|
std::string totals = stream.str();
|
||||||
stream.str("");
|
stream.str("");
|
||||||
@@ -1173,7 +1029,6 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||||||
row("King safety", KING);
|
row("King safety", KING);
|
||||||
row("Threats", THREAT);
|
row("Threats", THREAT);
|
||||||
row("Passed pawns", PASSED);
|
row("Passed pawns", PASSED);
|
||||||
row("Unstoppable pawns", UNSTOPPABLE);
|
|
||||||
row("Space", SPACE);
|
row("Space", SPACE);
|
||||||
|
|
||||||
stream << "---------------------+-------------+-------------+---------------\n";
|
stream << "---------------------+-------------+-------------+---------------\n";
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class Position;
|
|||||||
namespace Eval {
|
namespace Eval {
|
||||||
|
|
||||||
extern void init();
|
extern void init();
|
||||||
extern Value evaluate(const Position& pos, Value& margin);
|
extern Value evaluate(const Position& pos);
|
||||||
extern std::string trace(const Position& pos);
|
extern std::string trace(const Position& pos);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,13 +37,14 @@ int main(int argc, char* argv[]) {
|
|||||||
Position::init();
|
Position::init();
|
||||||
Bitbases::init_kpk();
|
Bitbases::init_kpk();
|
||||||
Search::init();
|
Search::init();
|
||||||
|
Pawns::init();
|
||||||
Eval::init();
|
Eval::init();
|
||||||
Threads.init();
|
Threads.init();
|
||||||
TT.set_size(Options["Hash"]);
|
TT.set_size(Options["Hash"]);
|
||||||
|
|
||||||
std::string args;
|
std::string args;
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -35,31 +35,29 @@ namespace {
|
|||||||
const int NoPawnsSF[4] = { 6, 12, 32 };
|
const int NoPawnsSF[4] = { 6, 12, 32 };
|
||||||
|
|
||||||
// Polynomial material balance parameters
|
// Polynomial material balance parameters
|
||||||
const Value RedundantQueen = Value(320);
|
|
||||||
const Value RedundantRook = Value(554);
|
|
||||||
|
|
||||||
// pair pawn knight bishop rook queen
|
// pair pawn knight bishop rook queen
|
||||||
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
|
const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -52 };
|
||||||
|
|
||||||
const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = {
|
const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = {
|
||||||
// pair pawn knight bishop rook queen
|
// pair pawn knight bishop rook queen
|
||||||
{ 7 }, // Bishop pair
|
{ 0 }, // Bishop pair
|
||||||
{ 39, 2 }, // Pawn
|
{ 39, 2 }, // Pawn
|
||||||
{ 35, 271, -4 }, // Knight
|
{ 35, 271, -4 }, // Knight
|
||||||
{ 7, 105, 4, 7 }, // Bishop
|
{ 0, 105, 4, 0 }, // Bishop
|
||||||
{ -27, -2, 46, 100, 56 }, // Rook
|
{ -27, -2, 46, 100, -141 }, // Rook
|
||||||
{ 58, 29, 83, 148, -3, -25 } // Queen
|
{ 58, 29, 83, 148, -163, 0 } // Queen
|
||||||
};
|
};
|
||||||
|
|
||||||
const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = {
|
const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = {
|
||||||
// THEIR PIECES
|
// THEIR PIECES
|
||||||
// pair pawn knight bishop rook queen
|
// pair pawn knight bishop rook queen
|
||||||
{ 41 }, // Bishop pair
|
{ 0 }, // Bishop pair
|
||||||
{ 37, 41 }, // Pawn
|
{ 37, 0 }, // Pawn
|
||||||
{ 10, 62, 41 }, // Knight OUR PIECES
|
{ 10, 62, 0 }, // Knight OUR PIECES
|
||||||
{ 57, 64, 39, 41 }, // Bishop
|
{ 57, 64, 39, 0 }, // Bishop
|
||||||
{ 50, 40, 23, -22, 41 }, // Rook
|
{ 50, 40, 23, -22, 0 }, // Rook
|
||||||
{ 106, 101, 3, 151, 171, 41 } // Queen
|
{ 106, 101, 3, 151, 171, 0 } // Queen
|
||||||
};
|
};
|
||||||
|
|
||||||
// Endgame evaluation and scaling functions accessed direcly and not through
|
// Endgame evaluation and scaling functions accessed direcly and not through
|
||||||
@@ -106,14 +104,8 @@ namespace {
|
|||||||
int pt1, pt2, pc, v;
|
int pt1, pt2, pc, v;
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
|
||||||
// Redundancy of major pieces, formula based on Kaufman's paper
|
|
||||||
// "The Evaluation of Material Imbalances in Chess"
|
|
||||||
if (pieceCount[Us][ROOK] > 0)
|
|
||||||
value -= RedundantRook * (pieceCount[Us][ROOK] - 1)
|
|
||||||
+ RedundantQueen * pieceCount[Us][QUEEN];
|
|
||||||
|
|
||||||
// Second-degree polynomial material imbalance by Tord Romstad
|
// Second-degree polynomial material imbalance by Tord Romstad
|
||||||
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
|
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
|
||||||
{
|
{
|
||||||
pc = pieceCount[Us][pt1];
|
pc = pieceCount[Us][pt1];
|
||||||
if (!pc)
|
if (!pc)
|
||||||
@@ -121,7 +113,7 @@ namespace {
|
|||||||
|
|
||||||
v = LinearCoefficients[pt1];
|
v = LinearCoefficients[pt1];
|
||||||
|
|
||||||
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
|
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
|
||||||
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
|
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
|
||||||
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
|
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
|
||||||
|
|
||||||
@@ -240,7 +232,8 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No pawns makes it difficult to win, even with a material advantage
|
// No pawns makes it difficult to win, even with a material advantage. This
|
||||||
|
// catches some trivial draws like KK, KBK and KNK
|
||||||
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
|
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
|
||||||
{
|
{
|
||||||
e->factor[WHITE] = (uint8_t)
|
e->factor[WHITE] = (uint8_t)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ using namespace std;
|
|||||||
|
|
||||||
/// Version number. If Version is left empty, then compile date, in the
|
/// Version number. If Version is left empty, then compile date, in the
|
||||||
/// format DD-MM-YY, is shown in engine_info.
|
/// format DD-MM-YY, is shown in engine_info.
|
||||||
static const string Version = "4";
|
static const string Version = "DD";
|
||||||
|
|
||||||
|
|
||||||
/// engine_info() returns the full name of the current Stockfish version. This
|
/// engine_info() returns the full name of the current Stockfish version. This
|
||||||
@@ -63,9 +63,9 @@ const string engine_info(bool to_uci) {
|
|||||||
|
|
||||||
static uint64_t hits[2], means[2];
|
static uint64_t hits[2], means[2];
|
||||||
|
|
||||||
void dbg_hit_on(bool b) { hits[0]++; if (b) hits[1]++; }
|
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
|
||||||
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
|
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
|
||||||
void dbg_mean_of(int v) { means[0]++; means[1] += v; }
|
void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
|
||||||
|
|
||||||
void dbg_print() {
|
void dbg_print() {
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ void dbg_print() {
|
|||||||
|
|
||||||
if (means[0])
|
if (means[0])
|
||||||
cerr << "Total " << means[0] << " Mean "
|
cerr << "Total " << means[0] << " Mean "
|
||||||
<< (float)means[1] / means[0] << endl;
|
<< (double)means[1] / means[0] << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,13 +56,13 @@ namespace {
|
|||||||
// Because we generate only legal castling moves we need to verify that
|
// Because we generate only legal castling moves we need to verify that
|
||||||
// when moving the castling rook we do not discover some hidden checker.
|
// when moving the castling rook we do not discover some hidden checker.
|
||||||
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
|
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
|
||||||
if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies))
|
if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN)))
|
||||||
return mlist;
|
return mlist;
|
||||||
|
|
||||||
(mlist++)->move = make<CASTLE>(kfrom, rfrom);
|
(mlist++)->move = make<CASTLE>(kfrom, rfrom);
|
||||||
|
|
||||||
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
|
if (Checks && !pos.gives_check((mlist - 1)->move, CheckInfo(pos)))
|
||||||
mlist--;
|
--mlist;
|
||||||
|
|
||||||
return mlist;
|
return mlist;
|
||||||
}
|
}
|
||||||
@@ -359,31 +359,14 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
|
|||||||
// evasions so to skip known illegal moves avoiding useless legality check later.
|
// evasions so to skip known illegal moves avoiding useless legality check later.
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
checkersCnt++;
|
++checkersCnt;
|
||||||
checksq = pop_lsb(&b);
|
checksq = pop_lsb(&b);
|
||||||
|
|
||||||
assert(color_of(pos.piece_on(checksq)) == ~us);
|
assert(color_of(pos.piece_on(checksq)) == ~us);
|
||||||
|
|
||||||
switch (type_of(pos.piece_on(checksq)))
|
if (type_of(pos.piece_on(checksq)) > KNIGHT) // A slider
|
||||||
{
|
sliderAttacks |= LineBB[checksq][ksq] ^ checksq;
|
||||||
case BISHOP: sliderAttacks |= PseudoAttacks[BISHOP][checksq]; break;
|
|
||||||
case ROOK: sliderAttacks |= PseudoAttacks[ROOK][checksq]; break;
|
|
||||||
case QUEEN:
|
|
||||||
// If queen and king are far or not on a diagonal line we can safely
|
|
||||||
// remove all the squares attacked in the other direction becuase are
|
|
||||||
// not reachable by the king anyway.
|
|
||||||
if (between_bb(ksq, checksq) || !(PseudoAttacks[BISHOP][checksq] & ksq))
|
|
||||||
sliderAttacks |= PseudoAttacks[QUEEN][checksq];
|
|
||||||
|
|
||||||
// Otherwise we need to use real rook attacks to check if king is safe
|
|
||||||
// to move in the other direction. For example: king in B2, queen in A1
|
|
||||||
// a knight in B1, and we can safely move to C1.
|
|
||||||
else
|
|
||||||
sliderAttacks |= PseudoAttacks[BISHOP][checksq] | pos.attacks_from<ROOK>(checksq);
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (b);
|
} while (b);
|
||||||
|
|
||||||
// Generate evasions for king, capture and non capture moves
|
// Generate evasions for king, capture and non capture moves
|
||||||
@@ -407,17 +390,17 @@ template<>
|
|||||||
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* mlist) {
|
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* mlist) {
|
||||||
|
|
||||||
ExtMove *end, *cur = mlist;
|
ExtMove *end, *cur = mlist;
|
||||||
Bitboard pinned = pos.pinned_pieces();
|
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||||
Square ksq = pos.king_square(pos.side_to_move());
|
Square ksq = pos.king_square(pos.side_to_move());
|
||||||
|
|
||||||
end = pos.checkers() ? generate<EVASIONS>(pos, mlist)
|
end = pos.checkers() ? generate<EVASIONS>(pos, mlist)
|
||||||
: generate<NON_EVASIONS>(pos, mlist);
|
: generate<NON_EVASIONS>(pos, mlist);
|
||||||
while (cur != end)
|
while (cur != end)
|
||||||
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
|
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
|
||||||
&& !pos.pl_move_is_legal(cur->move, pinned))
|
&& !pos.legal(cur->move, pinned))
|
||||||
cur->move = (--end)->move;
|
cur->move = (--end)->move;
|
||||||
else
|
else
|
||||||
cur++;
|
++cur;
|
||||||
|
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ template<GenType T>
|
|||||||
struct MoveList {
|
struct MoveList {
|
||||||
|
|
||||||
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) { last->move = MOVE_NONE; }
|
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) { last->move = MOVE_NONE; }
|
||||||
void operator++() { cur++; }
|
void operator++() { ++cur; }
|
||||||
Move operator*() const { return cur->move; }
|
Move operator*() const { return cur->move; }
|
||||||
size_t size() const { return last - mlist; }
|
size_t size() const { return last - mlist; }
|
||||||
bool contains(Move m) const {
|
bool contains(Move m) const {
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
|
|||||||
else
|
else
|
||||||
stage = MAIN_SEARCH;
|
stage = MAIN_SEARCH;
|
||||||
|
|
||||||
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||||
end += (ttMove != MOVE_NONE);
|
end += (ttMove != MOVE_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
|
|||||||
// Skip TT move if is not a capture or a promotion, this avoids qsearch
|
// Skip TT move if is not a capture or a promotion, this avoids qsearch
|
||||||
// tree explosion due to a possible perpetual check or similar rare cases
|
// tree explosion due to a possible perpetual check or similar rare cases
|
||||||
// when TT table is full.
|
// when TT table is full.
|
||||||
if (ttm && !pos.is_capture_or_promotion(ttm))
|
if (ttm && !pos.capture_or_promotion(ttm))
|
||||||
ttm = MOVE_NONE;
|
ttm = MOVE_NONE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -118,7 +118,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
|
|||||||
ttm = MOVE_NONE;
|
ttm = MOVE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||||
end += (ttMove != MOVE_NONE);
|
end += (ttMove != MOVE_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,9 +131,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Piece
|
|||||||
|
|
||||||
// In ProbCut we generate only captures better than parent's captured piece
|
// In ProbCut we generate only captures better than parent's captured piece
|
||||||
captureThreshold = PieceValue[MG][pt];
|
captureThreshold = PieceValue[MG][pt];
|
||||||
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
||||||
|
|
||||||
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold))
|
if (ttMove && (!pos.capture(ttMove) || pos.see(ttMove) <= captureThreshold))
|
||||||
ttMove = MOVE_NONE;
|
ttMove = MOVE_NONE;
|
||||||
|
|
||||||
end += (ttMove != MOVE_NONE);
|
end += (ttMove != MOVE_NONE);
|
||||||
@@ -163,7 +163,7 @@ void MovePicker::score<CAPTURES>() {
|
|||||||
{
|
{
|
||||||
m = it->move;
|
m = it->move;
|
||||||
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
|
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||||
- type_of(pos.piece_moved(m));
|
- type_of(pos.moved_piece(m));
|
||||||
|
|
||||||
if (type_of(m) == PROMOTION)
|
if (type_of(m) == PROMOTION)
|
||||||
it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN];
|
it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN];
|
||||||
@@ -181,7 +181,7 @@ void MovePicker::score<QUIETS>() {
|
|||||||
for (ExtMove* it = moves; it != end; ++it)
|
for (ExtMove* it = moves; it != end; ++it)
|
||||||
{
|
{
|
||||||
m = it->move;
|
m = it->move;
|
||||||
it->score = history[pos.piece_moved(m)][to_sq(m)];
|
it->score = history[pos.moved_piece(m)][to_sq(m)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,11 +199,11 @@ void MovePicker::score<EVASIONS>() {
|
|||||||
if ((seeScore = pos.see_sign(m)) < 0)
|
if ((seeScore = pos.see_sign(m)) < 0)
|
||||||
it->score = seeScore - HistoryStats::Max; // At the bottom
|
it->score = seeScore - HistoryStats::Max; // At the bottom
|
||||||
|
|
||||||
else if (pos.is_capture(m))
|
else if (pos.capture(m))
|
||||||
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
|
it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||||
- type_of(pos.piece_moved(m)) + HistoryStats::Max;
|
- type_of(pos.moved_piece(m)) + HistoryStats::Max;
|
||||||
else
|
else
|
||||||
it->score = history[pos.piece_moved(m)][to_sq(m)];
|
it->score = history[pos.moved_piece(m)][to_sq(m)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ void MovePicker::generate_next() {
|
|||||||
killers[2].move = killers[3].move = MOVE_NONE;
|
killers[2].move = killers[3].move = MOVE_NONE;
|
||||||
|
|
||||||
// Be sure countermoves are different from killers
|
// Be sure countermoves are different from killers
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; ++i)
|
||||||
if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move)
|
if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move)
|
||||||
(end++)->move = countermoves[i];
|
(end++)->move = countermoves[i];
|
||||||
|
|
||||||
@@ -299,7 +299,7 @@ Move MovePicker::next_move<false>() {
|
|||||||
switch (stage) {
|
switch (stage) {
|
||||||
|
|
||||||
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
|
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
|
||||||
cur++;
|
++cur;
|
||||||
return ttMove;
|
return ttMove;
|
||||||
|
|
||||||
case CAPTURES_S1:
|
case CAPTURES_S1:
|
||||||
@@ -317,9 +317,9 @@ Move MovePicker::next_move<false>() {
|
|||||||
case KILLERS_S1:
|
case KILLERS_S1:
|
||||||
move = (cur++)->move;
|
move = (cur++)->move;
|
||||||
if ( move != MOVE_NONE
|
if ( move != MOVE_NONE
|
||||||
&& pos.is_pseudo_legal(move)
|
&& pos.pseudo_legal(move)
|
||||||
&& move != ttMove
|
&& move != ttMove
|
||||||
&& !pos.is_capture(move))
|
&& !pos.capture(move))
|
||||||
return move;
|
return move;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ const string move_to_san(Position& pos, Move m) {
|
|||||||
while (b)
|
while (b)
|
||||||
{
|
{
|
||||||
Move move = make_move(pop_lsb(&b), to);
|
Move move = make_move(pop_lsb(&b), to);
|
||||||
if (!pos.pl_move_is_legal(move, pos.pinned_pieces()))
|
if (!pos.legal(move, pos.pinned_pieces(pos.side_to_move())))
|
||||||
others ^= from_sq(move);
|
others ^= from_sq(move);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,10 +149,10 @@ const string move_to_san(Position& pos, Move m) {
|
|||||||
san += square_to_string(from);
|
san += square_to_string(from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pos.is_capture(m))
|
else if (pos.capture(m))
|
||||||
san = file_to_char(file_of(from));
|
san = file_to_char(file_of(from));
|
||||||
|
|
||||||
if (pos.is_capture(m))
|
if (pos.capture(m))
|
||||||
san += 'x';
|
san += 'x';
|
||||||
|
|
||||||
san += square_to_string(to);
|
san += square_to_string(to);
|
||||||
@@ -161,7 +161,7 @@ const string move_to_san(Position& pos, Move m) {
|
|||||||
san += string("=") + PieceToChar[WHITE][promotion_type(m)];
|
san += string("=") + PieceToChar[WHITE][promotion_type(m)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos.move_gives_check(m, CheckInfo(pos)))
|
if (pos.gives_check(m, CheckInfo(pos)))
|
||||||
{
|
{
|
||||||
StateInfo st;
|
StateInfo st;
|
||||||
pos.do_move(m, st);
|
pos.do_move(m, st);
|
||||||
@@ -207,7 +207,7 @@ static string score_to_string(Value v) {
|
|||||||
s << "-#" << (VALUE_MATE + v) / 2;
|
s << "-#" << (VALUE_MATE + v) / 2;
|
||||||
|
|
||||||
else
|
else
|
||||||
s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg;
|
s << setprecision(2) << fixed << showpos << double(v) / PawnValueMg;
|
||||||
|
|
||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,38 +30,32 @@ namespace {
|
|||||||
#define V Value
|
#define V Value
|
||||||
#define S(mg, eg) make_score(mg, eg)
|
#define S(mg, eg) make_score(mg, eg)
|
||||||
|
|
||||||
// Doubled pawn penalty by opposed flag and file
|
// Doubled pawn penalty by file
|
||||||
const Score Doubled[2][FILE_NB] = {
|
const Score Doubled[FILE_NB] = {
|
||||||
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
|
S(23, 48), S(23, 48), S(20, 48), S(13, 43) };
|
||||||
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
|
||||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
|
|
||||||
|
|
||||||
// Isolated pawn penalty by opposed flag and file
|
// Isolated pawn penalty by opposed flag and file
|
||||||
const Score Isolated[2][FILE_NB] = {
|
const Score Isolated[2][FILE_NB] = {
|
||||||
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
|
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
|
||||||
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
|
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
|
||||||
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
||||||
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
|
S(40, 35), S(40, 35), S(36, 35), S(25, 30) } };
|
||||||
|
|
||||||
// Backward pawn penalty by opposed flag and file
|
// Backward pawn penalty by opposed flag and file
|
||||||
const Score Backward[2][FILE_NB] = {
|
const Score Backward[2][FILE_NB] = {
|
||||||
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46),
|
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46),
|
||||||
S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
|
S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
|
||||||
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31),
|
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31),
|
||||||
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
|
S(33, 31), S(33, 31), S(29, 31), S(20, 28) } };
|
||||||
|
|
||||||
// Pawn chain membership bonus by file
|
// Pawn chain membership bonus by file and rank (initialized by formula)
|
||||||
const Score ChainMember[FILE_NB] = {
|
Score ChainMember[FILE_NB][RANK_NB];
|
||||||
S(11,-1), S(13,-1), S(13,-1), S(14,-1),
|
|
||||||
S(14,-1), S(13,-1), S(13,-1), S(11,-1)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Candidate passed pawn bonus by rank
|
// Candidate passed pawn bonus by rank
|
||||||
const Score CandidatePassed[RANK_NB] = {
|
const Score CandidatePassed[RANK_NB] = {
|
||||||
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
|
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
|
||||||
S(34,68), S(83,166), S(0, 0), S( 0, 0)
|
S(34,68), S(83,166), S(0, 0), S( 0, 0) };
|
||||||
};
|
|
||||||
|
|
||||||
// Weakness of our pawn shelter in front of the king indexed by [rank]
|
// Weakness of our pawn shelter in front of the king indexed by [rank]
|
||||||
const Value ShelterWeakness[RANK_NB] =
|
const Value ShelterWeakness[RANK_NB] =
|
||||||
@@ -72,10 +66,10 @@ namespace {
|
|||||||
const Value StormDanger[3][RANK_NB] = {
|
const Value StormDanger[3][RANK_NB] = {
|
||||||
{ V( 0), V(64), V(128), V(51), V(26) },
|
{ V( 0), V(64), V(128), V(51), V(26) },
|
||||||
{ V(26), V(32), V( 96), V(38), V(20) },
|
{ V(26), V(32), V( 96), V(38), V(20) },
|
||||||
{ V( 0), V( 0), V( 64), V(25), V(13) }};
|
{ V( 0), V( 0), V( 64), V(25), V(13) } };
|
||||||
|
|
||||||
// Max bonus for king safety. Corresponds to start position with all the pawns
|
// Max bonus for king safety. Corresponds to start position with all the pawns
|
||||||
// in front of the king and no enemy pawn on the horizont.
|
// in front of the king and no enemy pawn on the horizon.
|
||||||
const Value MaxSafetyBonus = V(263);
|
const Value MaxSafetyBonus = V(263);
|
||||||
|
|
||||||
#undef S
|
#undef S
|
||||||
@@ -92,7 +86,6 @@ namespace {
|
|||||||
Bitboard b;
|
Bitboard b;
|
||||||
Square s;
|
Square s;
|
||||||
File f;
|
File f;
|
||||||
Rank r;
|
|
||||||
bool passed, isolated, doubled, opposed, chain, backward, candidate;
|
bool passed, isolated, doubled, opposed, chain, backward, candidate;
|
||||||
Score value = SCORE_ZERO;
|
Score value = SCORE_ZERO;
|
||||||
const Square* pl = pos.list<PAWN>(Us);
|
const Square* pl = pos.list<PAWN>(Us);
|
||||||
@@ -100,7 +93,7 @@ namespace {
|
|||||||
Bitboard ourPawns = pos.pieces(Us, PAWN);
|
Bitboard ourPawns = pos.pieces(Us, PAWN);
|
||||||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
||||||
|
|
||||||
e->passedPawns[Us] = 0;
|
e->passedPawns[Us] = e->candidatePawns[Us] = 0;
|
||||||
e->kingSquares[Us] = SQ_NONE;
|
e->kingSquares[Us] = SQ_NONE;
|
||||||
e->semiopenFiles[Us] = 0xFF;
|
e->semiopenFiles[Us] = 0xFF;
|
||||||
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
|
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
|
||||||
@@ -113,13 +106,12 @@ namespace {
|
|||||||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
||||||
|
|
||||||
f = file_of(s);
|
f = file_of(s);
|
||||||
r = rank_of(s);
|
|
||||||
|
|
||||||
// This file cannot be semi-open
|
// This file cannot be semi-open
|
||||||
e->semiopenFiles[Us] &= ~(1 << f);
|
e->semiopenFiles[Us] &= ~(1 << f);
|
||||||
|
|
||||||
// Our rank plus previous one. Used for chain detection
|
// Our rank plus previous one. Used for chain detection
|
||||||
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
|
b = rank_bb(s) | rank_bb(s - pawn_push(Us));
|
||||||
|
|
||||||
// Flag the pawn as passed, isolated, doubled or member of a pawn
|
// Flag the pawn as passed, isolated, doubled or member of a pawn
|
||||||
// chain (but not the backward one).
|
// chain (but not the backward one).
|
||||||
@@ -129,35 +121,31 @@ namespace {
|
|||||||
opposed = theirPawns & forward_bb(Us, s);
|
opposed = theirPawns & forward_bb(Us, s);
|
||||||
passed = !(theirPawns & passed_pawn_mask(Us, s));
|
passed = !(theirPawns & passed_pawn_mask(Us, s));
|
||||||
|
|
||||||
// Test for backward pawn
|
// Test for backward pawn.
|
||||||
backward = false;
|
|
||||||
|
|
||||||
// If the pawn is passed, isolated, or member of a pawn chain it cannot
|
// If the pawn is passed, isolated, or member of a pawn chain it cannot
|
||||||
// be backward. If there are friendly pawns behind on adjacent files
|
// be backward. If there are friendly pawns behind on adjacent files
|
||||||
// or if can capture an enemy pawn it cannot be backward either.
|
// or if can capture an enemy pawn it cannot be backward either.
|
||||||
if ( !(passed | isolated | chain)
|
if ( (passed | isolated | chain)
|
||||||
&& !(ourPawns & pawn_attack_span(Them, s))
|
|| (ourPawns & pawn_attack_span(Them, s))
|
||||||
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
|
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns))
|
||||||
|
backward = false;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// We now know that there are no friendly pawns beside or behind this
|
// We now know that there are no friendly pawns beside or behind this
|
||||||
// pawn on adjacent files. We now check whether the pawn is
|
// pawn on adjacent files. We now check whether the pawn is
|
||||||
// backward by looking in the forward direction on the adjacent
|
// backward by looking in the forward direction on the adjacent
|
||||||
// files, and seeing whether we meet a friendly or an enemy pawn first.
|
// files, and picking the closest pawn there.
|
||||||
b = pos.attacks_from<PAWN>(s, Us);
|
b = pawn_attack_span(Us, s) & (ourPawns | theirPawns);
|
||||||
|
b = pawn_attack_span(Us, s) & rank_bb(backmost_sq(Us, b));
|
||||||
|
|
||||||
// Note that we are sure to find something because pawn is not passed
|
// If we have an enemy pawn in the same or next rank, the pawn is
|
||||||
// nor isolated, so loop is potentially infinite, but it isn't.
|
// backward because it cannot advance without being captured.
|
||||||
while (!(b & (ourPawns | theirPawns)))
|
|
||||||
b = shift_bb<Up>(b);
|
|
||||||
|
|
||||||
// The friendly pawn needs to be at least two ranks closer than the
|
|
||||||
// enemy pawn in order to help the potentially backward pawn advance.
|
|
||||||
backward = (b | shift_bb<Up>(b)) & theirPawns;
|
backward = (b | shift_bb<Up>(b)) & theirPawns;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
|
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
|
||||||
|
|
||||||
// A not passed pawn is a candidate to become passed if it is free to
|
// A not passed pawn is a candidate to become passed, if it is free to
|
||||||
// advance and if the number of friendly pawns beside or behind this
|
// advance and if the number of friendly pawns beside or behind this
|
||||||
// pawn on adjacent files is higher or equal than the number of
|
// pawn on adjacent files is higher or equal than the number of
|
||||||
// enemy pawns in the forward direction on the adjacent files.
|
// enemy pawns in the forward direction on the adjacent files.
|
||||||
@@ -176,16 +164,21 @@ namespace {
|
|||||||
value -= Isolated[opposed][f];
|
value -= Isolated[opposed][f];
|
||||||
|
|
||||||
if (doubled)
|
if (doubled)
|
||||||
value -= Doubled[opposed][f];
|
value -= Doubled[f];
|
||||||
|
|
||||||
if (backward)
|
if (backward)
|
||||||
value -= Backward[opposed][f];
|
value -= Backward[opposed][f];
|
||||||
|
|
||||||
if (chain)
|
if (chain)
|
||||||
value += ChainMember[f];
|
value += ChainMember[f][relative_rank(Us, s)];
|
||||||
|
|
||||||
if (candidate)
|
if (candidate)
|
||||||
|
{
|
||||||
value += CandidatePassed[relative_rank(Us, s)];
|
value += CandidatePassed[relative_rank(Us, s)];
|
||||||
|
|
||||||
|
if (!doubled)
|
||||||
|
e->candidatePawns[Us] |= s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@@ -195,6 +188,22 @@ namespace {
|
|||||||
|
|
||||||
namespace Pawns {
|
namespace Pawns {
|
||||||
|
|
||||||
|
/// init() initializes some tables by formula instead of hard-code their values
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
|
||||||
|
const int chainByFile[8] = { 1, 3, 3, 4, 4, 3, 3, 1 };
|
||||||
|
int bonus;
|
||||||
|
|
||||||
|
for (Rank r = RANK_1; r < RANK_8; ++r)
|
||||||
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||||
|
{
|
||||||
|
bonus = r * (r-1) * (r-2) + chainByFile[f] * (r/2 + 1);
|
||||||
|
ChainMember[f][r] = make_score(bonus, bonus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// probe() takes a position object as input, computes a Entry object, and returns
|
/// probe() takes a position object as input, computes a Entry object, and returns
|
||||||
/// a pointer to it. The result is also stored in a hash table, so we don't have
|
/// a pointer to it. The result is also stored in a hash table, so we don't have
|
||||||
/// to recompute everything when the same pawn structure occurs again.
|
/// to recompute everything when the same pawn structure occurs again.
|
||||||
@@ -226,18 +235,16 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
|
|||||||
Bitboard ourPawns = b & pos.pieces(Us);
|
Bitboard ourPawns = b & pos.pieces(Us);
|
||||||
Bitboard theirPawns = b & pos.pieces(Them);
|
Bitboard theirPawns = b & pos.pieces(Them);
|
||||||
Rank rkUs, rkThem;
|
Rank rkUs, rkThem;
|
||||||
File kf = file_of(ksq);
|
File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
|
||||||
|
|
||||||
kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf;
|
for (File f = kf - File(1); f <= kf + File(1); ++f)
|
||||||
|
|
||||||
for (int f = kf - 1; f <= kf + 1; f++)
|
|
||||||
{
|
{
|
||||||
b = ourPawns & FileBB[f];
|
b = ourPawns & file_bb(f);
|
||||||
rkUs = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
|
rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
|
||||||
safety -= ShelterWeakness[rkUs];
|
safety -= ShelterWeakness[rkUs];
|
||||||
|
|
||||||
b = theirPawns & FileBB[f];
|
b = theirPawns & file_bb(f);
|
||||||
rkThem = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
|
rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
|
||||||
safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
|
safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +271,7 @@ Score Entry::update_safety(const Position& pos, Square ksq) {
|
|||||||
|
|
||||||
Value bonus = shelter_storm<Us>(pos, ksq);
|
Value bonus = shelter_storm<Us>(pos, ksq);
|
||||||
|
|
||||||
// If we can castle use the bonus after the castle if is bigger
|
// If we can castle use the bonus after the castle if it is bigger
|
||||||
if (pos.can_castle(make_castle_right(Us, KING_SIDE)))
|
if (pos.can_castle(make_castle_right(Us, KING_SIDE)))
|
||||||
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
|
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ struct Entry {
|
|||||||
Score pawns_value() const { return value; }
|
Score pawns_value() const { return value; }
|
||||||
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
|
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
|
||||||
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
|
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
|
||||||
|
Bitboard candidate_pawns(Color c) const { return candidatePawns[c]; }
|
||||||
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; }
|
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; }
|
||||||
int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); }
|
int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); }
|
||||||
int semiopen_on_side(Color c, File f, bool left) const {
|
int semiopen_on_side(Color c, File f, bool left) const {
|
||||||
@@ -59,6 +60,7 @@ struct Entry {
|
|||||||
|
|
||||||
Key key;
|
Key key;
|
||||||
Bitboard passedPawns[COLOR_NB];
|
Bitboard passedPawns[COLOR_NB];
|
||||||
|
Bitboard candidatePawns[COLOR_NB];
|
||||||
Bitboard pawnAttacks[COLOR_NB];
|
Bitboard pawnAttacks[COLOR_NB];
|
||||||
Square kingSquares[COLOR_NB];
|
Square kingSquares[COLOR_NB];
|
||||||
int minKPdistance[COLOR_NB];
|
int minKPdistance[COLOR_NB];
|
||||||
@@ -71,6 +73,7 @@ struct Entry {
|
|||||||
|
|
||||||
typedef HashTable<Entry, 16384> Table;
|
typedef HashTable<Entry, 16384> Table;
|
||||||
|
|
||||||
|
void init();
|
||||||
Entry* probe(const Position& pos, Table& entries);
|
Entry* probe(const Position& pos, Table& entries);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ CheckInfo::CheckInfo(const Position& pos) {
|
|||||||
Color them = ~pos.side_to_move();
|
Color them = ~pos.side_to_move();
|
||||||
ksq = pos.king_square(them);
|
ksq = pos.king_square(them);
|
||||||
|
|
||||||
pinned = pos.pinned_pieces();
|
pinned = pos.pinned_pieces(pos.side_to_move());
|
||||||
dcCandidates = pos.discovered_check_candidates();
|
dcCandidates = pos.discovered_check_candidates();
|
||||||
|
|
||||||
checkSq[PAWN] = pos.attacks_from<PAWN>(ksq, them);
|
checkSq[PAWN] = pos.attacks_from<PAWN>(ksq, them);
|
||||||
@@ -120,15 +120,15 @@ void Position::init() {
|
|||||||
|
|
||||||
RKISS rk;
|
RKISS rk;
|
||||||
|
|
||||||
for (Color c = WHITE; c <= BLACK; c++)
|
for (Color c = WHITE; c <= BLACK; ++c)
|
||||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
Zobrist::psq[c][pt][s] = rk.rand<Key>();
|
Zobrist::psq[c][pt][s] = rk.rand<Key>();
|
||||||
|
|
||||||
for (File f = FILE_A; f <= FILE_H; f++)
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||||
Zobrist::enpassant[f] = rk.rand<Key>();
|
Zobrist::enpassant[f] = rk.rand<Key>();
|
||||||
|
|
||||||
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
|
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; ++cr)
|
||||||
{
|
{
|
||||||
Bitboard b = cr;
|
Bitboard b = cr;
|
||||||
while (b)
|
while (b)
|
||||||
@@ -141,14 +141,14 @@ void Position::init() {
|
|||||||
Zobrist::side = rk.rand<Key>();
|
Zobrist::side = rk.rand<Key>();
|
||||||
Zobrist::exclusion = rk.rand<Key>();
|
Zobrist::exclusion = rk.rand<Key>();
|
||||||
|
|
||||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||||
{
|
{
|
||||||
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
|
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
|
||||||
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
|
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
|
||||||
|
|
||||||
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
|
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
{
|
{
|
||||||
psq[WHITE][pt][ s] = (v + PSQT[pt][s]);
|
psq[WHITE][pt][ s] = (v + PSQT[pt][s]);
|
||||||
psq[BLACK][pt][~s] = -(v + PSQT[pt][s]);
|
psq[BLACK][pt][~s] = -(v + PSQT[pt][s]);
|
||||||
@@ -233,7 +233,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
|
|||||||
else if ((p = PieceToChar.find(token)) != string::npos)
|
else if ((p = PieceToChar.find(token)) != string::npos)
|
||||||
{
|
{
|
||||||
put_piece(sq, color_of(Piece(p)), type_of(Piece(p)));
|
put_piece(sq, color_of(Piece(p)), type_of(Piece(p)));
|
||||||
sq++;
|
++sq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,10 +255,10 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
|
|||||||
token = char(toupper(token));
|
token = char(toupper(token));
|
||||||
|
|
||||||
if (token == 'K')
|
if (token == 'K')
|
||||||
for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; rsq--) {}
|
for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; --rsq) {}
|
||||||
|
|
||||||
else if (token == 'Q')
|
else if (token == 'Q')
|
||||||
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 = File(token - 'A') | relative_rank(c, RANK_1);
|
rsq = File(token - 'A') | relative_rank(c, RANK_1);
|
||||||
@@ -317,11 +317,11 @@ void Position::set_castle_right(Color c, Square rfrom) {
|
|||||||
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
|
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
|
||||||
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
|
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
|
||||||
|
|
||||||
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); s++)
|
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s)
|
||||||
if (s != kfrom && s != rfrom)
|
if (s != kfrom && s != rfrom)
|
||||||
castlePath[c][cs] |= s;
|
castlePath[c][cs] |= s;
|
||||||
|
|
||||||
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++)
|
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s)
|
||||||
if (s != kfrom && s != rfrom)
|
if (s != kfrom && s != rfrom)
|
||||||
castlePath[c][cs] |= s;
|
castlePath[c][cs] |= s;
|
||||||
}
|
}
|
||||||
@@ -334,18 +334,18 @@ const string Position::fen() const {
|
|||||||
|
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
|
|
||||||
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
|
for (Rank rank = RANK_8; rank >= RANK_1; --rank)
|
||||||
{
|
{
|
||||||
for (File file = FILE_A; file <= FILE_H; file++)
|
for (File file = FILE_A; file <= FILE_H; ++file)
|
||||||
{
|
{
|
||||||
Square sq = file | rank;
|
Square sq = file | rank;
|
||||||
|
|
||||||
if (is_empty(sq))
|
if (empty(sq))
|
||||||
{
|
{
|
||||||
int emptyCnt = 1;
|
int emptyCnt = 1;
|
||||||
|
|
||||||
for ( ; file < FILE_H && is_empty(sq++); file++)
|
for ( ; file < FILE_H && empty(++sq); ++file)
|
||||||
emptyCnt++;
|
++emptyCnt;
|
||||||
|
|
||||||
ss << emptyCnt;
|
ss << emptyCnt;
|
||||||
}
|
}
|
||||||
@@ -422,7 +422,7 @@ const string Position::pretty(Move move) const {
|
|||||||
/// pieces, according to the call parameters. Pinned pieces protect our king,
|
/// pieces, according to the call parameters. Pinned pieces protect our king,
|
||||||
/// discovery check pieces attack the enemy king.
|
/// discovery check pieces attack the enemy king.
|
||||||
|
|
||||||
Bitboard Position::hidden_checkers(Square ksq, Color c) const {
|
Bitboard Position::hidden_checkers(Square ksq, Color c, Color toMove) const {
|
||||||
|
|
||||||
Bitboard b, pinners, result = 0;
|
Bitboard b, pinners, result = 0;
|
||||||
|
|
||||||
@@ -435,7 +435,7 @@ Bitboard Position::hidden_checkers(Square ksq, Color c) const {
|
|||||||
b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
|
b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
|
||||||
|
|
||||||
if (!more_than_one(b))
|
if (!more_than_one(b))
|
||||||
result |= b & pieces(sideToMove);
|
result |= b & pieces(toMove);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -472,17 +472,17 @@ Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal
|
/// Position::legal() tests whether a pseudo-legal move is legal
|
||||||
|
|
||||||
bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
bool Position::legal(Move m, Bitboard pinned) const {
|
||||||
|
|
||||||
assert(is_ok(m));
|
assert(is_ok(m));
|
||||||
assert(pinned == pinned_pieces());
|
assert(pinned == pinned_pieces(sideToMove));
|
||||||
|
|
||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Square from = from_sq(m);
|
Square from = from_sq(m);
|
||||||
|
|
||||||
assert(color_of(piece_moved(m)) == us);
|
assert(color_of(moved_piece(m)) == us);
|
||||||
assert(piece_on(king_square(us)) == make_piece(us, KING));
|
assert(piece_on(king_square(us)) == make_piece(us, KING));
|
||||||
|
|
||||||
// En passant captures are a tricky special case. Because they are rather
|
// En passant captures are a tricky special case. Because they are rather
|
||||||
@@ -497,7 +497,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
|||||||
Bitboard b = (pieces() ^ from ^ capsq) | to;
|
Bitboard b = (pieces() ^ from ^ capsq) | to;
|
||||||
|
|
||||||
assert(to == ep_square());
|
assert(to == ep_square());
|
||||||
assert(piece_moved(m) == make_piece(us, PAWN));
|
assert(moved_piece(m) == make_piece(us, PAWN));
|
||||||
assert(piece_on(capsq) == make_piece(them, PAWN));
|
assert(piece_on(capsq) == make_piece(them, PAWN));
|
||||||
assert(piece_on(to) == NO_PIECE);
|
assert(piece_on(to) == NO_PIECE);
|
||||||
|
|
||||||
@@ -515,20 +515,20 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
|||||||
// is moving along the ray towards or away from the king.
|
// is moving along the ray towards or away from the king.
|
||||||
return !pinned
|
return !pinned
|
||||||
|| !(pinned & from)
|
|| !(pinned & from)
|
||||||
|| squares_aligned(from, to_sq(m), king_square(us));
|
|| aligned(from, to_sq(m), king_square(us));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Position::is_pseudo_legal() takes a random move and tests whether the move
|
/// Position::pseudo_legal() takes a random move and tests whether the move is
|
||||||
/// is pseudo legal. It is used to validate moves from TT that can be corrupted
|
/// pseudo legal. It is used to validate moves from TT that can be corrupted
|
||||||
/// due to SMP concurrent access or hash position key aliasing.
|
/// due to SMP concurrent access or hash position key aliasing.
|
||||||
|
|
||||||
bool Position::is_pseudo_legal(const Move m) const {
|
bool Position::pseudo_legal(const Move m) const {
|
||||||
|
|
||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Square from = from_sq(m);
|
Square from = from_sq(m);
|
||||||
Square to = to_sq(m);
|
Square to = to_sq(m);
|
||||||
Piece pc = piece_moved(m);
|
Piece pc = moved_piece(m);
|
||||||
|
|
||||||
// Use a slower but simpler function for uncommon cases
|
// Use a slower but simpler function for uncommon cases
|
||||||
if (type_of(m) != NORMAL)
|
if (type_of(m) != NORMAL)
|
||||||
@@ -581,7 +581,7 @@ bool Position::is_pseudo_legal(const Move m) const {
|
|||||||
case DELTA_N:
|
case DELTA_N:
|
||||||
case DELTA_S:
|
case DELTA_S:
|
||||||
// Pawn push. The destination square must be empty.
|
// Pawn push. The destination square must be empty.
|
||||||
if (!is_empty(to))
|
if (!empty(to))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -590,8 +590,8 @@ bool Position::is_pseudo_legal(const Move m) const {
|
|||||||
// rank, and both the destination square and the square between the
|
// rank, and both the destination square and the square between the
|
||||||
// source and destination squares must be empty.
|
// source and destination squares must be empty.
|
||||||
if ( rank_of(to) != RANK_4
|
if ( rank_of(to) != RANK_4
|
||||||
|| !is_empty(to)
|
|| !empty(to)
|
||||||
|| !is_empty(from + DELTA_N))
|
|| !empty(from + DELTA_N))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -600,8 +600,8 @@ bool Position::is_pseudo_legal(const Move m) const {
|
|||||||
// rank, and both the destination square and the square between the
|
// rank, and both the destination square and the square between the
|
||||||
// source and destination squares must be empty.
|
// source and destination squares must be empty.
|
||||||
if ( rank_of(to) != RANK_5
|
if ( rank_of(to) != RANK_5
|
||||||
|| !is_empty(to)
|
|| !empty(to)
|
||||||
|| !is_empty(from + DELTA_S))
|
|| !empty(from + DELTA_S))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -639,11 +639,11 @@ bool Position::is_pseudo_legal(const Move m) const {
|
|||||||
|
|
||||||
/// Position::move_gives_check() tests whether a pseudo-legal move gives a check
|
/// Position::move_gives_check() tests whether a pseudo-legal move gives a check
|
||||||
|
|
||||||
bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
|
bool Position::gives_check(Move m, const CheckInfo& ci) const {
|
||||||
|
|
||||||
assert(is_ok(m));
|
assert(is_ok(m));
|
||||||
assert(ci.dcCandidates == discovered_check_candidates());
|
assert(ci.dcCandidates == discovered_check_candidates());
|
||||||
assert(color_of(piece_moved(m)) == sideToMove);
|
assert(color_of(moved_piece(m)) == sideToMove);
|
||||||
|
|
||||||
Square from = from_sq(m);
|
Square from = from_sq(m);
|
||||||
Square to = to_sq(m);
|
Square to = to_sq(m);
|
||||||
@@ -658,7 +658,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
|
|||||||
{
|
{
|
||||||
// For pawn and king moves we need to verify also direction
|
// For pawn and king moves we need to verify also direction
|
||||||
if ( (pt != PAWN && pt != KING)
|
if ( (pt != PAWN && pt != KING)
|
||||||
|| !squares_aligned(from, to, king_square(~sideToMove)))
|
|| !aligned(from, to, king_square(~sideToMove)))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,7 +710,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
|
|||||||
void Position::do_move(Move m, StateInfo& newSt) {
|
void Position::do_move(Move m, StateInfo& newSt) {
|
||||||
|
|
||||||
CheckInfo ci(*this);
|
CheckInfo ci(*this);
|
||||||
do_move(m, newSt, ci, move_gives_check(m, ci));
|
do_move(m, newSt, ci, gives_check(m, ci));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) {
|
void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) {
|
||||||
@@ -718,7 +718,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
assert(is_ok(m));
|
assert(is_ok(m));
|
||||||
assert(&newSt != st);
|
assert(&newSt != st);
|
||||||
|
|
||||||
nodes++;
|
++nodes;
|
||||||
Key k = st->key;
|
Key k = st->key;
|
||||||
|
|
||||||
// Copy some fields of old state to our new StateInfo object except the ones
|
// Copy some fields of old state to our new StateInfo object except the ones
|
||||||
@@ -734,9 +734,9 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
|
|
||||||
// Increment ply counters.In particular rule50 will be later reset it to zero
|
// Increment ply counters.In particular rule50 will be later reset it to zero
|
||||||
// in case of a capture or a pawn move.
|
// in case of a capture or a pawn move.
|
||||||
gamePly++;
|
++gamePly;
|
||||||
st->rule50++;
|
++st->rule50;
|
||||||
st->pliesFromNull++;
|
++st->pliesFromNull;
|
||||||
|
|
||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Color them = ~us;
|
Color them = ~us;
|
||||||
@@ -744,11 +744,11 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
Square to = to_sq(m);
|
Square to = to_sq(m);
|
||||||
Piece pc = piece_on(from);
|
Piece pc = piece_on(from);
|
||||||
PieceType pt = type_of(pc);
|
PieceType pt = type_of(pc);
|
||||||
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
|
PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
|
||||||
|
|
||||||
assert(color_of(pc) == us);
|
assert(color_of(pc) == us);
|
||||||
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
|
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
|
||||||
assert(capture != KING);
|
assert(captured != KING);
|
||||||
|
|
||||||
if (type_of(m) == CASTLE)
|
if (type_of(m) == CASTLE)
|
||||||
{
|
{
|
||||||
@@ -758,7 +758,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
|
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
|
||||||
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
||||||
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
||||||
capture = NO_PIECE_TYPE;
|
captured = NO_PIECE_TYPE;
|
||||||
|
|
||||||
do_castle(from, to, rfrom, rto);
|
do_castle(from, to, rfrom, rto);
|
||||||
|
|
||||||
@@ -766,13 +766,13 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
|
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (capture)
|
if (captured)
|
||||||
{
|
{
|
||||||
Square capsq = to;
|
Square capsq = to;
|
||||||
|
|
||||||
// If the captured piece is a pawn, update pawn hash key, otherwise
|
// If the captured piece is a pawn, update pawn hash key, otherwise
|
||||||
// update non-pawn material.
|
// update non-pawn material.
|
||||||
if (capture == PAWN)
|
if (captured == PAWN)
|
||||||
{
|
{
|
||||||
if (type_of(m) == ENPASSANT)
|
if (type_of(m) == ENPASSANT)
|
||||||
{
|
{
|
||||||
@@ -790,18 +790,18 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
|
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
st->npMaterial[them] -= PieceValue[MG][capture];
|
st->npMaterial[them] -= PieceValue[MG][captured];
|
||||||
|
|
||||||
// Update board and piece lists
|
// Update board and piece lists
|
||||||
remove_piece(capsq, them, capture);
|
remove_piece(capsq, them, captured);
|
||||||
|
|
||||||
// Update material hash key and prefetch access to materialTable
|
// Update material hash key and prefetch access to materialTable
|
||||||
k ^= Zobrist::psq[them][capture][capsq];
|
k ^= Zobrist::psq[them][captured][capsq];
|
||||||
st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]];
|
st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]];
|
||||||
prefetch((char*)thisThread->materialTable[st->materialKey]);
|
prefetch((char*)thisThread->materialTable[st->materialKey]);
|
||||||
|
|
||||||
// Update incremental scores
|
// Update incremental scores
|
||||||
st->psq -= psq[them][capture][capsq];
|
st->psq -= psq[them][captured][capsq];
|
||||||
|
|
||||||
// Reset rule 50 counter
|
// Reset rule 50 counter
|
||||||
st->rule50 = 0;
|
st->rule50 = 0;
|
||||||
@@ -878,7 +878,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||||||
st->psq += psq[us][pt][to] - psq[us][pt][from];
|
st->psq += psq[us][pt][to] - psq[us][pt][from];
|
||||||
|
|
||||||
// Set capture piece
|
// Set capture piece
|
||||||
st->capturedType = capture;
|
st->capturedType = captured;
|
||||||
|
|
||||||
// Update the key with the final value
|
// Update the key with the final value
|
||||||
st->key = k;
|
st->key = k;
|
||||||
@@ -928,10 +928,10 @@ void Position::undo_move(Move m) {
|
|||||||
Square from = from_sq(m);
|
Square from = from_sq(m);
|
||||||
Square to = to_sq(m);
|
Square to = to_sq(m);
|
||||||
PieceType pt = type_of(piece_on(to));
|
PieceType pt = type_of(piece_on(to));
|
||||||
PieceType capture = st->capturedType;
|
PieceType captured = st->capturedType;
|
||||||
|
|
||||||
assert(is_empty(from) || type_of(m) == CASTLE);
|
assert(empty(from) || type_of(m) == CASTLE);
|
||||||
assert(capture != KING);
|
assert(captured != KING);
|
||||||
|
|
||||||
if (type_of(m) == PROMOTION)
|
if (type_of(m) == PROMOTION)
|
||||||
{
|
{
|
||||||
@@ -952,14 +952,14 @@ void Position::undo_move(Move m) {
|
|||||||
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
|
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
|
||||||
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
||||||
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
||||||
capture = NO_PIECE_TYPE;
|
captured = NO_PIECE_TYPE;
|
||||||
pt = KING;
|
pt = KING;
|
||||||
do_castle(to, from, rto, rfrom);
|
do_castle(to, from, rto, rfrom);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
move_piece(to, from, us, pt); // Put the piece back at the source square
|
move_piece(to, from, us, pt); // Put the piece back at the source square
|
||||||
|
|
||||||
if (capture)
|
if (captured)
|
||||||
{
|
{
|
||||||
Square capsq = to;
|
Square capsq = to;
|
||||||
|
|
||||||
@@ -973,12 +973,12 @@ void Position::undo_move(Move m) {
|
|||||||
assert(piece_on(capsq) == NO_PIECE);
|
assert(piece_on(capsq) == NO_PIECE);
|
||||||
}
|
}
|
||||||
|
|
||||||
put_piece(capsq, them, capture); // Restore the captured piece
|
put_piece(capsq, them, captured); // Restore the captured piece
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally point our state pointer back to the previous state
|
// Finally point our state pointer back to the previous state
|
||||||
st = st->previous;
|
st = st->previous;
|
||||||
gamePly--;
|
--gamePly;
|
||||||
|
|
||||||
assert(pos_is_ok());
|
assert(pos_is_ok());
|
||||||
}
|
}
|
||||||
@@ -1019,7 +1019,7 @@ void Position::do_null_move(StateInfo& newSt) {
|
|||||||
st->key ^= Zobrist::side;
|
st->key ^= Zobrist::side;
|
||||||
prefetch((char*)TT.first_entry(st->key));
|
prefetch((char*)TT.first_entry(st->key));
|
||||||
|
|
||||||
st->rule50++;
|
++st->rule50;
|
||||||
st->pliesFromNull = 0;
|
st->pliesFromNull = 0;
|
||||||
|
|
||||||
sideToMove = ~sideToMove;
|
sideToMove = ~sideToMove;
|
||||||
@@ -1049,7 +1049,7 @@ int Position::see_sign(Move m) const {
|
|||||||
// Early return if SEE cannot be negative because captured piece value
|
// Early return if SEE cannot be negative because captured piece value
|
||||||
// is not less then capturing one. Note that king moves always return
|
// is not less then capturing one. Note that king moves always return
|
||||||
// here because king midgame value is set to 0.
|
// here because king midgame value is set to 0.
|
||||||
if (PieceValue[MG][piece_moved(m)] <= PieceValue[MG][piece_on(to_sq(m))])
|
if (PieceValue[MG][moved_piece(m)] <= PieceValue[MG][piece_on(to_sq(m))])
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return see(m);
|
return see(m);
|
||||||
@@ -1067,7 +1067,7 @@ int Position::see(Move m, int asymmThreshold) const {
|
|||||||
|
|
||||||
from = from_sq(m);
|
from = from_sq(m);
|
||||||
to = to_sq(m);
|
to = to_sq(m);
|
||||||
swapList[0] = PieceValue[MG][type_of(piece_on(to))];
|
swapList[0] = PieceValue[MG][piece_on(to)];
|
||||||
stm = color_of(piece_on(from));
|
stm = color_of(piece_on(from));
|
||||||
occupied = pieces() ^ from;
|
occupied = pieces() ^ from;
|
||||||
|
|
||||||
@@ -1106,7 +1106,7 @@ int Position::see(Move m, int asymmThreshold) const {
|
|||||||
|
|
||||||
// Add the new entry to the swap list
|
// Add the new entry to the swap list
|
||||||
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
|
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
|
||||||
slIndex++;
|
++slIndex;
|
||||||
|
|
||||||
// Locate and remove the next least valuable attacker
|
// Locate and remove the next least valuable attacker
|
||||||
captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
|
captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
|
||||||
@@ -1134,7 +1134,7 @@ int Position::see(Move m, int asymmThreshold) const {
|
|||||||
// 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
|
||||||
// achievable score from the point of view of the side to move.
|
// achievable score from the point of view of the side to move.
|
||||||
while (--slIndex)
|
while (--slIndex)
|
||||||
swapList[slIndex-1] = std::min(-swapList[slIndex], swapList[slIndex-1]);
|
swapList[slIndex - 1] = std::min(-swapList[slIndex], swapList[slIndex - 1]);
|
||||||
|
|
||||||
return swapList[0];
|
return swapList[0];
|
||||||
}
|
}
|
||||||
@@ -1149,9 +1149,9 @@ void Position::clear() {
|
|||||||
startState.epSquare = SQ_NONE;
|
startState.epSquare = SQ_NONE;
|
||||||
st = &startState;
|
st = &startState;
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < PIECE_TYPE_NB; ++i)
|
||||||
for (int j = 0; j < 16; j++)
|
for (int j = 0; j < 16; ++j)
|
||||||
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
|
pieceList[WHITE][i][j] = pieceList[BLACK][i][j] = SQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1210,9 +1210,9 @@ Key Position::compute_material_key() const {
|
|||||||
|
|
||||||
Key k = 0;
|
Key k = 0;
|
||||||
|
|
||||||
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 < pieceCount[c][pt]; cnt++)
|
for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt)
|
||||||
k ^= Zobrist::psq[c][pt][cnt];
|
k ^= Zobrist::psq[c][pt][cnt];
|
||||||
|
|
||||||
return k;
|
return k;
|
||||||
@@ -1223,6 +1223,7 @@ Key Position::compute_material_key() const {
|
|||||||
/// game and the endgame. These functions are used to initialize the incremental
|
/// game and the endgame. These functions are used to initialize the incremental
|
||||||
/// scores when a new position is set up, and to verify that the scores are correctly
|
/// scores when a new position is set up, and to verify that the scores are correctly
|
||||||
/// updated by do_move and undo_move when the program is running in debug mode.
|
/// updated by do_move and undo_move when the program is running in debug mode.
|
||||||
|
|
||||||
Score Position::compute_psq_score() const {
|
Score Position::compute_psq_score() const {
|
||||||
|
|
||||||
Score score = SCORE_ZERO;
|
Score score = SCORE_ZERO;
|
||||||
@@ -1247,7 +1248,7 @@ Value Position::compute_non_pawn_material(Color c) const {
|
|||||||
|
|
||||||
Value value = VALUE_ZERO;
|
Value value = VALUE_ZERO;
|
||||||
|
|
||||||
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
|
for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt)
|
||||||
value += pieceCount[c][pt] * PieceValue[MG][pt];
|
value += pieceCount[c][pt] * PieceValue[MG][pt];
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@@ -1268,7 +1269,6 @@ bool Position::is_draw() const {
|
|||||||
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
|
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Draw by repetition?
|
|
||||||
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
|
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
|
||||||
|
|
||||||
if (i <= e)
|
if (i <= e)
|
||||||
@@ -1279,7 +1279,7 @@ bool Position::is_draw() const {
|
|||||||
stp = stp->previous->previous;
|
stp = stp->previous->previous;
|
||||||
|
|
||||||
if (stp->key == st->key)
|
if (stp->key == st->key)
|
||||||
return true;
|
return true; // Draw after first repetition
|
||||||
|
|
||||||
i += 2;
|
i += 2;
|
||||||
|
|
||||||
@@ -1302,7 +1302,7 @@ void Position::flip() {
|
|||||||
string f, token;
|
string f, token;
|
||||||
std::stringstream ss(fen());
|
std::stringstream ss(fen());
|
||||||
|
|
||||||
for (Rank rank = RANK_8; rank >= RANK_1; rank--) // Piece placement
|
for (Rank rank = RANK_8; rank >= RANK_1; --rank) // Piece placement
|
||||||
{
|
{
|
||||||
std::getline(ss, token, rank > RANK_1 ? '/' : ' ');
|
std::getline(ss, token, rank > RANK_1 ? '/' : ' ');
|
||||||
f.insert(0, token + (f.empty() ? " " : "/"));
|
f.insert(0, token + (f.empty() ? " " : "/"));
|
||||||
@@ -1366,9 +1366,9 @@ bool Position::pos_is_ok(int* failedStep) const {
|
|||||||
{
|
{
|
||||||
int kingCount[COLOR_NB] = {};
|
int kingCount[COLOR_NB] = {};
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
if (type_of(piece_on(s)) == KING)
|
if (type_of(piece_on(s)) == KING)
|
||||||
kingCount[color_of(piece_on(s))]++;
|
++kingCount[color_of(piece_on(s))];
|
||||||
|
|
||||||
if (kingCount[0] != 1 || kingCount[1] != 1)
|
if (kingCount[0] != 1 || kingCount[1] != 1)
|
||||||
return false;
|
return false;
|
||||||
@@ -1393,8 +1393,8 @@ bool Position::pos_is_ok(int* failedStep) const {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Separate piece type bitboards must have empty intersections
|
// Separate piece type bitboards must have empty intersections
|
||||||
for (PieceType p1 = PAWN; p1 <= KING; p1++)
|
for (PieceType p1 = PAWN; p1 <= KING; ++p1)
|
||||||
for (PieceType p2 = PAWN; p2 <= KING; p2++)
|
for (PieceType p2 = PAWN; p2 <= KING; ++p2)
|
||||||
if (p1 != p2 && (pieces(p1) & pieces(p2)))
|
if (p1 != p2 && (pieces(p1) & pieces(p2)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1420,21 +1420,21 @@ bool Position::pos_is_ok(int* failedStep) const {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((*step)++, debugPieceCounts)
|
if ((*step)++, debugPieceCounts)
|
||||||
for (Color c = WHITE; c <= BLACK; c++)
|
for (Color c = WHITE; c <= BLACK; ++c)
|
||||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||||
if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt)))
|
if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((*step)++, debugPieceList)
|
if ((*step)++, debugPieceList)
|
||||||
for (Color c = WHITE; c <= BLACK; c++)
|
for (Color c = WHITE; c <= BLACK; ++c)
|
||||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||||
for (int i = 0; i < pieceCount[c][pt]; i++)
|
for (int i = 0; i < pieceCount[c][pt]; ++i)
|
||||||
if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
|
if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
|
||||||
|| index[pieceList[c][pt][i]] != i)
|
|| index[pieceList[c][pt][i]] != i)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((*step)++, debugCastleSquares)
|
if ((*step)++, debugCastleSquares)
|
||||||
for (Color c = WHITE; c <= BLACK; c++)
|
for (Color c = WHITE; c <= BLACK; ++c)
|
||||||
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
|
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
|
||||||
{
|
{
|
||||||
CastleRight cr = make_castle_right(c, s);
|
CastleRight cr = make_castle_right(c, s);
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ struct CheckInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// The StateInfo struct stores information we need to restore a Position
|
/// The StateInfo struct stores information needed to restore a Position
|
||||||
/// object to its previous state when we retract a move. Whenever a move
|
/// object to its previous state when we retract a move. Whenever a move
|
||||||
/// is made on the board (by calling Position::do_move), a StateInfo object
|
/// is made on the board (by calling Position::do_move), a StateInfo
|
||||||
/// must be passed as a parameter.
|
/// object must be passed as a parameter.
|
||||||
|
|
||||||
struct StateInfo {
|
struct StateInfo {
|
||||||
Key pawnKey, materialKey;
|
Key pawnKey, materialKey;
|
||||||
@@ -67,27 +67,10 @@ struct StateInfo {
|
|||||||
const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
|
const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
|
||||||
|
|
||||||
|
|
||||||
/// The position data structure. A position consists of the following data:
|
/// The Position class stores the information regarding the board representation
|
||||||
///
|
/// like pieces, side to move, hash keys, castling info, etc. The most important
|
||||||
/// * For each piece type, a bitboard representing the squares occupied
|
/// methods are do_move() and undo_move(), used by the search to update node info
|
||||||
/// by pieces of that type.
|
/// when traversing the search tree.
|
||||||
/// * For each color, a bitboard representing the squares occupied by
|
|
||||||
/// pieces of that color.
|
|
||||||
/// * A bitboard of all occupied squares.
|
|
||||||
/// * A bitboard of all checking pieces.
|
|
||||||
/// * A 64-entry array of pieces, indexed by the squares of the board.
|
|
||||||
/// * The current side to move.
|
|
||||||
/// * Information about the castling rights for both sides.
|
|
||||||
/// * The initial files of the kings and both pairs of rooks. This is
|
|
||||||
/// used to implement the Chess960 castling rules.
|
|
||||||
/// * The en passant square (which is SQ_NONE if no en passant capture is
|
|
||||||
/// possible).
|
|
||||||
/// * The squares of the kings for both sides.
|
|
||||||
/// * Hash keys for the position itself, the current pawn structure, and
|
|
||||||
/// the current material situation.
|
|
||||||
/// * Hash keys for all previous positions in the game for detecting
|
|
||||||
/// repetition draws.
|
|
||||||
/// * A counter for detecting 50 move rule draws.
|
|
||||||
|
|
||||||
class Position {
|
class Position {
|
||||||
public:
|
public:
|
||||||
@@ -112,7 +95,7 @@ public:
|
|||||||
Piece piece_on(Square s) const;
|
Piece piece_on(Square s) const;
|
||||||
Square king_square(Color c) const;
|
Square king_square(Color c) const;
|
||||||
Square ep_square() const;
|
Square ep_square() const;
|
||||||
bool is_empty(Square s) const;
|
bool empty(Square s) const;
|
||||||
template<PieceType Pt> int count(Color c) const;
|
template<PieceType Pt> int count(Color c) const;
|
||||||
template<PieceType Pt> const Square* list(Color c) const;
|
template<PieceType Pt> const Square* list(Color c) const;
|
||||||
|
|
||||||
@@ -125,7 +108,7 @@ public:
|
|||||||
// Checking
|
// Checking
|
||||||
Bitboard checkers() const;
|
Bitboard checkers() const;
|
||||||
Bitboard discovered_check_candidates() const;
|
Bitboard discovered_check_candidates() const;
|
||||||
Bitboard pinned_pieces() const;
|
Bitboard pinned_pieces(Color toMove) const;
|
||||||
|
|
||||||
// Attacks to/from a given square
|
// Attacks to/from a given square
|
||||||
Bitboard attackers_to(Square s) const;
|
Bitboard attackers_to(Square s) const;
|
||||||
@@ -136,20 +119,20 @@ public:
|
|||||||
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
|
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
|
||||||
|
|
||||||
// Properties of moves
|
// Properties of moves
|
||||||
bool move_gives_check(Move m, const CheckInfo& ci) const;
|
bool legal(Move m, Bitboard pinned) const;
|
||||||
bool pl_move_is_legal(Move m, Bitboard pinned) const;
|
bool pseudo_legal(const Move m) const;
|
||||||
bool is_pseudo_legal(const Move m) const;
|
bool capture(Move m) const;
|
||||||
bool is_capture(Move m) const;
|
bool capture_or_promotion(Move m) const;
|
||||||
bool is_capture_or_promotion(Move m) const;
|
bool gives_check(Move m, const CheckInfo& ci) const;
|
||||||
bool is_passed_pawn_push(Move m) const;
|
bool passed_pawn_push(Move m) const;
|
||||||
Piece piece_moved(Move m) const;
|
Piece moved_piece(Move m) const;
|
||||||
PieceType captured_piece_type() const;
|
PieceType captured_piece_type() const;
|
||||||
|
|
||||||
// Piece specific
|
// Piece specific
|
||||||
bool pawn_is_passed(Color c, Square s) const;
|
bool pawn_passed(Color c, Square s) const;
|
||||||
bool pawn_on_7th(Color c) const;
|
bool pawn_on_7th(Color c) const;
|
||||||
bool opposite_bishops() const;
|
|
||||||
bool bishop_pair(Color c) const;
|
bool bishop_pair(Color c) const;
|
||||||
|
bool opposite_bishops() const;
|
||||||
|
|
||||||
// Doing and undoing moves
|
// Doing and undoing moves
|
||||||
void do_move(Move m, StateInfo& st);
|
void do_move(Move m, StateInfo& st);
|
||||||
@@ -192,7 +175,7 @@ private:
|
|||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
void do_castle(Square kfrom, Square kto, Square rfrom, Square rto);
|
void do_castle(Square kfrom, Square kto, Square rfrom, Square rto);
|
||||||
Bitboard hidden_checkers(Square ksq, Color c) const;
|
Bitboard hidden_checkers(Square ksq, Color c, Color toMove) const;
|
||||||
void put_piece(Square s, Color c, PieceType pt);
|
void put_piece(Square s, Color c, PieceType pt);
|
||||||
void remove_piece(Square s, Color c, PieceType pt);
|
void remove_piece(Square s, Color c, PieceType pt);
|
||||||
void move_piece(Square from, Square to, Color c, PieceType pt);
|
void move_piece(Square from, Square to, Color c, PieceType pt);
|
||||||
@@ -239,11 +222,11 @@ inline Piece Position::piece_on(Square s) const {
|
|||||||
return board[s];
|
return board[s];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Piece Position::piece_moved(Move m) const {
|
inline Piece Position::moved_piece(Move m) const {
|
||||||
return board[from_sq(m)];
|
return board[from_sq(m)];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Position::is_empty(Square s) const {
|
inline bool Position::empty(Square s) const {
|
||||||
return board[s] == NO_PIECE;
|
return board[s] == NO_PIECE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,17 +316,23 @@ inline Bitboard Position::checkers() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Bitboard Position::discovered_check_candidates() const {
|
inline Bitboard Position::discovered_check_candidates() const {
|
||||||
return hidden_checkers(king_square(~sideToMove), sideToMove);
|
return hidden_checkers(king_square(~sideToMove), sideToMove, sideToMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Bitboard Position::pinned_pieces() const {
|
inline Bitboard Position::pinned_pieces(Color toMove) const {
|
||||||
return hidden_checkers(king_square(sideToMove), ~sideToMove);
|
return hidden_checkers(king_square(toMove), ~toMove, toMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Position::pawn_is_passed(Color c, Square s) const {
|
inline bool Position::pawn_passed(Color c, Square s) const {
|
||||||
return !(pieces(~c, PAWN) & passed_pawn_mask(c, s));
|
return !(pieces(~c, PAWN) & passed_pawn_mask(c, s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool Position::passed_pawn_push(Move m) const {
|
||||||
|
|
||||||
|
return type_of(moved_piece(m)) == PAWN
|
||||||
|
&& pawn_passed(sideToMove, to_sq(m));
|
||||||
|
}
|
||||||
|
|
||||||
inline Key Position::key() const {
|
inline Key Position::key() const {
|
||||||
return st->key;
|
return st->key;
|
||||||
}
|
}
|
||||||
@@ -364,12 +353,6 @@ inline Value Position::non_pawn_material(Color c) const {
|
|||||||
return st->npMaterial[c];
|
return st->npMaterial[c];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Position::is_passed_pawn_push(Move m) const {
|
|
||||||
|
|
||||||
return type_of(piece_moved(m)) == PAWN
|
|
||||||
&& pawn_is_passed(sideToMove, to_sq(m));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Position::game_ply() const {
|
inline int Position::game_ply() const {
|
||||||
return gamePly;
|
return gamePly;
|
||||||
}
|
}
|
||||||
@@ -395,17 +378,17 @@ inline bool Position::is_chess960() const {
|
|||||||
return chess960;
|
return chess960;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Position::is_capture_or_promotion(Move m) const {
|
inline bool Position::capture_or_promotion(Move m) const {
|
||||||
|
|
||||||
assert(is_ok(m));
|
assert(is_ok(m));
|
||||||
return type_of(m) ? type_of(m) != CASTLE : !is_empty(to_sq(m));
|
return type_of(m) ? type_of(m) != CASTLE : !empty(to_sq(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Position::is_capture(Move m) const {
|
inline bool Position::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)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT;
|
return (!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 {
|
||||||
@@ -422,6 +405,7 @@ inline void Position::put_piece(Square s, Color c, PieceType pt) {
|
|||||||
byTypeBB[ALL_PIECES] |= s;
|
byTypeBB[ALL_PIECES] |= s;
|
||||||
byTypeBB[pt] |= s;
|
byTypeBB[pt] |= s;
|
||||||
byColorBB[c] |= s;
|
byColorBB[c] |= s;
|
||||||
|
pieceCount[c][ALL_PIECES]++;
|
||||||
index[s] = pieceCount[c][pt]++;
|
index[s] = pieceCount[c][pt]++;
|
||||||
pieceList[c][pt][index[s]] = s;
|
pieceList[c][pt][index[s]] = s;
|
||||||
}
|
}
|
||||||
@@ -450,6 +434,7 @@ inline void Position::remove_piece(Square s, Color c, PieceType pt) {
|
|||||||
byTypeBB[pt] ^= s;
|
byTypeBB[pt] ^= s;
|
||||||
byColorBB[c] ^= s;
|
byColorBB[c] ^= s;
|
||||||
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
|
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
|
||||||
|
pieceCount[c][ALL_PIECES]--;
|
||||||
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
|
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
|
||||||
index[lastSquare] = index[s];
|
index[lastSquare] = index[s];
|
||||||
pieceList[c][pt][index[lastSquare]] = lastSquare;
|
pieceList[c][pt][index[lastSquare]] = lastSquare;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
class RKISS {
|
class RKISS {
|
||||||
|
|
||||||
struct S { uint64_t a, b, c, d; } s; // Keep variables always together
|
uint64_t a, b, c, d;
|
||||||
|
|
||||||
uint64_t rotate(uint64_t x, uint64_t k) const {
|
uint64_t rotate(uint64_t x, uint64_t k) const {
|
||||||
return (x << k) | (x >> (64 - k));
|
return (x << k) | (x >> (64 - k));
|
||||||
@@ -51,20 +51,19 @@ class RKISS {
|
|||||||
|
|
||||||
uint64_t rand64() {
|
uint64_t rand64() {
|
||||||
|
|
||||||
const uint64_t
|
const uint64_t e = a - rotate(b, 7);
|
||||||
e = s.a - rotate(s.b, 7);
|
a = b ^ rotate(c, 13);
|
||||||
s.a = s.b ^ rotate(s.c, 13);
|
b = c + rotate(d, 37);
|
||||||
s.b = s.c + rotate(s.d, 37);
|
c = d + e;
|
||||||
s.c = s.d + e;
|
return d = e + a;
|
||||||
return s.d = e + s.a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RKISS(int seed = 73) {
|
RKISS(int seed = 73) {
|
||||||
|
|
||||||
s.a = 0xf1ea5eed;
|
a = 0xF1EA5EED, b = c = d = 0xD4E12C77;
|
||||||
s.b = s.c = s.d = 0xd4e12c77;
|
|
||||||
for (int i = 0; i < seed; i++) // Scramble a few rounds
|
for (int i = 0; i < seed; ++i) // Scramble a few rounds
|
||||||
rand64();
|
rand64();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cfloat>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -55,9 +56,6 @@ 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
|
||||||
const bool FakeSplit = false;
|
const bool FakeSplit = false;
|
||||||
|
|
||||||
// This is the minimum interval in msec between two check_time() calls
|
|
||||||
const int TimerResolution = 5;
|
|
||||||
|
|
||||||
// Different node types, used as template parameter
|
// Different node types, used as template parameter
|
||||||
enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV };
|
enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV };
|
||||||
|
|
||||||
@@ -65,13 +63,10 @@ namespace {
|
|||||||
inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); }
|
inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); }
|
||||||
|
|
||||||
// Futility lookup tables (initialized at startup) and their access functions
|
// Futility lookup tables (initialized at startup) and their access functions
|
||||||
Value FutilityMargins[16][64]; // [depth][moveNumber]
|
|
||||||
int FutilityMoveCounts[2][32]; // [improving][depth]
|
int FutilityMoveCounts[2][32]; // [improving][depth]
|
||||||
|
|
||||||
inline Value futility_margin(Depth d, int mn) {
|
inline Value futility_margin(Depth d) {
|
||||||
|
return Value(100 * int(d));
|
||||||
return d < 7 * ONE_PLY ? FutilityMargins[std::max(int(d), 1)][std::min(mn, 63)]
|
|
||||||
: 2 * VALUE_INFINITE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduction lookup tables (initialized at startup) and their access function
|
// Reduction lookup tables (initialized at startup) and their access function
|
||||||
@@ -84,7 +79,7 @@ namespace {
|
|||||||
|
|
||||||
size_t PVSize, PVIdx;
|
size_t PVSize, PVIdx;
|
||||||
TimeManager TimeMgr;
|
TimeManager TimeMgr;
|
||||||
int BestMoveChanges;
|
double BestMoveChanges;
|
||||||
Value DrawValue[COLOR_NB];
|
Value DrawValue[COLOR_NB];
|
||||||
HistoryStats History;
|
HistoryStats History;
|
||||||
GainsStats Gains;
|
GainsStats Gains;
|
||||||
@@ -99,7 +94,6 @@ namespace {
|
|||||||
void id_loop(Position& pos);
|
void id_loop(Position& pos);
|
||||||
Value value_to_tt(Value v, int ply);
|
Value value_to_tt(Value v, int ply);
|
||||||
Value value_from_tt(Value v, int ply);
|
Value value_from_tt(Value v, int ply);
|
||||||
bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta);
|
|
||||||
bool allows(const Position& pos, Move first, Move second);
|
bool allows(const Position& pos, Move first, Move second);
|
||||||
bool refutes(const Position& pos, Move first, Move second);
|
bool refutes(const Position& pos, Move first, Move second);
|
||||||
string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
|
string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
|
||||||
@@ -132,7 +126,7 @@ void Search::init() {
|
|||||||
int mc; // moveCount
|
int mc; // moveCount
|
||||||
|
|
||||||
// Init reductions array
|
// Init reductions array
|
||||||
for (hd = 1; hd < 64; hd++) for (mc = 1; mc < 64; mc++)
|
for (hd = 1; hd < 64; ++hd) for (mc = 1; mc < 64; ++mc)
|
||||||
{
|
{
|
||||||
double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
|
double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
|
||||||
double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
|
double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
|
||||||
@@ -144,17 +138,16 @@ void Search::init() {
|
|||||||
|
|
||||||
if (Reductions[0][0][hd][mc] > 2 * ONE_PLY)
|
if (Reductions[0][0][hd][mc] > 2 * ONE_PLY)
|
||||||
Reductions[0][0][hd][mc] += ONE_PLY;
|
Reductions[0][0][hd][mc] += ONE_PLY;
|
||||||
|
|
||||||
|
else if (Reductions[0][0][hd][mc] > 1 * ONE_PLY)
|
||||||
|
Reductions[0][0][hd][mc] += ONE_PLY / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init futility margins array
|
|
||||||
for (d = 1; d < 16; d++) for (mc = 0; mc < 64; mc++)
|
|
||||||
FutilityMargins[d][mc] = Value(112 * int(log(double(d * d) / 2) / log(2.0) + 1.001) - 8 * mc + 45);
|
|
||||||
|
|
||||||
// Init futility move count array
|
// Init futility move count array
|
||||||
for (d = 0; d < 32; d++)
|
for (d = 0; d < 32; ++d)
|
||||||
{
|
{
|
||||||
FutilityMoveCounts[0][d] = int(3.001 + 0.3 * pow(double(d ), 1.8)) * (d < 5 ? 4 : 3) / 4;
|
FutilityMoveCounts[0][d] = int(2.4 + 0.222 * pow(d + 0.0, 1.8));
|
||||||
FutilityMoveCounts[1][d] = int(3.001 + 0.3 * pow(double(d + 0.98), 1.8));
|
FutilityMoveCounts[1][d] = int(3.0 + 0.3 * pow(d + 0.98, 1.8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +164,7 @@ static size_t perft(Position& pos, Depth depth) {
|
|||||||
|
|
||||||
for (MoveList<LEGAL> it(pos); *it; ++it)
|
for (MoveList<LEGAL> it(pos); *it; ++it)
|
||||||
{
|
{
|
||||||
pos.do_move(*it, st, ci, pos.move_gives_check(*it, ci));
|
pos.do_move(*it, st, ci, pos.gives_check(*it, ci));
|
||||||
cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY);
|
cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY);
|
||||||
pos.undo_move(*it);
|
pos.undo_move(*it);
|
||||||
}
|
}
|
||||||
@@ -237,23 +230,16 @@ void Search::think() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset the threads, still sleeping: will be wake up at split time
|
// Reset the threads, still sleeping: will be wake up at split time
|
||||||
for (size_t i = 0; i < Threads.size(); i++)
|
for (size_t i = 0; i < Threads.size(); ++i)
|
||||||
Threads[i]->maxPly = 0;
|
Threads[i]->maxPly = 0;
|
||||||
|
|
||||||
Threads.sleepWhileIdle = Options["Idle Threads Sleep"];
|
Threads.sleepWhileIdle = Options["Idle Threads Sleep"];
|
||||||
|
Threads.timer->run = true;
|
||||||
// Set best timer interval to avoid lagging under time pressure. Timer is
|
|
||||||
// used to check for remaining available thinking time.
|
|
||||||
Threads.timer->msec =
|
|
||||||
Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) :
|
|
||||||
Limits.nodes ? 2 * TimerResolution
|
|
||||||
: 100;
|
|
||||||
|
|
||||||
Threads.timer->notify_one(); // Wake up the recurring timer
|
Threads.timer->notify_one(); // Wake up the recurring timer
|
||||||
|
|
||||||
id_loop(RootPos); // Let's start searching !
|
id_loop(RootPos); // Let's start searching !
|
||||||
|
|
||||||
Threads.timer->msec = 0; // Stop the timer
|
Threads.timer->run = false; // Stop the timer
|
||||||
Threads.sleepWhileIdle = true; // Send idle threads to sleep
|
Threads.sleepWhileIdle = true; // Send idle threads to sleep
|
||||||
|
|
||||||
if (Options["Write Search Log"])
|
if (Options["Write Search Log"])
|
||||||
@@ -304,13 +290,14 @@ namespace {
|
|||||||
void id_loop(Position& pos) {
|
void id_loop(Position& pos) {
|
||||||
|
|
||||||
Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
|
Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
|
||||||
int depth, prevBestMoveChanges;
|
int depth;
|
||||||
Value bestValue, alpha, beta, delta;
|
Value bestValue, alpha, beta, delta;
|
||||||
|
|
||||||
std::memset(ss-2, 0, 5 * sizeof(Stack));
|
std::memset(ss-2, 0, 5 * sizeof(Stack));
|
||||||
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
|
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
|
||||||
|
|
||||||
depth = BestMoveChanges = 0;
|
depth = 0;
|
||||||
|
BestMoveChanges = 0;
|
||||||
bestValue = delta = alpha = -VALUE_INFINITE;
|
bestValue = delta = alpha = -VALUE_INFINITE;
|
||||||
beta = VALUE_INFINITE;
|
beta = VALUE_INFINITE;
|
||||||
|
|
||||||
@@ -332,16 +319,16 @@ namespace {
|
|||||||
// Iterative deepening loop until requested to stop or target depth reached
|
// Iterative deepening loop until requested to stop or target depth reached
|
||||||
while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth))
|
while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth))
|
||||||
{
|
{
|
||||||
|
// Age out PV variability metric
|
||||||
|
BestMoveChanges *= 0.8;
|
||||||
|
|
||||||
// Save last iteration's scores before first PV line is searched and all
|
// Save last iteration's scores before first PV line is searched and all
|
||||||
// the move scores but the (new) PV are set to -VALUE_INFINITE.
|
// the move scores but the (new) PV are set to -VALUE_INFINITE.
|
||||||
for (size_t i = 0; i < RootMoves.size(); i++)
|
for (size_t i = 0; i < RootMoves.size(); ++i)
|
||||||
RootMoves[i].prevScore = RootMoves[i].score;
|
RootMoves[i].prevScore = RootMoves[i].score;
|
||||||
|
|
||||||
prevBestMoveChanges = BestMoveChanges; // Only sensible when PVSize == 1
|
|
||||||
BestMoveChanges = 0;
|
|
||||||
|
|
||||||
// MultiPV loop. We perform a full root search for each PV line
|
// MultiPV loop. We perform a full root search for each PV line
|
||||||
for (PVIdx = 0; PVIdx < PVSize; PVIdx++)
|
for (PVIdx = 0; PVIdx < PVSize && !Signals.stop; ++PVIdx)
|
||||||
{
|
{
|
||||||
// Reset aspiration window starting size
|
// Reset aspiration window starting size
|
||||||
if (depth >= 5)
|
if (depth >= 5)
|
||||||
@@ -367,14 +354,14 @@ namespace {
|
|||||||
|
|
||||||
// Write PV back to transposition table in case the relevant
|
// Write PV back to transposition table in case the relevant
|
||||||
// entries have been overwritten during the search.
|
// entries have been overwritten during the search.
|
||||||
for (size_t i = 0; i <= PVIdx; i++)
|
for (size_t i = 0; i <= PVIdx; ++i)
|
||||||
RootMoves[i].insert_pv_in_tt(pos);
|
RootMoves[i].insert_pv_in_tt(pos);
|
||||||
|
|
||||||
// If search has been stopped return immediately. Sorting and
|
// If search has been stopped break immediately. Sorting and
|
||||||
// writing PV back to TT is safe becuase RootMoves is still
|
// writing PV back to TT is safe becuase RootMoves is still
|
||||||
// valid, although refers to previous iteration.
|
// valid, although refers to previous iteration.
|
||||||
if (Signals.stop)
|
if (Signals.stop)
|
||||||
return;
|
break;
|
||||||
|
|
||||||
// When failing high/low give some update (without cluttering
|
// When failing high/low give some update (without cluttering
|
||||||
// the UI) before to research.
|
// the UI) before to research.
|
||||||
@@ -431,13 +418,13 @@ namespace {
|
|||||||
Signals.stop = true;
|
Signals.stop = true;
|
||||||
|
|
||||||
// Do we have time for the next iteration? Can we stop searching now?
|
// Do we have time for the next iteration? Can we stop searching now?
|
||||||
if (Limits.use_time_management() && !Signals.stopOnPonderhit)
|
if (Limits.use_time_management() && !Signals.stop && !Signals.stopOnPonderhit)
|
||||||
{
|
{
|
||||||
bool stop = false; // Local variable, not the volatile Signals.stop
|
bool stop = false; // Local variable, not the volatile Signals.stop
|
||||||
|
|
||||||
// Take in account some extra time if the best move has changed
|
// Take in account some extra time if the best move has changed
|
||||||
if (depth > 4 && depth < 50 && PVSize == 1)
|
if (depth > 4 && depth < 50 && PVSize == 1)
|
||||||
TimeMgr.pv_instability(BestMoveChanges, prevBestMoveChanges);
|
TimeMgr.pv_instability(BestMoveChanges);
|
||||||
|
|
||||||
// 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
|
||||||
@@ -447,6 +434,7 @@ namespace {
|
|||||||
|
|
||||||
// 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
|
||||||
|
&& BestMoveChanges <= DBL_EPSILON
|
||||||
&& !stop
|
&& !stop
|
||||||
&& PVSize == 1
|
&& PVSize == 1
|
||||||
&& bestValue > VALUE_MATED_IN_MAX_PLY
|
&& bestValue > VALUE_MATED_IN_MAX_PLY
|
||||||
@@ -502,9 +490,8 @@ namespace {
|
|||||||
SplitPoint* splitPoint;
|
SplitPoint* splitPoint;
|
||||||
Key posKey;
|
Key posKey;
|
||||||
Move ttMove, move, excludedMove, bestMove, threatMove;
|
Move ttMove, move, excludedMove, bestMove, threatMove;
|
||||||
Depth ext, newDepth;
|
Depth ext, newDepth, predictedDepth;
|
||||||
Value bestValue, value, ttValue;
|
Value bestValue, value, ttValue, eval, nullValue, futilityValue;
|
||||||
Value eval, nullValue, futilityValue;
|
|
||||||
bool inCheck, givesCheck, pvMove, singularExtensionNode, improving;
|
bool inCheck, givesCheck, pvMove, singularExtensionNode, improving;
|
||||||
bool captureOrPromotion, dangerous, doFullDepthSearch;
|
bool captureOrPromotion, dangerous, doFullDepthSearch;
|
||||||
int moveCount, quietCount;
|
int moveCount, quietCount;
|
||||||
@@ -532,7 +519,6 @@ namespace {
|
|||||||
bestValue = -VALUE_INFINITE;
|
bestValue = -VALUE_INFINITE;
|
||||||
ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
|
ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
|
||||||
ss->ply = (ss-1)->ply + 1;
|
ss->ply = (ss-1)->ply + 1;
|
||||||
ss->futilityMoveCount = 0;
|
|
||||||
(ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
|
(ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
|
||||||
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
|
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
|
||||||
|
|
||||||
@@ -584,7 +570,7 @@ namespace {
|
|||||||
|
|
||||||
if ( ttValue >= beta
|
if ( ttValue >= beta
|
||||||
&& ttMove
|
&& ttMove
|
||||||
&& !pos.is_capture_or_promotion(ttMove)
|
&& !pos.capture_or_promotion(ttMove)
|
||||||
&& ttMove != ss->killers[0])
|
&& ttMove != ss->killers[0])
|
||||||
{
|
{
|
||||||
ss->killers[1] = ss->killers[0];
|
ss->killers[1] = ss->killers[0];
|
||||||
@@ -596,32 +582,27 @@ namespace {
|
|||||||
// Step 5. Evaluate the position statically and update parent's gain statistics
|
// Step 5. Evaluate the position statically and update parent's gain statistics
|
||||||
if (inCheck)
|
if (inCheck)
|
||||||
{
|
{
|
||||||
ss->staticEval = ss->evalMargin = eval = VALUE_NONE;
|
ss->staticEval = eval = VALUE_NONE;
|
||||||
goto moves_loop;
|
goto moves_loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (tte)
|
else if (tte)
|
||||||
{
|
{
|
||||||
// Never assume anything on values stored in TT
|
// Never assume anything on values stored in TT
|
||||||
if ( (ss->staticEval = eval = tte->eval_value()) == VALUE_NONE
|
if ((ss->staticEval = eval = tte->eval_value()) == VALUE_NONE)
|
||||||
||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE)
|
eval = ss->staticEval = evaluate(pos);
|
||||||
eval = ss->staticEval = evaluate(pos, ss->evalMargin);
|
|
||||||
|
|
||||||
// Can ttValue be used as a better position evaluation?
|
// Can ttValue be used as a better position evaluation?
|
||||||
if (ttValue != VALUE_NONE)
|
if (ttValue != VALUE_NONE)
|
||||||
if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval)
|
if (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))
|
||||||
|| ((tte->bound() & BOUND_UPPER) && ttValue < eval))
|
|
||||||
eval = ttValue;
|
eval = ttValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
eval = ss->staticEval = evaluate(pos, ss->evalMargin);
|
eval = ss->staticEval = evaluate(pos);
|
||||||
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
|
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval);
|
||||||
ss->staticEval, ss->evalMargin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update gain for the parent non-capture move given the static position
|
|
||||||
// evaluation before and after the move.
|
|
||||||
if ( !pos.captured_piece_type()
|
if ( !pos.captured_piece_type()
|
||||||
&& ss->staticEval != VALUE_NONE
|
&& ss->staticEval != VALUE_NONE
|
||||||
&& (ss-1)->staticEval != VALUE_NONE
|
&& (ss-1)->staticEval != VALUE_NONE
|
||||||
@@ -648,22 +629,20 @@ namespace {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 7. Static null move pruning (skipped when in check)
|
// Step 7. Futility pruning: child node (skipped when in check)
|
||||||
// We're betting that the opponent doesn't have a move that will reduce
|
|
||||||
// the score by more than futility_margin(depth) if we do a null move.
|
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& !ss->skipNullMove
|
&& !ss->skipNullMove
|
||||||
&& depth < 4 * ONE_PLY
|
&& depth < 7 * ONE_PLY
|
||||||
&& eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta
|
&& eval - futility_margin(depth) >= beta
|
||||||
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
|
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
|
||||||
&& abs(eval) < VALUE_KNOWN_WIN
|
&& abs(eval) < VALUE_KNOWN_WIN
|
||||||
&& pos.non_pawn_material(pos.side_to_move()))
|
&& pos.non_pawn_material(pos.side_to_move()))
|
||||||
return eval - futility_margin(depth, (ss-1)->futilityMoveCount);
|
return eval - futility_margin(depth);
|
||||||
|
|
||||||
// Step 8. Null move search with verification search (is omitted in PV nodes)
|
// Step 8. Null move search with verification search (is omitted in PV nodes)
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& !ss->skipNullMove
|
&& !ss->skipNullMove
|
||||||
&& depth > ONE_PLY
|
&& depth >= 2 * ONE_PLY
|
||||||
&& eval >= beta
|
&& eval >= beta
|
||||||
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
|
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
|
||||||
&& pos.non_pawn_material(pos.side_to_move()))
|
&& pos.non_pawn_material(pos.side_to_move()))
|
||||||
@@ -739,10 +718,10 @@ namespace {
|
|||||||
CheckInfo ci(pos);
|
CheckInfo ci(pos);
|
||||||
|
|
||||||
while ((move = mp.next_move<false>()) != MOVE_NONE)
|
while ((move = mp.next_move<false>()) != MOVE_NONE)
|
||||||
if (pos.pl_move_is_legal(move, ci.pinned))
|
if (pos.legal(move, ci.pinned))
|
||||||
{
|
{
|
||||||
ss->currentMove = move;
|
ss->currentMove = move;
|
||||||
pos.do_move(move, st, ci, pos.move_gives_check(move, ci));
|
pos.do_move(move, st, ci, pos.gives_check(move, ci));
|
||||||
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode);
|
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode);
|
||||||
pos.undo_move(move);
|
pos.undo_move(move);
|
||||||
if (value >= rbeta)
|
if (value >= rbeta)
|
||||||
@@ -780,7 +759,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
|
|
||||||
singularExtensionNode = !RootNode
|
singularExtensionNode = !RootNode
|
||||||
&& !SpNode
|
&& !SpNode
|
||||||
&& depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY)
|
&& depth >= 8 * ONE_PLY
|
||||||
&& ttMove != MOVE_NONE
|
&& ttMove != MOVE_NONE
|
||||||
&& !excludedMove // Recursive singular search is not allowed
|
&& !excludedMove // Recursive singular search is not allowed
|
||||||
&& (tte->bound() & BOUND_LOWER)
|
&& (tte->bound() & BOUND_LOWER)
|
||||||
@@ -804,14 +783,14 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
if (SpNode)
|
if (SpNode)
|
||||||
{
|
{
|
||||||
// Shared counter cannot be decremented later if move turns out to be illegal
|
// Shared counter cannot be decremented later if move turns out to be illegal
|
||||||
if (!pos.pl_move_is_legal(move, ci.pinned))
|
if (!pos.legal(move, ci.pinned))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
moveCount = ++splitPoint->moveCount;
|
moveCount = ++splitPoint->moveCount;
|
||||||
splitPoint->mutex.unlock();
|
splitPoint->mutex.unlock();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
moveCount++;
|
++moveCount;
|
||||||
|
|
||||||
if (RootNode)
|
if (RootNode)
|
||||||
{
|
{
|
||||||
@@ -824,19 +803,16 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
}
|
}
|
||||||
|
|
||||||
ext = DEPTH_ZERO;
|
ext = DEPTH_ZERO;
|
||||||
captureOrPromotion = pos.is_capture_or_promotion(move);
|
captureOrPromotion = pos.capture_or_promotion(move);
|
||||||
givesCheck = pos.move_gives_check(move, ci);
|
givesCheck = pos.gives_check(move, ci);
|
||||||
dangerous = givesCheck
|
dangerous = givesCheck
|
||||||
|| pos.is_passed_pawn_push(move)
|
|| pos.passed_pawn_push(move)
|
||||||
|| type_of(move) == CASTLE;
|
|| type_of(move) == CASTLE;
|
||||||
|
|
||||||
// Step 12. Extend checks and, in PV nodes, also dangerous moves
|
// Step 12. Extend checks
|
||||||
if (PvNode && dangerous)
|
if (givesCheck && pos.see_sign(move) >= 0)
|
||||||
ext = ONE_PLY;
|
ext = ONE_PLY;
|
||||||
|
|
||||||
else if (givesCheck && pos.see_sign(move) >= 0)
|
|
||||||
ext = ONE_PLY / 2;
|
|
||||||
|
|
||||||
// Singular extension search. If all moves but one fail low on a search of
|
// Singular extension search. If all moves but one fail low on a search of
|
||||||
// (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move
|
// (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move
|
||||||
// is singular and should be extended. To verify this we do a reduced search
|
// is singular and should be extended. To verify this we do a reduced search
|
||||||
@@ -845,7 +821,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
if ( singularExtensionNode
|
if ( singularExtensionNode
|
||||||
&& move == ttMove
|
&& move == ttMove
|
||||||
&& !ext
|
&& !ext
|
||||||
&& pos.pl_move_is_legal(move, ci.pinned)
|
&& pos.legal(move, ci.pinned)
|
||||||
&& abs(ttValue) < VALUE_KNOWN_WIN)
|
&& abs(ttValue) < VALUE_KNOWN_WIN)
|
||||||
{
|
{
|
||||||
assert(ttValue != VALUE_NONE);
|
assert(ttValue != VALUE_NONE);
|
||||||
@@ -864,7 +840,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
// Update current move (this must be done after singular extension search)
|
// Update current move (this must be done after singular extension search)
|
||||||
newDepth = depth - ONE_PLY + ext;
|
newDepth = depth - ONE_PLY + ext;
|
||||||
|
|
||||||
// Step 13. Futility pruning (is omitted in PV nodes)
|
// Step 13. Pruning at shallow depth (exclude PV nodes)
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& !captureOrPromotion
|
&& !captureOrPromotion
|
||||||
&& !inCheck
|
&& !inCheck
|
||||||
@@ -883,29 +859,30 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value based pruning
|
predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
|
||||||
// We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth,
|
|
||||||
// but fixing this made program slightly weaker.
|
|
||||||
Depth predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
|
|
||||||
futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount)
|
|
||||||
+ Gains[pos.piece_moved(move)][to_sq(move)];
|
|
||||||
|
|
||||||
if (futilityValue < beta)
|
// Futility pruning: parent node
|
||||||
|
if (predictedDepth < 7 * ONE_PLY)
|
||||||
{
|
{
|
||||||
bestValue = std::max(bestValue, futilityValue);
|
futilityValue = ss->staticEval + futility_margin(predictedDepth)
|
||||||
|
+ Value(128) + Gains[pos.moved_piece(move)][to_sq(move)];
|
||||||
|
|
||||||
if (SpNode)
|
if (futilityValue <= alpha)
|
||||||
{
|
{
|
||||||
splitPoint->mutex.lock();
|
bestValue = std::max(bestValue, futilityValue);
|
||||||
if (bestValue > splitPoint->bestValue)
|
|
||||||
splitPoint->bestValue = bestValue;
|
if (SpNode)
|
||||||
|
{
|
||||||
|
splitPoint->mutex.lock();
|
||||||
|
if (bestValue > splitPoint->bestValue)
|
||||||
|
splitPoint->bestValue = bestValue;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune moves with negative SEE at low depths
|
// Prune moves with negative SEE at low depths
|
||||||
if ( predictedDepth < 4 * ONE_PLY
|
if (predictedDepth < 4 * ONE_PLY && pos.see_sign(move) < 0)
|
||||||
&& pos.see_sign(move) < 0)
|
|
||||||
{
|
{
|
||||||
if (SpNode)
|
if (SpNode)
|
||||||
splitPoint->mutex.lock();
|
splitPoint->mutex.lock();
|
||||||
@@ -913,15 +890,10 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have not pruned the move that will be searched, but remember how
|
|
||||||
// far in the move list we are to be more aggressive in the child node.
|
|
||||||
ss->futilityMoveCount = moveCount;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
ss->futilityMoveCount = 0;
|
|
||||||
|
|
||||||
// Check for legality only before to do the move
|
// Check for legality only before to do the move
|
||||||
if (!RootNode && !SpNode && !pos.pl_move_is_legal(move, ci.pinned))
|
if (!RootNode && !SpNode && !pos.legal(move, ci.pinned))
|
||||||
{
|
{
|
||||||
moveCount--;
|
moveCount--;
|
||||||
continue;
|
continue;
|
||||||
@@ -937,10 +909,9 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
|
|
||||||
// Step 15. Reduced depth search (LMR). If the move fails high will be
|
// Step 15. Reduced depth search (LMR). If the move fails high will be
|
||||||
// re-searched at full depth.
|
// re-searched at full depth.
|
||||||
if ( depth > 3 * ONE_PLY
|
if ( depth >= 3 * ONE_PLY
|
||||||
&& !pvMove
|
&& !pvMove
|
||||||
&& !captureOrPromotion
|
&& !captureOrPromotion
|
||||||
&& !dangerous
|
|
||||||
&& move != ttMove
|
&& move != ttMove
|
||||||
&& move != ss->killers[0]
|
&& move != ss->killers[0]
|
||||||
&& move != ss->killers[1])
|
&& move != ss->killers[1])
|
||||||
@@ -950,8 +921,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
if (!PvNode && cutNode)
|
if (!PvNode && cutNode)
|
||||||
ss->reduction += ONE_PLY;
|
ss->reduction += ONE_PLY;
|
||||||
|
|
||||||
|
else if (History[pos.piece_on(to_sq(move))][to_sq(move)] < 0)
|
||||||
|
ss->reduction += ONE_PLY / 2;
|
||||||
|
|
||||||
if (move == countermoves[0] || move == countermoves[1])
|
if (move == countermoves[0] || move == countermoves[1])
|
||||||
ss->reduction = std::max(DEPTH_ZERO, ss->reduction-ONE_PLY);
|
ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY);
|
||||||
|
|
||||||
Depth d = std::max(newDepth - ss->reduction, ONE_PLY);
|
Depth d = std::max(newDepth - ss->reduction, ONE_PLY);
|
||||||
if (SpNode)
|
if (SpNode)
|
||||||
@@ -1019,7 +993,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
// iteration. This information is used for time management: When
|
// iteration. This information is used for time management: When
|
||||||
// the best move changes frequently, we allocate some more time.
|
// the best move changes frequently, we allocate some more time.
|
||||||
if (!pvMove)
|
if (!pvMove)
|
||||||
BestMoveChanges++;
|
++BestMoveChanges;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// All other moves but the PV are set to the lowest value, this
|
// All other moves but the PV are set to the lowest value, this
|
||||||
@@ -1086,11 +1060,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
TT.store(posKey, value_to_tt(bestValue, ss->ply),
|
TT.store(posKey, value_to_tt(bestValue, ss->ply),
|
||||||
bestValue >= beta ? BOUND_LOWER :
|
bestValue >= beta ? BOUND_LOWER :
|
||||||
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
|
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
|
||||||
depth, bestMove, ss->staticEval, ss->evalMargin);
|
depth, bestMove, ss->staticEval);
|
||||||
|
|
||||||
// Quiet best move: update killers, history and countermoves
|
// Quiet best move: update killers, history and countermoves
|
||||||
if ( bestValue >= beta
|
if ( bestValue >= beta
|
||||||
&& !pos.is_capture_or_promotion(bestMove)
|
&& !pos.capture_or_promotion(bestMove)
|
||||||
&& !inCheck)
|
&& !inCheck)
|
||||||
{
|
{
|
||||||
if (ss->killers[0] != bestMove)
|
if (ss->killers[0] != bestMove)
|
||||||
@@ -1102,11 +1076,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
// Increase history value of the cut-off move and decrease all the other
|
// Increase history value of the cut-off move and decrease all the other
|
||||||
// played non-capture moves.
|
// played non-capture moves.
|
||||||
Value bonus = Value(int(depth) * int(depth));
|
Value bonus = Value(int(depth) * int(depth));
|
||||||
History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus);
|
History.update(pos.moved_piece(bestMove), to_sq(bestMove), bonus);
|
||||||
for (int i = 0; i < quietCount - 1; i++)
|
for (int i = 0; i < quietCount - 1; ++i)
|
||||||
{
|
{
|
||||||
Move m = quietsSearched[i];
|
Move m = quietsSearched[i];
|
||||||
History.update(pos.piece_moved(m), to_sq(m), -bonus);
|
History.update(pos.moved_piece(m), to_sq(m), -bonus);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_ok((ss-1)->currentMove))
|
if (is_ok((ss-1)->currentMove))
|
||||||
@@ -1179,7 +1153,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
// Evaluate the position statically
|
// Evaluate the position statically
|
||||||
if (InCheck)
|
if (InCheck)
|
||||||
{
|
{
|
||||||
ss->staticEval = ss->evalMargin = VALUE_NONE;
|
ss->staticEval = VALUE_NONE;
|
||||||
bestValue = futilityBase = -VALUE_INFINITE;
|
bestValue = futilityBase = -VALUE_INFINITE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1187,19 +1161,23 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
if (tte)
|
if (tte)
|
||||||
{
|
{
|
||||||
// Never assume anything on values stored in TT
|
// Never assume anything on values stored in TT
|
||||||
if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE
|
if ((ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE)
|
||||||
||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE)
|
ss->staticEval = bestValue = evaluate(pos);
|
||||||
ss->staticEval = bestValue = evaluate(pos, ss->evalMargin);
|
|
||||||
|
// Can ttValue be used as a better position evaluation?
|
||||||
|
if (ttValue != VALUE_NONE)
|
||||||
|
if (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))
|
||||||
|
bestValue = ttValue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ss->staticEval = bestValue = evaluate(pos, ss->evalMargin);
|
ss->staticEval = bestValue = evaluate(pos);
|
||||||
|
|
||||||
// Stand pat. Return immediately if static value is at least beta
|
// Stand pat. Return immediately if static value is at least beta
|
||||||
if (bestValue >= beta)
|
if (bestValue >= beta)
|
||||||
{
|
{
|
||||||
if (!tte)
|
if (!tte)
|
||||||
TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER,
|
TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER,
|
||||||
DEPTH_NONE, MOVE_NONE, ss->staticEval, ss->evalMargin);
|
DEPTH_NONE, MOVE_NONE, ss->staticEval);
|
||||||
|
|
||||||
return bestValue;
|
return bestValue;
|
||||||
}
|
}
|
||||||
@@ -1207,7 +1185,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
if (PvNode && bestValue > alpha)
|
if (PvNode && bestValue > alpha)
|
||||||
alpha = bestValue;
|
alpha = bestValue;
|
||||||
|
|
||||||
futilityBase = ss->staticEval + ss->evalMargin + Value(128);
|
futilityBase = bestValue + Value(128);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a MovePicker object for the current position, and prepare
|
// Initialize a MovePicker object for the current position, and prepare
|
||||||
@@ -1222,7 +1200,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
{
|
{
|
||||||
assert(is_ok(move));
|
assert(is_ok(move));
|
||||||
|
|
||||||
givesCheck = pos.move_gives_check(move, ci);
|
givesCheck = pos.gives_check(move, ci);
|
||||||
|
|
||||||
// Futility pruning
|
// Futility pruning
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
@@ -1230,7 +1208,8 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
&& !givesCheck
|
&& !givesCheck
|
||||||
&& move != ttMove
|
&& move != ttMove
|
||||||
&& type_of(move) != PROMOTION
|
&& type_of(move) != PROMOTION
|
||||||
&& !pos.is_passed_pawn_push(move))
|
&& futilityBase > -VALUE_KNOWN_WIN
|
||||||
|
&& !pos.passed_pawn_push(move))
|
||||||
{
|
{
|
||||||
futilityValue = futilityBase
|
futilityValue = futilityBase
|
||||||
+ PieceValue[EG][pos.piece_on(to_sq(move))]
|
+ PieceValue[EG][pos.piece_on(to_sq(move))]
|
||||||
@@ -1255,7 +1234,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
// Detect non-capture evasions that are candidate to be pruned
|
// Detect non-capture evasions that are candidate to be pruned
|
||||||
evasionPrunable = InCheck
|
evasionPrunable = InCheck
|
||||||
&& bestValue > VALUE_MATED_IN_MAX_PLY
|
&& bestValue > VALUE_MATED_IN_MAX_PLY
|
||||||
&& !pos.is_capture(move)
|
&& !pos.capture(move)
|
||||||
&& !pos.can_castle(pos.side_to_move());
|
&& !pos.can_castle(pos.side_to_move());
|
||||||
|
|
||||||
// Don't search moves with negative SEE values
|
// Don't search moves with negative SEE values
|
||||||
@@ -1266,18 +1245,8 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
&& pos.see_sign(move) < 0)
|
&& pos.see_sign(move) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Don't search useless checks
|
|
||||||
if ( !PvNode
|
|
||||||
&& !InCheck
|
|
||||||
&& givesCheck
|
|
||||||
&& move != ttMove
|
|
||||||
&& !pos.is_capture_or_promotion(move)
|
|
||||||
&& ss->staticEval + PawnValueMg / 4 < beta
|
|
||||||
&& !check_is_dangerous(pos, move, futilityBase, beta))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Check for legality only before to do the move
|
// Check for legality only before to do the move
|
||||||
if (!pos.pl_move_is_legal(move, ci.pinned))
|
if (!pos.legal(move, ci.pinned))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ss->currentMove = move;
|
ss->currentMove = move;
|
||||||
@@ -1305,7 +1274,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
else // Fail high
|
else // Fail high
|
||||||
{
|
{
|
||||||
TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER,
|
TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER,
|
||||||
ttDepth, move, ss->staticEval, ss->evalMargin);
|
ttDepth, move, ss->staticEval);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -1320,7 +1289,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
|
|
||||||
TT.store(posKey, value_to_tt(bestValue, ss->ply),
|
TT.store(posKey, value_to_tt(bestValue, ss->ply),
|
||||||
PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
|
PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
|
||||||
ttDepth, bestMove, ss->staticEval, ss->evalMargin);
|
ttDepth, bestMove, ss->staticEval);
|
||||||
|
|
||||||
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
|
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
|
||||||
|
|
||||||
@@ -1353,42 +1322,6 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// check_is_dangerous() tests if a checking move can be pruned in qsearch()
|
|
||||||
|
|
||||||
bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta)
|
|
||||||
{
|
|
||||||
Piece pc = pos.piece_moved(move);
|
|
||||||
Square from = from_sq(move);
|
|
||||||
Square to = to_sq(move);
|
|
||||||
Color them = ~pos.side_to_move();
|
|
||||||
Square ksq = pos.king_square(them);
|
|
||||||
Bitboard enemies = pos.pieces(them);
|
|
||||||
Bitboard kingAtt = pos.attacks_from<KING>(ksq);
|
|
||||||
Bitboard occ = pos.pieces() ^ from ^ ksq;
|
|
||||||
Bitboard oldAtt = pos.attacks_from(pc, from, occ);
|
|
||||||
Bitboard newAtt = pos.attacks_from(pc, to, occ);
|
|
||||||
|
|
||||||
// Checks which give opponent's king at most one escape square are dangerous
|
|
||||||
if (!more_than_one(kingAtt & ~(enemies | newAtt | to)))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Queen contact check is very dangerous
|
|
||||||
if (type_of(pc) == QUEEN && (kingAtt & to))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Creating new double threats with checks is dangerous
|
|
||||||
Bitboard b = (enemies ^ ksq) & newAtt & ~oldAtt;
|
|
||||||
while (b)
|
|
||||||
{
|
|
||||||
// Note that here we generate illegal "double move"!
|
|
||||||
if (futilityBase + PieceValue[EG][pos.piece_on(pop_lsb(&b))] >= beta)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// allows() tests whether the 'first' move at previous ply somehow makes the
|
// allows() tests whether the 'first' move at previous ply somehow makes the
|
||||||
// 'second' move possible, for instance if the moving piece is the same in
|
// 'second' move possible, for instance if the moving piece is the same in
|
||||||
// both moves. Normally the second move is the threat (the best move returned
|
// both moves. Normally the second move is the threat (the best move returned
|
||||||
@@ -1399,7 +1332,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
assert(is_ok(first));
|
assert(is_ok(first));
|
||||||
assert(is_ok(second));
|
assert(is_ok(second));
|
||||||
assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move());
|
assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move());
|
||||||
assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
|
assert(type_of(first) == CASTLE || color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
|
||||||
|
|
||||||
Square m1from = from_sq(first);
|
Square m1from = from_sq(first);
|
||||||
Square m2from = from_sq(second);
|
Square m2from = from_sq(second);
|
||||||
@@ -1407,7 +1340,10 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
Square m2to = to_sq(second);
|
Square m2to = to_sq(second);
|
||||||
|
|
||||||
// The piece is the same or second's destination was vacated by the first move
|
// The piece is the same or second's destination was vacated by the first move
|
||||||
if (m1to == m2from || m2to == m1from)
|
// We exclude the trivial case where a sliding piece does in two moves what
|
||||||
|
// it could do in one move: eg. Ra1a2, Ra2a3.
|
||||||
|
if ( m2to == m1from
|
||||||
|
|| (m1to == m2from && !aligned(m1from, m2from, m2to)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Second one moves through the square vacated by first one
|
// Second one moves through the square vacated by first one
|
||||||
@@ -1450,7 +1386,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
|
|
||||||
// If the threatened piece has value less than or equal to the value of the
|
// If the threatened piece has value less than or equal to the value of the
|
||||||
// threat piece, don't prune moves which defend it.
|
// threat piece, don't prune moves which defend it.
|
||||||
if ( pos.is_capture(second)
|
if ( pos.capture(second)
|
||||||
&& ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)]
|
&& ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)]
|
||||||
|| type_of(pos.piece_on(m2from)) == KING))
|
|| type_of(pos.piece_on(m2from)) == KING))
|
||||||
{
|
{
|
||||||
@@ -1487,7 +1423,7 @@ moves_loop: // When in check and at SpNode 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::now() % 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
|
||||||
@@ -1499,7 +1435,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
// Choose best move. For each move score we add two terms both dependent on
|
// Choose best move. For each move score we add two terms both dependent on
|
||||||
// weakness, one deterministic and bigger for weaker moves, and one random,
|
// weakness, one deterministic and bigger for weaker moves, and one random,
|
||||||
// then we choose the move with the resulting highest score.
|
// then we choose the move with the resulting highest score.
|
||||||
for (size_t i = 0; i < PVSize; i++)
|
for (size_t i = 0; i < PVSize; ++i)
|
||||||
{
|
{
|
||||||
int s = RootMoves[i].score;
|
int s = RootMoves[i].score;
|
||||||
|
|
||||||
@@ -1532,11 +1468,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size());
|
size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size());
|
||||||
int selDepth = 0;
|
int selDepth = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < Threads.size(); i++)
|
for (size_t i = 0; i < Threads.size(); ++i)
|
||||||
if (Threads[i]->maxPly > selDepth)
|
if (Threads[i]->maxPly > selDepth)
|
||||||
selDepth = Threads[i]->maxPly;
|
selDepth = Threads[i]->maxPly;
|
||||||
|
|
||||||
for (size_t i = 0; i < uciPVSize; i++)
|
for (size_t i = 0; i < uciPVSize; ++i)
|
||||||
{
|
{
|
||||||
bool updated = (i <= PVIdx);
|
bool updated = (i <= PVIdx);
|
||||||
|
|
||||||
@@ -1558,7 +1494,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||||||
<< " multipv " << i + 1
|
<< " multipv " << i + 1
|
||||||
<< " pv";
|
<< " pv";
|
||||||
|
|
||||||
for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++)
|
for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; ++j)
|
||||||
s << " " << move_to_uci(RootMoves[i].pv[j], pos.is_chess960());
|
s << " " << move_to_uci(RootMoves[i].pv[j], pos.is_chess960());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1591,8 +1527,8 @@ void RootMove::extract_pv_from_tt(Position& pos) {
|
|||||||
tte = TT.probe(pos.key());
|
tte = TT.probe(pos.key());
|
||||||
|
|
||||||
} while ( tte
|
} while ( tte
|
||||||
&& pos.is_pseudo_legal(m = tte->move()) // Local copy, TT could change
|
&& pos.pseudo_legal(m = tte->move()) // Local copy, TT could change
|
||||||
&& pos.pl_move_is_legal(m, pos.pinned_pieces())
|
&& pos.legal(m, pos.pinned_pieces(pos.side_to_move()))
|
||||||
&& ply < MAX_PLY
|
&& ply < MAX_PLY
|
||||||
&& (!pos.is_draw() || ply < 2));
|
&& (!pos.is_draw() || ply < 2));
|
||||||
|
|
||||||
@@ -1616,7 +1552,7 @@ void RootMove::insert_pv_in_tt(Position& pos) {
|
|||||||
tte = TT.probe(pos.key());
|
tte = TT.probe(pos.key());
|
||||||
|
|
||||||
if (!tte || tte->move() != pv[ply]) // Don't overwrite correct entries
|
if (!tte || tte->move() != pv[ply]) // Don't overwrite correct entries
|
||||||
TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], VALUE_NONE, VALUE_NONE);
|
TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], VALUE_NONE);
|
||||||
|
|
||||||
assert(MoveList<LEGAL>(pos).contains(pv[ply]));
|
assert(MoveList<LEGAL>(pos).contains(pv[ply]));
|
||||||
|
|
||||||
@@ -1773,8 +1709,8 @@ void check_time() {
|
|||||||
|
|
||||||
// Loop across all split points and sum accumulated SplitPoint nodes plus
|
// Loop across all split points and sum accumulated SplitPoint nodes plus
|
||||||
// all the currently active positions nodes.
|
// all the currently active positions nodes.
|
||||||
for (size_t i = 0; i < Threads.size(); i++)
|
for (size_t i = 0; i < Threads.size(); ++i)
|
||||||
for (int j = 0; j < Threads[i]->splitPointsSize; j++)
|
for (int j = 0; j < Threads[i]->splitPointsSize; ++j)
|
||||||
{
|
{
|
||||||
SplitPoint& sp = Threads[i]->splitPoints[j];
|
SplitPoint& sp = Threads[i]->splitPoints[j];
|
||||||
|
|
||||||
@@ -1800,7 +1736,7 @@ void check_time() {
|
|||||||
&& !Signals.failedLowAtRoot
|
&& !Signals.failedLowAtRoot
|
||||||
&& elapsed > TimeMgr.available_time();
|
&& elapsed > TimeMgr.available_time();
|
||||||
|
|
||||||
bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerResolution
|
bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerThread::Resolution
|
||||||
|| stillAtFirstMove;
|
|| stillAtFirstMove;
|
||||||
|
|
||||||
if ( (Limits.use_time_management() && noMoreTime)
|
if ( (Limits.use_time_management() && noMoreTime)
|
||||||
|
|||||||
@@ -45,9 +45,7 @@ struct Stack {
|
|||||||
Move killers[2];
|
Move killers[2];
|
||||||
Depth reduction;
|
Depth reduction;
|
||||||
Value staticEval;
|
Value staticEval;
|
||||||
Value evalMargin;
|
|
||||||
int skipNullMove;
|
int skipNullMove;
|
||||||
int futilityMoveCount;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -101,11 +101,11 @@ void TimerThread::idle_loop() {
|
|||||||
mutex.lock();
|
mutex.lock();
|
||||||
|
|
||||||
if (!exit)
|
if (!exit)
|
||||||
sleepCondition.wait_for(mutex, msec ? msec : INT_MAX);
|
sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX);
|
||||||
|
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
|
|
||||||
if (msec)
|
if (run)
|
||||||
check_time();
|
check_time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,14 +157,14 @@ bool Thread::cutoff_occurred() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Thread::is_available_to() checks whether the thread is available to help the
|
// Thread::available_to() checks whether the thread is available to help the
|
||||||
// thread 'master' at a split point. An obvious requirement is that thread must
|
// thread 'master' at a split point. An obvious requirement is that thread must
|
||||||
// be idle. With more than two threads, this is not sufficient: If the thread is
|
// be idle. With more than two threads, this is not sufficient: If the thread is
|
||||||
// the master of some split point, it is only available as a slave to the slaves
|
// the master of some split point, it is only available as a slave to the slaves
|
||||||
// which are busy searching the split point at the top of slaves split point
|
// which are busy searching the split point at the top of slaves split point
|
||||||
// stack (the "helpful master concept" in YBWC terminology).
|
// stack (the "helpful master concept" in YBWC terminology).
|
||||||
|
|
||||||
bool Thread::is_available_to(const Thread* master) const {
|
bool Thread::available_to(const Thread* master) const {
|
||||||
|
|
||||||
if (searching)
|
if (searching)
|
||||||
return false;
|
return false;
|
||||||
@@ -241,7 +241,7 @@ void ThreadPool::read_uci_options() {
|
|||||||
Thread* ThreadPool::available_slave(const Thread* master) const {
|
Thread* ThreadPool::available_slave(const Thread* master) const {
|
||||||
|
|
||||||
for (const_iterator it = begin(); it != end(); ++it)
|
for (const_iterator it = begin(); it != end(); ++it)
|
||||||
if ((*it)->is_available_to(master))
|
if ((*it)->available_to(master))
|
||||||
return *it;
|
return *it;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -296,7 +296,7 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
|
|||||||
Threads.mutex.lock();
|
Threads.mutex.lock();
|
||||||
sp.mutex.lock();
|
sp.mutex.lock();
|
||||||
|
|
||||||
splitPointsSize++;
|
++splitPointsSize;
|
||||||
activeSplitPoint = &sp;
|
activeSplitPoint = &sp;
|
||||||
activePosition = NULL;
|
activePosition = NULL;
|
||||||
|
|
||||||
@@ -330,13 +330,13 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
|
|||||||
|
|
||||||
// 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 'searching' and decreasing splitPointsSize is
|
// finished. Note that setting 'searching' and decreasing splitPointsSize is
|
||||||
// done under lock protection to avoid a race with Thread::is_available_to().
|
// done under lock protection to avoid a race with Thread::available_to().
|
||||||
Threads.mutex.lock();
|
Threads.mutex.lock();
|
||||||
sp.mutex.lock();
|
sp.mutex.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
searching = true;
|
searching = true;
|
||||||
splitPointsSize--;
|
--splitPointsSize;
|
||||||
activeSplitPoint = sp.parentSplitPoint;
|
activeSplitPoint = sp.parentSplitPoint;
|
||||||
activePosition = &pos;
|
activePosition = &pos;
|
||||||
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
|
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ struct Thread : public ThreadBase {
|
|||||||
Thread();
|
Thread();
|
||||||
virtual void idle_loop();
|
virtual void idle_loop();
|
||||||
bool cutoff_occurred() const;
|
bool cutoff_occurred() const;
|
||||||
bool is_available_to(const Thread* master) const;
|
bool available_to(const Thread* master) const;
|
||||||
|
|
||||||
template <bool Fake>
|
template <bool Fake>
|
||||||
void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
|
void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
|
||||||
@@ -143,9 +143,10 @@ struct MainThread : public Thread {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct TimerThread : public ThreadBase {
|
struct TimerThread : public ThreadBase {
|
||||||
TimerThread() : msec(0) {}
|
TimerThread() : run(false) {}
|
||||||
virtual void idle_loop();
|
virtual void idle_loop();
|
||||||
int msec;
|
bool run;
|
||||||
|
static const int Resolution = 5; // msec between two check_time() calls
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
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 <cmath>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "search.h"
|
#include "search.h"
|
||||||
#include "timeman.h"
|
#include "timeman.h"
|
||||||
@@ -29,8 +29,8 @@ namespace {
|
|||||||
/// Constants
|
/// Constants
|
||||||
|
|
||||||
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
|
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
|
||||||
const float MaxRatio = 7.0f; // When in trouble, we can step over reserved time with this ratio
|
const double MaxRatio = 7.0; // When in trouble, we can step over reserved time with this ratio
|
||||||
const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio
|
const double StealRatio = 0.33; // However we must not steal time from remaining moves over this ratio
|
||||||
|
|
||||||
|
|
||||||
// MoveImportance[] is based on naive statistical analysis of "how many games are still undecided
|
// MoveImportance[] is based on naive statistical analysis of "how many games are still undecided
|
||||||
@@ -76,10 +76,9 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TimeManager::pv_instability(int curChanges, int prevChanges) {
|
void TimeManager::pv_instability(double bestMoveChanges) {
|
||||||
|
|
||||||
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
|
unstablePVExtraTime = int(bestMoveChanges * optimumSearchTime / 1.4);
|
||||||
+ prevChanges * (optimumSearchTime / 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -115,7 +114,7 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u
|
|||||||
|
|
||||||
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
|
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
|
||||||
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
|
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
|
||||||
for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); hypMTG++)
|
for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); ++hypMTG)
|
||||||
{
|
{
|
||||||
// Calculate thinking time for hypothetic "moves to go"-value
|
// Calculate thinking time for hypothetic "moves to go"-value
|
||||||
hypMyTime = limits.time[us]
|
hypMyTime = limits.time[us]
|
||||||
@@ -145,17 +144,17 @@ namespace {
|
|||||||
template<TimeType T>
|
template<TimeType T>
|
||||||
int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
|
int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
|
||||||
{
|
{
|
||||||
const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
|
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
|
||||||
const float TStealRatio = (T == OptimumTime ? 0 : StealRatio);
|
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
|
||||||
|
|
||||||
int thisMoveImportance = move_importance(currentPly) * slowMover / 100;
|
double thisMoveImportance = double(move_importance(currentPly) * slowMover) / 100;
|
||||||
int otherMovesImportance = 0;
|
int otherMovesImportance = 0;
|
||||||
|
|
||||||
for (int i = 1; i < movesToGo; i++)
|
for (int i = 1; i < movesToGo; ++i)
|
||||||
otherMovesImportance += move_importance(currentPly + 2 * i);
|
otherMovesImportance += move_importance(currentPly + 2 * i);
|
||||||
|
|
||||||
float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance);
|
double ratio1 = (TMaxRatio * thisMoveImportance) / (TMaxRatio * thisMoveImportance + otherMovesImportance);
|
||||||
float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance);
|
double ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / (thisMoveImportance + otherMovesImportance);
|
||||||
|
|
||||||
return int(floor(myTime * std::min(ratio1, ratio2)));
|
return int(floor(myTime * std::min(ratio1, ratio2)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
class TimeManager {
|
class TimeManager {
|
||||||
public:
|
public:
|
||||||
void init(const Search::LimitsType& limits, int currentPly, Color us);
|
void init(const Search::LimitsType& limits, int currentPly, Color us);
|
||||||
void pv_instability(int curChanges, int prevChanges);
|
void pv_instability(double bestMoveChanges);
|
||||||
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
|
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
|
||||||
int maximum_time() const { return maximumSearchTime; }
|
int maximum_time() const { return maximumSearchTime; }
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ const TTEntry* TranspositionTable::probe(const Key key) const {
|
|||||||
const TTEntry* tte = first_entry(key);
|
const TTEntry* tte = first_entry(key);
|
||||||
uint32_t key32 = key >> 32;
|
uint32_t key32 = key >> 32;
|
||||||
|
|
||||||
for (unsigned i = 0; i < ClusterSize; i++, tte++)
|
for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
|
||||||
if (tte->key() == key32)
|
if (tte->key() == key32)
|
||||||
return tte;
|
return tte;
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ const TTEntry* TranspositionTable::probe(const Key key) const {
|
|||||||
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
|
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
|
||||||
/// a previous search, or if the depth of t1 is bigger than the depth of t2.
|
/// a previous search, or if the depth of t1 is bigger than the depth of t2.
|
||||||
|
|
||||||
void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV, Value evalM) {
|
void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) {
|
||||||
|
|
||||||
int c1, c2, c3;
|
int c1, c2, c3;
|
||||||
TTEntry *tte, *replace;
|
TTEntry *tte, *replace;
|
||||||
@@ -97,7 +97,7 @@ void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m,
|
|||||||
|
|
||||||
tte = replace = first_entry(key);
|
tte = replace = first_entry(key);
|
||||||
|
|
||||||
for (unsigned i = 0; i < ClusterSize; i++, tte++)
|
for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
|
||||||
{
|
{
|
||||||
if (!tte->key() || tte->key() == key32) // Empty or overwrite old
|
if (!tte->key() || tte->key() == key32) // Empty or overwrite old
|
||||||
{
|
{
|
||||||
@@ -117,5 +117,5 @@ void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m,
|
|||||||
replace = tte;
|
replace = tte;
|
||||||
}
|
}
|
||||||
|
|
||||||
replace->save(key32, v, b, d, m, generation, statV, evalM);
|
replace->save(key32, v, b, d, m, generation, statV);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
struct TTEntry {
|
struct TTEntry {
|
||||||
|
|
||||||
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) {
|
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev) {
|
||||||
|
|
||||||
key32 = (uint32_t)k;
|
key32 = (uint32_t)k;
|
||||||
move16 = (uint16_t)m;
|
move16 = (uint16_t)m;
|
||||||
@@ -45,7 +45,6 @@ struct TTEntry {
|
|||||||
value16 = (int16_t)v;
|
value16 = (int16_t)v;
|
||||||
depth16 = (int16_t)d;
|
depth16 = (int16_t)d;
|
||||||
evalValue = (int16_t)ev;
|
evalValue = (int16_t)ev;
|
||||||
evalMargin = (int16_t)em;
|
|
||||||
}
|
}
|
||||||
void set_generation(uint8_t g) { generation8 = g; }
|
void set_generation(uint8_t g) { generation8 = g; }
|
||||||
|
|
||||||
@@ -56,13 +55,12 @@ struct TTEntry {
|
|||||||
Bound bound() const { return (Bound)bound8; }
|
Bound bound() const { return (Bound)bound8; }
|
||||||
int generation() const { return (int)generation8; }
|
int generation() const { return (int)generation8; }
|
||||||
Value eval_value() const { return (Value)evalValue; }
|
Value eval_value() const { return (Value)evalValue; }
|
||||||
Value eval_margin() const { return (Value)evalMargin; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t key32;
|
uint32_t key32;
|
||||||
uint16_t move16;
|
uint16_t move16;
|
||||||
uint8_t bound8, generation8;
|
uint8_t bound8, generation8;
|
||||||
int16_t value16, depth16, evalValue, evalMargin;
|
int16_t value16, depth16, evalValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -78,14 +76,14 @@ class TranspositionTable {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
~TranspositionTable() { free(mem); }
|
~TranspositionTable() { free(mem); }
|
||||||
void new_search() { generation++; }
|
void new_search() { ++generation; }
|
||||||
|
|
||||||
const TTEntry* probe(const Key key) const;
|
const TTEntry* probe(const Key key) const;
|
||||||
TTEntry* first_entry(const Key key) const;
|
TTEntry* first_entry(const Key key) const;
|
||||||
void refresh(const TTEntry* tte) const;
|
void refresh(const TTEntry* tte) const;
|
||||||
void set_size(size_t mbSize);
|
void set_size(size_t mbSize);
|
||||||
void clear();
|
void clear();
|
||||||
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD);
|
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t hashMask;
|
uint32_t hashMask;
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ const bool Is64Bit = false;
|
|||||||
typedef uint64_t Key;
|
typedef uint64_t Key;
|
||||||
typedef uint64_t Bitboard;
|
typedef uint64_t Bitboard;
|
||||||
|
|
||||||
const int MAX_MOVES = 192;
|
const int MAX_MOVES = 256;
|
||||||
const int MAX_PLY = 100;
|
const int MAX_PLY = 100;
|
||||||
const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
|
const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
|
||||||
|
|
||||||
@@ -194,9 +194,9 @@ enum Depth {
|
|||||||
ONE_PLY = 2,
|
ONE_PLY = 2,
|
||||||
|
|
||||||
DEPTH_ZERO = 0 * ONE_PLY,
|
DEPTH_ZERO = 0 * ONE_PLY,
|
||||||
DEPTH_QS_CHECKS = -1 * ONE_PLY,
|
DEPTH_QS_CHECKS = 0 * ONE_PLY,
|
||||||
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
|
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
|
||||||
DEPTH_QS_RECAPTURES = -7 * ONE_PLY,
|
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
|
||||||
|
|
||||||
DEPTH_NONE = -127 * ONE_PLY
|
DEPTH_NONE = -127 * ONE_PLY
|
||||||
};
|
};
|
||||||
@@ -274,15 +274,15 @@ inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \
|
|||||||
inline T operator*(int i, const T d) { return T(i * int(d)); } \
|
inline T operator*(int i, const T d) { return T(i * int(d)); } \
|
||||||
inline T operator*(const T d, int i) { return T(int(d) * i); } \
|
inline T operator*(const T d, int i) { return T(int(d) * i); } \
|
||||||
inline T operator-(const T d) { return T(-int(d)); } \
|
inline T operator-(const T d) { return T(-int(d)); } \
|
||||||
inline T& operator+=(T& d1, const T d2) { d1 = d1 + d2; return d1; } \
|
inline T& operator+=(T& d1, const T d2) { return d1 = d1 + d2; } \
|
||||||
inline T& operator-=(T& d1, const T d2) { d1 = d1 - d2; return d1; } \
|
inline T& operator-=(T& d1, const T d2) { return d1 = d1 - d2; } \
|
||||||
inline T& operator*=(T& d, int i) { d = T(int(d) * i); return d; }
|
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); }
|
||||||
|
|
||||||
#define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \
|
#define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \
|
||||||
inline T operator++(T& d, int) { d = T(int(d) + 1); return d; } \
|
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
|
||||||
inline T operator--(T& d, int) { d = T(int(d) - 1); return d; } \
|
inline T& operator--(T& d) { return d = T(int(d) - 1); } \
|
||||||
inline T operator/(const T d, int i) { return T(int(d) / i); } \
|
inline T operator/(const T d, int i) { return T(int(d) / i); } \
|
||||||
inline T& operator/=(T& d, int i) { d = T(int(d) / i); return d; }
|
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
|
||||||
|
|
||||||
ENABLE_OPERATORS_ON(Value)
|
ENABLE_OPERATORS_ON(Value)
|
||||||
ENABLE_OPERATORS_ON(PieceType)
|
ENABLE_OPERATORS_ON(PieceType)
|
||||||
@@ -323,11 +323,11 @@ inline bool operator<(const ExtMove& f, const ExtMove& s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline Color operator~(Color c) {
|
inline Color operator~(Color c) {
|
||||||
return Color(c ^ 1);
|
return Color(c ^ BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Square operator~(Square s) {
|
inline Square operator~(Square s) {
|
||||||
return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8
|
return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Square operator|(File f, Rank r) {
|
inline Square operator|(File f, Rank r) {
|
||||||
@@ -371,10 +371,6 @@ inline Rank rank_of(Square s) {
|
|||||||
return Rank(s >> 3);
|
return Rank(s >> 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Square mirror(Square s) {
|
|
||||||
return Square(s ^ 7); // Horizontal flip SQ_A1 -> SQ_H1
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Square relative_square(Color c, Square s) {
|
inline Square relative_square(Color c, Square s) {
|
||||||
return Square(s ^ (c * 56));
|
return Square(s ^ (c * 56));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,10 +80,10 @@ void init(OptionsMap& o) {
|
|||||||
o["MultiPV"] = Option(1, 1, 500);
|
o["MultiPV"] = Option(1, 1, 500);
|
||||||
o["Skill Level"] = Option(20, 0, 20);
|
o["Skill Level"] = Option(20, 0, 20);
|
||||||
o["Emergency Move Horizon"] = Option(40, 0, 50);
|
o["Emergency Move Horizon"] = Option(40, 0, 50);
|
||||||
o["Emergency Base Time"] = Option(200, 0, 30000);
|
o["Emergency Base Time"] = Option(60, 0, 30000);
|
||||||
o["Emergency Move Time"] = Option(70, 0, 5000);
|
o["Emergency Move Time"] = Option(30, 0, 5000);
|
||||||
o["Minimum Thinking Time"] = Option(20, 0, 5000);
|
o["Minimum Thinking Time"] = Option(20, 0, 5000);
|
||||||
o["Slow Mover"] = Option(100, 10, 1000);
|
o["Slow Mover"] = Option(70, 10, 1000);
|
||||||
o["UCI_Chess960"] = Option(false);
|
o["UCI_Chess960"] = Option(false);
|
||||||
o["UCI_AnalyseMode"] = Option(false, on_eval);
|
o["UCI_AnalyseMode"] = Option(false, on_eval);
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ void init(OptionsMap& o) {
|
|||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
||||||
|
|
||||||
for (size_t idx = 0; idx < om.size(); idx++)
|
for (size_t idx = 0; idx < om.size(); ++idx)
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user