DroidFish: Updated stockfish engine to version DD.

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

View File

@ -47,7 +47,21 @@ static const char* Defaults[] = {
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"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());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -43,10 +43,10 @@ struct CheckInfo {
};
/// The StateInfo struct stores information we need to restore a Position
/// The StateInfo struct stores information needed to restore a Position
/// object to its previous state when we retract a move. Whenever a move
/// 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;

View File

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

View File

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

View File

@ -45,9 +45,7 @@ struct Stack {
Move killers[2];
Depth reduction;
Value staticEval;
Value evalMargin;
int skipNullMove;
int futilityMoveCount;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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