mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2024-11-23 19:34:08 +01:00
DroidFish: Updated stockfish engine to version DD.
This commit is contained in:
parent
ef2ea196b4
commit
43e92323e4
|
@ -47,7 +47,21 @@ static const char* Defaults[] = {
|
|||
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
|
||||
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
|
||||
"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
|
||||
string ttSize = (is >> token) ? token : "32";
|
||||
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 limitType = (is >> token) ? token : "depth";
|
||||
|
||||
|
@ -89,7 +103,7 @@ void benchmark(const Position& current, istream& is) {
|
|||
limits.depth = atoi(limit.c_str());
|
||||
|
||||
if (fenFile == "default")
|
||||
fens.assign(Defaults, Defaults + 16);
|
||||
fens.assign(Defaults, Defaults + 30);
|
||||
|
||||
else if (fenFile == "current")
|
||||
fens.push_back(current.fen());
|
||||
|
@ -116,7 +130,7 @@ void benchmark(const Position& current, istream& is) {
|
|||
Search::StateStackPtr st;
|
||||
Time::point elapsed = Time::now();
|
||||
|
||||
for (size_t i = 0; i < fens.size(); i++)
|
||||
for (size_t i = 0; i < fens.size(); ++i)
|
||||
{
|
||||
Position pos(fens[i], Options["UCI_Chess960"], Threads.main());
|
||||
|
||||
|
|
|
@ -55,8 +55,8 @@ namespace {
|
|||
|
||||
struct KPKPosition {
|
||||
|
||||
operator Result() const { return res; }
|
||||
Result classify_leaf(unsigned idx);
|
||||
KPKPosition(unsigned idx);
|
||||
operator Result() const { return result; }
|
||||
Result classify(const std::vector<KPKPosition>& db)
|
||||
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
|
||||
|
||||
|
@ -65,7 +65,7 @@ namespace {
|
|||
|
||||
Color us;
|
||||
Square bksq, wksq, psq;
|
||||
Result res;
|
||||
Result result;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -83,21 +83,21 @@ bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
|
|||
void Bitbases::init_kpk() {
|
||||
|
||||
unsigned idx, repeat = 1;
|
||||
std::vector<KPKPosition> db(IndexMax);
|
||||
std::vector<KPKPosition> db;
|
||||
db.reserve(IndexMax);
|
||||
|
||||
// Initialize db with known win / draw positions
|
||||
for (idx = 0; idx < IndexMax; idx++)
|
||||
db[idx].classify_leaf(idx);
|
||||
for (idx = 0; idx < IndexMax; ++idx)
|
||||
db.push_back(KPKPosition(idx));
|
||||
|
||||
// Iterate through the positions until no more of the unknown positions can be
|
||||
// changed to either wins or draws (15 cycles needed).
|
||||
while (repeat)
|
||||
for (repeat = idx = 0; idx < IndexMax; idx++)
|
||||
if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN)
|
||||
repeat = 1;
|
||||
for (repeat = idx = 0; idx < IndexMax; ++idx)
|
||||
repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
|
||||
|
||||
// Map 32 results into one KPKBitbase[] entry
|
||||
for (idx = 0; idx < IndexMax; idx++)
|
||||
for (idx = 0; idx < IndexMax; ++idx)
|
||||
if (db[idx] == WIN)
|
||||
KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
|
||||
}
|
||||
|
@ -105,34 +105,32 @@ void Bitbases::init_kpk() {
|
|||
|
||||
namespace {
|
||||
|
||||
Result KPKPosition::classify_leaf(unsigned idx) {
|
||||
KPKPosition::KPKPosition(unsigned idx) {
|
||||
|
||||
wksq = Square((idx >> 0) & 0x3F);
|
||||
bksq = Square((idx >> 6) & 0x3F);
|
||||
us = Color ((idx >> 12) & 0x01);
|
||||
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
|
||||
if ( wksq == psq || wksq == bksq || bksq == psq
|
||||
|| (StepAttacksBB[KING][wksq] & bksq)
|
||||
if ( square_distance(wksq, bksq) <= 1 || wksq == psq || bksq == psq
|
||||
|| (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
|
||||
if ( rank_of(psq) == RANK_7
|
||||
&& wksq != psq + DELTA_N
|
||||
&& ( square_distance(bksq, psq + DELTA_N) > 1
|
||||
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
|
||||
return res = WIN;
|
||||
result = WIN;
|
||||
}
|
||||
// Immediate draw if is stalemate or king captures undefended pawn
|
||||
else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq]))
|
||||
|| (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq]))
|
||||
return res = DRAW;
|
||||
|
||||
return res = UNKNOWN;
|
||||
result = DRAW;
|
||||
}
|
||||
|
||||
template<Color Us>
|
||||
|
@ -162,14 +160,14 @@ namespace {
|
|||
Square s = psq + DELTA_N;
|
||||
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
|
||||
}
|
||||
|
||||
if (Us == WHITE)
|
||||
return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW;
|
||||
return result = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW;
|
||||
else
|
||||
return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN;
|
||||
return result = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -45,6 +45,7 @@ Bitboard AdjacentFilesBB[FILE_NB];
|
|||
Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
||||
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
|
||||
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||
Bitboard DistanceRingsBB[SQUARE_NB][8];
|
||||
Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
|
||||
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
|
||||
|
@ -132,11 +133,11 @@ void Bitboards::print(Bitboard b) {
|
|||
|
||||
sync_cout;
|
||||
|
||||
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
|
||||
for (Rank rank = RANK_8; rank >= RANK_1; --rank)
|
||||
{
|
||||
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
|
||||
|
||||
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 << "|\n";
|
||||
|
@ -150,41 +151,41 @@ void Bitboards::print(Bitboard b) {
|
|||
|
||||
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))
|
||||
MS1BTable[k++] = i;
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
for (int i = 0; i < 64; ++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;
|
||||
|
||||
FileBB[FILE_A] = FileABB;
|
||||
RankBB[RANK_1] = Rank1BB;
|
||||
|
||||
for (int i = 1; i < 8; i++)
|
||||
for (int i = 1; i < 8; ++i)
|
||||
{
|
||||
FileBB[i] = FileBB[i - 1] << 1;
|
||||
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);
|
||||
|
||||
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]);
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++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)];
|
||||
PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s];
|
||||
}
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
{
|
||||
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(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 },
|
||||
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||
for (int k = 0; steps[pt][k]; k++)
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
for (int k = 0; steps[pt][k]; ++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(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[ ROOK][s] = attacks_bb< ROOK>(s, 0);
|
||||
}
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
if (PseudoAttacks[QUEEN][s1] & s2)
|
||||
{
|
||||
Square delta = (s2 - s1) / square_distance(s1, s2);
|
||||
|
||||
for (Square s = s1 + delta; s != s2; s += delta)
|
||||
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;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (Square s = sq + deltas[i];
|
||||
is_ok(s) && square_distance(s, s - deltas[i]) == 1;
|
||||
s += deltas[i])
|
||||
|
@ -281,7 +285,7 @@ namespace {
|
|||
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
|
||||
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
|
||||
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.
|
||||
// Note that we build up the database for square 's' as a side
|
||||
// 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])];
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ extern Bitboard AdjacentFilesBB[FILE_NB];
|
|||
extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
|
||||
extern Bitboard StepAttacksBB[PIECE_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 ForwardBB[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.
|
||||
|
||||
inline bool squares_aligned(Square s1, Square s2, Square s3) {
|
||||
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3])
|
||||
& ( SquareBB[s1] | SquareBB[s2] | SquareBB[s3]);
|
||||
inline bool aligned(Square s1, Square s2, Square s3) {
|
||||
return LineBB[s1][s2] & s3;
|
||||
}
|
||||
|
||||
|
||||
|
@ -319,4 +319,10 @@ extern Square pop_lsb(Bitboard* b);
|
|||
|
||||
#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
|
||||
|
|
|
@ -361,7 +361,7 @@ PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
|
|||
template<typename T> PolyglotBook& PolyglotBook::operator>>(T& n) {
|
||||
|
||||
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());
|
||||
|
||||
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
|
||||
// high score it has higher probability to be choosen than a move
|
||||
// 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))
|
||||
move = Move(e.move);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -33,9 +33,6 @@ enum EndgameType {
|
|||
|
||||
// Evaluation functions
|
||||
|
||||
KK, // K vs K
|
||||
KBK, // KB vs K
|
||||
KNK, // KN vs K
|
||||
KNNK, // KNN vs K
|
||||
KXK, // Generic "mate lone king" eval
|
||||
KBNK, // KBN vs K
|
||||
|
@ -55,6 +52,7 @@ enum EndgameType {
|
|||
KBPsK, // KB+pawns vs K
|
||||
KQKRPs, // KQ vs KR+pawns
|
||||
KRPKR, // KRP vs KR
|
||||
KRPKB, // KRP vs KB
|
||||
KRPPKRP, // KRPP vs KRP
|
||||
KPsK, // King and pawns vs king
|
||||
KBPKB, // KBP vs KB
|
||||
|
@ -88,12 +86,12 @@ struct EndgameBase {
|
|||
template<EndgameType E, typename T = typename eg_fun<(E > SCALE_FUNS)>::type>
|
||||
struct Endgame : public EndgameBase<T> {
|
||||
|
||||
explicit Endgame(Color c) : strongerSide(c), weakerSide(~c) {}
|
||||
Color color() const { return strongerSide; }
|
||||
explicit Endgame(Color c) : strongSide(c), weakSide(~c) {}
|
||||
Color color() const { return strongSide; }
|
||||
T operator()(const Position&) const;
|
||||
|
||||
private:
|
||||
Color strongerSide, weakerSide;
|
||||
const Color strongSide, weakSide;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
namespace {
|
||||
|
||||
enum ExtendedPieceType { // Used for tracing
|
||||
PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, UNSTOPPABLE, SPACE, TOTAL
|
||||
PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL
|
||||
};
|
||||
|
||||
namespace Tracing {
|
||||
|
@ -83,6 +83,8 @@ namespace {
|
|||
// king is on g8 and there's a white knight on g5, this knight adds
|
||||
// 2 to kingAdjacentZoneAttacksCount[BLACK].
|
||||
int kingAdjacentZoneAttacksCount[COLOR_NB];
|
||||
|
||||
Bitboard pinnedPieces[COLOR_NB];
|
||||
};
|
||||
|
||||
// Evaluation grain size, must be a power of 2
|
||||
|
@ -114,16 +116,15 @@ namespace {
|
|||
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( 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( 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( 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( 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
|
||||
|
@ -173,9 +174,11 @@ namespace {
|
|||
const Score RookOpenFile = make_score(43, 21);
|
||||
const Score RookSemiopenFile = make_score(19, 10);
|
||||
const Score BishopPawns = make_score( 8, 12);
|
||||
const Score KnightPawns = make_score( 8, 4);
|
||||
const Score MinorBehindPawn = make_score(16, 0);
|
||||
const Score UndefendedMinor = make_score(25, 10);
|
||||
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
|
||||
// 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
|
||||
// friendly minor pieces.
|
||||
const Bitboard SpaceMask[] = {
|
||||
(1ULL << SQ_C2) | (1ULL << SQ_D2) | (1ULL << SQ_E2) | (1ULL << SQ_F2) |
|
||||
(1ULL << SQ_C3) | (1ULL << SQ_D3) | (1ULL << SQ_E3) | (1ULL << SQ_F3) |
|
||||
(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)
|
||||
(FileCBB | FileDBB | FileEBB | FileFBB) & (Rank2BB | Rank3BB | Rank4BB),
|
||||
(FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB)
|
||||
};
|
||||
|
||||
// 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 };
|
||||
|
||||
// Bonuses for enemy's safe checks
|
||||
const int QueenContactCheck = 6;
|
||||
const int RookContactCheck = 4;
|
||||
const int QueenCheck = 3;
|
||||
const int RookCheck = 2;
|
||||
const int BishopCheck = 1;
|
||||
const int KnightCheck = 1;
|
||||
const int QueenContactCheck = 24;
|
||||
const int RookContactCheck = 16;
|
||||
const int QueenCheck = 12;
|
||||
const int RookCheck = 8;
|
||||
const int BishopCheck = 2;
|
||||
const int KnightCheck = 3;
|
||||
|
||||
// KingExposed[Square] contains penalties based on the position of the
|
||||
// defending king, indexed by king's square (from white's point of view).
|
||||
|
@ -230,16 +229,16 @@ namespace {
|
|||
|
||||
// Function prototypes
|
||||
template<bool Trace>
|
||||
Value do_evaluate(const Position& pos, Value& margin);
|
||||
Value do_evaluate(const Position& pos);
|
||||
|
||||
template<Color Us>
|
||||
void init_eval_info(const Position& pos, EvalInfo& ei);
|
||||
|
||||
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>
|
||||
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>
|
||||
Score evaluate_threats(const Position& pos, const EvalInfo& ei);
|
||||
|
@ -250,7 +249,7 @@ namespace {
|
|||
template<Color Us>
|
||||
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);
|
||||
Score apply_weight(Score v, Score w);
|
||||
|
@ -265,8 +264,8 @@ namespace Eval {
|
|||
/// values, an endgame score and a middle game score, and interpolates
|
||||
/// between them based on the remaining material.
|
||||
|
||||
Value evaluate(const Position& pos, Value& margin) {
|
||||
return do_evaluate<false>(pos, margin);
|
||||
Value evaluate(const Position& pos) {
|
||||
return do_evaluate<false>(pos);
|
||||
}
|
||||
|
||||
|
||||
|
@ -293,7 +292,7 @@ namespace Eval {
|
|||
const int MaxSlope = 30;
|
||||
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));
|
||||
|
||||
|
@ -308,19 +307,14 @@ namespace Eval {
|
|||
namespace {
|
||||
|
||||
template<bool Trace>
|
||||
Value do_evaluate(const Position& pos, Value& margin) {
|
||||
Value do_evaluate(const Position& pos) {
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
EvalInfo ei;
|
||||
Value margins[COLOR_NB];
|
||||
Score score, mobilityWhite, mobilityBlack;
|
||||
Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO };
|
||||
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
|
||||
// in the position object (material + piece square tables) and adding
|
||||
// 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
|
||||
// configuration, call it and return.
|
||||
if (ei.mi->specialized_eval_exists())
|
||||
{
|
||||
margin = VALUE_ZERO;
|
||||
return ei.mi->evaluate(pos);
|
||||
}
|
||||
|
||||
// Probe the pawn hash table
|
||||
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);
|
||||
|
||||
// Evaluate pieces and mobility
|
||||
score += evaluate_pieces_of_color<WHITE, Trace>(pos, ei, mobilityWhite)
|
||||
- evaluate_pieces_of_color<BLACK, Trace>(pos, ei, mobilityBlack);
|
||||
score += evaluate_pieces_of_color<WHITE, Trace>(pos, ei, mobility)
|
||||
- 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
|
||||
// information when computing the king safety evaluation.
|
||||
score += evaluate_king<WHITE, Trace>(pos, ei, margins)
|
||||
- evaluate_king<BLACK, Trace>(pos, ei, margins);
|
||||
score += evaluate_king<WHITE, Trace>(pos, ei)
|
||||
- evaluate_king<BLACK, Trace>(pos, ei);
|
||||
|
||||
// Evaluate tactical threats, we need full attack information including king
|
||||
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)
|
||||
- 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))
|
||||
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.
|
||||
if (ei.mi->space_weight())
|
||||
|
@ -401,7 +393,6 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||
sf = ScaleFactor(50);
|
||||
}
|
||||
|
||||
margin = margins[pos.side_to_move()];
|
||||
Value v = interpolate(score, ei.mi->game_phase(), sf);
|
||||
|
||||
// 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(IMBALANCE, ei.mi->material_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 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(TOTAL, score);
|
||||
Tracing::stream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
|
||||
<< ", Black: " << to_cp(margins[BLACK])
|
||||
<< "\nScaling: " << std::noshowpos
|
||||
Tracing::stream << "\nScaling: " << std::noshowpos
|
||||
<< 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 * 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 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));
|
||||
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];
|
||||
ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0;
|
||||
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
|
||||
} else
|
||||
}
|
||||
else
|
||||
ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0;
|
||||
}
|
||||
|
||||
|
@ -474,14 +465,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||
else
|
||||
bonus += bonus / 2;
|
||||
}
|
||||
|
||||
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>
|
||||
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;
|
||||
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))
|
||||
: pos.attacks_from<Piece>(s);
|
||||
|
||||
if (ei.pinnedPieces[Us] & s)
|
||||
b &= LineBB[pos.king_square(Us)][s];
|
||||
|
||||
ei.attackedBy[Us][Piece] |= b;
|
||||
|
||||
if (b & ei.kingRing[Them])
|
||||
{
|
||||
ei.kingAttackersCount[Us]++;
|
||||
ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
|
||||
Bitboard bb = (b & ei.attackedBy[Them][KING]);
|
||||
Bitboard bb = b & ei.attackedBy[Them][KING];
|
||||
if (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)
|
||||
: 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
|
||||
// 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)
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
|
||||
if (pos.piece_on(s + d) == P)
|
||||
score -= !pos.is_empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
|
||||
: pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2
|
||||
: TrappedBishopA1H1;
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
|
||||
: pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2
|
||||
: TrappedBishopA1H1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,82 +599,37 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||
}
|
||||
|
||||
|
||||
// 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_pieces_of_color<>() assigns bonuses and penalties to all the
|
||||
// evaluate_pieces_of_color() assigns bonuses and penalties to all the
|
||||
// pieces of a given color.
|
||||
|
||||
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);
|
||||
|
||||
Score score = mobility = SCORE_ZERO;
|
||||
|
||||
// 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));
|
||||
|
||||
score += evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea);
|
||||
score += evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea);
|
||||
score += evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea);
|
||||
score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
|
||||
Score score = evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea)
|
||||
+ evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea)
|
||||
+ evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea)
|
||||
+ evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
|
||||
|
||||
// Sum up all attacked squares
|
||||
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
||||
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
||||
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
|
||||
// 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][BISHOP] | ei.attackedBy[Us][ROOK]
|
||||
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
|
||||
if (Trace)
|
||||
Tracing::scores[Us][MOBILITY] = apply_weight(mobility, Weights[Mobility]);
|
||||
Tracing::scores[Us][MOBILITY] = apply_weight(mobility[Us], Weights[Mobility]);
|
||||
|
||||
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>
|
||||
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);
|
||||
|
||||
|
@ -686,15 +640,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||
// King shelter and enemy pawns storm
|
||||
Score score = ei.pi->king_safety<Us>(pos, ksq);
|
||||
|
||||
// King safety. This is quite complicated, and is almost certainly far
|
||||
// from optimally tuned.
|
||||
// Main king safety evaluation
|
||||
if ( ei.kingAttackersCount[Them] >= 2
|
||||
&& ei.kingAdjacentZoneAttacksCount[Them])
|
||||
{
|
||||
// Find the attacked squares around the king which has no defenders
|
||||
// apart from the king itself
|
||||
undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING];
|
||||
undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
||||
undefended = ei.attackedBy[Them][ALL_PIECES]
|
||||
& ei.attackedBy[Us][KING]
|
||||
& ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
|
||||
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
|
||||
| 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
|
||||
// attacked and undefended squares around our king, the square of the
|
||||
// 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))
|
||||
+ KingExposed[relative_square(Us, ksq)]
|
||||
- mg_value(score) / 32;
|
||||
|
@ -770,12 +724,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||
attackUnits = std::min(99, std::max(0, attackUnits));
|
||||
|
||||
// Finally, extract the king danger score from the KingDanger[]
|
||||
// array and subtract the score from evaluation. Set also margins[]
|
||||
// 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.
|
||||
// array and subtract the score from evaluation.
|
||||
score -= KingDanger[Us == Search::RootColor][attackUnits];
|
||||
margins[Us] += mg_value(KingDanger[Us == Search::RootColor][attackUnits]);
|
||||
}
|
||||
|
||||
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>
|
||||
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);
|
||||
|
||||
assert(pos.pawn_is_passed(Us, s));
|
||||
assert(pos.pawn_passed(Us, s));
|
||||
|
||||
int r = int(relative_rank(Us, s) - RANK_2);
|
||||
int rr = r * (r - 1);
|
||||
|
@ -815,18 +808,17 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||
Square blockSq = s + pawn_push(Us);
|
||||
|
||||
// Adjust bonus based on kings proximity
|
||||
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr);
|
||||
ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 2 * rr);
|
||||
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * 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 (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);
|
||||
|
||||
// If the pawn is free to advance, increase bonus
|
||||
if (pos.is_empty(blockSq))
|
||||
if (pos.empty(blockSq))
|
||||
{
|
||||
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,
|
||||
// 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
|
||||
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
|
||||
// least block square is not attacked, otherwise smallest bonus.
|
||||
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)
|
||||
ebonus += ebonus / 4;
|
||||
|
||||
else if (pos.pieces(Them, ROOK, QUEEN))
|
||||
ebonus -= ebonus / 4;
|
||||
}
|
||||
|
||||
if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them))
|
||||
ebonus += ebonus / 4;
|
||||
|
||||
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
|
||||
// conservative and returns a winning score only when we are very sure that the pawn is winning.
|
||||
// evaluate_unstoppable_pawns() scores the most advanced among the passed and
|
||||
// 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;
|
||||
Square s, blockSq, queeningSquare;
|
||||
Color c, winnerSide, loserSide;
|
||||
bool pathDefended, opposed;
|
||||
int pliesToGo, movesToGo, oppMovesToGo, sacptg, blockersCount, minKingDist, kingptg, d;
|
||||
int pliesToQueen[] = { 256, 256 };
|
||||
Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us);
|
||||
|
||||
// Step 1. Hunt for unstoppable passed pawns. If we find at least one,
|
||||
// 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)
|
||||
if (!b || pos.non_pawn_material(~us))
|
||||
return SCORE_ZERO;
|
||||
|
||||
winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK);
|
||||
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;
|
||||
return Unstoppable * int(relative_rank(us, frontmost_sq(us, b)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1089,9 +946,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||
assert(eg_value(v) > -VALUE_INFINITE && eg_value(v) < VALUE_INFINITE);
|
||||
assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME);
|
||||
|
||||
int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
|
||||
int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128;
|
||||
return Value((result / GrainSize) * GrainSize); // Sign independent
|
||||
int e = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
|
||||
int r = (mg_value(v) * int(ph) + e * int(PHASE_MIDGAME - ph)) / PHASE_MIDGAME;
|
||||
return Value((r / GrainSize) * GrainSize); // Sign independent
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
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 << " | --- --- | --- --- | "
|
||||
<< std::setw(6) << to_cp(mg_value(wScore)) << " "
|
||||
<< 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);
|
||||
std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score));
|
||||
|
||||
Value margin;
|
||||
do_evaluate<true>(pos, margin);
|
||||
do_evaluate<true>(pos);
|
||||
|
||||
std::string totals = stream.str();
|
||||
stream.str("");
|
||||
|
@ -1173,7 +1029,6 @@ Value do_evaluate(const Position& pos, Value& margin) {
|
|||
row("King safety", KING);
|
||||
row("Threats", THREAT);
|
||||
row("Passed pawns", PASSED);
|
||||
row("Unstoppable pawns", UNSTOPPABLE);
|
||||
row("Space", SPACE);
|
||||
|
||||
stream << "---------------------+-------------+-------------+---------------\n";
|
||||
|
|
|
@ -27,7 +27,7 @@ class Position;
|
|||
namespace Eval {
|
||||
|
||||
extern void init();
|
||||
extern Value evaluate(const Position& pos, Value& margin);
|
||||
extern Value evaluate(const Position& pos);
|
||||
extern std::string trace(const Position& pos);
|
||||
|
||||
}
|
||||
|
|
|
@ -37,13 +37,14 @@ int main(int argc, char* argv[]) {
|
|||
Position::init();
|
||||
Bitbases::init_kpk();
|
||||
Search::init();
|
||||
Pawns::init();
|
||||
Eval::init();
|
||||
Threads.init();
|
||||
TT.set_size(Options["Hash"]);
|
||||
|
||||
std::string args;
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
for (int i = 1; i < argc; ++i)
|
||||
args += std::string(argv[i]) + " ";
|
||||
|
||||
UCI::loop(args);
|
||||
|
|
|
@ -35,31 +35,29 @@ namespace {
|
|||
const int NoPawnsSF[4] = { 6, 12, 32 };
|
||||
|
||||
// Polynomial material balance parameters
|
||||
const Value RedundantQueen = Value(320);
|
||||
const Value RedundantRook = Value(554);
|
||||
|
||||
// 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] = {
|
||||
// pair pawn knight bishop rook queen
|
||||
{ 7 }, // Bishop pair
|
||||
{ 0 }, // Bishop pair
|
||||
{ 39, 2 }, // Pawn
|
||||
{ 35, 271, -4 }, // Knight
|
||||
{ 7, 105, 4, 7 }, // Bishop
|
||||
{ -27, -2, 46, 100, 56 }, // Rook
|
||||
{ 58, 29, 83, 148, -3, -25 } // Queen
|
||||
{ 0, 105, 4, 0 }, // Bishop
|
||||
{ -27, -2, 46, 100, -141 }, // Rook
|
||||
{ 58, 29, 83, 148, -163, 0 } // Queen
|
||||
};
|
||||
|
||||
const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = {
|
||||
// THEIR PIECES
|
||||
// pair pawn knight bishop rook queen
|
||||
{ 41 }, // Bishop pair
|
||||
{ 37, 41 }, // Pawn
|
||||
{ 10, 62, 41 }, // Knight OUR PIECES
|
||||
{ 57, 64, 39, 41 }, // Bishop
|
||||
{ 50, 40, 23, -22, 41 }, // Rook
|
||||
{ 106, 101, 3, 151, 171, 41 } // Queen
|
||||
{ 0 }, // Bishop pair
|
||||
{ 37, 0 }, // Pawn
|
||||
{ 10, 62, 0 }, // Knight OUR PIECES
|
||||
{ 57, 64, 39, 0 }, // Bishop
|
||||
{ 50, 40, 23, -22, 0 }, // Rook
|
||||
{ 106, 101, 3, 151, 171, 0 } // Queen
|
||||
};
|
||||
|
||||
// Endgame evaluation and scaling functions accessed direcly and not through
|
||||
|
@ -106,14 +104,8 @@ namespace {
|
|||
int pt1, pt2, pc, v;
|
||||
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
|
||||
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
|
||||
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
|
||||
{
|
||||
pc = pieceCount[Us][pt1];
|
||||
if (!pc)
|
||||
|
@ -121,7 +113,7 @@ namespace {
|
|||
|
||||
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]
|
||||
+ 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)
|
||||
{
|
||||
e->factor[WHITE] = (uint8_t)
|
||||
|
|
|
@ -28,7 +28,7 @@ using namespace std;
|
|||
|
||||
/// Version number. If Version is left empty, then compile date, in the
|
||||
/// 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
|
||||
|
@ -63,9 +63,9 @@ const string engine_info(bool to_uci) {
|
|||
|
||||
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_mean_of(int v) { means[0]++; means[1] += v; }
|
||||
void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
|
||||
|
||||
void dbg_print() {
|
||||
|
||||
|
@ -75,7 +75,7 @@ void dbg_print() {
|
|||
|
||||
if (means[0])
|
||||
cerr << "Total " << means[0] << " Mean "
|
||||
<< (float)means[1] / means[0] << endl;
|
||||
<< (double)means[1] / means[0] << endl;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -56,13 +56,13 @@ namespace {
|
|||
// Because we generate only legal castling moves we need to verify that
|
||||
// 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.
|
||||
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;
|
||||
|
||||
(mlist++)->move = make<CASTLE>(kfrom, rfrom);
|
||||
|
||||
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
|
||||
mlist--;
|
||||
if (Checks && !pos.gives_check((mlist - 1)->move, CheckInfo(pos)))
|
||||
--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.
|
||||
do
|
||||
{
|
||||
checkersCnt++;
|
||||
++checkersCnt;
|
||||
checksq = pop_lsb(&b);
|
||||
|
||||
assert(color_of(pos.piece_on(checksq)) == ~us);
|
||||
|
||||
switch (type_of(pos.piece_on(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];
|
||||
if (type_of(pos.piece_on(checksq)) > KNIGHT) // A slider
|
||||
sliderAttacks |= LineBB[checksq][ksq] ^ 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);
|
||||
|
||||
// Generate evasions for king, capture and non capture moves
|
||||
|
@ -407,17 +390,17 @@ template<>
|
|||
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* 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());
|
||||
|
||||
end = pos.checkers() ? generate<EVASIONS>(pos, mlist)
|
||||
: generate<NON_EVASIONS>(pos, mlist);
|
||||
while (cur != end)
|
||||
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
|
||||
&& !pos.pl_move_is_legal(cur->move, pinned))
|
||||
&& !pos.legal(cur->move, pinned))
|
||||
cur->move = (--end)->move;
|
||||
else
|
||||
cur++;
|
||||
++cur;
|
||||
|
||||
return end;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ template<GenType T>
|
|||
struct MoveList {
|
||||
|
||||
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; }
|
||||
size_t size() const { return last - mlist; }
|
||||
bool contains(Move m) const {
|
||||
|
|
|
@ -86,7 +86,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
|
|||
else
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
// tree explosion due to a possible perpetual check or similar rare cases
|
||||
// when TT table is full.
|
||||
if (ttm && !pos.is_capture_or_promotion(ttm))
|
||||
if (ttm && !pos.capture_or_promotion(ttm))
|
||||
ttm = MOVE_NONE;
|
||||
}
|
||||
else
|
||||
|
@ -118,7 +118,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
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;
|
||||
|
||||
end += (ttMove != MOVE_NONE);
|
||||
|
@ -163,7 +163,7 @@ void MovePicker::score<CAPTURES>() {
|
|||
{
|
||||
m = it->move;
|
||||
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)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
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))]
|
||||
- type_of(pos.piece_moved(m)) + HistoryStats::Max;
|
||||
- type_of(pos.moved_piece(m)) + HistoryStats::Max;
|
||||
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;
|
||||
|
||||
// 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)
|
||||
(end++)->move = countermoves[i];
|
||||
|
||||
|
@ -299,7 +299,7 @@ Move MovePicker::next_move<false>() {
|
|||
switch (stage) {
|
||||
|
||||
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
|
||||
cur++;
|
||||
++cur;
|
||||
return ttMove;
|
||||
|
||||
case CAPTURES_S1:
|
||||
|
@ -317,9 +317,9 @@ Move MovePicker::next_move<false>() {
|
|||
case KILLERS_S1:
|
||||
move = (cur++)->move;
|
||||
if ( move != MOVE_NONE
|
||||
&& pos.is_pseudo_legal(move)
|
||||
&& pos.pseudo_legal(move)
|
||||
&& move != ttMove
|
||||
&& !pos.is_capture(move))
|
||||
&& !pos.capture(move))
|
||||
return move;
|
||||
break;
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ const string move_to_san(Position& pos, Move m) {
|
|||
while (b)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -149,10 +149,10 @@ const string move_to_san(Position& pos, Move m) {
|
|||
san += square_to_string(from);
|
||||
}
|
||||
}
|
||||
else if (pos.is_capture(m))
|
||||
else if (pos.capture(m))
|
||||
san = file_to_char(file_of(from));
|
||||
|
||||
if (pos.is_capture(m))
|
||||
if (pos.capture(m))
|
||||
san += 'x';
|
||||
|
||||
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)];
|
||||
}
|
||||
|
||||
if (pos.move_gives_check(m, CheckInfo(pos)))
|
||||
if (pos.gives_check(m, CheckInfo(pos)))
|
||||
{
|
||||
StateInfo st;
|
||||
pos.do_move(m, st);
|
||||
|
@ -207,7 +207,7 @@ static string score_to_string(Value v) {
|
|||
s << "-#" << (VALUE_MATE + v) / 2;
|
||||
|
||||
else
|
||||
s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg;
|
||||
s << setprecision(2) << fixed << showpos << double(v) / PawnValueMg;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
|
|
@ -30,38 +30,32 @@ namespace {
|
|||
#define V Value
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Doubled pawn penalty by opposed flag and file
|
||||
const Score Doubled[2][FILE_NB] = {
|
||||
{ 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(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
|
||||
// Doubled pawn penalty by file
|
||||
const Score Doubled[FILE_NB] = {
|
||||
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
|
||||
const Score Isolated[2][FILE_NB] = {
|
||||
{ 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(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
|
||||
const Score Backward[2][FILE_NB] = {
|
||||
{ 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(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
|
||||
const Score ChainMember[FILE_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)
|
||||
};
|
||||
// Pawn chain membership bonus by file and rank (initialized by formula)
|
||||
Score ChainMember[FILE_NB][RANK_NB];
|
||||
|
||||
// Candidate passed pawn bonus by rank
|
||||
const Score CandidatePassed[RANK_NB] = {
|
||||
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]
|
||||
const Value ShelterWeakness[RANK_NB] =
|
||||
|
@ -72,10 +66,10 @@ namespace {
|
|||
const Value StormDanger[3][RANK_NB] = {
|
||||
{ V( 0), V(64), V(128), V(51), V(26) },
|
||||
{ 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
|
||||
// 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);
|
||||
|
||||
#undef S
|
||||
|
@ -92,7 +86,6 @@ namespace {
|
|||
Bitboard b;
|
||||
Square s;
|
||||
File f;
|
||||
Rank r;
|
||||
bool passed, isolated, doubled, opposed, chain, backward, candidate;
|
||||
Score value = SCORE_ZERO;
|
||||
const Square* pl = pos.list<PAWN>(Us);
|
||||
|
@ -100,7 +93,7 @@ namespace {
|
|||
Bitboard ourPawns = pos.pieces(Us, PAWN);
|
||||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
||||
|
||||
e->passedPawns[Us] = 0;
|
||||
e->passedPawns[Us] = e->candidatePawns[Us] = 0;
|
||||
e->kingSquares[Us] = SQ_NONE;
|
||||
e->semiopenFiles[Us] = 0xFF;
|
||||
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));
|
||||
|
||||
f = file_of(s);
|
||||
r = rank_of(s);
|
||||
|
||||
// This file cannot be semi-open
|
||||
e->semiopenFiles[Us] &= ~(1 << f);
|
||||
|
||||
// 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
|
||||
// chain (but not the backward one).
|
||||
|
@ -129,35 +121,31 @@ namespace {
|
|||
opposed = theirPawns & forward_bb(Us, s);
|
||||
passed = !(theirPawns & passed_pawn_mask(Us, s));
|
||||
|
||||
// Test for backward pawn
|
||||
backward = false;
|
||||
|
||||
// Test for backward pawn.
|
||||
// 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
|
||||
// or if can capture an enemy pawn it cannot be backward either.
|
||||
if ( !(passed | isolated | chain)
|
||||
&& !(ourPawns & pawn_attack_span(Them, s))
|
||||
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
|
||||
if ( (passed | isolated | chain)
|
||||
|| (ourPawns & pawn_attack_span(Them, s))
|
||||
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns))
|
||||
backward = false;
|
||||
else
|
||||
{
|
||||
// We now know that there are no friendly pawns beside or behind this
|
||||
// pawn on adjacent files. We now check whether the pawn is
|
||||
// backward by looking in the forward direction on the adjacent
|
||||
// files, and seeing whether we meet a friendly or an enemy pawn first.
|
||||
b = pos.attacks_from<PAWN>(s, Us);
|
||||
// files, and picking the closest pawn there.
|
||||
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
|
||||
// nor isolated, so loop is potentially infinite, but it isn't.
|
||||
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.
|
||||
// If we have an enemy pawn in the same or next rank, the pawn is
|
||||
// backward because it cannot advance without being captured.
|
||||
backward = (b | shift_bb<Up>(b)) & 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
|
||||
// pawn on adjacent files is higher or equal than the number of
|
||||
// enemy pawns in the forward direction on the adjacent files.
|
||||
|
@ -176,16 +164,21 @@ namespace {
|
|||
value -= Isolated[opposed][f];
|
||||
|
||||
if (doubled)
|
||||
value -= Doubled[opposed][f];
|
||||
value -= Doubled[f];
|
||||
|
||||
if (backward)
|
||||
value -= Backward[opposed][f];
|
||||
|
||||
if (chain)
|
||||
value += ChainMember[f];
|
||||
value += ChainMember[f][relative_rank(Us, s)];
|
||||
|
||||
if (candidate)
|
||||
{
|
||||
value += CandidatePassed[relative_rank(Us, s)];
|
||||
|
||||
if (!doubled)
|
||||
e->candidatePawns[Us] |= s;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
|
@ -195,6 +188,22 @@ namespace {
|
|||
|
||||
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
|
||||
/// 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.
|
||||
|
@ -226,18 +235,16 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
|
|||
Bitboard ourPawns = b & pos.pieces(Us);
|
||||
Bitboard theirPawns = b & pos.pieces(Them);
|
||||
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 (int f = kf - 1; f <= kf + 1; f++)
|
||||
for (File f = kf - File(1); f <= kf + File(1); ++f)
|
||||
{
|
||||
b = ourPawns & FileBB[f];
|
||||
rkUs = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
|
||||
b = ourPawns & file_bb(f);
|
||||
rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
|
||||
safety -= ShelterWeakness[rkUs];
|
||||
|
||||
b = theirPawns & FileBB[f];
|
||||
rkThem = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
|
||||
b = theirPawns & file_bb(f);
|
||||
rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
|
||||
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);
|
||||
|
||||
// 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)))
|
||||
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ struct Entry {
|
|||
Score pawns_value() const { return value; }
|
||||
Bitboard pawn_attacks(Color c) const { return pawnAttacks[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 semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); }
|
||||
int semiopen_on_side(Color c, File f, bool left) const {
|
||||
|
@ -59,6 +60,7 @@ struct Entry {
|
|||
|
||||
Key key;
|
||||
Bitboard passedPawns[COLOR_NB];
|
||||
Bitboard candidatePawns[COLOR_NB];
|
||||
Bitboard pawnAttacks[COLOR_NB];
|
||||
Square kingSquares[COLOR_NB];
|
||||
int minKPdistance[COLOR_NB];
|
||||
|
@ -71,6 +73,7 @@ struct Entry {
|
|||
|
||||
typedef HashTable<Entry, 16384> Table;
|
||||
|
||||
void init();
|
||||
Entry* probe(const Position& pos, Table& entries);
|
||||
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ CheckInfo::CheckInfo(const Position& pos) {
|
|||
Color them = ~pos.side_to_move();
|
||||
ksq = pos.king_square(them);
|
||||
|
||||
pinned = pos.pinned_pieces();
|
||||
pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
dcCandidates = pos.discovered_check_candidates();
|
||||
|
||||
checkSq[PAWN] = pos.attacks_from<PAWN>(ksq, them);
|
||||
|
@ -120,15 +120,15 @@ void Position::init() {
|
|||
|
||||
RKISS rk;
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
Zobrist::psq[c][pt][s] = rk.rand<Key>();
|
||||
|
||||
for (File f = FILE_A; f <= FILE_H; f++)
|
||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||
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;
|
||||
while (b)
|
||||
|
@ -141,14 +141,14 @@ void Position::init() {
|
|||
Zobrist::side = 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[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
|
||||
|
||||
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
psq[WHITE][pt][ s] = (v + PSQT[pt][s]);
|
||||
psq[BLACK][pt][~s] = -(v + PSQT[pt][s]);
|
||||
|
@ -233,7 +233,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
|
|||
else if ((p = PieceToChar.find(token)) != string::npos)
|
||||
{
|
||||
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));
|
||||
|
||||
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')
|
||||
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')
|
||||
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 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)
|
||||
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)
|
||||
castlePath[c][cs] |= s;
|
||||
}
|
||||
|
@ -334,18 +334,18 @@ const string Position::fen() const {
|
|||
|
||||
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;
|
||||
|
||||
if (is_empty(sq))
|
||||
if (empty(sq))
|
||||
{
|
||||
int emptyCnt = 1;
|
||||
|
||||
for ( ; file < FILE_H && is_empty(sq++); file++)
|
||||
emptyCnt++;
|
||||
for ( ; file < FILE_H && empty(++sq); ++file)
|
||||
++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,
|
||||
/// 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;
|
||||
|
||||
|
@ -435,7 +435,7 @@ Bitboard Position::hidden_checkers(Square ksq, Color c) const {
|
|||
b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
|
||||
|
||||
if (!more_than_one(b))
|
||||
result |= b & pieces(sideToMove);
|
||||
result |= b & pieces(toMove);
|
||||
}
|
||||
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(pinned == pinned_pieces());
|
||||
assert(pinned == pinned_pieces(sideToMove));
|
||||
|
||||
Color us = sideToMove;
|
||||
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));
|
||||
|
||||
// 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;
|
||||
|
||||
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(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.
|
||||
return !pinned
|
||||
|| !(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
|
||||
/// is pseudo legal. It is used to validate moves from TT that can be corrupted
|
||||
/// Position::pseudo_legal() takes a random move and tests whether the move is
|
||||
/// pseudo legal. It is used to validate moves from TT that can be corrupted
|
||||
/// 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;
|
||||
Square from = from_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
|
||||
if (type_of(m) != NORMAL)
|
||||
|
@ -581,7 +581,7 @@ bool Position::is_pseudo_legal(const Move m) const {
|
|||
case DELTA_N:
|
||||
case DELTA_S:
|
||||
// Pawn push. The destination square must be empty.
|
||||
if (!is_empty(to))
|
||||
if (!empty(to))
|
||||
return false;
|
||||
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
|
||||
// source and destination squares must be empty.
|
||||
if ( rank_of(to) != RANK_4
|
||||
|| !is_empty(to)
|
||||
|| !is_empty(from + DELTA_N))
|
||||
|| !empty(to)
|
||||
|| !empty(from + DELTA_N))
|
||||
return false;
|
||||
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
|
||||
// source and destination squares must be empty.
|
||||
if ( rank_of(to) != RANK_5
|
||||
|| !is_empty(to)
|
||||
|| !is_empty(from + DELTA_S))
|
||||
|| !empty(to)
|
||||
|| !empty(from + DELTA_S))
|
||||
return false;
|
||||
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
|
||||
|
||||
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(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 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
|
||||
if ( (pt != PAWN && pt != KING)
|
||||
|| !squares_aligned(from, to, king_square(~sideToMove)))
|
||||
|| !aligned(from, to, king_square(~sideToMove)))
|
||||
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) {
|
||||
|
||||
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) {
|
||||
|
@ -718,7 +718,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
|||
assert(is_ok(m));
|
||||
assert(&newSt != st);
|
||||
|
||||
nodes++;
|
||||
++nodes;
|
||||
Key k = st->key;
|
||||
|
||||
// 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
|
||||
// in case of a capture or a pawn move.
|
||||
gamePly++;
|
||||
st->rule50++;
|
||||
st->pliesFromNull++;
|
||||
++gamePly;
|
||||
++st->rule50;
|
||||
++st->pliesFromNull;
|
||||
|
||||
Color us = sideToMove;
|
||||
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);
|
||||
Piece pc = piece_on(from);
|
||||
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(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)
|
||||
{
|
||||
|
@ -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 rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
||||
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
||||
capture = NO_PIECE_TYPE;
|
||||
captured = NO_PIECE_TYPE;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
if (capture)
|
||||
if (captured)
|
||||
{
|
||||
Square capsq = to;
|
||||
|
||||
// If the captured piece is a pawn, update pawn hash key, otherwise
|
||||
// update non-pawn material.
|
||||
if (capture == PAWN)
|
||||
if (captured == PAWN)
|
||||
{
|
||||
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];
|
||||
}
|
||||
else
|
||||
st->npMaterial[them] -= PieceValue[MG][capture];
|
||||
st->npMaterial[them] -= PieceValue[MG][captured];
|
||||
|
||||
// Update board and piece lists
|
||||
remove_piece(capsq, them, capture);
|
||||
remove_piece(capsq, them, captured);
|
||||
|
||||
// Update material hash key and prefetch access to materialTable
|
||||
k ^= Zobrist::psq[them][capture][capsq];
|
||||
st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]];
|
||||
k ^= Zobrist::psq[them][captured][capsq];
|
||||
st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]];
|
||||
prefetch((char*)thisThread->materialTable[st->materialKey]);
|
||||
|
||||
// Update incremental scores
|
||||
st->psq -= psq[them][capture][capsq];
|
||||
st->psq -= psq[them][captured][capsq];
|
||||
|
||||
// Reset rule 50 counter
|
||||
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];
|
||||
|
||||
// Set capture piece
|
||||
st->capturedType = capture;
|
||||
st->capturedType = captured;
|
||||
|
||||
// Update the key with the final value
|
||||
st->key = k;
|
||||
|
@ -928,10 +928,10 @@ void Position::undo_move(Move m) {
|
|||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
PieceType pt = type_of(piece_on(to));
|
||||
PieceType capture = st->capturedType;
|
||||
PieceType captured = st->capturedType;
|
||||
|
||||
assert(is_empty(from) || type_of(m) == CASTLE);
|
||||
assert(capture != KING);
|
||||
assert(empty(from) || type_of(m) == CASTLE);
|
||||
assert(captured != KING);
|
||||
|
||||
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 rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
||||
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
||||
capture = NO_PIECE_TYPE;
|
||||
captured = NO_PIECE_TYPE;
|
||||
pt = KING;
|
||||
do_castle(to, from, rto, rfrom);
|
||||
}
|
||||
else
|
||||
move_piece(to, from, us, pt); // Put the piece back at the source square
|
||||
|
||||
if (capture)
|
||||
if (captured)
|
||||
{
|
||||
Square capsq = to;
|
||||
|
||||
|
@ -973,12 +973,12 @@ void Position::undo_move(Move m) {
|
|||
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
|
||||
st = st->previous;
|
||||
gamePly--;
|
||||
--gamePly;
|
||||
|
||||
assert(pos_is_ok());
|
||||
}
|
||||
|
@ -1019,7 +1019,7 @@ void Position::do_null_move(StateInfo& newSt) {
|
|||
st->key ^= Zobrist::side;
|
||||
prefetch((char*)TT.first_entry(st->key));
|
||||
|
||||
st->rule50++;
|
||||
++st->rule50;
|
||||
st->pliesFromNull = 0;
|
||||
|
||||
sideToMove = ~sideToMove;
|
||||
|
@ -1049,7 +1049,7 @@ int Position::see_sign(Move m) const {
|
|||
// Early return if SEE cannot be negative because captured piece value
|
||||
// is not less then capturing one. Note that king moves always return
|
||||
// here because king midgame value is set to 0.
|
||||
if (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 see(m);
|
||||
|
@ -1067,7 +1067,7 @@ int Position::see(Move m, int asymmThreshold) const {
|
|||
|
||||
from = from_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));
|
||||
occupied = pieces() ^ from;
|
||||
|
||||
|
@ -1106,7 +1106,7 @@ int Position::see(Move m, int asymmThreshold) const {
|
|||
|
||||
// Add the new entry to the swap list
|
||||
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
|
||||
slIndex++;
|
||||
++slIndex;
|
||||
|
||||
// Locate and remove the next least valuable attacker
|
||||
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
|
||||
// achievable score from the point of view of the side to move.
|
||||
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];
|
||||
}
|
||||
|
@ -1149,9 +1149,9 @@ void Position::clear() {
|
|||
startState.epSquare = SQ_NONE;
|
||||
st = &startState;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
for (int j = 0; j < 16; j++)
|
||||
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
|
||||
for (int i = 0; i < PIECE_TYPE_NB; ++i)
|
||||
for (int j = 0; j < 16; ++j)
|
||||
pieceList[WHITE][i][j] = pieceList[BLACK][i][j] = SQ_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1210,9 +1210,9 @@ Key Position::compute_material_key() const {
|
|||
|
||||
Key k = 0;
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
for (PieceType pt = PAWN; pt <= QUEEN; pt++)
|
||||
for (int cnt = 0; cnt < pieceCount[c][pt]; cnt++)
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt = PAWN; pt <= QUEEN; ++pt)
|
||||
for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt)
|
||||
k ^= Zobrist::psq[c][pt][cnt];
|
||||
|
||||
return k;
|
||||
|
@ -1223,6 +1223,7 @@ Key Position::compute_material_key() const {
|
|||
/// 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
|
||||
/// updated by do_move and undo_move when the program is running in debug mode.
|
||||
|
||||
Score Position::compute_psq_score() const {
|
||||
|
||||
Score score = SCORE_ZERO;
|
||||
|
@ -1247,7 +1248,7 @@ Value Position::compute_non_pawn_material(Color c) const {
|
|||
|
||||
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];
|
||||
|
||||
return value;
|
||||
|
@ -1268,7 +1269,6 @@ bool Position::is_draw() const {
|
|||
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
|
||||
return true;
|
||||
|
||||
// Draw by repetition?
|
||||
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
|
||||
|
||||
if (i <= e)
|
||||
|
@ -1279,7 +1279,7 @@ bool Position::is_draw() const {
|
|||
stp = stp->previous->previous;
|
||||
|
||||
if (stp->key == st->key)
|
||||
return true;
|
||||
return true; // Draw after first repetition
|
||||
|
||||
i += 2;
|
||||
|
||||
|
@ -1302,7 +1302,7 @@ void Position::flip() {
|
|||
string f, token;
|
||||
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 ? '/' : ' ');
|
||||
f.insert(0, token + (f.empty() ? " " : "/"));
|
||||
|
@ -1366,9 +1366,9 @@ bool Position::pos_is_ok(int* failedStep) const {
|
|||
{
|
||||
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)
|
||||
kingCount[color_of(piece_on(s))]++;
|
||||
++kingCount[color_of(piece_on(s))];
|
||||
|
||||
if (kingCount[0] != 1 || kingCount[1] != 1)
|
||||
return false;
|
||||
|
@ -1393,8 +1393,8 @@ bool Position::pos_is_ok(int* failedStep) const {
|
|||
return false;
|
||||
|
||||
// Separate piece type bitboards must have empty intersections
|
||||
for (PieceType p1 = PAWN; p1 <= KING; p1++)
|
||||
for (PieceType p2 = PAWN; p2 <= KING; p2++)
|
||||
for (PieceType p1 = PAWN; p1 <= KING; ++p1)
|
||||
for (PieceType p2 = PAWN; p2 <= KING; ++p2)
|
||||
if (p1 != p2 && (pieces(p1) & pieces(p2)))
|
||||
return false;
|
||||
}
|
||||
|
@ -1420,21 +1420,21 @@ bool Position::pos_is_ok(int* failedStep) const {
|
|||
return false;
|
||||
|
||||
if ((*step)++, debugPieceCounts)
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||
if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt)))
|
||||
return false;
|
||||
|
||||
if ((*step)++, debugPieceList)
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
for (PieceType pt = PAWN; pt <= KING; pt++)
|
||||
for (int i = 0; i < pieceCount[c][pt]; i++)
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt = PAWN; pt <= KING; ++pt)
|
||||
for (int i = 0; i < pieceCount[c][pt]; ++i)
|
||||
if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
|
||||
|| index[pieceList[c][pt][i]] != i)
|
||||
return false;
|
||||
|
||||
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))
|
||||
{
|
||||
CastleRight cr = make_castle_right(c, s);
|
||||
|
|
|
@ -43,10 +43,10 @@ struct CheckInfo {
|
|||
};
|
||||
|
||||
|
||||
/// The StateInfo struct stores information we need to restore a Position
|
||||
/// The StateInfo struct stores information needed to restore a Position
|
||||
/// object to its previous state when we retract a move. Whenever a move
|
||||
/// is made on the board (by calling Position::do_move), a StateInfo object
|
||||
/// must be passed as a parameter.
|
||||
/// is made on the board (by calling Position::do_move), a StateInfo
|
||||
/// object must be passed as a parameter.
|
||||
|
||||
struct StateInfo {
|
||||
Key pawnKey, materialKey;
|
||||
|
@ -67,27 +67,10 @@ struct StateInfo {
|
|||
const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
|
||||
|
||||
|
||||
/// The position data structure. A position consists of the following data:
|
||||
///
|
||||
/// * For each piece type, a bitboard representing the squares occupied
|
||||
/// by pieces of that type.
|
||||
/// * 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.
|
||||
/// The Position class stores the information regarding the board representation
|
||||
/// like pieces, side to move, hash keys, castling info, etc. The most important
|
||||
/// methods are do_move() and undo_move(), used by the search to update node info
|
||||
/// when traversing the search tree.
|
||||
|
||||
class Position {
|
||||
public:
|
||||
|
@ -112,7 +95,7 @@ public:
|
|||
Piece piece_on(Square s) const;
|
||||
Square king_square(Color c) 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> const Square* list(Color c) const;
|
||||
|
||||
|
@ -125,7 +108,7 @@ public:
|
|||
// Checking
|
||||
Bitboard checkers() const;
|
||||
Bitboard discovered_check_candidates() const;
|
||||
Bitboard pinned_pieces() const;
|
||||
Bitboard pinned_pieces(Color toMove) const;
|
||||
|
||||
// Attacks to/from a given square
|
||||
Bitboard attackers_to(Square s) const;
|
||||
|
@ -136,20 +119,20 @@ public:
|
|||
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
|
||||
|
||||
// Properties of moves
|
||||
bool move_gives_check(Move m, const CheckInfo& ci) const;
|
||||
bool pl_move_is_legal(Move m, Bitboard pinned) const;
|
||||
bool is_pseudo_legal(const Move m) const;
|
||||
bool is_capture(Move m) const;
|
||||
bool is_capture_or_promotion(Move m) const;
|
||||
bool is_passed_pawn_push(Move m) const;
|
||||
Piece piece_moved(Move m) const;
|
||||
bool legal(Move m, Bitboard pinned) const;
|
||||
bool pseudo_legal(const Move m) const;
|
||||
bool capture(Move m) const;
|
||||
bool capture_or_promotion(Move m) const;
|
||||
bool gives_check(Move m, const CheckInfo& ci) const;
|
||||
bool passed_pawn_push(Move m) const;
|
||||
Piece moved_piece(Move m) const;
|
||||
PieceType captured_piece_type() const;
|
||||
|
||||
// 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 opposite_bishops() const;
|
||||
bool bishop_pair(Color c) const;
|
||||
bool opposite_bishops() const;
|
||||
|
||||
// Doing and undoing moves
|
||||
void do_move(Move m, StateInfo& st);
|
||||
|
@ -192,7 +175,7 @@ private:
|
|||
|
||||
// Helper functions
|
||||
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 remove_piece(Square s, 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];
|
||||
}
|
||||
|
||||
inline Piece Position::piece_moved(Move m) const {
|
||||
inline Piece Position::moved_piece(Move m) const {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -333,17 +316,23 @@ inline Bitboard Position::checkers() 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 {
|
||||
return hidden_checkers(king_square(sideToMove), ~sideToMove);
|
||||
inline Bitboard Position::pinned_pieces(Color toMove) const {
|
||||
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));
|
||||
}
|
||||
|
||||
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 {
|
||||
return st->key;
|
||||
}
|
||||
|
@ -364,12 +353,6 @@ inline Value Position::non_pawn_material(Color c) const {
|
|||
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 {
|
||||
return gamePly;
|
||||
}
|
||||
|
@ -395,17 +378,17 @@ inline bool Position::is_chess960() const {
|
|||
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));
|
||||
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"
|
||||
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 {
|
||||
|
@ -422,6 +405,7 @@ inline void Position::put_piece(Square s, Color c, PieceType pt) {
|
|||
byTypeBB[ALL_PIECES] |= s;
|
||||
byTypeBB[pt] |= s;
|
||||
byColorBB[c] |= s;
|
||||
pieceCount[c][ALL_PIECES]++;
|
||||
index[s] = pieceCount[c][pt]++;
|
||||
pieceList[c][pt][index[s]] = s;
|
||||
}
|
||||
|
@ -450,6 +434,7 @@ inline void Position::remove_piece(Square s, Color c, PieceType pt) {
|
|||
byTypeBB[pt] ^= s;
|
||||
byColorBB[c] ^= s;
|
||||
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
|
||||
pieceCount[c][ALL_PIECES]--;
|
||||
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
|
||||
index[lastSquare] = index[s];
|
||||
pieceList[c][pt][index[lastSquare]] = lastSquare;
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
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 {
|
||||
return (x << k) | (x >> (64 - k));
|
||||
|
@ -51,20 +51,19 @@ class RKISS {
|
|||
|
||||
uint64_t rand64() {
|
||||
|
||||
const uint64_t
|
||||
e = s.a - rotate(s.b, 7);
|
||||
s.a = s.b ^ rotate(s.c, 13);
|
||||
s.b = s.c + rotate(s.d, 37);
|
||||
s.c = s.d + e;
|
||||
return s.d = e + s.a;
|
||||
const uint64_t e = a - rotate(b, 7);
|
||||
a = b ^ rotate(c, 13);
|
||||
b = c + rotate(d, 37);
|
||||
c = d + e;
|
||||
return d = e + a;
|
||||
}
|
||||
|
||||
public:
|
||||
RKISS(int seed = 73) {
|
||||
|
||||
s.a = 0xf1ea5eed;
|
||||
s.b = s.c = s.d = 0xd4e12c77;
|
||||
for (int i = 0; i < seed; i++) // Scramble a few rounds
|
||||
a = 0xF1EA5EED, b = c = d = 0xD4E12C77;
|
||||
|
||||
for (int i = 0; i < seed; ++i) // Scramble a few rounds
|
||||
rand64();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
@ -55,9 +56,6 @@ namespace {
|
|||
// Set to true to force running with one thread. Used for debugging
|
||||
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
|
||||
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)); }
|
||||
|
||||
// Futility lookup tables (initialized at startup) and their access functions
|
||||
Value FutilityMargins[16][64]; // [depth][moveNumber]
|
||||
int FutilityMoveCounts[2][32]; // [improving][depth]
|
||||
|
||||
inline Value futility_margin(Depth d, int mn) {
|
||||
|
||||
return d < 7 * ONE_PLY ? FutilityMargins[std::max(int(d), 1)][std::min(mn, 63)]
|
||||
: 2 * VALUE_INFINITE;
|
||||
inline Value futility_margin(Depth d) {
|
||||
return Value(100 * int(d));
|
||||
}
|
||||
|
||||
// Reduction lookup tables (initialized at startup) and their access function
|
||||
|
@ -84,7 +79,7 @@ namespace {
|
|||
|
||||
size_t PVSize, PVIdx;
|
||||
TimeManager TimeMgr;
|
||||
int BestMoveChanges;
|
||||
double BestMoveChanges;
|
||||
Value DrawValue[COLOR_NB];
|
||||
HistoryStats History;
|
||||
GainsStats Gains;
|
||||
|
@ -99,7 +94,6 @@ namespace {
|
|||
void id_loop(Position& pos);
|
||||
Value value_to_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 refutes(const Position& pos, Move first, Move second);
|
||||
string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
|
||||
|
@ -132,7 +126,7 @@ void Search::init() {
|
|||
int mc; // moveCount
|
||||
|
||||
// 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 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)
|
||||
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
|
||||
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[1][d] = int(3.001 + 0.3 * pow(double(d + 0.98), 1.8));
|
||||
FutilityMoveCounts[0][d] = int(2.4 + 0.222 * pow(d + 0.0, 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)
|
||||
{
|
||||
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);
|
||||
pos.undo_move(*it);
|
||||
}
|
||||
|
@ -237,23 +230,16 @@ void Search::think() {
|
|||
}
|
||||
|
||||
// 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.sleepWhileIdle = Options["Idle Threads Sleep"];
|
||||
|
||||
// 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->run = true;
|
||||
Threads.timer->notify_one(); // Wake up the recurring timer
|
||||
|
||||
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
|
||||
|
||||
if (Options["Write Search Log"])
|
||||
|
@ -304,13 +290,14 @@ namespace {
|
|||
void id_loop(Position& pos) {
|
||||
|
||||
Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
|
||||
int depth, prevBestMoveChanges;
|
||||
int depth;
|
||||
Value bestValue, alpha, beta, delta;
|
||||
|
||||
std::memset(ss-2, 0, 5 * sizeof(Stack));
|
||||
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
|
||||
|
||||
depth = BestMoveChanges = 0;
|
||||
depth = 0;
|
||||
BestMoveChanges = 0;
|
||||
bestValue = delta = alpha = -VALUE_INFINITE;
|
||||
beta = VALUE_INFINITE;
|
||||
|
||||
|
@ -332,16 +319,16 @@ namespace {
|
|||
// Iterative deepening loop until requested to stop or target depth reached
|
||||
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
|
||||
// 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;
|
||||
|
||||
prevBestMoveChanges = BestMoveChanges; // Only sensible when PVSize == 1
|
||||
BestMoveChanges = 0;
|
||||
|
||||
// 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
|
||||
if (depth >= 5)
|
||||
|
@ -367,14 +354,14 @@ namespace {
|
|||
|
||||
// Write PV back to transposition table in case the relevant
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
// valid, although refers to previous iteration.
|
||||
if (Signals.stop)
|
||||
return;
|
||||
break;
|
||||
|
||||
// When failing high/low give some update (without cluttering
|
||||
// the UI) before to research.
|
||||
|
@ -431,13 +418,13 @@ namespace {
|
|||
Signals.stop = true;
|
||||
|
||||
// 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
|
||||
|
||||
// Take in account some extra time if the best move has changed
|
||||
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
|
||||
// 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
|
||||
if ( depth >= 12
|
||||
&& BestMoveChanges <= DBL_EPSILON
|
||||
&& !stop
|
||||
&& PVSize == 1
|
||||
&& bestValue > VALUE_MATED_IN_MAX_PLY
|
||||
|
@ -502,9 +490,8 @@ namespace {
|
|||
SplitPoint* splitPoint;
|
||||
Key posKey;
|
||||
Move ttMove, move, excludedMove, bestMove, threatMove;
|
||||
Depth ext, newDepth;
|
||||
Value bestValue, value, ttValue;
|
||||
Value eval, nullValue, futilityValue;
|
||||
Depth ext, newDepth, predictedDepth;
|
||||
Value bestValue, value, ttValue, eval, nullValue, futilityValue;
|
||||
bool inCheck, givesCheck, pvMove, singularExtensionNode, improving;
|
||||
bool captureOrPromotion, dangerous, doFullDepthSearch;
|
||||
int moveCount, quietCount;
|
||||
|
@ -532,7 +519,6 @@ namespace {
|
|||
bestValue = -VALUE_INFINITE;
|
||||
ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
|
||||
ss->ply = (ss-1)->ply + 1;
|
||||
ss->futilityMoveCount = 0;
|
||||
(ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
|
||||
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
|
||||
|
||||
|
@ -584,7 +570,7 @@ namespace {
|
|||
|
||||
if ( ttValue >= beta
|
||||
&& ttMove
|
||||
&& !pos.is_capture_or_promotion(ttMove)
|
||||
&& !pos.capture_or_promotion(ttMove)
|
||||
&& ttMove != 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
|
||||
if (inCheck)
|
||||
{
|
||||
ss->staticEval = ss->evalMargin = eval = VALUE_NONE;
|
||||
ss->staticEval = eval = VALUE_NONE;
|
||||
goto moves_loop;
|
||||
}
|
||||
|
||||
else if (tte)
|
||||
{
|
||||
// Never assume anything on values stored in TT
|
||||
if ( (ss->staticEval = eval = tte->eval_value()) == VALUE_NONE
|
||||
||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE)
|
||||
eval = ss->staticEval = evaluate(pos, ss->evalMargin);
|
||||
if ((ss->staticEval = eval = tte->eval_value()) == VALUE_NONE)
|
||||
eval = ss->staticEval = evaluate(pos);
|
||||
|
||||
// Can ttValue be used as a better position evaluation?
|
||||
if (ttValue != VALUE_NONE)
|
||||
if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval)
|
||||
|| ((tte->bound() & BOUND_UPPER) && ttValue < eval))
|
||||
if (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))
|
||||
eval = ttValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
eval = ss->staticEval = evaluate(pos, ss->evalMargin);
|
||||
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
|
||||
ss->staticEval, ss->evalMargin);
|
||||
eval = ss->staticEval = evaluate(pos);
|
||||
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval);
|
||||
}
|
||||
|
||||
// Update gain for the parent non-capture move given the static position
|
||||
// evaluation before and after the move.
|
||||
if ( !pos.captured_piece_type()
|
||||
&& ss->staticEval != VALUE_NONE
|
||||
&& (ss-1)->staticEval != VALUE_NONE
|
||||
|
@ -648,22 +629,20 @@ namespace {
|
|||
return v;
|
||||
}
|
||||
|
||||
// Step 7. Static null move pruning (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.
|
||||
// Step 7. Futility pruning: child node (skipped when in check)
|
||||
if ( !PvNode
|
||||
&& !ss->skipNullMove
|
||||
&& depth < 4 * ONE_PLY
|
||||
&& eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta
|
||||
&& depth < 7 * ONE_PLY
|
||||
&& eval - futility_margin(depth) >= beta
|
||||
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
|
||||
&& abs(eval) < VALUE_KNOWN_WIN
|
||||
&& 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)
|
||||
if ( !PvNode
|
||||
&& !ss->skipNullMove
|
||||
&& depth > ONE_PLY
|
||||
&& depth >= 2 * ONE_PLY
|
||||
&& eval >= beta
|
||||
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
|
||||
&& pos.non_pawn_material(pos.side_to_move()))
|
||||
|
@ -739,10 +718,10 @@ namespace {
|
|||
CheckInfo ci(pos);
|
||||
|
||||
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;
|
||||
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);
|
||||
pos.undo_move(move);
|
||||
if (value >= rbeta)
|
||||
|
@ -780,7 +759,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
|
||||
singularExtensionNode = !RootNode
|
||||
&& !SpNode
|
||||
&& depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY)
|
||||
&& depth >= 8 * ONE_PLY
|
||||
&& ttMove != MOVE_NONE
|
||||
&& !excludedMove // Recursive singular search is not allowed
|
||||
&& (tte->bound() & BOUND_LOWER)
|
||||
|
@ -804,14 +783,14 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
if (SpNode)
|
||||
{
|
||||
// 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;
|
||||
|
||||
moveCount = ++splitPoint->moveCount;
|
||||
splitPoint->mutex.unlock();
|
||||
}
|
||||
else
|
||||
moveCount++;
|
||||
++moveCount;
|
||||
|
||||
if (RootNode)
|
||||
{
|
||||
|
@ -824,19 +803,16 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
}
|
||||
|
||||
ext = DEPTH_ZERO;
|
||||
captureOrPromotion = pos.is_capture_or_promotion(move);
|
||||
givesCheck = pos.move_gives_check(move, ci);
|
||||
captureOrPromotion = pos.capture_or_promotion(move);
|
||||
givesCheck = pos.gives_check(move, ci);
|
||||
dangerous = givesCheck
|
||||
|| pos.is_passed_pawn_push(move)
|
||||
|| pos.passed_pawn_push(move)
|
||||
|| type_of(move) == CASTLE;
|
||||
|
||||
// Step 12. Extend checks and, in PV nodes, also dangerous moves
|
||||
if (PvNode && dangerous)
|
||||
// Step 12. Extend checks
|
||||
if (givesCheck && pos.see_sign(move) >= 0)
|
||||
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
|
||||
// (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
|
||||
|
@ -845,7 +821,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
if ( singularExtensionNode
|
||||
&& move == ttMove
|
||||
&& !ext
|
||||
&& pos.pl_move_is_legal(move, ci.pinned)
|
||||
&& pos.legal(move, ci.pinned)
|
||||
&& abs(ttValue) < VALUE_KNOWN_WIN)
|
||||
{
|
||||
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)
|
||||
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
|
||||
&& !captureOrPromotion
|
||||
&& !inCheck
|
||||
|
@ -883,29 +859,30 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
continue;
|
||||
}
|
||||
|
||||
// Value based pruning
|
||||
// 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)];
|
||||
predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
|
||||
|
||||
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();
|
||||
if (bestValue > splitPoint->bestValue)
|
||||
splitPoint->bestValue = bestValue;
|
||||
bestValue = std::max(bestValue, futilityValue);
|
||||
|
||||
if (SpNode)
|
||||
{
|
||||
splitPoint->mutex.lock();
|
||||
if (bestValue > splitPoint->bestValue)
|
||||
splitPoint->bestValue = bestValue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prune moves with negative SEE at low depths
|
||||
if ( predictedDepth < 4 * ONE_PLY
|
||||
&& pos.see_sign(move) < 0)
|
||||
if (predictedDepth < 4 * ONE_PLY && pos.see_sign(move) < 0)
|
||||
{
|
||||
if (SpNode)
|
||||
splitPoint->mutex.lock();
|
||||
|
@ -913,15 +890,10 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
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
|
||||
if (!RootNode && !SpNode && !pos.pl_move_is_legal(move, ci.pinned))
|
||||
if (!RootNode && !SpNode && !pos.legal(move, ci.pinned))
|
||||
{
|
||||
moveCount--;
|
||||
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
|
||||
// re-searched at full depth.
|
||||
if ( depth > 3 * ONE_PLY
|
||||
if ( depth >= 3 * ONE_PLY
|
||||
&& !pvMove
|
||||
&& !captureOrPromotion
|
||||
&& !dangerous
|
||||
&& move != ttMove
|
||||
&& move != ss->killers[0]
|
||||
&& move != ss->killers[1])
|
||||
|
@ -950,8 +921,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
if (!PvNode && cutNode)
|
||||
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])
|
||||
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);
|
||||
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
|
||||
// the best move changes frequently, we allocate some more time.
|
||||
if (!pvMove)
|
||||
BestMoveChanges++;
|
||||
++BestMoveChanges;
|
||||
}
|
||||
else
|
||||
// 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),
|
||||
bestValue >= beta ? BOUND_LOWER :
|
||||
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
|
||||
depth, bestMove, ss->staticEval, ss->evalMargin);
|
||||
depth, bestMove, ss->staticEval);
|
||||
|
||||
// Quiet best move: update killers, history and countermoves
|
||||
if ( bestValue >= beta
|
||||
&& !pos.is_capture_or_promotion(bestMove)
|
||||
&& !pos.capture_or_promotion(bestMove)
|
||||
&& !inCheck)
|
||||
{
|
||||
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
|
||||
// played non-capture moves.
|
||||
Value bonus = Value(int(depth) * int(depth));
|
||||
History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus);
|
||||
for (int i = 0; i < quietCount - 1; i++)
|
||||
History.update(pos.moved_piece(bestMove), to_sq(bestMove), bonus);
|
||||
for (int i = 0; i < quietCount - 1; ++i)
|
||||
{
|
||||
Move m = quietsSearched[i];
|
||||
History.update(pos.piece_moved(m), to_sq(m), -bonus);
|
||||
History.update(pos.moved_piece(m), to_sq(m), -bonus);
|
||||
}
|
||||
|
||||
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
|
||||
if (InCheck)
|
||||
{
|
||||
ss->staticEval = ss->evalMargin = VALUE_NONE;
|
||||
ss->staticEval = VALUE_NONE;
|
||||
bestValue = futilityBase = -VALUE_INFINITE;
|
||||
}
|
||||
else
|
||||
|
@ -1187,19 +1161,23 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
if (tte)
|
||||
{
|
||||
// Never assume anything on values stored in TT
|
||||
if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE
|
||||
||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE)
|
||||
ss->staticEval = bestValue = evaluate(pos, ss->evalMargin);
|
||||
if ((ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE)
|
||||
ss->staticEval = bestValue = evaluate(pos);
|
||||
|
||||
// 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
|
||||
ss->staticEval = bestValue = evaluate(pos, ss->evalMargin);
|
||||
ss->staticEval = bestValue = evaluate(pos);
|
||||
|
||||
// Stand pat. Return immediately if static value is at least beta
|
||||
if (bestValue >= beta)
|
||||
{
|
||||
if (!tte)
|
||||
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;
|
||||
}
|
||||
|
@ -1207,7 +1185,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
if (PvNode && bestValue > alpha)
|
||||
alpha = bestValue;
|
||||
|
||||
futilityBase = ss->staticEval + ss->evalMargin + Value(128);
|
||||
futilityBase = bestValue + Value(128);
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
givesCheck = pos.move_gives_check(move, ci);
|
||||
givesCheck = pos.gives_check(move, ci);
|
||||
|
||||
// Futility pruning
|
||||
if ( !PvNode
|
||||
|
@ -1230,7 +1208,8 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
&& !givesCheck
|
||||
&& move != ttMove
|
||||
&& type_of(move) != PROMOTION
|
||||
&& !pos.is_passed_pawn_push(move))
|
||||
&& futilityBase > -VALUE_KNOWN_WIN
|
||||
&& !pos.passed_pawn_push(move))
|
||||
{
|
||||
futilityValue = futilityBase
|
||||
+ 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
|
||||
evasionPrunable = InCheck
|
||||
&& bestValue > VALUE_MATED_IN_MAX_PLY
|
||||
&& !pos.is_capture(move)
|
||||
&& !pos.capture(move)
|
||||
&& !pos.can_castle(pos.side_to_move());
|
||||
|
||||
// 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)
|
||||
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
|
||||
if (!pos.pl_move_is_legal(move, ci.pinned))
|
||||
if (!pos.legal(move, ci.pinned))
|
||||
continue;
|
||||
|
||||
ss->currentMove = move;
|
||||
|
@ -1305,7 +1274,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
else // Fail high
|
||||
{
|
||||
TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER,
|
||||
ttDepth, move, ss->staticEval, ss->evalMargin);
|
||||
ttDepth, move, ss->staticEval);
|
||||
|
||||
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),
|
||||
PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
|
||||
ttDepth, bestMove, ss->staticEval, ss->evalMargin);
|
||||
ttDepth, bestMove, ss->staticEval);
|
||||
|
||||
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
|
||||
// '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
|
||||
|
@ -1399,7 +1332,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
assert(is_ok(first));
|
||||
assert(is_ok(second));
|
||||
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 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);
|
||||
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
// 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)]
|
||||
|| 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;
|
||||
|
||||
// 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>();
|
||||
|
||||
// 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
|
||||
// weakness, one deterministic and bigger for weaker moves, and one random,
|
||||
// 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;
|
||||
|
||||
|
@ -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());
|
||||
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)
|
||||
selDepth = Threads[i]->maxPly;
|
||||
|
||||
for (size_t i = 0; i < uciPVSize; i++)
|
||||
for (size_t i = 0; i < uciPVSize; ++i)
|
||||
{
|
||||
bool updated = (i <= PVIdx);
|
||||
|
||||
|
@ -1558,7 +1494,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
|||
<< " multipv " << i + 1
|
||||
<< " 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());
|
||||
}
|
||||
|
||||
|
@ -1591,8 +1527,8 @@ void RootMove::extract_pv_from_tt(Position& pos) {
|
|||
tte = TT.probe(pos.key());
|
||||
|
||||
} while ( tte
|
||||
&& pos.is_pseudo_legal(m = tte->move()) // Local copy, TT could change
|
||||
&& pos.pl_move_is_legal(m, pos.pinned_pieces())
|
||||
&& pos.pseudo_legal(m = tte->move()) // Local copy, TT could change
|
||||
&& pos.legal(m, pos.pinned_pieces(pos.side_to_move()))
|
||||
&& ply < MAX_PLY
|
||||
&& (!pos.is_draw() || ply < 2));
|
||||
|
||||
|
@ -1616,7 +1552,7 @@ void RootMove::insert_pv_in_tt(Position& pos) {
|
|||
tte = TT.probe(pos.key());
|
||||
|
||||
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]));
|
||||
|
||||
|
@ -1773,8 +1709,8 @@ void check_time() {
|
|||
|
||||
// Loop across all split points and sum accumulated SplitPoint nodes plus
|
||||
// all the currently active positions nodes.
|
||||
for (size_t i = 0; i < Threads.size(); i++)
|
||||
for (int j = 0; j < Threads[i]->splitPointsSize; j++)
|
||||
for (size_t i = 0; i < Threads.size(); ++i)
|
||||
for (int j = 0; j < Threads[i]->splitPointsSize; ++j)
|
||||
{
|
||||
SplitPoint& sp = Threads[i]->splitPoints[j];
|
||||
|
||||
|
@ -1800,7 +1736,7 @@ void check_time() {
|
|||
&& !Signals.failedLowAtRoot
|
||||
&& elapsed > TimeMgr.available_time();
|
||||
|
||||
bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerResolution
|
||||
bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerThread::Resolution
|
||||
|| stillAtFirstMove;
|
||||
|
||||
if ( (Limits.use_time_management() && noMoreTime)
|
||||
|
|
|
@ -45,9 +45,7 @@ struct Stack {
|
|||
Move killers[2];
|
||||
Depth reduction;
|
||||
Value staticEval;
|
||||
Value evalMargin;
|
||||
int skipNullMove;
|
||||
int futilityMoveCount;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -101,11 +101,11 @@ void TimerThread::idle_loop() {
|
|||
mutex.lock();
|
||||
|
||||
if (!exit)
|
||||
sleepCondition.wait_for(mutex, msec ? msec : INT_MAX);
|
||||
sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX);
|
||||
|
||||
mutex.unlock();
|
||||
|
||||
if (msec)
|
||||
if (run)
|
||||
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
|
||||
// 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
|
||||
// which are busy searching the split point at the top of slaves split point
|
||||
// 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)
|
||||
return false;
|
||||
|
@ -241,7 +241,7 @@ void ThreadPool::read_uci_options() {
|
|||
Thread* ThreadPool::available_slave(const Thread* master) const {
|
||||
|
||||
for (const_iterator it = begin(); it != end(); ++it)
|
||||
if ((*it)->is_available_to(master))
|
||||
if ((*it)->available_to(master))
|
||||
return *it;
|
||||
|
||||
return NULL;
|
||||
|
@ -296,7 +296,7 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
|
|||
Threads.mutex.lock();
|
||||
sp.mutex.lock();
|
||||
|
||||
splitPointsSize++;
|
||||
++splitPointsSize;
|
||||
activeSplitPoint = &sp;
|
||||
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
|
||||
// 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();
|
||||
sp.mutex.lock();
|
||||
}
|
||||
|
||||
searching = true;
|
||||
splitPointsSize--;
|
||||
--splitPointsSize;
|
||||
activeSplitPoint = sp.parentSplitPoint;
|
||||
activePosition = &pos;
|
||||
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
|
||||
|
|
|
@ -114,7 +114,7 @@ struct Thread : public ThreadBase {
|
|||
Thread();
|
||||
virtual void idle_loop();
|
||||
bool cutoff_occurred() const;
|
||||
bool is_available_to(const Thread* master) const;
|
||||
bool available_to(const Thread* master) const;
|
||||
|
||||
template <bool Fake>
|
||||
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 {
|
||||
TimerThread() : msec(0) {}
|
||||
TimerThread() : run(false) {}
|
||||
virtual void idle_loop();
|
||||
int msec;
|
||||
bool run;
|
||||
static const int Resolution = 5; // msec between two check_time() calls
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "search.h"
|
||||
#include "timeman.h"
|
||||
|
@ -29,8 +29,8 @@ namespace {
|
|||
/// Constants
|
||||
|
||||
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 float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio
|
||||
const double MaxRatio = 7.0; // When in trouble, we can step over reserved time with 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
|
||||
|
@ -76,10 +76,9 @@ namespace {
|
|||
}
|
||||
|
||||
|
||||
void TimeManager::pv_instability(int curChanges, int prevChanges) {
|
||||
void TimeManager::pv_instability(double bestMoveChanges) {
|
||||
|
||||
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
|
||||
+ prevChanges * (optimumSearchTime / 3);
|
||||
unstablePVExtraTime = int(bestMoveChanges * optimumSearchTime / 1.4);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
// 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
|
||||
hypMyTime = limits.time[us]
|
||||
|
@ -145,17 +144,17 @@ namespace {
|
|||
template<TimeType T>
|
||||
int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
|
||||
{
|
||||
const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
|
||||
const float TStealRatio = (T == OptimumTime ? 0 : StealRatio);
|
||||
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
|
||||
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;
|
||||
|
||||
for (int i = 1; i < movesToGo; i++)
|
||||
for (int i = 1; i < movesToGo; ++i)
|
||||
otherMovesImportance += move_importance(currentPly + 2 * i);
|
||||
|
||||
float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance);
|
||||
float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance);
|
||||
double ratio1 = (TMaxRatio * thisMoveImportance) / (TMaxRatio * thisMoveImportance + otherMovesImportance);
|
||||
double ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / (thisMoveImportance + otherMovesImportance);
|
||||
|
||||
return int(floor(myTime * std::min(ratio1, ratio2)));
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
class TimeManager {
|
||||
public:
|
||||
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 maximum_time() const { return maximumSearchTime; }
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ const TTEntry* TranspositionTable::probe(const Key key) const {
|
|||
const TTEntry* tte = first_entry(key);
|
||||
uint32_t key32 = key >> 32;
|
||||
|
||||
for (unsigned i = 0; i < ClusterSize; i++, tte++)
|
||||
for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
|
||||
if (tte->key() == key32)
|
||||
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
|
||||
/// 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;
|
||||
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);
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -117,5 +117,5 @@ void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m,
|
|||
replace = tte;
|
||||
}
|
||||
|
||||
replace->save(key32, v, b, d, m, generation, statV, evalM);
|
||||
replace->save(key32, v, b, d, m, generation, statV);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
struct TTEntry {
|
||||
|
||||
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;
|
||||
move16 = (uint16_t)m;
|
||||
|
@ -45,7 +45,6 @@ struct TTEntry {
|
|||
value16 = (int16_t)v;
|
||||
depth16 = (int16_t)d;
|
||||
evalValue = (int16_t)ev;
|
||||
evalMargin = (int16_t)em;
|
||||
}
|
||||
void set_generation(uint8_t g) { generation8 = g; }
|
||||
|
||||
|
@ -56,13 +55,12 @@ struct TTEntry {
|
|||
Bound bound() const { return (Bound)bound8; }
|
||||
int generation() const { return (int)generation8; }
|
||||
Value eval_value() const { return (Value)evalValue; }
|
||||
Value eval_margin() const { return (Value)evalMargin; }
|
||||
|
||||
private:
|
||||
uint32_t key32;
|
||||
uint16_t move16;
|
||||
uint8_t bound8, generation8;
|
||||
int16_t value16, depth16, evalValue, evalMargin;
|
||||
int16_t value16, depth16, evalValue;
|
||||
};
|
||||
|
||||
|
||||
|
@ -78,14 +76,14 @@ class TranspositionTable {
|
|||
|
||||
public:
|
||||
~TranspositionTable() { free(mem); }
|
||||
void new_search() { generation++; }
|
||||
void new_search() { ++generation; }
|
||||
|
||||
const TTEntry* probe(const Key key) const;
|
||||
TTEntry* first_entry(const Key key) const;
|
||||
void refresh(const TTEntry* tte) const;
|
||||
void set_size(size_t mbSize);
|
||||
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:
|
||||
uint32_t hashMask;
|
||||
|
|
|
@ -88,7 +88,7 @@ const bool Is64Bit = false;
|
|||
typedef uint64_t Key;
|
||||
typedef uint64_t Bitboard;
|
||||
|
||||
const int MAX_MOVES = 192;
|
||||
const int MAX_MOVES = 256;
|
||||
const int MAX_PLY = 100;
|
||||
const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
|
||||
|
||||
|
@ -194,9 +194,9 @@ enum Depth {
|
|||
ONE_PLY = 2,
|
||||
|
||||
DEPTH_ZERO = 0 * ONE_PLY,
|
||||
DEPTH_QS_CHECKS = -1 * ONE_PLY,
|
||||
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
|
||||
DEPTH_QS_RECAPTURES = -7 * ONE_PLY,
|
||||
DEPTH_QS_CHECKS = 0 * ONE_PLY,
|
||||
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
|
||||
DEPTH_QS_RECAPTURES = -5 * 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*(const T d, int i) { return T(int(d) * i); } \
|
||||
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) { d1 = d1 - d2; return d1; } \
|
||||
inline T& operator*=(T& d, int i) { d = T(int(d) * i); return d; }
|
||||
inline T& operator+=(T& d1, const T d2) { return d1 = d1 + d2; } \
|
||||
inline T& operator-=(T& d1, const T d2) { return d1 = d1 - d2; } \
|
||||
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); }
|
||||
|
||||
#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, int) { d = T(int(d) - 1); return d; } \
|
||||
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
|
||||
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/=(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(PieceType)
|
||||
|
@ -323,11 +323,11 @@ inline bool operator<(const ExtMove& f, const ExtMove& s) {
|
|||
}
|
||||
|
||||
inline Color operator~(Color c) {
|
||||
return Color(c ^ 1);
|
||||
return Color(c ^ BLACK);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -371,10 +371,6 @@ inline Rank rank_of(Square s) {
|
|||
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) {
|
||||
return Square(s ^ (c * 56));
|
||||
}
|
||||
|
|
|
@ -80,10 +80,10 @@ void init(OptionsMap& o) {
|
|||
o["MultiPV"] = Option(1, 1, 500);
|
||||
o["Skill Level"] = Option(20, 0, 20);
|
||||
o["Emergency Move Horizon"] = Option(40, 0, 50);
|
||||
o["Emergency Base Time"] = Option(200, 0, 30000);
|
||||
o["Emergency Move Time"] = Option(70, 0, 5000);
|
||||
o["Emergency Base Time"] = Option(60, 0, 30000);
|
||||
o["Emergency Move Time"] = Option(30, 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_AnalyseMode"] = Option(false, on_eval);
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ void init(OptionsMap& o) {
|
|||
|
||||
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)
|
||||
if (it->second.idx == idx)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user