From 43e92323e4a5d79762a4f6b9ff209215e24a16a0 Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Sat, 30 Nov 2013 19:12:34 +0000 Subject: [PATCH] DroidFish: Updated stockfish engine to version DD. --- DroidFish/jni/stockfish/benchmark.cpp | 22 +- DroidFish/jni/stockfish/bitbase.cpp | 42 +- DroidFish/jni/stockfish/bitboard.cpp | 48 +- DroidFish/jni/stockfish/bitboard.h | 14 +- DroidFish/jni/stockfish/book.cpp | 4 +- DroidFish/jni/stockfish/endgame.cpp | 695 ++++++++++++-------------- DroidFish/jni/stockfish/endgame.h | 10 +- DroidFish/jni/stockfish/evaluate.cpp | 433 ++++++---------- DroidFish/jni/stockfish/evaluate.h | 2 +- DroidFish/jni/stockfish/main.cpp | 3 +- DroidFish/jni/stockfish/material.cpp | 37 +- DroidFish/jni/stockfish/misc.cpp | 8 +- DroidFish/jni/stockfish/movegen.cpp | 35 +- DroidFish/jni/stockfish/movegen.h | 2 +- DroidFish/jni/stockfish/movepick.cpp | 28 +- DroidFish/jni/stockfish/notation.cpp | 10 +- DroidFish/jni/stockfish/pawns.cpp | 103 ++-- DroidFish/jni/stockfish/pawns.h | 3 + DroidFish/jni/stockfish/position.cpp | 172 +++---- DroidFish/jni/stockfish/position.h | 89 ++-- DroidFish/jni/stockfish/rkiss.h | 19 +- DroidFish/jni/stockfish/search.cpp | 300 +++++------ DroidFish/jni/stockfish/search.h | 2 - DroidFish/jni/stockfish/thread.cpp | 16 +- DroidFish/jni/stockfish/thread.h | 7 +- DroidFish/jni/stockfish/timeman.cpp | 25 +- DroidFish/jni/stockfish/timeman.h | 2 +- DroidFish/jni/stockfish/tt.cpp | 8 +- DroidFish/jni/stockfish/tt.h | 10 +- DroidFish/jni/stockfish/types.h | 28 +- DroidFish/jni/stockfish/ucioption.cpp | 8 +- 31 files changed, 940 insertions(+), 1245 deletions(-) diff --git a/DroidFish/jni/stockfish/benchmark.cpp b/DroidFish/jni/stockfish/benchmark.cpp index f22ea6d..450afa9 100644 --- a/DroidFish/jni/stockfish/benchmark.cpp +++ b/DroidFish/jni/stockfish/benchmark.cpp @@ -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()); diff --git a/DroidFish/jni/stockfish/bitbase.cpp b/DroidFish/jni/stockfish/bitbase.cpp index 570a977..37b0564 100644 --- a/DroidFish/jni/stockfish/bitbase.cpp +++ b/DroidFish/jni/stockfish/bitbase.cpp @@ -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& db) { return us == WHITE ? classify(db) : classify(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 db(IndexMax); + std::vector 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 @@ -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 diff --git a/DroidFish/jni/stockfish/bitboard.cpp b/DroidFish/jni/stockfish/bitboard.cpp index 9598aa7..3683b36 100644 --- a/DroidFish/jni/stockfish/bitboard.cpp +++ b/DroidFish/jni/stockfish/bitboard.cpp @@ -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); init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index); - 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(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])]; diff --git a/DroidFish/jni/stockfish/bitboard.h b/DroidFish/jni/stockfish/bitboard.h index bc3ed87..e95bf0d 100644 --- a/DroidFish/jni/stockfish/bitboard.h +++ b/DroidFish/jni/stockfish/bitboard.h @@ -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 diff --git a/DroidFish/jni/stockfish/book.cpp b/DroidFish/jni/stockfish/book.cpp index caf2b06..ec0a0f8 100644 --- a/DroidFish/jni/stockfish/book.cpp +++ b/DroidFish/jni/stockfish/book.cpp @@ -361,7 +361,7 @@ PolyglotBook::~PolyglotBook() { if (is_open()) close(); } template 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() % sum < e.count) + if ( (!pickBest && sum && rkiss.rand() % sum < e.count) || (pickBest && e.count == best)) move = Move(e.move); } diff --git a/DroidFish/jni/stockfish/endgame.cpp b/DroidFish/jni/stockfish/endgame.cpp index cc98de9..6ead680 100644 --- a/DroidFish/jni/stockfish/endgame.cpp +++ b/DroidFish/jni/stockfish/endgame.cpp @@ -29,9 +29,9 @@ using std::string; namespace { - // Table used to drive the defending king towards the edge of the board + // Table used to drive the king towards the edge of the board // in KX vs K and KQ vs KR endgames. - const int MateTable[SQUARE_NB] = { + const int PushToEdges[SQUARE_NB] = { 100, 90, 80, 70, 70, 80, 90, 100, 90, 70, 60, 50, 50, 60, 70, 90, 80, 60, 40, 30, 30, 40, 60, 80, @@ -42,9 +42,9 @@ namespace { 100, 90, 80, 70, 70, 80, 90, 100, }; - // Table used to drive the defending king towards a corner square of the + // Table used to drive the king towards a corner square of the // right color in KBN vs K endgames. - const int KBNKMateTable[SQUARE_NB] = { + const int PushToCorners[SQUARE_NB] = { 200, 190, 180, 170, 160, 150, 140, 130, 190, 180, 170, 160, 150, 140, 130, 140, 180, 170, 155, 140, 140, 125, 140, 150, @@ -55,9 +55,30 @@ namespace { 130, 140, 150, 160, 170, 180, 190, 200 }; - // The attacking side is given a descending bonus based on distance between - // the two kings in basic endgames. - const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; + // Tables used to drive a piece towards or away from another piece + const int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; + const int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 }; + +#ifndef NDEBUG + bool verify_material(const Position& pos, Color c, Value npm, int num_pawns) { + return pos.non_pawn_material(c) == npm && pos.count(c) == num_pawns; + } +#endif + + // Map the square as if strongSide is white and strongSide's only pawn + // is on the left half of the board. + Square normalize(const Position& pos, Color strongSide, Square sq) { + + assert(pos.count(strongSide) == 1); + + if (file_of(pos.list(strongSide)[0]) >= FILE_E) + sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 + + if (strongSide == BLACK) + sq = ~sq; + + return sq; + } // Get the material key of a Position out of the given endgame key code // like "KBPKN". The trick here is to first forge an ad-hoc fen string @@ -68,8 +89,8 @@ namespace { assert(code.length() > 0 && code.length() < 8); assert(code[0] == 'K'); - string sides[] = { code.substr(code.find('K', 1)), // Weaker - code.substr(0, code.find('K', 1)) }; // Stronger + string sides[] = { code.substr(code.find('K', 1)), // Weak + code.substr(0, code.find('K', 1)) }; // Strong std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); @@ -89,10 +110,7 @@ namespace { Endgames::Endgames() { - add("KK"); add("KPK"); - add("KBK"); - add("KNK"); add("KNNK"); add("KBNK"); add("KRKP"); @@ -105,6 +123,7 @@ Endgames::Endgames() { add("KNPK"); add("KNPKB"); add("KRPKR"); + add("KRPKB"); add("KBPKB"); add("KBPKN"); add("KBPPKB"); @@ -132,28 +151,27 @@ void Endgames::add(const string& code) { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(!pos.count(weakerSide)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(!pos.checkers()); // Eval is never called when in check // Stalemate detection with lone king - if (pos.side_to_move() == weakerSide && !MoveList(pos).size()) + if (pos.side_to_move() == weakSide && !MoveList(pos).size()) return VALUE_DRAW; - Square winnerKSq = pos.king_square(strongerSide); - Square loserKSq = pos.king_square(weakerSide); + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); - Value result = pos.non_pawn_material(strongerSide) - + pos.count(strongerSide) * PawnValueEg - + MateTable[loserKSq] - + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + Value result = pos.non_pawn_material(strongSide) + + pos.count(strongSide) * PawnValueEg + + PushToEdges[loserKSq] + + PushClose[square_distance(winnerKSq, loserKSq)]; - if ( pos.count(strongerSide) - || pos.count(strongerSide) - || pos.bishop_pair(strongerSide)) + if ( pos.count(strongSide) + || pos.count(strongSide) + || pos.bishop_pair(strongSide)) result += VALUE_KNOWN_WIN; - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -162,31 +180,27 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg); - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(pos.count(strongerSide) == 1); - assert(pos.count(strongerSide) == 1); - assert(pos.count< PAWN>(strongerSide) == 0); - assert(pos.count< PAWN>(weakerSide ) == 0); + assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square winnerKSq = pos.king_square(strongerSide); - Square loserKSq = pos.king_square(weakerSide); - Square bishopSq = pos.list(strongerSide)[0]; + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); + Square bishopSq = pos.list(strongSide)[0]; // kbnk_mate_table() tries to drive toward corners A1 or H8, // if we have a bishop that cannot reach the above squares we - // mirror the kings so to drive enemy toward corners A8 or H1. + // flip the kings so to drive enemy toward corners A8 or H1. if (opposite_colors(bishopSq, SQ_A1)) { - winnerKSq = mirror(winnerKSq); - loserKSq = mirror(loserKSq); + winnerKSq = ~winnerKSq; + loserKSq = ~loserKSq; } Value result = VALUE_KNOWN_WIN - + DistanceBonus[square_distance(winnerKSq, loserKSq)] - + KBNKMateTable[loserKSq]; + + PushClose[square_distance(winnerKSq, loserKSq)] + + PushToCorners[loserKSq]; - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -194,42 +208,22 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 0); + assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square wksq, bksq, wpsq; - Color us; + // Assume strongSide is white and the pawn is on files A-D + Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); + Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); + Square psq = normalize(pos, strongSide, pos.list(strongSide)[0]); - if (strongerSide == WHITE) - { - wksq = pos.king_square(WHITE); - bksq = pos.king_square(BLACK); - wpsq = pos.list(WHITE)[0]; - us = pos.side_to_move(); - } - else - { - wksq = ~pos.king_square(BLACK); - bksq = ~pos.king_square(WHITE); - wpsq = ~pos.list(BLACK)[0]; - us = ~pos.side_to_move(); - } + Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; - if (file_of(wpsq) >= FILE_E) - { - wksq = mirror(wksq); - bksq = mirror(bksq); - wpsq = mirror(wpsq); - } - - if (!Bitbases::probe_kpk(wksq, wpsq, bksq, us)) + if (!Bitbases::probe_kpk(wksq, psq, bksq, us)) return VALUE_DRAW; - Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq)); + Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -240,55 +234,42 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide) == 0); - assert(pos.count(strongerSide) == 0); - assert(pos.count(weakerSide ) == 1); + assert(verify_material(pos, strongSide, RookValueMg, 0)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square wksq, wrsq, bksq, bpsq; - int tempo = (pos.side_to_move() == strongerSide); + Square wksq = relative_square(strongSide, pos.king_square(strongSide)); + Square bksq = relative_square(strongSide, pos.king_square(weakSide)); + Square rsq = relative_square(strongSide, pos.list(strongSide)[0]); + Square psq = relative_square(strongSide, pos.list(weakSide)[0]); - wksq = pos.king_square(strongerSide); - bksq = pos.king_square(weakerSide); - wrsq = pos.list(strongerSide)[0]; - bpsq = pos.list(weakerSide)[0]; - - if (strongerSide == BLACK) - { - wksq = ~wksq; - wrsq = ~wrsq; - bksq = ~bksq; - bpsq = ~bpsq; - } - - Square queeningSq = file_of(bpsq) | RANK_1; + Square queeningSq = file_of(psq) | RANK_1; Value result; // If the stronger side's king is in front of the pawn, it's a win - if (wksq < bpsq && file_of(wksq) == file_of(bpsq)) - result = RookValueEg - Value(square_distance(wksq, bpsq)); + if (wksq < psq && file_of(wksq) == file_of(psq)) + result = RookValueEg - Value(square_distance(wksq, psq)); // If the weaker side's king is too far from the pawn and the rook, - // it's a win - else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3 - && square_distance(bksq, wrsq) >= 3) - result = RookValueEg - Value(square_distance(wksq, bpsq)); + // it's a win. + else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) + && square_distance(bksq, rsq) >= 3) + result = RookValueEg - Value(square_distance(wksq, psq)); // If the pawn is far advanced and supported by the defending king, // the position is drawish else if ( rank_of(bksq) <= RANK_3 - && square_distance(bksq, bpsq) == 1 + && square_distance(bksq, psq) == 1 && rank_of(wksq) >= RANK_4 - && square_distance(wksq, bpsq) - tempo > 2) - result = Value(80 - square_distance(wksq, bpsq) * 8); + && square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) + result = Value(80 - square_distance(wksq, psq) * 8); else result = Value(200) - - Value(square_distance(wksq, bpsq + DELTA_S) * 8) - + Value(square_distance(bksq, bpsq + DELTA_S) * 8) - + Value(square_distance(bpsq, queeningSq) * 8); + - Value(square_distance(wksq, psq + DELTA_S) * 8) + + Value(square_distance(bksq, psq + DELTA_S) * 8) + + Value(square_distance(psq, queeningSq) * 8); - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -297,14 +278,11 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); - assert(pos.count< PAWN>(strongerSide) == 0); + assert(verify_material(pos, strongSide, RookValueMg, 0)); + assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Value result = Value(MateTable[pos.king_square(weakerSide)]); - return strongerSide == pos.side_to_move() ? result : -result; + Value result = Value(PushToEdges[pos.king_square(weakSide)]); + return strongSide == pos.side_to_move() ? result : -result; } @@ -313,18 +291,13 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); - assert(pos.count< PAWN>(strongerSide) == 0); + assert(verify_material(pos, strongSide, RookValueMg, 0)); + assert(verify_material(pos, weakSide, KnightValueMg, 0)); - const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 }; - - Square bksq = pos.king_square(weakerSide); - Square bnsq = pos.list(weakerSide)[0]; - Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]); - return strongerSide == pos.side_to_move() ? result : -result; + Square bksq = pos.king_square(weakSide); + Square bnsq = pos.list(weakSide)[0]; + Value result = Value(PushToEdges[bksq] + PushAway[square_distance(bksq, bnsq)]); + return strongSide == pos.side_to_move() ? result : -result; } @@ -334,28 +307,21 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMg); - assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); - assert(pos.count(strongerSide) == 0); - assert(pos.count(weakerSide ) == 1); + assert(verify_material(pos, strongSide, QueenValueMg, 0)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square winnerKSq = pos.king_square(strongerSide); - Square loserKSq = pos.king_square(weakerSide); - Square pawnSq = pos.list(weakerSide)[0]; + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); + Square pawnSq = pos.list(weakSide)[0]; - Value result = QueenValueEg - - PawnValueEg - + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + Value result = Value(PushClose[square_distance(winnerKSq, loserKSq)]); - if ( square_distance(loserKSq, pawnSq) == 1 - && relative_rank(weakerSide, pawnSq) == RANK_7) - { - File f = file_of(pawnSq); + if ( relative_rank(weakSide, pawnSq) != RANK_7 + || square_distance(loserKSq, pawnSq) != 1 + || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) + result += QueenValueEg - PawnValueEg; - if (f == FILE_A || f == FILE_C || f == FILE_F || f == FILE_H) - result = Value(DistanceBonus[square_distance(winnerKSq, loserKSq)]); - } - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -367,53 +333,45 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMg); - assert(pos.non_pawn_material(weakerSide ) == RookValueMg); - assert(pos.count(strongerSide) == 0); - assert(pos.count(weakerSide ) == 0); + assert(verify_material(pos, strongSide, QueenValueMg, 0)); + assert(verify_material(pos, weakSide, RookValueMg, 0)); - Square winnerKSq = pos.king_square(strongerSide); - Square loserKSq = pos.king_square(weakerSide); + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); Value result = QueenValueEg - RookValueEg - + MateTable[loserKSq] - + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + + PushToEdges[loserKSq] + + PushClose[square_distance(winnerKSq, loserKSq)]; - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } + +/// KBB vs KN. This is almost always a win. We try to push enemy king to a corner +/// and away from his knight. For a reference of this difficult endgame see: +/// en.wikipedia.org/wiki/Chess_endgame#Effect_of_tablebases_on_endgame_theory + template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg); - assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); - assert(pos.count(strongerSide) == 2); - assert(pos.count(weakerSide ) == 1); - assert(!pos.pieces(PAWN)); + assert(verify_material(pos, strongSide, 2 * BishopValueMg, 0)); + assert(verify_material(pos, weakSide, KnightValueMg, 0)); - Value result = BishopValueEg; - Square wksq = pos.king_square(strongerSide); - Square bksq = pos.king_square(weakerSide); - Square nsq = pos.list(weakerSide)[0]; + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); + Square knightSq = pos.list(weakSide)[0]; - // Bonus for attacking king close to defending king - result += Value(DistanceBonus[square_distance(wksq, bksq)]); + Value result = VALUE_KNOWN_WIN + + PushToCorners[loserKSq] + + PushClose[square_distance(winnerKSq, loserKSq)] + + PushAway[square_distance(loserKSq, knightSq)]; - // Bonus for driving the defending king and knight apart - result += Value(square_distance(bksq, nsq) * 32); - - // Bonus for restricting the knight's mobility - result += Value((8 - popcount(pos.attacks_from(nsq))) * 8); - - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } /// Some cases of trivial draws -template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } -template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } -template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } @@ -425,70 +383,61 @@ template<> Value Endgame::operator()(const Position&) const { return VALU template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count< PAWN>(strongerSide) >= 1); + assert(pos.non_pawn_material(strongSide) == BishopValueMg); + assert(pos.count(strongSide) >= 1); - // No assertions about the material of weakerSide, because we want draws to + // No assertions about the material of weakSide, because we want draws to // be detected even when the weaker side has some pawns. - Bitboard pawns = pos.pieces(strongerSide, PAWN); - File pawnFile = file_of(pos.list(strongerSide)[0]); + Bitboard pawns = pos.pieces(strongSide, PAWN); + File pawnFile = file_of(pos.list(strongSide)[0]); // All pawns are on a single rook file ? if ( (pawnFile == FILE_A || pawnFile == FILE_H) && !(pawns & ~file_bb(pawnFile))) { - Square bishopSq = pos.list(strongerSide)[0]; - Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8); - Square kingSq = pos.king_square(weakerSide); + Square bishopSq = pos.list(strongSide)[0]; + Square queeningSq = relative_square(strongSide, pawnFile | RANK_8); + Square kingSq = pos.king_square(weakSide); if ( opposite_colors(queeningSq, bishopSq) - && abs(file_of(kingSq) - pawnFile) <= 1) - { - // The bishop has the wrong color, and the defending king is on the - // file of the pawn(s) or the adjacent file. Find the rank of the - // frontmost pawn. - Rank rank; - if (strongerSide == WHITE) - { - for (rank = RANK_7; !(rank_bb(rank) & pawns); rank--) {} - assert(rank >= RANK_2 && rank <= RANK_7); - } - else - { - for (rank = RANK_2; !(rank_bb(rank) & pawns); rank++) {} - rank = Rank(rank ^ 7); // HACK to get the relative rank - assert(rank >= RANK_2 && rank <= RANK_7); - } - // If the defending king has distance 1 to the promotion square or - // is placed somewhere in front of the pawn, it's a draw. - if ( square_distance(kingSq, queeningSq) <= 1 - || relative_rank(strongerSide, kingSq) >= rank) - return SCALE_FACTOR_DRAW; - } + && square_distance(queeningSq, kingSq) <= 1) + return SCALE_FACTOR_DRAW; } // All pawns on same B or G file? Then potential draw if ( (pawnFile == FILE_B || pawnFile == FILE_G) && !(pos.pieces(PAWN) & ~file_bb(pawnFile)) - && pos.non_pawn_material(weakerSide) == 0 - && pos.count(weakerSide) >= 1) + && pos.non_pawn_material(weakSide) == 0 + && pos.count(weakSide) >= 1) { - // Get weaker pawn closest to opponent's queening square - Bitboard wkPawns = pos.pieces(weakerSide, PAWN); - Square weakerPawnSq = strongerSide == WHITE ? msb(wkPawns) : lsb(wkPawns); + // Get weakSide pawn that is closest to home rank + Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN)); - Square strongerKingSq = pos.king_square(strongerSide); - Square weakerKingSq = pos.king_square(weakerSide); - Square bishopSq = pos.list(strongerSide)[0]; + Square strongKingSq = pos.king_square(strongSide); + Square weakKingSq = pos.king_square(weakSide); + Square bishopSq = pos.list(strongSide)[0]; - // Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and - // weaker king can stop opposing opponent's king from penetrating. - if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7 - && opposite_colors(bishopSq, weakerPawnSq) - && square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq)) - return SCALE_FACTOR_DRAW; + // Potential for a draw if our pawn is blocked on the 7th rank + // the bishop cannot attack it or they only have one pawn left + if ( relative_rank(strongSide, weakPawnSq) == RANK_7 + && (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide))) + && (opposite_colors(bishopSq, weakPawnSq) || pos.count(strongSide) == 1)) + { + int strongKingDist = square_distance(weakPawnSq, strongKingSq); + int weakKingDist = square_distance(weakPawnSq, weakKingSq); + + // Draw if the weak king is on it's back two ranks, within 2 + // squares of the blocking pawn and the strong king is not + // closer. (I think this rule only fails in practically + // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w + // and positions where qsearch will immediately correct the + // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) + if ( relative_rank(strongSide, weakKingSq) >= RANK_7 + && weakKingDist <= 2 + && weakKingDist <= strongKingDist) + return SCALE_FACTOR_DRAW; + } } return SCALE_FACTOR_NONE; @@ -500,23 +449,21 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count< PAWN>(strongerSide) == 0); - assert(pos.count< ROOK>(weakerSide ) == 1); - assert(pos.count< PAWN>(weakerSide ) >= 1); + assert(verify_material(pos, strongSide, QueenValueMg, 0)); + assert(pos.count(weakSide) == 1); + assert(pos.count(weakSide) >= 1); - Square kingSq = pos.king_square(weakerSide); - if ( relative_rank(weakerSide, kingSq) <= RANK_2 - && relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4 - && (pos.pieces(weakerSide, ROOK) & rank_bb(relative_rank(weakerSide, RANK_3))) - && (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2))) - && (pos.attacks_from(kingSq) & pos.pieces(weakerSide, PAWN))) - { - Square rsq = pos.list(weakerSide)[0]; - if (pos.attacks_from(rsq, strongerSide) & pos.pieces(weakerSide, PAWN)) + Square kingSq = pos.king_square(weakSide); + Square rsq = pos.list(weakSide)[0]; + + if ( relative_rank(weakSide, kingSq) <= RANK_2 + && relative_rank(weakSide, pos.king_square(strongSide)) >= RANK_4 + && relative_rank(weakSide, rsq) == RANK_3 + && ( pos.pieces(weakSide, PAWN) + & pos.attacks_from(kingSq) + & pos.attacks_from(rsq, strongSide))) return SCALE_FACTOR_DRAW; - } + return SCALE_FACTOR_NONE; } @@ -530,40 +477,20 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide) == RookValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 0); + assert(verify_material(pos, strongSide, RookValueMg, 1)); + assert(verify_material(pos, weakSide, RookValueMg, 0)); - Square wksq = pos.king_square(strongerSide); - Square bksq = pos.king_square(weakerSide); - Square wrsq = pos.list(strongerSide)[0]; - Square wpsq = pos.list(strongerSide)[0]; - Square brsq = pos.list(weakerSide)[0]; - - // Orient the board in such a way that the stronger side is white, and the - // pawn is on the left half of the board. - if (strongerSide == BLACK) - { - wksq = ~wksq; - wrsq = ~wrsq; - wpsq = ~wpsq; - bksq = ~bksq; - brsq = ~brsq; - } - if (file_of(wpsq) > FILE_D) - { - wksq = mirror(wksq); - wrsq = mirror(wrsq); - wpsq = mirror(wpsq); - bksq = mirror(bksq); - brsq = mirror(brsq); - } + // Assume strongSide is white and the pawn is on files A-D + Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); + Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); + Square wrsq = normalize(pos, strongSide, pos.list(strongSide)[0]); + Square wpsq = normalize(pos, strongSide, pos.list(strongSide)[0]); + Square brsq = normalize(pos, strongSide, pos.list(weakSide)[0]); File f = file_of(wpsq); Rank r = rank_of(wpsq); Square queeningSq = f | RANK_8; - int tempo = (pos.side_to_move() == strongerSide); + int tempo = (pos.side_to_move() == strongSide); // If the pawn is not too far advanced and the defending king defends the // queening square, use the third-rank defence. @@ -641,6 +568,49 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_NONE; } +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + assert(verify_material(pos, strongSide, RookValueMg, 1)); + assert(verify_material(pos, weakSide, BishopValueMg, 0)); + + // Test for a rook pawn + if (pos.pieces(PAWN) & (FileABB | FileHBB)) + { + Square ksq = pos.king_square(weakSide); + Square bsq = pos.list(weakSide)[0]; + Square psq = pos.list(strongSide)[0]; + Rank rk = relative_rank(strongSide, psq); + Square push = pawn_push(strongSide); + + // If the pawn is on the 5th rank and the pawn (currently) is on + // the same color square as the bishop then there is a chance of + // a fortress. Depending on the king position give a moderate + // reduction or a stronger one if the defending king is near the + // corner but not trapped there. + if (rk == RANK_5 && !opposite_colors(bsq, psq)) + { + int d = square_distance(psq + 3 * push, ksq); + + if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push)) + return ScaleFactor(24); + else + return ScaleFactor(48); + } + + // When the pawn has moved to the 6th rank we can be fairly sure + // it's drawn if the bishop attacks the square in front of the + // pawn from a reasonable distance and the defending king is near + // the corner + if ( rk == RANK_6 + && square_distance(psq + 2 * push, ksq) <= 1 + && (PseudoAttacks[BISHOP][bsq] & (psq + push)) + && file_distance(bsq, psq) >= 2) + return ScaleFactor(8); + } + + return SCALE_FACTOR_NONE; +} /// K, rook and two pawns vs K, rook and one pawn. There is only a single /// pattern: If the stronger side has no passed pawns and the defending king @@ -648,25 +618,22 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide) == RookValueMg); - assert(pos.count(strongerSide) == 2); - assert(pos.count(weakerSide ) == 1); + assert(verify_material(pos, strongSide, RookValueMg, 2)); + assert(verify_material(pos, weakSide, RookValueMg, 1)); - Square wpsq1 = pos.list(strongerSide)[0]; - Square wpsq2 = pos.list(strongerSide)[1]; - Square bksq = pos.king_square(weakerSide); + Square wpsq1 = pos.list(strongSide)[0]; + Square wpsq2 = pos.list(strongSide)[1]; + Square bksq = pos.king_square(weakSide); // Does the stronger side have a passed pawn? - if ( pos.pawn_is_passed(strongerSide, wpsq1) - || pos.pawn_is_passed(strongerSide, wpsq2)) + if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) return SCALE_FACTOR_NONE; - Rank r = std::max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2)); + Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); if ( file_distance(bksq, wpsq1) <= 1 && file_distance(bksq, wpsq2) <= 1 - && relative_rank(strongerSide, bksq) > r) + && relative_rank(strongSide, bksq) > r) { switch (r) { case RANK_2: return ScaleFactor(10); @@ -686,32 +653,21 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(pos.count(strongerSide) >= 2); - assert(pos.count(weakerSide ) == 0); + assert(pos.non_pawn_material(strongSide) == VALUE_ZERO); + assert(pos.count(strongSide) >= 2); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square ksq = pos.king_square(weakerSide); - Bitboard pawns = pos.pieces(strongerSide, PAWN); + Square ksq = pos.king_square(weakSide); + Bitboard pawns = pos.pieces(strongSide, PAWN); + Square psq = pos.list(strongSide)[0]; + + // If all pawns are ahead of the king, all pawns are on a single + // rook file and the king is within one file of the pawns then draw. + if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq))) + && !((pawns & ~FileABB) && (pawns & ~FileHBB)) + && file_distance(ksq, psq) <= 1) + return SCALE_FACTOR_DRAW; - // Are all pawns on the 'a' file? - if (!(pawns & ~FileABB)) - { - // Does the defending king block the pawns? - if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1 - || ( file_of(ksq) == FILE_A - && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns))) - return SCALE_FACTOR_DRAW; - } - // Are all pawns on the 'h' file? - else if (!(pawns & ~FileHBB)) - { - // Does the defending king block the pawns? - if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1 - || ( file_of(ksq) == FILE_H - && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns))) - return SCALE_FACTOR_DRAW; - } return SCALE_FACTOR_NONE; } @@ -723,27 +679,23 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMg); - assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(strongerSide) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); + assert(verify_material(pos, strongSide, BishopValueMg, 1)); + assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Square pawnSq = pos.list(strongerSide)[0]; - Square strongerBishopSq = pos.list(strongerSide)[0]; - Square weakerBishopSq = pos.list(weakerSide)[0]; - Square weakerKingSq = pos.king_square(weakerSide); + Square pawnSq = pos.list(strongSide)[0]; + Square strongBishopSq = pos.list(strongSide)[0]; + Square weakBishopSq = pos.list(weakSide)[0]; + Square weakKingSq = pos.king_square(weakSide); // Case 1: Defending king blocks the pawn, and cannot be driven away - if ( file_of(weakerKingSq) == file_of(pawnSq) - && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) - && ( opposite_colors(weakerKingSq, strongerBishopSq) - || relative_rank(strongerSide, weakerKingSq) <= RANK_6)) + if ( file_of(weakKingSq) == file_of(pawnSq) + && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) + && ( opposite_colors(weakKingSq, strongBishopSq) + || relative_rank(strongSide, weakKingSq) <= RANK_6)) return SCALE_FACTOR_DRAW; // Case 2: Opposite colored bishops - if (opposite_colors(strongerBishopSq, weakerBishopSq)) + if (opposite_colors(strongBishopSq, weakBishopSq)) { // We assume that the position is drawn in the following three situations: // @@ -755,17 +707,17 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // These rules are probably not perfect, but in practice they work // reasonably well. - if (relative_rank(strongerSide, pawnSq) <= RANK_5) + if (relative_rank(strongSide, pawnSq) <= RANK_5) return SCALE_FACTOR_DRAW; else { - Bitboard path = forward_bb(strongerSide, pawnSq); + Bitboard path = forward_bb(strongSide, pawnSq); - if (path & pos.pieces(weakerSide, KING)) + if (path & pos.pieces(weakSide, KING)) return SCALE_FACTOR_DRAW; - if ( (pos.attacks_from(weakerBishopSq) & path) - && square_distance(weakerBishopSq, pawnSq) >= 3) + if ( (pos.attacks_from(weakBishopSq) & path) + && square_distance(weakBishopSq, pawnSq) >= 3) return SCALE_FACTOR_DRAW; } } @@ -778,34 +730,30 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMg); - assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(strongerSide) == 2); - assert(pos.count< PAWN>(weakerSide ) == 0); + assert(verify_material(pos, strongSide, BishopValueMg, 2)); + assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Square wbsq = pos.list(strongerSide)[0]; - Square bbsq = pos.list(weakerSide)[0]; + Square wbsq = pos.list(strongSide)[0]; + Square bbsq = pos.list(weakSide)[0]; if (!opposite_colors(wbsq, bbsq)) return SCALE_FACTOR_NONE; - Square ksq = pos.king_square(weakerSide); - Square psq1 = pos.list(strongerSide)[0]; - Square psq2 = pos.list(strongerSide)[1]; + Square ksq = pos.king_square(weakSide); + Square psq1 = pos.list(strongSide)[0]; + Square psq2 = pos.list(strongSide)[1]; Rank r1 = rank_of(psq1); Rank r2 = rank_of(psq2); Square blockSq1, blockSq2; - if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2)) + if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) { - blockSq1 = psq1 + pawn_push(strongerSide); + blockSq1 = psq1 + pawn_push(strongSide); blockSq2 = file_of(psq2) | rank_of(psq1); } else { - blockSq1 = psq2 + pawn_push(strongerSide); + blockSq1 = psq2 + pawn_push(strongSide); blockSq2 = file_of(psq1) | rank_of(psq2); } @@ -815,7 +763,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // Both pawns are on the same file. Easy draw if defender firmly controls // some square in the frontmost pawn's path. if ( file_of(ksq) == file_of(blockSq1) - && relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1) + && relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1) && opposite_colors(ksq, wbsq)) return SCALE_FACTOR_DRAW; else @@ -828,14 +776,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if ( ksq == blockSq1 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq2 - || (pos.attacks_from(blockSq2) & pos.pieces(weakerSide, BISHOP)) + || (pos.attacks_from(blockSq2) & pos.pieces(weakSide, BISHOP)) || abs(r1 - r2) >= 2)) return SCALE_FACTOR_DRAW; else if ( ksq == blockSq2 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq1 - || (pos.attacks_from(blockSq1) & pos.pieces(weakerSide, BISHOP)))) + || (pos.attacks_from(blockSq1) & pos.pieces(weakSide, BISHOP)))) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; @@ -853,21 +801,17 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMg); - assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(strongerSide) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); + assert(verify_material(pos, strongSide, BishopValueMg, 1)); + assert(verify_material(pos, weakSide, KnightValueMg, 0)); - Square pawnSq = pos.list(strongerSide)[0]; - Square strongerBishopSq = pos.list(strongerSide)[0]; - Square weakerKingSq = pos.king_square(weakerSide); + Square pawnSq = pos.list(strongSide)[0]; + Square strongBishopSq = pos.list(strongSide)[0]; + Square weakKingSq = pos.king_square(weakSide); - if ( file_of(weakerKingSq) == file_of(pawnSq) - && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) - && ( opposite_colors(weakerKingSq, strongerBishopSq) - || relative_rank(strongerSide, weakerKingSq) <= RANK_6)) + if ( file_of(weakKingSq) == file_of(pawnSq) + && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) + && ( opposite_colors(weakKingSq, strongBishopSq) + || relative_rank(strongSide, weakKingSq) <= RANK_6)) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -880,21 +824,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == KnightValueMg); - assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); - assert(pos.count(strongerSide) == 1); - assert(pos.count< PAWN>(strongerSide) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); + assert(verify_material(pos, strongSide, KnightValueMg, 1)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square pawnSq = pos.list(strongerSide)[0]; - Square weakerKingSq = pos.king_square(weakerSide); + // Assume strongSide is white and the pawn is on files A-D + Square pawnSq = normalize(pos, strongSide, pos.list(strongSide)[0]); + Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide)); - if ( pawnSq == relative_square(strongerSide, SQ_A7) - && square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1) - return SCALE_FACTOR_DRAW; - - if ( pawnSq == relative_square(strongerSide, SQ_H7) - && square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1) + if (pawnSq == SQ_A7 && square_distance(SQ_A8, weakKingSq) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -906,14 +843,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - Square pawnSq = pos.list(strongerSide)[0]; - Square bishopSq = pos.list(weakerSide)[0]; - Square weakerKingSq = pos.king_square(weakerSide); + Square pawnSq = pos.list(strongSide)[0]; + Square bishopSq = pos.list(weakSide)[0]; + Square weakKingSq = pos.king_square(weakSide); // King needs to get close to promoting pawn to prevent knight from blocking. // Rules for this are very tricky, so just approximate. - if (forward_bb(strongerSide, pawnSq) & pos.attacks_from(bishopSq)) - return ScaleFactor(square_distance(weakerKingSq, pawnSq)); + if (forward_bb(strongSide, pawnSq) & pos.attacks_from(bishopSq)) + return ScaleFactor(square_distance(weakKingSq, pawnSq)); return SCALE_FACTOR_NONE; } @@ -927,38 +864,22 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); - assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); - assert(pos.count(WHITE) == 1); - assert(pos.count(BLACK) == 1); + assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square wksq = pos.king_square(strongerSide); - Square bksq = pos.king_square(weakerSide); - Square wpsq = pos.list(strongerSide)[0]; - Color us = pos.side_to_move(); + // Assume strongSide is white and the pawn is on files A-D + Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); + Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); + Square psq = normalize(pos, strongSide, pos.list(strongSide)[0]); - if (strongerSide == BLACK) - { - wksq = ~wksq; - bksq = ~bksq; - wpsq = ~wpsq; - us = ~us; - } - - if (file_of(wpsq) >= FILE_E) - { - wksq = mirror(wksq); - bksq = mirror(bksq); - wpsq = mirror(wpsq); - } + Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; // If the pawn has advanced to the fifth rank or further, and is not a // rook pawn, it's too dangerous to assume that it's at least a draw. - if ( rank_of(wpsq) >= RANK_5 - && file_of(wpsq) != FILE_A) + if (rank_of(psq) >= RANK_5 && file_of(psq) != FILE_A) return SCALE_FACTOR_NONE; // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // it's probably at least a draw even with the pawn. - return Bitbases::probe_kpk(wksq, wpsq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; + return Bitbases::probe_kpk(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; } diff --git a/DroidFish/jni/stockfish/endgame.h b/DroidFish/jni/stockfish/endgame.h index 5529eae..278ddb6 100644 --- a/DroidFish/jni/stockfish/endgame.h +++ b/DroidFish/jni/stockfish/endgame.h @@ -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 SCALE_FUNS)>::type> struct Endgame : public EndgameBase { - 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; }; diff --git a/DroidFish/jni/stockfish/evaluate.cpp b/DroidFish/jni/stockfish/evaluate.cpp index 12634dd..dd71955 100644 --- a/DroidFish/jni/stockfish/evaluate.cpp +++ b/DroidFish/jni/stockfish/evaluate.cpp @@ -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 - Value do_evaluate(const Position& pos, Value& margin); + Value do_evaluate(const Position& pos); template void init_eval_info(const Position& pos, EvalInfo& ei); template - 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 - Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]); + Score evaluate_king(const Position& pos, const EvalInfo& ei); template Score evaluate_threats(const Position& pos, const EvalInfo& ei); @@ -250,7 +249,7 @@ namespace { template 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(pos, margin); + Value evaluate(const Position& pos) { + return do_evaluate(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 -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(pos, ei); // Evaluate pieces and mobility - score += evaluate_pieces_of_color(pos, ei, mobilityWhite) - - evaluate_pieces_of_color(pos, ei, mobilityBlack); + score += evaluate_pieces_of_color(pos, ei, mobility) + - evaluate_pieces_of_color(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(pos, ei, margins) - - evaluate_king(pos, ei, margins); + score += evaluate_king(pos, ei) + - evaluate_king(pos, ei); // Evaluate tactical threats, we need full attack information including king score += evaluate_threats(pos, ei) @@ -365,9 +356,10 @@ Value do_evaluate(const Position& pos, Value& margin) { score += evaluate_passed_pawns(pos, ei) - evaluate_passed_pawns(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(pos, ei); Score b = ei.mi->space_weight() * evaluate_space(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(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(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 - 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(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(bb); } @@ -513,7 +508,7 @@ Value do_evaluate(const Position& pos, Value& margin) { int mob = Piece != QUEEN ? popcount(b & mobilityArea) : popcount(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(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 - 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 - 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(pos, ei, mobility, mobilityArea); - score += evaluate_pieces(pos, ei, mobility, mobilityArea); - score += evaluate_pieces(pos, ei, mobility, mobilityArea); - score += evaluate_pieces(pos, ei, mobility, mobilityArea); + Score score = evaluate_pieces(pos, ei, mobility, mobilityArea) + + evaluate_pieces(pos, ei, mobility, mobilityArea) + + evaluate_pieces(pos, ei, mobility, mobilityArea) + + evaluate_pieces(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 - 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(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(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 + 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 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(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(Us) < pos.count(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(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(pos, margin); + do_evaluate(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"; diff --git a/DroidFish/jni/stockfish/evaluate.h b/DroidFish/jni/stockfish/evaluate.h index 2234a4a..ae9b6e7 100644 --- a/DroidFish/jni/stockfish/evaluate.h +++ b/DroidFish/jni/stockfish/evaluate.h @@ -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); } diff --git a/DroidFish/jni/stockfish/main.cpp b/DroidFish/jni/stockfish/main.cpp index 3d26b63..c0a9a34 100644 --- a/DroidFish/jni/stockfish/main.cpp +++ b/DroidFish/jni/stockfish/main.cpp @@ -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); diff --git a/DroidFish/jni/stockfish/material.cpp b/DroidFish/jni/stockfish/material.cpp index abad2ab..068d88e 100644 --- a/DroidFish/jni/stockfish/material.cpp +++ b/DroidFish/jni/stockfish/material.cpp @@ -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(WHITE) && npm_w - npm_b <= BishopValueMg) { e->factor[WHITE] = (uint8_t) diff --git a/DroidFish/jni/stockfish/misc.cpp b/DroidFish/jni/stockfish/misc.cpp index 495ad4a..d11ab8d 100644 --- a/DroidFish/jni/stockfish/misc.cpp +++ b/DroidFish/jni/stockfish/misc.cpp @@ -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; } diff --git a/DroidFish/jni/stockfish/movegen.cpp b/DroidFish/jni/stockfish/movegen.cpp index fab5e46..a3623a6 100644 --- a/DroidFish/jni/stockfish/movegen.cpp +++ b/DroidFish/jni/stockfish/movegen.cpp @@ -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(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN))) return mlist; (mlist++)->move = make(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(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(checksq); - - default: - break; - } } while (b); // Generate evasions for king, capture and non capture moves @@ -407,17 +390,17 @@ template<> ExtMove* generate(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(pos, mlist) : generate(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; } diff --git a/DroidFish/jni/stockfish/movegen.h b/DroidFish/jni/stockfish/movegen.h index 2a4cda9..c93252a 100644 --- a/DroidFish/jni/stockfish/movegen.h +++ b/DroidFish/jni/stockfish/movegen.h @@ -42,7 +42,7 @@ template struct MoveList { explicit MoveList(const Position& pos) : cur(mlist), last(generate(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 { diff --git a/DroidFish/jni/stockfish/movepick.cpp b/DroidFish/jni/stockfish/movepick.cpp index 4f2fa01..8baf32a 100644 --- a/DroidFish/jni/stockfish/movepick.cpp +++ b/DroidFish/jni/stockfish/movepick.cpp @@ -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() { { 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() { 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() { 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() { 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() { 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; diff --git a/DroidFish/jni/stockfish/notation.cpp b/DroidFish/jni/stockfish/notation.cpp index c96b9db..d017c07 100644 --- a/DroidFish/jni/stockfish/notation.cpp +++ b/DroidFish/jni/stockfish/notation.cpp @@ -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(); } diff --git a/DroidFish/jni/stockfish/pawns.cpp b/DroidFish/jni/stockfish/pawns.cpp index 718dd57..d2a53ef 100644 --- a/DroidFish/jni/stockfish/pawns.cpp +++ b/DroidFish/jni/stockfish/pawns.cpp @@ -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(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(ourPawns) | shift_bb(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(s, Us) & theirPawns)) + if ( (passed | isolated | chain) + || (ourPawns & pawn_attack_span(Them, s)) + || (pos.attacks_from(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(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(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(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(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(pos, relative_square(Us, SQ_G1))); diff --git a/DroidFish/jni/stockfish/pawns.h b/DroidFish/jni/stockfish/pawns.h index 9f2af30..f6be84e 100644 --- a/DroidFish/jni/stockfish/pawns.h +++ b/DroidFish/jni/stockfish/pawns.h @@ -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 Table; +void init(); Entry* probe(const Position& pos, Table& entries); } diff --git a/DroidFish/jni/stockfish/position.cpp b/DroidFish/jni/stockfish/position.cpp index 615763c..c40b119 100644 --- a/DroidFish/jni/stockfish/position.cpp +++ b/DroidFish/jni/stockfish/position.cpp @@ -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(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(); - for (File f = FILE_A; f <= FILE_H; f++) + for (File f = FILE_A; f <= FILE_H; ++f) Zobrist::enpassant[f] = rk.rand(); - 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(); Zobrist::exclusion = rk.rand(); - 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(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(*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(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); diff --git a/DroidFish/jni/stockfish/position.h b/DroidFish/jni/stockfish/position.h index bf387dc..c333e55 100644 --- a/DroidFish/jni/stockfish/position.h +++ b/DroidFish/jni/stockfish/position.h @@ -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 int count(Color c) const; template 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 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; diff --git a/DroidFish/jni/stockfish/rkiss.h b/DroidFish/jni/stockfish/rkiss.h index 9564253..ffcaf35 100644 --- a/DroidFish/jni/stockfish/rkiss.h +++ b/DroidFish/jni/stockfish/rkiss.h @@ -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(); } diff --git a/DroidFish/jni/stockfish/search.cpp b/DroidFish/jni/stockfish/search.cpp index 2a7cde2..f1ab80f 100644 --- a/DroidFish/jni/stockfish/search.cpp +++ b/DroidFish/jni/stockfish/search.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -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 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(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()) != 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(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(improving, depth, moveCount); - futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount) - + Gains[pos.piece_moved(move)][to_sq(move)]; + predictedDepth = newDepth - reduction(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(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(); // 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(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) diff --git a/DroidFish/jni/stockfish/search.h b/DroidFish/jni/stockfish/search.h index e2a1360..ecc9a39 100644 --- a/DroidFish/jni/stockfish/search.h +++ b/DroidFish/jni/stockfish/search.h @@ -45,9 +45,7 @@ struct Stack { Move killers[2]; Depth reduction; Value staticEval; - Value evalMargin; int skipNullMove; - int futilityMoveCount; }; diff --git a/DroidFish/jni/stockfish/thread.cpp b/DroidFish/jni/stockfish/thread.cpp index 7d85db8..a6899de 100644 --- a/DroidFish/jni/stockfish/thread.cpp +++ b/DroidFish/jni/stockfish/thread.cpp @@ -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); diff --git a/DroidFish/jni/stockfish/thread.h b/DroidFish/jni/stockfish/thread.h index 56d000f..92f5ed3 100644 --- a/DroidFish/jni/stockfish/thread.h +++ b/DroidFish/jni/stockfish/thread.h @@ -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 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 }; diff --git a/DroidFish/jni/stockfish/timeman.cpp b/DroidFish/jni/stockfish/timeman.cpp index 8d2bacc..146818e 100644 --- a/DroidFish/jni/stockfish/timeman.cpp +++ b/DroidFish/jni/stockfish/timeman.cpp @@ -17,8 +17,8 @@ along with this program. If not, see . */ -#include #include +#include #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 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))); } diff --git a/DroidFish/jni/stockfish/timeman.h b/DroidFish/jni/stockfish/timeman.h index e12b000..92ab0e7 100644 --- a/DroidFish/jni/stockfish/timeman.h +++ b/DroidFish/jni/stockfish/timeman.h @@ -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; } diff --git a/DroidFish/jni/stockfish/tt.cpp b/DroidFish/jni/stockfish/tt.cpp index 9774a23..2643680 100644 --- a/DroidFish/jni/stockfish/tt.cpp +++ b/DroidFish/jni/stockfish/tt.cpp @@ -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); } diff --git a/DroidFish/jni/stockfish/tt.h b/DroidFish/jni/stockfish/tt.h index 3113751..fb50ddf 100644 --- a/DroidFish/jni/stockfish/tt.h +++ b/DroidFish/jni/stockfish/tt.h @@ -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; diff --git a/DroidFish/jni/stockfish/types.h b/DroidFish/jni/stockfish/types.h index 86a86d1..ff37638 100644 --- a/DroidFish/jni/stockfish/types.h +++ b/DroidFish/jni/stockfish/types.h @@ -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)); } diff --git a/DroidFish/jni/stockfish/ucioption.cpp b/DroidFish/jni/stockfish/ucioption.cpp index 384d931..08afa04 100644 --- a/DroidFish/jni/stockfish/ucioption.cpp +++ b/DroidFish/jni/stockfish/ucioption.cpp @@ -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) {