DroidFish: Updated stockfish engine to version DD.

This commit is contained in:
Peter Osterlund
2013-11-30 19:12:34 +00:00
parent ef2ea196b4
commit 43e92323e4
31 changed files with 940 additions and 1245 deletions

View File

@@ -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());

View File

@@ -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

View File

@@ -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])];

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}; };

View File

@@ -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";

View File

@@ -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);
} }

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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();
} }

View File

@@ -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)));

View File

@@ -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);
} }

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();
} }

View File

@@ -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)

View File

@@ -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;
}; };

View File

@@ -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);

View File

@@ -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
}; };

View File

@@ -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)));
} }

View File

@@ -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; }

View File

@@ -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);
} }

View File

@@ -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;

View File

@@ -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));
} }

View File

@@ -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)
{ {