From a7bd973995c65ba1503f7220f482ef94e2a7744b Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Sun, 16 Sep 2012 15:16:15 +0000 Subject: [PATCH] DroidFish: Updated stockfish to version 2.3. --- DroidFish/jni/stockfish/Android.mk | 2 +- DroidFish/jni/stockfish/benchmark.cpp | 13 +- DroidFish/jni/stockfish/bitbase.cpp | 10 +- DroidFish/jni/stockfish/bitboard.cpp | 83 ++--- DroidFish/jni/stockfish/bitboard.h | 57 ++-- DroidFish/jni/stockfish/bitcount.h | 2 +- DroidFish/jni/stockfish/book.cpp | 75 ++--- DroidFish/jni/stockfish/book.h | 3 +- DroidFish/jni/stockfish/endgame.cpp | 81 +++-- DroidFish/jni/stockfish/evaluate.cpp | 32 +- DroidFish/jni/stockfish/lock.h | 66 ---- DroidFish/jni/stockfish/main.cpp | 14 +- DroidFish/jni/stockfish/material.cpp | 16 +- DroidFish/jni/stockfish/misc.cpp | 78 +++-- DroidFish/jni/stockfish/misc.h | 28 +- DroidFish/jni/stockfish/move.cpp | 161 --------- DroidFish/jni/stockfish/movegen.cpp | 275 +++++++--------- DroidFish/jni/stockfish/movegen.h | 20 +- DroidFish/jni/stockfish/movepick.cpp | 132 ++++---- DroidFish/jni/stockfish/movepick.h | 5 +- DroidFish/jni/stockfish/notation.cpp | 271 +++++++++++++++ DroidFish/jni/stockfish/notation.h | 35 ++ DroidFish/jni/stockfish/pawns.cpp | 13 +- DroidFish/jni/stockfish/pawns.h | 1 + DroidFish/jni/stockfish/platform.h | 4 +- DroidFish/jni/stockfish/position.cpp | 363 ++++++++++---------- DroidFish/jni/stockfish/position.h | 22 +- DroidFish/jni/stockfish/search.cpp | 455 ++++++++++---------------- DroidFish/jni/stockfish/search.h | 11 +- DroidFish/jni/stockfish/thread.cpp | 199 ++++++----- DroidFish/jni/stockfish/thread.h | 72 ++-- DroidFish/jni/stockfish/tt.cpp | 18 +- DroidFish/jni/stockfish/types.h | 100 +++--- DroidFish/jni/stockfish/uci.cpp | 46 ++- DroidFish/jni/stockfish/ucioption.cpp | 139 ++++---- DroidFish/jni/stockfish/ucioption.h | 58 ++-- 36 files changed, 1465 insertions(+), 1495 deletions(-) delete mode 100644 DroidFish/jni/stockfish/lock.h delete mode 100644 DroidFish/jni/stockfish/move.cpp create mode 100644 DroidFish/jni/stockfish/notation.cpp create mode 100644 DroidFish/jni/stockfish/notation.h diff --git a/DroidFish/jni/stockfish/Android.mk b/DroidFish/jni/stockfish/Android.mk index 4ff5c1b..529dab5 100644 --- a/DroidFish/jni/stockfish/Android.mk +++ b/DroidFish/jni/stockfish/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := stockfish LOCAL_SRC_FILES := \ - evaluate.cpp move.cpp search.cpp \ + evaluate.cpp notation.cpp search.cpp \ benchmark.cpp movegen.cpp tt.cpp \ bitbase.cpp main.cpp movepick.cpp uci.cpp \ bitboard.cpp pawns.cpp ucioption.cpp \ diff --git a/DroidFish/jni/stockfish/benchmark.cpp b/DroidFish/jni/stockfish/benchmark.cpp index b0b9e16..f63cb8e 100644 --- a/DroidFish/jni/stockfish/benchmark.cpp +++ b/DroidFish/jni/stockfish/benchmark.cpp @@ -110,7 +110,8 @@ void benchmark(const Position& current, istream& is) { } int64_t nodes = 0; - Time time = Time::current_time(); + Search::StateStackPtr st; + Time::point elapsed = Time::now(); for (size_t i = 0; i < fens.size(); i++) { @@ -120,22 +121,22 @@ void benchmark(const Position& current, istream& is) { if (limitType == "perft") { - int64_t cnt = Search::perft(pos, limits.depth * ONE_PLY); + size_t cnt = Search::perft(pos, limits.depth * ONE_PLY); cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl; nodes += cnt; } else { - Threads.start_searching(pos, limits, vector()); + Threads.start_searching(pos, limits, vector(), st); Threads.wait_for_search_finished(); nodes += Search::RootPosition.nodes_searched(); } } - int e = time.elapsed(); + elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero' cerr << "\n===========================" - << "\nTotal time (ms) : " << e + << "\nTotal time (ms) : " << elapsed << "\nNodes searched : " << nodes - << "\nNodes/second : " << int(nodes / (e / 1000.0)) << endl; + << "\nNodes/second : " << 1000 * nodes / elapsed << endl; } diff --git a/DroidFish/jni/stockfish/bitbase.cpp b/DroidFish/jni/stockfish/bitbase.cpp index ceb94a0..fdfb207 100644 --- a/DroidFish/jni/stockfish/bitbase.cpp +++ b/DroidFish/jni/stockfish/bitbase.cpp @@ -62,14 +62,14 @@ namespace { } -uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm) { +uint32_t Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) { int idx = index(wksq, bksq, wpsq, stm); return KPKBitbase[idx / 32] & (1 << (idx & 31)); } -void kpk_bitbase_init() { +void Bitbases::init_kpk() { Result db[IndexMax]; KPKPosition pos; @@ -117,7 +117,7 @@ namespace { stm = Color(idx & 1); bksq = Square((idx >> 1) & 63); wksq = Square((idx >> 7) & 63); - psq = make_square(File((idx >> 13) & 3), Rank((idx >> 15) + 1)); + psq = File((idx >> 13) & 3) | Rank((idx >> 15) + 1); } Result KPKPosition::classify_leaf(int idx) { @@ -196,8 +196,8 @@ namespace { while (b) { - r |= Us == WHITE ? db[index(pop_1st_bit(&b), bksq, psq, BLACK)] - : db[index(wksq, pop_1st_bit(&b), psq, WHITE)]; + r |= Us == WHITE ? db[index(pop_lsb(&b), bksq, psq, BLACK)] + : db[index(wksq, pop_lsb(&b), psq, WHITE)]; if (Us == WHITE && (r & WIN)) return WIN; diff --git a/DroidFish/jni/stockfish/bitboard.cpp b/DroidFish/jni/stockfish/bitboard.cpp index 1bae684..639ef3f 100644 --- a/DroidFish/jni/stockfish/bitboard.cpp +++ b/DroidFish/jni/stockfish/bitboard.cpp @@ -23,6 +23,7 @@ #include "bitboard.h" #include "bitcount.h" +#include "misc.h" #include "rkiss.h" CACHE_LINE_ALIGNMENT @@ -45,22 +46,27 @@ Bitboard ThisAndAdjacentFilesBB[8]; Bitboard InFrontBB[2][8]; Bitboard StepAttacksBB[16][64]; Bitboard BetweenBB[64][64]; +Bitboard DistanceRingsBB[64][8]; Bitboard ForwardBB[2][64]; Bitboard PassedPawnMask[2][64]; Bitboard AttackSpanMask[2][64]; Bitboard PseudoAttacks[6][64]; -uint8_t BitCount8Bit[256]; int SquareDistance[64][64]; namespace { + // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan + const uint64_t DeBruijn_64 = 0x218A392CD3D5DBFULL; + const uint32_t DeBruijn_32 = 0x783A9B23; + CACHE_LINE_ALIGNMENT int BSFTable[64]; int MS1BTable[256]; Bitboard RTable[0x19000]; // Storage space for rook attacks Bitboard BTable[0x1480]; // Storage space for bishop attacks + uint8_t BitCount8Bit[256]; typedef unsigned (Fn)(Square, Bitboard); @@ -68,40 +74,35 @@ namespace { Bitboard masks[], unsigned shifts[], Square deltas[], Fn index); } -/// first_1() finds the least significant nonzero bit in a nonzero bitboard. -/// pop_1st_bit() finds and clears the least significant nonzero bit in a -/// nonzero bitboard. +/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. +/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. -#if defined(IS_64BIT) && !defined(USE_BSFQ) +#if !defined(USE_BSFQ) -Square first_1(Bitboard b) { - return Square(BSFTable[((b & -b) * 0x218A392CD3D5DBFULL) >> 58]); -} +Square lsb(Bitboard b) { -Square pop_1st_bit(Bitboard* b) { - Bitboard bb = *b; - *b &= (*b - 1); - return Square(BSFTable[((bb & -bb) * 0x218A392CD3D5DBFULL) >> 58]); -} + if (Is64Bit) + return Square(BSFTable[((b & -b) * DeBruijn_64) >> 58]); -#elif !defined(USE_BSFQ) - -Square first_1(Bitboard b) { b ^= (b - 1); uint32_t fold = unsigned(b) ^ unsigned(b >> 32); - return Square(BSFTable[(fold * 0x783A9B23) >> 26]); + return Square(BSFTable[(fold * DeBruijn_32) >> 26]); } -Square pop_1st_bit(Bitboard* b) { +Square pop_lsb(Bitboard* b) { Bitboard bb = *b; *b = bb & (bb - 1); + + if (Is64Bit) + return Square(BSFTable[((bb & -bb) * DeBruijn_64) >> 58]); + bb ^= (bb - 1); uint32_t fold = unsigned(bb) ^ unsigned(bb >> 32); - return Square(BSFTable[(fold * 0x783A9B23) >> 26]); + return Square(BSFTable[(fold * DeBruijn_32) >> 26]); } -Square last_1(Bitboard b) { +Square msb(Bitboard b) { unsigned b32; int result = 0; @@ -137,16 +138,18 @@ Square last_1(Bitboard b) { void Bitboards::print(Bitboard b) { + sync_cout; + for (Rank rank = RANK_8; rank >= RANK_1; rank--) { std::cout << "+---+---+---+---+---+---+---+---+" << '\n'; for (File file = FILE_A; file <= FILE_H; file++) - std::cout << "| " << ((b & make_square(file, rank)) ? "X " : " "); + std::cout << "| " << (b & (file | rank) ? "X " : " "); std::cout << "|\n"; } - std::cout << "+---+---+---+---+---+---+---+---+" << std::endl; + std::cout << "+---+---+---+---+---+---+---+---+" << sync_endl; } @@ -195,16 +198,22 @@ void Bitboards::init() { for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2)); + for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) + for (int d = 1; d < 8; d++) + for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) + if (SquareDistance[s1][s2] == d) + DistanceRingsBB[s1][d - 1] |= s2; + for (int i = 0; i < 64; i++) if (!Is64Bit) // Matt Taylor's folding trick for 32 bit systems { Bitboard b = 1ULL << i; b ^= b - 1; b ^= b >> 32; - BSFTable[(uint32_t)(b * 0x783A9B23) >> 26] = i; + BSFTable[(uint32_t)(b * DeBruijn_32) >> 26] = i; } else - BSFTable[((1ULL << i) * 0x218A392CD3D5DBFULL) >> 58] = i; + BSFTable[((1ULL << i) * DeBruijn_64) >> 58] = i; int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 }, {}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } }; @@ -265,25 +274,17 @@ namespace { } - Bitboard pick_random(Bitboard mask, RKISS& rk, int booster) { - - Bitboard magic; + Bitboard pick_random(RKISS& rk, int booster) { // Values s1 and s2 are used to rotate the candidate magic of a // quantity known to be the optimal to quickly find the magics. int s1 = booster & 63, s2 = (booster >> 6) & 63; - while (true) - { - magic = rk.rand(); - magic = (magic >> s1) | (magic << (64 - s1)); - magic &= rk.rand(); - magic = (magic >> s2) | (magic << (64 - s2)); - magic &= rk.rand(); - - if (BitCount8Bit[(mask * magic) >> 56] >= 6) - return magic; - } + Bitboard m = rk.rand(); + m = (m >> s1) | (m << (64 - s1)); + m &= rk.rand(); + m = (m >> s2) | (m << (64 - s2)); + return m & rk.rand(); } @@ -336,7 +337,9 @@ namespace { // Find a magic for square 's' picking up an (almost) random number // until we find the one that passes the verification test. do { - magics[s] = pick_random(masks[s], rk, booster); + do magics[s] = pick_random(rk, booster); + while (BitCount8Bit[(magics[s] * masks[s]) >> 56] < 6); + memset(attacks[s], 0, size * sizeof(Bitboard)); // A good magic must map every possible occupancy to an index that @@ -350,6 +353,8 @@ namespace { if (attack && attack != reference[i]) break; + assert(reference[i] != 0); + attack = reference[i]; } } while (i != size); diff --git a/DroidFish/jni/stockfish/bitboard.h b/DroidFish/jni/stockfish/bitboard.h index cc03939..4c7c948 100644 --- a/DroidFish/jni/stockfish/bitboard.h +++ b/DroidFish/jni/stockfish/bitboard.h @@ -25,8 +25,15 @@ namespace Bitboards { -extern void init(); -extern void print(Bitboard b); +void init(); +void print(Bitboard b); + +} + +namespace Bitbases { + +void init_kpk(); +uint32_t probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm); } @@ -50,6 +57,7 @@ extern Bitboard ThisAndAdjacentFilesBB[8]; extern Bitboard InFrontBB[2][8]; extern Bitboard StepAttacksBB[16][64]; extern Bitboard BetweenBB[64][64]; +extern Bitboard DistanceRingsBB[64][8]; extern Bitboard ForwardBB[2][64]; extern Bitboard PassedPawnMask[2][64]; extern Bitboard AttackSpanMask[2][64]; @@ -220,51 +228,52 @@ inline Bitboard attacks_bb(Square s, Bitboard occ) { } -/// first_1() finds the least significant nonzero bit in a nonzero bitboard. -/// pop_1st_bit() finds and clears the least significant nonzero bit in a -/// nonzero bitboard. +/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. +/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. #if defined(USE_BSFQ) -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -FORCE_INLINE Square first_1(Bitboard b) { +FORCE_INLINE Square lsb(Bitboard b) { unsigned long index; _BitScanForward64(&index, b); return (Square) index; } -FORCE_INLINE Square last_1(Bitboard b) { +FORCE_INLINE Square msb(Bitboard b) { unsigned long index; _BitScanReverse64(&index, b); return (Square) index; } -#else -FORCE_INLINE Square first_1(Bitboard b) { // Assembly code by Heinz van Saanen - Bitboard dummy; - __asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) ); - return (Square) dummy; +# else + +FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen + Bitboard index; + __asm__("bsfq %1, %0": "=r"(index): "rm"(b) ); + return (Square) index; } -FORCE_INLINE Square last_1(Bitboard b) { - Bitboard dummy; - __asm__("bsrq %1, %0": "=r"(dummy): "rm"(b) ); - return (Square) dummy; +FORCE_INLINE Square msb(Bitboard b) { + Bitboard index; + __asm__("bsrq %1, %0": "=r"(index): "rm"(b) ); + return (Square) index; } -#endif -FORCE_INLINE Square pop_1st_bit(Bitboard* b) { - const Square s = first_1(*b); - *b &= ~(1ULL<(Bitboard b) { #if !defined(USE_POPCNT) assert(false); - return int(b != 0); // Avoid 'b not used' warning + return b != 0; // Avoid 'b not used' warning #elif defined(_MSC_VER) && defined(__INTEL_COMPILER) diff --git a/DroidFish/jni/stockfish/book.cpp b/DroidFish/jni/stockfish/book.cpp index 40b647c..77bdc74 100644 --- a/DroidFish/jni/stockfish/book.cpp +++ b/DroidFish/jni/stockfish/book.cpp @@ -301,10 +301,10 @@ namespace { }; // Offsets to the PolyGlotRandoms[] array of zobrist keys - const Key* ZobPiece = PolyGlotRandoms + 0; - const Key* ZobCastle = PolyGlotRandoms + 768; - const Key* ZobEnPassant = PolyGlotRandoms + 772; - const Key* ZobTurn = PolyGlotRandoms + 780; + const Key* ZobPiece = PolyGlotRandoms; + const Key* ZobCastle = ZobPiece + 12 * 64; // Pieces * squares + const Key* ZobEnPassant = ZobCastle + 4; // Castle flags + const Key* ZobTurn = ZobEnPassant + 8; // Number of files // book_key() returns the PolyGlot hash key of the given position uint64_t book_key(const Position& pos) { @@ -314,18 +314,18 @@ namespace { while (b) { - // Piece offset is at 64 * polyPiece where polyPiece is defined as: + // In PolyGlotRandoms[] pieces are stored in the following sequence: // BP = 0, WP = 1, BN = 2, WN = 3, ... BK = 10, WK = 11 - Square s = pop_1st_bit(&b); + Square s = pop_lsb(&b); Piece p = pos.piece_on(s); - int polyPiece = 2 * (type_of(p) - 1) + (color_of(p) == WHITE); - key ^= ZobPiece[64 * polyPiece + s]; + int pieceOfs = 2 * (type_of(p) - 1) + (color_of(p) == WHITE); + key ^= ZobPiece[64 * pieceOfs + s]; } b = pos.can_castle(ALL_CASTLES); while (b) - key ^= ZobCastle[pop_1st_bit(&b)]; + key ^= ZobCastle[pop_lsb(&b)]; if (pos.ep_square() != SQ_NONE) key ^= ZobEnPassant[file_of(pos.ep_square())]; @@ -338,9 +338,9 @@ namespace { } // namespace -Book::Book() : size(0) { +Book::Book() { - for (int i = Time::current_time().msec() % 10000; i > 0; i--) + for (int i = Time::now() % 10000; i > 0; i--) RKiss.rand(); // Make random number generation less deterministic } @@ -370,30 +370,14 @@ template<> Book& Book::operator>>(BookEntry& e) { bool Book::open(const char* fName) { - fileName = ""; - if (is_open()) // Cannot close an already closed file close(); - ifstream::open(fName, ifstream::in | ifstream::binary | ios::ate); + ifstream::open(fName, ifstream::in | ifstream::binary); - if (!is_open()) - { - clear(); - return false; // Silently fail if the file is not found - } - - // Get the book size in number of entries, we are already at the end of file - size = (size_t)tellg() / sizeof(BookEntry); - - if (!good()) - { - cerr << "Failed to open book file " << fName << endl; - exit(EXIT_FAILURE); - } - - fileName = fName; // Set only if successful - return true; + fileName = is_open() ? fName : ""; + ifstream::clear(); // Reset any error flag to allow retry ifstream::open() + return !fileName.empty(); } @@ -403,16 +387,16 @@ bool Book::open(const char* fName) { Move Book::probe(const Position& pos, const string& fName, bool pickBest) { + if (fileName != fName && !open(fName.c_str())) + return MOVE_NONE; + BookEntry e; uint16_t best = 0; unsigned sum = 0; Move move = MOVE_NONE; uint64_t key = book_key(pos); - if (fileName != fName && !open(fName.c_str())) - return MOVE_NONE; - - binary_search(key); + seekg(find_first(key) * sizeof(BookEntry), ios_base::beg); while (*this >> e, e.key == key && good()) { @@ -442,10 +426,10 @@ Move Book::probe(const Position& pos, const string& fName, bool pickBest) { // the special Move's flags (bit 14-15) that are not supported by PolyGlot. int pt = (move >> 12) & 7; if (pt) - move = make_promotion(from_sq(move), to_sq(move), PieceType(pt + 1)); + move = make(from_sq(move), to_sq(move), PieceType(pt + 1)); // Add 'special move' flags and verify it is legal - for (MoveList ml(pos); !ml.end(); ++ml) + for (MoveList ml(pos); !ml.end(); ++ml) if (move == (ml.move() & 0x3FFF)) return ml.move(); @@ -453,18 +437,17 @@ Move Book::probe(const Position& pos, const string& fName, bool pickBest) { } -/// Book::binary_search() takes a book key as input, and does a binary search -/// through the book file for the given key. File stream current position is set -/// to the leftmost book entry with the same key as the input. +/// Book::find_first() takes a book key as input, and does a binary search +/// through the book file for the given key. Returns the index of the leftmost +/// book entry with the same key as the input. -void Book::binary_search(uint64_t key) { +size_t Book::find_first(uint64_t key) { - size_t low, high, mid; + seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size + + size_t low = 0, mid, high = (size_t)tellg() / sizeof(BookEntry) - 1; BookEntry e; - low = 0; - high = size - 1; - assert(low <= high); while (low < high && good()) @@ -484,5 +467,5 @@ void Book::binary_search(uint64_t key) { assert(low == high); - seekg(low * sizeof(BookEntry), ios_base::beg); + return low; } diff --git a/DroidFish/jni/stockfish/book.h b/DroidFish/jni/stockfish/book.h index 24fa9f2..9497b9b 100644 --- a/DroidFish/jni/stockfish/book.h +++ b/DroidFish/jni/stockfish/book.h @@ -48,11 +48,10 @@ private: template Book& operator>>(T& n); bool open(const char* fName); - void binary_search(uint64_t key); + size_t find_first(uint64_t key); RKISS RKiss; std::string fileName; - size_t size; }; #endif // !defined(BOOK_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/endgame.cpp b/DroidFish/jni/stockfish/endgame.cpp index feaf59a..dbf1058 100644 --- a/DroidFish/jni/stockfish/endgame.cpp +++ b/DroidFish/jni/stockfish/endgame.cpp @@ -20,14 +20,13 @@ #include #include +#include "bitboard.h" #include "bitcount.h" #include "endgame.h" #include "movegen.h" using std::string; -extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm); - namespace { // Table used to drive the defending king towards the edge of the board @@ -134,7 +133,7 @@ Value Endgame::operator()(const Position& pos) const { // Stalemate detection with lone king if ( pos.side_to_move() == weakerSide && !pos.in_check() - && !MoveList(pos).size()) { + && !MoveList(pos).size()) { return VALUE_DRAW; } @@ -142,7 +141,7 @@ Value Endgame::operator()(const Position& pos) const { Square loserKSq = pos.king_square(weakerSide); Value result = pos.non_pawn_material(strongerSide) - + pos.piece_count(strongerSide, PAWN) * PawnValueEndgame + + pos.piece_count(strongerSide, PAWN) * PawnValueEg + MateTable[loserKSq] + DistanceBonus[square_distance(winnerKSq, loserKSq)]; @@ -163,7 +162,7 @@ Value Endgame::operator()(const Position& pos) const { assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO); - assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame + BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.piece_count(strongerSide, PAWN) == 0); @@ -223,12 +222,10 @@ Value Endgame::operator()(const Position& pos) const { wpsq = mirror(wpsq); } - if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm)) + if (!Bitbases::probe_kpk(wksq, wpsq, bksq, stm)) return VALUE_DRAW; - Value result = VALUE_KNOWN_WIN - + PawnValueEndgame - + Value(rank_of(wpsq)); + Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq)); return strongerSide == pos.side_to_move() ? result : -result; } @@ -241,7 +238,7 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.non_pawn_material(weakerSide) == 0); assert(pos.piece_count(weakerSide, PAWN) == 1); @@ -262,18 +259,18 @@ Value Endgame::operator()(const Position& pos) const { bpsq = ~bpsq; } - Square queeningSq = make_square(file_of(bpsq), RANK_1); + Square queeningSq = file_of(bpsq) | 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 = RookValueEndgame - Value(square_distance(wksq, bpsq)); + result = RookValueEg - Value(square_distance(wksq, bpsq)); // 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 = RookValueEndgame - Value(square_distance(wksq, bpsq)); + result = RookValueEg - Value(square_distance(wksq, bpsq)); // If the pawn is far advanced and supported by the defending king, // the position is drawish @@ -298,9 +295,9 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(weakerSide) == BishopValueMg); assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, BISHOP) == 1); @@ -314,9 +311,9 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); + assert(pos.non_pawn_material(weakerSide) == KnightValueMg); assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, KNIGHT) == 1); @@ -337,16 +334,16 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.piece_count(weakerSide, PAWN) == 0); Square winnerKSq = pos.king_square(strongerSide); Square loserKSq = pos.king_square(weakerSide); - Value result = QueenValueEndgame - - RookValueEndgame + Value result = QueenValueEg + - RookValueEg + MateTable[loserKSq] + DistanceBonus[square_distance(winnerKSq, loserKSq)]; @@ -357,12 +354,12 @@ template<> Value Endgame::operator()(const Position& pos) const { assert(pos.piece_count(strongerSide, BISHOP) == 2); - assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMg); assert(pos.piece_count(weakerSide, KNIGHT) == 1); - assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); + assert(pos.non_pawn_material(weakerSide) == KnightValueMg); assert(!pos.pieces(PAWN)); - Value result = BishopValueEndgame; + Value result = BishopValueEg; Square wksq = pos.king_square(strongerSide); Square bksq = pos.king_square(weakerSide); Square nsq = pos.piece_list(weakerSide, KNIGHT)[0]; @@ -399,7 +396,7 @@ Value Endgame::operator()(const Position&) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, PAWN) >= 1); @@ -414,7 +411,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && !(pawns & ~file_bb(pawnFile))) { Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; - Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8)); + Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8); Square kingSq = pos.king_square(weakerSide); if ( opposite_colors(queeningSq, bishopSq) @@ -451,7 +448,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.piece_count(strongerSide, QUEEN) == 1); assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, ROOK) == 1); @@ -481,9 +478,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.piece_count(weakerSide, PAWN) == 0); Square wksq = pos.king_square(strongerSide); @@ -513,7 +510,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { File f = file_of(wpsq); Rank r = rank_of(wpsq); - Square queeningSq = make_square(f, RANK_8); + Square queeningSq = f | RANK_8; int tempo = (pos.side_to_move() == strongerSide); // If the pawn is not too far advanced and the defending king defends the @@ -599,9 +596,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 2); - assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.piece_count(weakerSide, PAWN) == 1); Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0]; @@ -674,10 +671,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(weakerSide) == BishopValueMg); assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.piece_count(weakerSide, PAWN) == 0); @@ -729,10 +726,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, PAWN) == 2); - assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(weakerSide) == BishopValueMg); assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.piece_count(weakerSide, PAWN) == 0); @@ -752,12 +749,12 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2)) { blockSq1 = psq1 + pawn_push(strongerSide); - blockSq2 = make_square(file_of(psq2), rank_of(psq1)); + blockSq2 = file_of(psq2) | rank_of(psq1); } else { blockSq1 = psq2 + pawn_push(strongerSide); - blockSq2 = make_square(file_of(psq1), rank_of(psq2)); + blockSq2 = file_of(psq1) | rank_of(psq2); } switch (file_distance(psq1, psq2)) @@ -804,10 +801,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); + assert(pos.non_pawn_material(weakerSide) == KnightValueMg); assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.piece_count(weakerSide, PAWN) == 0); @@ -831,7 +828,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame); + assert(pos.non_pawn_material(strongerSide) == KnightValueMg); assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); @@ -893,5 +890,5 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // 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 probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; + return Bitbases::probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; } diff --git a/DroidFish/jni/stockfish/evaluate.cpp b/DroidFish/jni/stockfish/evaluate.cpp index 8a5b661..46d9151 100644 --- a/DroidFish/jni/stockfish/evaluate.cpp +++ b/DroidFish/jni/stockfish/evaluate.cpp @@ -435,8 +435,8 @@ Value do_evaluate(const Position& pos, Value& margin) { && sf == SCALE_FACTOR_NORMAL) { // Only the two bishops ? - if ( pos.non_pawn_material(WHITE) == BishopValueMidgame - && pos.non_pawn_material(BLACK) == BishopValueMidgame) + if ( pos.non_pawn_material(WHITE) == BishopValueMg + && pos.non_pawn_material(BLACK) == BishopValueMg) { // Check for KBP vs KB with only a single pawn that is almost // certainly a draw or at least two pawns. @@ -492,7 +492,7 @@ Value do_evaluate(const Position& pos, Value& margin) { // Init king safety tables only if we are going to use them if ( pos.piece_count(Us, QUEEN) - && pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame) + && pos.non_pawn_material(Us) >= QueenValueMg + RookValueMg) { ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8)); b &= ei.attackedBy[Us][PAWN]; @@ -582,7 +582,7 @@ Value do_evaluate(const Position& pos, Value& margin) { assert(b); if (!more_than_one(b) && (b & pos.pieces(Them))) - score += ThreatBonus[Piece][type_of(pos.piece_on(first_1(b)))]; + score += ThreatBonus[Piece][type_of(pos.piece_on(lsb(b)))]; } // Decrease score if we are attacked by an enemy pawn. Remaining part @@ -870,7 +870,7 @@ Value do_evaluate(const Position& pos, Value& margin) { return SCORE_ZERO; do { - Square s = pop_1st_bit(&b); + Square s = pop_lsb(&b); assert(pos.pawn_is_passed(Us, s)); @@ -938,7 +938,7 @@ Value do_evaluate(const Position& pos, Value& margin) { // value if the other side has a rook or queen. if (file_of(s) == FILE_A || file_of(s) == FILE_H) { - if (pos.non_pawn_material(Them) <= KnightValueMidgame) + if (pos.non_pawn_material(Them) <= KnightValueMg) ebonus += ebonus / 4; else if (pos.pieces(Them, ROOK, QUEEN)) ebonus -= ebonus / 4; @@ -976,8 +976,8 @@ Value do_evaluate(const Position& pos, Value& margin) { while (b) { - s = pop_1st_bit(&b); - queeningSquare = relative_square(c, make_square(file_of(s), RANK_8)); + 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 @@ -1017,10 +1017,10 @@ Value do_evaluate(const Position& pos, Value& margin) { while (b) { - s = pop_1st_bit(&b); + s = pop_lsb(&b); // Compute plies from queening - queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8)); + 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()); @@ -1039,12 +1039,12 @@ Value do_evaluate(const Position& pos, Value& margin) { while (b) { - s = pop_1st_bit(&b); + s = pop_lsb(&b); sacptg = blockersCount = 0; minKingDist = kingptg = 256; // Compute plies from queening - queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8)); + 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()); @@ -1058,7 +1058,7 @@ Value do_evaluate(const Position& pos, Value& margin) { // How many plies does it take to remove all the blocking pawns? while (blockers) { - blockSq = pop_1st_bit(&blockers); + blockSq = pop_lsb(&blockers); movesToGo = 256; // Check pawns that can give support to overcome obstacle, for instance @@ -1069,7 +1069,7 @@ Value do_evaluate(const Position& pos, Value& margin) { while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) { - d = square_distance(blockSq, pop_1st_bit(&b2)) - 2; + d = square_distance(blockSq, pop_lsb(&b2)) - 2; movesToGo = std::min(movesToGo, d); } } @@ -1079,7 +1079,7 @@ Value do_evaluate(const Position& pos, Value& margin) { while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) { - d = square_distance(blockSq, pop_1st_bit(&b2)) - 2; + d = square_distance(blockSq, pop_lsb(&b2)) - 2; movesToGo = std::min(movesToGo, d); } @@ -1172,7 +1172,7 @@ Value do_evaluate(const Position& pos, Value& margin) { // A couple of little helpers used by tracing code, to_cp() converts a value to // a double in centipawns scale, trace_add() stores white and black scores. - double to_cp(Value v) { return double(v) / double(PawnValueMidgame); } + double to_cp(Value v) { return double(v) / double(PawnValueMg); } void trace_add(int idx, Score wScore, Score bScore) { diff --git a/DroidFish/jni/stockfish/lock.h b/DroidFish/jni/stockfish/lock.h deleted file mode 100644 index 7f74fcf..0000000 --- a/DroidFish/jni/stockfish/lock.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#if !defined(LOCK_H_INCLUDED) -#define LOCK_H_INCLUDED - -#if !defined(_MSC_VER) - -# include - -typedef pthread_mutex_t Lock; -typedef pthread_cond_t WaitCondition; - -# define lock_init(x) pthread_mutex_init(x, NULL) -# define lock_grab(x) pthread_mutex_lock(x) -# define lock_release(x) pthread_mutex_unlock(x) -# define lock_destroy(x) pthread_mutex_destroy(x) -# define cond_destroy(x) pthread_cond_destroy(x) -# define cond_init(x) pthread_cond_init(x, NULL) -# define cond_signal(x) pthread_cond_signal(x) -# define cond_wait(x,y) pthread_cond_wait(x,y) -# define cond_timedwait(x,y,z) pthread_cond_timedwait(x,y,z) - -#else - -#define NOMINMAX // disable macros min() and max() -#define WIN32_LEAN_AND_MEAN -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -// We use critical sections on Windows to support Windows XP and older versions, -// unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject() -// but apart from this they have the same speed performance of SRW locks. -typedef CRITICAL_SECTION Lock; -typedef HANDLE WaitCondition; - -# define lock_init(x) InitializeCriticalSection(x) -# define lock_grab(x) EnterCriticalSection(x) -# define lock_release(x) LeaveCriticalSection(x) -# define lock_destroy(x) DeleteCriticalSection(x) -# define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); } -# define cond_destroy(x) CloseHandle(*x) -# define cond_signal(x) SetEvent(*x) -# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); } -# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(*x,z); lock_grab(y); } - -#endif - -#endif // !defined(LOCK_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/main.cpp b/DroidFish/jni/stockfish/main.cpp index 6e88988..e4ebcc3 100644 --- a/DroidFish/jni/stockfish/main.cpp +++ b/DroidFish/jni/stockfish/main.cpp @@ -28,19 +28,17 @@ #include "tt.h" #include "ucioption.h" -extern void uci_loop(const std::string&); -extern void kpk_bitbase_init(); - int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; + UCI::init(Options); Bitboards::init(); - Position::init(); - kpk_bitbase_init(); + Zobrist::init(); + Bitbases::init_kpk(); Search::init(); - Threads.init(); Eval::init(); + Threads.init(); TT.set_size(Options["Hash"]); std::string args; @@ -48,5 +46,7 @@ int main(int argc, char* argv[]) { for (int i = 1; i < argc; i++) args += std::string(argv[i]) + " "; - uci_loop(args); + UCI::loop(args); + + Threads.exit(); } diff --git a/DroidFish/jni/stockfish/material.cpp b/DroidFish/jni/stockfish/material.cpp index b106892..3236117 100644 --- a/DroidFish/jni/stockfish/material.cpp +++ b/DroidFish/jni/stockfish/material.cpp @@ -63,11 +63,11 @@ namespace { const Color Them = (Us == WHITE ? BLACK : WHITE); return pos.non_pawn_material(Them) == VALUE_ZERO && pos.piece_count(Them, PAWN) == 0 - && pos.non_pawn_material(Us) >= RookValueMidgame; + && pos.non_pawn_material(Us) >= RookValueMg; } template bool is_KBPsKs(const Position& pos) { - return pos.non_pawn_material(Us) == BishopValueMidgame + return pos.non_pawn_material(Us) == BishopValueMg && pos.piece_count(Us, BISHOP) == 1 && pos.piece_count(Us, PAWN) >= 1; } @@ -75,7 +75,7 @@ namespace { template bool is_KQKRPs(const Position& pos) { const Color Them = (Us == WHITE ? BLACK : WHITE); return pos.piece_count(Us, PAWN) == 0 - && pos.non_pawn_material(Us) == QueenValueMidgame + && pos.non_pawn_material(Us) == QueenValueMg && pos.piece_count(Us, QUEEN) == 1 && pos.piece_count(Them, ROOK) == 1 && pos.piece_count(Them, PAWN) >= 1; @@ -191,20 +191,20 @@ MaterialEntry* MaterialTable::probe(const Position& pos) { } // No pawns makes it difficult to win, even with a material advantage - if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame) + if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMg) { e->factor[WHITE] = (uint8_t) - (npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]); + (npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]); } - if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame) + if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMg) { e->factor[BLACK] = (uint8_t) - (npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]); + (npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]); } // Compute the space weight - if (npm_w + npm_b >= 2 * QueenValueMidgame + 4 * RookValueMidgame + 2 * KnightValueMidgame) + if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) { int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP) + pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP); diff --git a/DroidFish/jni/stockfish/misc.cpp b/DroidFish/jni/stockfish/misc.cpp index 5c52321..33c3f1f 100644 --- a/DroidFish/jni/stockfish/misc.cpp +++ b/DroidFish/jni/stockfish/misc.cpp @@ -33,7 +33,7 @@ using namespace std; /// Version number. If Version is left empty, then Tag plus current /// date (in the format YYMMDD) is used as a version number. -static const string Version = "120603"; +static const string Version = "2.3"; static const string Tag = ""; @@ -68,6 +68,13 @@ const string engine_info(bool to_uci) { } +/// Convert system time to milliseconds. That's all we need. + +Time::point Time::now() { + sys_time_t t; system_time(&t); return time_to_msec(t); +} + + /// Debug functions used mainly to collect run-time statistics static uint64_t hits[2], means[2]; @@ -94,33 +101,33 @@ void dbg_print() { /// usual i/o functionality and without changing a single line of code! /// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 +struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout + + Tie(streambuf* b, ofstream* f) : buf(b), file(f) {} + + int sync() { return file->rdbuf()->pubsync(), buf->pubsync(); } + int overflow(int c) { return log(buf->sputc((char)c), "<< "); } + int underflow() { return buf->sgetc(); } + int uflow() { return log(buf->sbumpc(), ">> "); } + + streambuf* buf; + ofstream* file; + + int log(int c, const char* prefix) { + + static int last = '\n'; + + if (last == '\n') + file->rdbuf()->sputn(prefix, 3); + + return last = file->rdbuf()->sputc((char)c); + } +}; + class Logger { - Logger() : in(cin.rdbuf(), file), out(cout.rdbuf(), file) {} - ~Logger() { start(false); } - - struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout - - Tie(streambuf* b, ofstream& f) : buf(b), file(f) {} - - int sync() { return file.rdbuf()->pubsync(), buf->pubsync(); } - int overflow(int c) { return log(buf->sputc((char)c), "<< "); } - int underflow() { return buf->sgetc(); } - int uflow() { return log(buf->sbumpc(), ">> "); } - - int log(int c, const char* prefix) { - - static int last = '\n'; - - if (last == '\n') - file.rdbuf()->sputn(prefix, 3); - - return last = file.rdbuf()->sputc((char)c); - } - - streambuf* buf; - ofstream& file; - }; + Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {} + ~Logger() { start(false); } ofstream file; Tie in, out; @@ -146,6 +153,23 @@ public: }; +/// Used to serialize access to std::cout to avoid multiple threads to write at +/// the same time. + +std::ostream& operator<<(std::ostream& os, SyncCout sc) { + + static Mutex m; + + if (sc == io_lock) + m.lock(); + + if (sc == io_unlock) + m.unlock(); + + return os; +} + + /// Trampoline helper to avoid moving Logger to misc.h void start_logger(bool b) { Logger::start(b); } @@ -184,7 +208,7 @@ void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) { int tm = msec; #else timespec ts, *tm = &ts; - uint64_t ms = Time::current_time().msec() + msec; + uint64_t ms = Time::now() + msec; ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000LL; diff --git a/DroidFish/jni/stockfish/misc.h b/DroidFish/jni/stockfish/misc.h index 29d35d5..9650f06 100644 --- a/DroidFish/jni/stockfish/misc.h +++ b/DroidFish/jni/stockfish/misc.h @@ -37,11 +37,6 @@ extern void dbg_hit_on_c(bool c, bool b); extern void dbg_mean_of(int v); extern void dbg_print(); -class Position; -extern Move move_from_uci(const Position& pos, std::string& str); -extern const std::string move_to_uci(Move m, bool chess960); -extern const std::string move_to_san(Position& pos, Move m); - struct Log : public std::ofstream { Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {} @@ -49,25 +44,26 @@ struct Log : public std::ofstream { }; -struct Time { - void restart() { system_time(&t); } - uint64_t msec() const { return time_to_msec(t); } - int elapsed() const { return int(current_time().msec() - time_to_msec(t)); } - - static Time current_time() { Time t; t.restart(); return t; } - -private: - sys_time_t t; -}; +namespace Time { + typedef int64_t point; + point now(); +} template struct HashTable { - HashTable() : e(Size, Entry()) { memset(&e[0], 0, sizeof(Entry) * Size); } + HashTable() : e(Size, Entry()) {} Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; } private: std::vector e; }; + +enum SyncCout { io_lock, io_unlock }; +std::ostream& operator<<(std::ostream&, SyncCout); + +#define sync_cout std::cout << io_lock +#define sync_endl std::endl << io_unlock + #endif // !defined(MISC_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/move.cpp b/DroidFish/jni/stockfish/move.cpp deleted file mode 100644 index 74271b3..0000000 --- a/DroidFish/jni/stockfish/move.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include - -#include "movegen.h" -#include "position.h" - -using std::string; - -/// move_to_uci() converts a move to a string in coordinate notation -/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we -/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in -/// Chess960 mode. Instead internally Move is coded as "king captures rook". - -const string move_to_uci(Move m, bool chess960) { - - Square from = from_sq(m); - Square to = to_sq(m); - string promotion; - - if (m == MOVE_NONE) - return "(none)"; - - if (m == MOVE_NULL) - return "0000"; - - if (is_castle(m) && !chess960) - to = from + (file_of(to) == FILE_H ? Square(2) : -Square(2)); - - if (is_promotion(m)) - promotion = char(tolower(piece_type_to_char(promotion_type(m)))); - - return square_to_string(from) + square_to_string(to) + promotion; -} - - -/// move_from_uci() takes a position and a string representing a move in -/// simple coordinate notation and returns an equivalent Move if any. -/// Moves are guaranteed to be legal. - -Move move_from_uci(const Position& pos, string& str) { - - if (str.length() == 5) // Junior could send promotion in uppercase - str[4] = char(tolower(str[4])); - - for (MoveList ml(pos); !ml.end(); ++ml) - if (str == move_to_uci(ml.move(), pos.is_chess960())) - return ml.move(); - - return MOVE_NONE; -} - - -/// move_to_san() takes a position and a move as input, where it is assumed -/// that the move is a legal move for the position. The return value is -/// a string containing the move in short algebraic notation. - -const string move_to_san(Position& pos, Move m) { - - if (m == MOVE_NONE) - return "(none)"; - - if (m == MOVE_NULL) - return "(null)"; - - assert(is_ok(m)); - - Bitboard attackers; - bool ambiguousMove, ambiguousFile, ambiguousRank; - Square sq, from = from_sq(m); - Square to = to_sq(m); - PieceType pt = type_of(pos.piece_moved(m)); - string san; - - if (is_castle(m)) - san = (to_sq(m) < from_sq(m) ? "O-O-O" : "O-O"); - else - { - if (pt != PAWN) - { - san = piece_type_to_char(pt); - - // Disambiguation if we have more then one piece with destination 'to' - // note that for pawns is not needed because starting file is explicit. - attackers = pos.attackers_to(to) & pos.pieces(pos.side_to_move(), pt); - attackers ^= from; - ambiguousMove = ambiguousFile = ambiguousRank = false; - - while (attackers) - { - sq = pop_1st_bit(&attackers); - - // Pinned pieces are not included in the possible sub-set - if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces())) - continue; - - if (file_of(sq) == file_of(from)) - ambiguousFile = true; - - if (rank_of(sq) == rank_of(from)) - ambiguousRank = true; - - ambiguousMove = true; - } - - if (ambiguousMove) - { - if (!ambiguousFile) - san += file_to_char(file_of(from)); - else if (!ambiguousRank) - san += rank_to_char(rank_of(from)); - else - san += square_to_string(from); - } - } - - if (pos.is_capture(m)) - { - if (pt == PAWN) - san += file_to_char(file_of(from)); - - san += 'x'; - } - - san += square_to_string(to); - - if (is_promotion(m)) - { - san += '='; - san += piece_type_to_char(promotion_type(m)); - } - } - - if (pos.move_gives_check(m, CheckInfo(pos))) - { - StateInfo st; - pos.do_move(m, st); - san += MoveList(pos).size() ? "+" : "#"; - pos.undo_move(m); - } - - return san; -} diff --git a/DroidFish/jni/stockfish/movegen.cpp b/DroidFish/jni/stockfish/movegen.cpp index 98e411b..4a19034 100644 --- a/DroidFish/jni/stockfish/movegen.cpp +++ b/DroidFish/jni/stockfish/movegen.cpp @@ -17,7 +17,6 @@ along with this program. If not, see . */ -#include #include #include "movegen.h" @@ -25,14 +24,14 @@ /// Simple macro to wrap a very common while loop, no facny, no flexibility, /// hardcoded names 'mlist' and 'from'. -#define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_1st_bit(&b)) +#define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_lsb(&b)) /// Version used for pawns, where the 'from' square is given as a delta from the 'to' square -#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_1st_bit(&b); \ +#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \ (*mlist++).move = make_move(to - (d), to); } namespace { - template + template MoveStack* generate_castle(const Position& pos, MoveStack* mlist, Color us) { if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side))) @@ -47,9 +46,8 @@ namespace { assert(!pos.in_check()); - for (Square s = std::min(kfrom, kto), e = std::max(kfrom, kto); s <= e; s++) - if ( s != kfrom // We are not in check - && (pos.attackers_to(s) & enemies)) + for (Square s = kto; s != kfrom; s += (Square)(Side == KING_SIDE ? -1 : 1)) + if (pos.attackers_to(s) & enemies) return mlist; // Because we generate only legal castling moves we need to verify that @@ -59,9 +57,9 @@ namespace { && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies)) return mlist; - (*mlist++).move = make_castle(kfrom, rfrom); + (*mlist++).move = make(kfrom, rfrom); - if (OnlyChecks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos))) + if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos))) mlist--; return mlist; @@ -80,39 +78,41 @@ namespace { } - template - inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7, Bitboard target, Square ksq) { + template + inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7, + Bitboard target, const CheckInfo* ci) { Bitboard b = move_pawns(pawnsOn7) & target; while (b) { - Square to = pop_1st_bit(&b); + Square to = pop_lsb(&b); - if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) - (*mlist++).move = make_promotion(to - Delta, to, QUEEN); + if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + (*mlist++).move = make(to - Delta, to, QUEEN); - if (Type == MV_QUIET || Type == MV_EVASION || Type == MV_NON_EVASION) + if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { - (*mlist++).move = make_promotion(to - Delta, to, ROOK); - (*mlist++).move = make_promotion(to - Delta, to, BISHOP); - (*mlist++).move = make_promotion(to - Delta, to, KNIGHT); + (*mlist++).move = make(to - Delta, to, ROOK); + (*mlist++).move = make(to - Delta, to, BISHOP); + (*mlist++).move = make(to - Delta, to, KNIGHT); } // Knight-promotion is the only one that can give a direct check not // already included in the queen-promotion. - if (Type == MV_QUIET_CHECK && (StepAttacksBB[W_KNIGHT][to] & ksq)) - (*mlist++).move = make_promotion(to - Delta, to, KNIGHT); + if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq)) + (*mlist++).move = make(to - Delta, to, KNIGHT); else - (void)ksq; // Silence a warning under MSVC + (void)ci; // Silence a warning under MSVC } return mlist; } - template - MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq = SQ_NONE) { + template + MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, + Bitboard target, const CheckInfo* ci) { // Compute our parametrized parameters at compile time, named according to // the point of view of white side. @@ -129,35 +129,35 @@ namespace { Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; - Bitboard enemies = (Type == MV_EVASION ? pos.pieces(Them) & target: - Type == MV_CAPTURE ? target : pos.pieces(Them)); + Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target: + Type == CAPTURES ? target : pos.pieces(Them)); // Single and double pawn pushes, no promotions - if (Type != MV_CAPTURE) + if (Type != CAPTURES) { - emptySquares = (Type == MV_QUIET ? target : ~pos.pieces()); + emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); b1 = move_pawns(pawnsNotOn7) & emptySquares; b2 = move_pawns(b1 & TRank3BB) & emptySquares; - if (Type == MV_EVASION) // Consider only blocking squares + if (Type == EVASIONS) // Consider only blocking squares { b1 &= target; b2 &= target; } - if (Type == MV_QUIET_CHECK) + if (Type == QUIET_CHECKS) { - b1 &= pos.attacks_from(ksq, Them); - b2 &= pos.attacks_from(ksq, Them); + b1 &= pos.attacks_from(ci->ksq, Them); + b2 &= pos.attacks_from(ci->ksq, Them); // Add pawn pushes which give discovered check. This is possible only // if the pawn is not on the same file as the enemy king, because we // don't generate captures. Note that a possible discovery check // promotion has been already generated among captures. - if (pawnsNotOn7 & target) // Target is dc bitboard + if (pawnsNotOn7 & ci->dcCandidates) { - dc1 = move_pawns(pawnsNotOn7 & target) & emptySquares & ~file_bb(ksq); + dc1 = move_pawns(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); dc2 = move_pawns(dc1 & TRank3BB) & emptySquares; b1 |= dc1; @@ -170,21 +170,21 @@ namespace { } // Promotions and underpromotions - if (pawnsOn7 && (Type != MV_EVASION || (target & TRank8BB))) + if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB))) { - if (Type == MV_CAPTURE) + if (Type == CAPTURES) emptySquares = ~pos.pieces(); - if (Type == MV_EVASION) + if (Type == EVASIONS) emptySquares &= target; - mlist = generate_promotions(mlist, pawnsOn7, enemies, ksq); - mlist = generate_promotions(mlist, pawnsOn7, enemies, ksq); - mlist = generate_promotions(mlist, pawnsOn7, emptySquares, ksq); + mlist = generate_promotions(mlist, pawnsOn7, enemies, ci); + mlist = generate_promotions(mlist, pawnsOn7, enemies, ci); + mlist = generate_promotions(mlist, pawnsOn7, emptySquares, ci); } // Standard and en-passant captures - if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) + if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { b1 = move_pawns(pawnsNotOn7) & enemies; b2 = move_pawns(pawnsNotOn7) & enemies; @@ -199,7 +199,7 @@ namespace { // An en passant capture can be an evasion only if the checking piece // is the double pushed pawn and so is in the target. Otherwise this // is a discovery check and we are forced to do otherwise. - if (Type == MV_EVASION && !(target & (pos.ep_square() - UP))) + if (Type == EVASIONS && !(target & (pos.ep_square() - UP))) return mlist; b1 = pawnsNotOn7 & pos.attacks_from(pos.ep_square(), Them); @@ -207,7 +207,7 @@ namespace { assert(b1); while (b1) - (*mlist++).move = make_enpassant(pop_1st_bit(&b1), pos.ep_square()); + (*mlist++).move = make(pop_lsb(&b1), pos.ep_square()); } } @@ -215,125 +215,115 @@ namespace { } - template - inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, - Color us, const CheckInfo& ci) { + template FORCE_INLINE + MoveStack* generate_moves(const Position& pos, MoveStack* mlist, Color us, + Bitboard target, const CheckInfo* ci) { + assert(Pt != KING && Pt != PAWN); - Bitboard b, target; - Square from; const Square* pl = pos.piece_list(us, Pt); - if (*pl != SQ_NONE) + for (Square from = *pl; from != SQ_NONE; from = *++pl) { - target = ci.checkSq[Pt] & ~pos.pieces(); // Non capture checks only - - do { - from = *pl; - + if (Checks) + { if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) - && !(PseudoAttacks[Pt][from] & target)) + && !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt])) continue; - if (ci.dcCandidates && (ci.dcCandidates & from)) + if (ci->dcCandidates && (ci->dcCandidates & from)) continue; + } - b = pos.attacks_from(from) & target; - SERIALIZE(b); - } while (*++pl != SQ_NONE); + Bitboard b = pos.attacks_from(from) & target; + + if (Checks) + b &= ci->checkSq[Pt]; + + SERIALIZE(b); } return mlist; } - template - FORCE_INLINE MoveStack* generate_moves(const Position& pos, MoveStack* mlist, - Color us, Bitboard target) { - assert(Pt != KING && Pt != PAWN); - - Bitboard b; - Square from; - const Square* pl = pos.piece_list(us, Pt); - - if (*pl != SQ_NONE) - do { - from = *pl; - b = pos.attacks_from(from) & target; - SERIALIZE(b); - } while (*++pl != SQ_NONE); - - return mlist; - } - - - template<> - FORCE_INLINE MoveStack* generate_moves(const Position& pos, MoveStack* mlist, - Color us, Bitboard target) { + FORCE_INLINE MoveStack* generate_king_moves(const Position& pos, MoveStack* mlist, + Color us, Bitboard target) { Square from = pos.king_square(us); Bitboard b = pos.attacks_from(from) & target; SERIALIZE(b); return mlist; } + + template FORCE_INLINE + MoveStack* generate_all_moves(const Position& pos, MoveStack* mlist, Color us, + Bitboard target, const CheckInfo* ci = NULL) { + + mlist = (us == WHITE ? generate_pawn_moves(pos, mlist, target, ci) + : generate_pawn_moves(pos, mlist, target, ci)); + + mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); + + if (Type != QUIET_CHECKS && Type != EVASIONS) + mlist = generate_king_moves(pos, mlist, us, target); + + if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us)) + { + mlist = generate_castle(pos, mlist, us); + mlist = generate_castle(pos, mlist, us); + } + + return mlist; + } + + } // namespace -/// generate generates all pseudo-legal captures and queen +/// generate generates all pseudo-legal captures and queen /// promotions. Returns a pointer to the end of the move list. /// -/// generate generates all pseudo-legal non-captures and +/// generate generates all pseudo-legal non-captures and /// underpromotions. Returns a pointer to the end of the move list. /// -/// generate generates all pseudo-legal captures and +/// generate generates all pseudo-legal captures and /// non-captures. Returns a pointer to the end of the move list. -template +template MoveStack* generate(const Position& pos, MoveStack* mlist) { - assert(Type == MV_CAPTURE || Type == MV_QUIET || Type == MV_NON_EVASION); + assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); assert(!pos.in_check()); Color us = pos.side_to_move(); Bitboard target; - if (Type == MV_CAPTURE) + if (Type == CAPTURES) target = pos.pieces(~us); - else if (Type == MV_QUIET) + else if (Type == QUIETS) target = ~pos.pieces(); - else if (Type == MV_NON_EVASION) + else if (Type == NON_EVASIONS) target = ~pos.pieces(us); - mlist = (us == WHITE ? generate_pawn_moves(pos, mlist, target) - : generate_pawn_moves(pos, mlist, target)); - - mlist = generate_moves(pos, mlist, us, target); - mlist = generate_moves(pos, mlist, us, target); - mlist = generate_moves(pos, mlist, us, target); - mlist = generate_moves(pos, mlist, us, target); - mlist = generate_moves(pos, mlist, us, target); - - if (Type != MV_CAPTURE && pos.can_castle(us)) - { - mlist = generate_castle(pos, mlist, us); - mlist = generate_castle(pos, mlist, us); - } - - return mlist; + return generate_all_moves(pos, mlist, us, target); } // Explicit template instantiations -template MoveStack* generate(const Position& pos, MoveStack* mlist); -template MoveStack* generate(const Position& pos, MoveStack* mlist); -template MoveStack* generate(const Position& pos, MoveStack* mlist); +template MoveStack* generate(const Position&, MoveStack*); +template MoveStack* generate(const Position&, MoveStack*); +template MoveStack* generate(const Position&, MoveStack*); -/// generate generates all pseudo-legal non-captures and knight +/// generate generates all pseudo-legal non-captures and knight /// underpromotions that give check. Returns a pointer to the end of the move list. template<> -MoveStack* generate(const Position& pos, MoveStack* mlist) { +MoveStack* generate(const Position& pos, MoveStack* mlist) { assert(!pos.in_check()); @@ -343,7 +333,7 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { while (dc) { - Square from = pop_1st_bit(&dc); + Square from = pop_lsb(&dc); PieceType pt = type_of(pos.piece_on(from)); if (pt == PAWN) @@ -357,48 +347,32 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { SERIALIZE(b); } - mlist = (us == WHITE ? generate_pawn_moves(pos, mlist, ci.dcCandidates, ci.ksq) - : generate_pawn_moves(pos, mlist, ci.dcCandidates, ci.ksq)); - - mlist = generate_direct_checks(pos, mlist, us, ci); - mlist = generate_direct_checks(pos, mlist, us, ci); - mlist = generate_direct_checks(pos, mlist, us, ci); - mlist = generate_direct_checks(pos, mlist, us, ci); - - if (pos.can_castle(us)) - { - mlist = generate_castle(pos, mlist, us); - mlist = generate_castle(pos, mlist, us); - } - - return mlist; + return generate_all_moves(pos, mlist, us, ~pos.pieces(), &ci); } -/// generate generates all pseudo-legal check evasions when the side +/// generate generates all pseudo-legal check evasions when the side /// to move is in check. Returns a pointer to the end of the move list. template<> -MoveStack* generate(const Position& pos, MoveStack* mlist) { +MoveStack* generate(const Position& pos, MoveStack* mlist) { assert(pos.in_check()); - Bitboard b, target; Square from, checksq; int checkersCnt = 0; Color us = pos.side_to_move(); Square ksq = pos.king_square(us); Bitboard sliderAttacks = 0; - Bitboard checkers = pos.checkers(); + Bitboard b = pos.checkers(); - assert(checkers); + assert(pos.checkers()); // Find squares attacked by slider checkers, we will remove them from the king // evasions so to skip known illegal moves avoiding useless legality check later. - b = checkers; do { checkersCnt++; - checksq = pop_1st_bit(&b); + checksq = pop_lsb(&b); assert(color_of(pos.piece_on(checksq)) == ~us); @@ -429,38 +403,33 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { from = ksq; SERIALIZE(b); - // Generate evasions for other pieces only if not under a double check if (checkersCnt > 1) - return mlist; + return mlist; // Double check, only a king move can save the day - // Blocking evasions or captures of the checking piece - target = between_bb(checksq, ksq) | checkers; + // Generate blocking evasions or captures of the checking piece + Bitboard target = between_bb(checksq, ksq) | pos.checkers(); - mlist = (us == WHITE ? generate_pawn_moves(pos, mlist, target) - : generate_pawn_moves(pos, mlist, target)); - - mlist = generate_moves(pos, mlist, us, target); - mlist = generate_moves(pos, mlist, us, target); - mlist = generate_moves(pos, mlist, us, target); - return generate_moves(pos, mlist, us, target); + return generate_all_moves(pos, mlist, us, target); } -/// generate generates all the legal moves in the given position +/// generate generates all the legal moves in the given position template<> -MoveStack* generate(const Position& pos, MoveStack* mlist) { +MoveStack* generate(const Position& pos, MoveStack* mlist) { - MoveStack *last, *cur = mlist; + MoveStack *end, *cur = mlist; Bitboard pinned = pos.pinned_pieces(); + Square ksq = pos.king_square(pos.side_to_move()); - last = pos.in_check() ? generate(pos, mlist) - : generate(pos, mlist); - while (cur != last) - if (!pos.pl_move_is_legal(cur->move, pinned)) - cur->move = (--last)->move; + end = pos.in_check() ? 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)) + cur->move = (--end)->move; else cur++; - return last; + return end; } diff --git a/DroidFish/jni/stockfish/movegen.h b/DroidFish/jni/stockfish/movegen.h index 68ae705..84d5d35 100644 --- a/DroidFish/jni/stockfish/movegen.h +++ b/DroidFish/jni/stockfish/movegen.h @@ -22,30 +22,30 @@ #include "types.h" -enum MoveType { - MV_CAPTURE, - MV_QUIET, - MV_QUIET_CHECK, - MV_EVASION, - MV_NON_EVASION, - MV_LEGAL +enum GenType { + CAPTURES, + QUIETS, + QUIET_CHECKS, + EVASIONS, + NON_EVASIONS, + LEGAL }; class Position; -template +template MoveStack* generate(const Position& pos, MoveStack* mlist); /// The MoveList struct is a simple wrapper around generate(), sometimes comes /// handy to use this class instead of the low level generate() function. -template +template struct MoveList { explicit MoveList(const Position& pos) : cur(mlist), last(generate(pos, mlist)) {} void operator++() { cur++; } bool end() const { return cur == last; } Move move() const { return cur->move; } - int size() const { return int(last - mlist); } + size_t size() const { return last - mlist; } private: MoveStack mlist[MAX_MOVES]; diff --git a/DroidFish/jni/stockfish/movepick.cpp b/DroidFish/jni/stockfish/movepick.cpp index a86a170..89577ee 100644 --- a/DroidFish/jni/stockfish/movepick.cpp +++ b/DroidFish/jni/stockfish/movepick.cpp @@ -23,6 +23,7 @@ #include "movegen.h" #include "movepick.h" +#include "thread.h" namespace { @@ -38,15 +39,15 @@ namespace { // Unary predicate used by std::partition to split positive scores from remaining // ones so to sort separately the two sets, and with the second sort delayed. - inline bool has_positive_score(const MoveStack& move) { return move.score > 0; } + inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; } - // Picks and moves to the front the best move in the range [firstMove, lastMove), + // Picks and moves to the front the best move in the range [begin, end), // it is faster than sorting all the moves in advance when moves are few, as // normally are the possible captures. - inline MoveStack* pick_best(MoveStack* firstMove, MoveStack* lastMove) + inline MoveStack* pick_best(MoveStack* begin, MoveStack* end) { - std::swap(*firstMove, *std::max_element(firstMove, lastMove)); - return firstMove; + std::swap(*begin, *std::max_element(begin, end)); + return begin; } } @@ -58,13 +59,14 @@ namespace { /// move ordering is at the current node. MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, - Search::Stack* ss, Value beta) : pos(p), H(h), depth(d) { + Search::Stack* s, Value beta) : pos(p), H(h), depth(d) { assert(d > DEPTH_ZERO); captureThreshold = 0; - curMove = lastMove = moves; - lastBadCapture = moves + MAX_MOVES - 1; + cur = end = moves; + endBadCaptures = moves + MAX_MOVES - 1; + ss = s; if (p.in_check()) phase = EVASION; @@ -77,8 +79,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, killers[1].move = ss->killers[1]; // Consider sligtly negative captures as good if at low depth and far from beta - if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY) - captureThreshold = -PawnValueMidgame; + if (ss && ss->eval < beta - PawnValueMg && d < 3 * ONE_PLY) + captureThreshold = -PawnValueMg; // Consider negative captures as good if still enough to reach beta else if (ss && ss->eval > beta) @@ -86,11 +88,11 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, } ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); - lastMove += (ttMove != MOVE_NONE); + end += (ttMove != MOVE_NONE); } MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, - Square sq) : pos(p), H(h), curMove(moves), lastMove(moves) { + Square sq) : pos(p), H(h), cur(moves), end(moves) { assert(d <= DEPTH_ZERO); @@ -118,24 +120,24 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, } ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); - lastMove += (ttMove != MOVE_NONE); + end += (ttMove != MOVE_NONE); } MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt) - : pos(p), H(h), curMove(moves), lastMove(moves) { + : pos(p), H(h), cur(moves), end(moves) { assert(!pos.in_check()); phase = PROBCUT; // In ProbCut we generate only captures better than parent's captured piece - captureThreshold = PieceValueMidgame[pt]; + captureThreshold = PieceValue[Mg][pt]; ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold)) ttMove = MOVE_NONE; - lastMove += (ttMove != MOVE_NONE); + end += (ttMove != MOVE_NONE); } @@ -160,14 +162,14 @@ void MovePicker::score_captures() { // some SEE calls in case we get a cutoff (idea from Pablo Vazquez). Move m; - for (MoveStack* cur = moves; cur != lastMove; cur++) + for (MoveStack* it = moves; it != end; ++it) { - m = cur->move; - cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))] - - type_of(pos.piece_moved(m)); + m = it->move; + it->score = PieceValue[Mg][pos.piece_on(to_sq(m))] + - type_of(pos.piece_moved(m)); - if (is_promotion(m)) - cur->score += PieceValueMidgame[promotion_type(m)]; + if (type_of(m) == PROMOTION) + it->score += PieceValue[Mg][promotion_type(m)]; } } @@ -175,10 +177,10 @@ void MovePicker::score_noncaptures() { Move m; - for (MoveStack* cur = moves; cur != lastMove; cur++) + for (MoveStack* it = moves; it != end; ++it) { - m = cur->move; - cur->score = H.value(pos.piece_moved(m), to_sq(m)); + m = it->move; + it->score = H.value(pos.piece_moved(m), to_sq(m)); } } @@ -189,19 +191,19 @@ void MovePicker::score_evasions() { Move m; int seeScore; - if (lastMove < moves + 2) + if (end < moves + 2) return; - for (MoveStack* cur = moves; cur != lastMove; cur++) + for (MoveStack* it = moves; it != end; ++it) { - m = cur->move; + m = it->move; if ((seeScore = pos.see_sign(m)) < 0) - cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom + it->score = seeScore - History::MaxValue; // Be sure we are at the bottom else if (pos.is_capture(m)) - cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))] - - type_of(pos.piece_moved(m)) + History::MaxValue; + it->score = PieceValue[Mg][pos.piece_on(to_sq(m))] + - type_of(pos.piece_moved(m)) + History::MaxValue; else - cur->score = H.value(pos.piece_moved(m), to_sq(m)); + it->score = H.value(pos.piece_moved(m), to_sq(m)); } } @@ -211,53 +213,53 @@ void MovePicker::score_evasions() { void MovePicker::generate_next() { - curMove = moves; + cur = moves; switch (++phase) { case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: - lastMove = generate(pos, moves); + end = generate(pos, moves); score_captures(); return; case KILLERS_S1: - curMove = killers; - lastMove = curMove + 2; + cur = killers; + end = cur + 2; return; case QUIETS_1_S1: - lastQuiet = lastMove = generate(pos, moves); + endQuiets = end = generate(pos, moves); score_noncaptures(); - lastMove = std::partition(curMove, lastMove, has_positive_score); - sort(curMove, lastMove); + end = std::partition(cur, end, has_positive_score); + sort(cur, end); return; case QUIETS_2_S1: - curMove = lastMove; - lastMove = lastQuiet; + cur = end; + end = endQuiets; if (depth >= 3 * ONE_PLY) - sort(curMove, lastMove); + sort(cur, end); return; case BAD_CAPTURES_S1: // Just pick them in reverse order to get MVV/LVA ordering - curMove = moves + MAX_MOVES - 1; - lastMove = lastBadCapture; + cur = moves + MAX_MOVES - 1; + end = endBadCaptures; return; case EVASIONS_S2: - lastMove = generate(pos, moves); + end = generate(pos, moves); score_evasions(); return; case QUIET_CHECKS_S3: - lastMove = generate(pos, moves); + end = generate(pos, moves); return; case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE: phase = STOP; case STOP: - lastMove = curMove + 1; // Avoid another next_phase() call + end = cur + 1; // Avoid another next_phase() call return; default: @@ -270,26 +272,25 @@ void MovePicker::generate_next() { /// It returns a new pseudo legal move every time it is called, until there /// are no more moves left. It picks the move with the biggest score from a list /// of generated moves taking care not to return the tt move if has already been -/// searched previously. Note that this function is not thread safe so should be -/// lock protected by caller when accessed through a shared MovePicker object. - -Move MovePicker::next_move() { +/// searched previously. +template<> +Move MovePicker::next_move() { Move move; while (true) { - while (curMove == lastMove) + while (cur == end) generate_next(); switch (phase) { case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: - curMove++; + cur++; return ttMove; case CAPTURES_S1: - move = pick_best(curMove++, lastMove)->move; + move = pick_best(cur++, end)->move; if (move != ttMove) { assert(captureThreshold <= 0); // Otherwise we cannot use see_sign() @@ -298,12 +299,12 @@ Move MovePicker::next_move() { return move; // Losing capture, move it to the tail of the array - (lastBadCapture--)->move = move; + (endBadCaptures--)->move = move; } break; case KILLERS_S1: - move = (curMove++)->move; + move = (cur++)->move; if ( move != MOVE_NONE && pos.is_pseudo_legal(move) && move != ttMove @@ -312,7 +313,7 @@ Move MovePicker::next_move() { break; case QUIETS_1_S1: case QUIETS_2_S1: - move = (curMove++)->move; + move = (cur++)->move; if ( move != ttMove && move != killers[0].move && move != killers[1].move) @@ -320,28 +321,28 @@ Move MovePicker::next_move() { break; case BAD_CAPTURES_S1: - return (curMove--)->move; + return (cur--)->move; case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4: - move = pick_best(curMove++, lastMove)->move; + move = pick_best(cur++, end)->move; if (move != ttMove) return move; break; case CAPTURES_S5: - move = pick_best(curMove++, lastMove)->move; + move = pick_best(cur++, end)->move; if (move != ttMove && pos.see(move) > captureThreshold) return move; break; case CAPTURES_S6: - move = pick_best(curMove++, lastMove)->move; + move = pick_best(cur++, end)->move; if (to_sq(move) == recaptureSquare) return move; break; case QUIET_CHECKS_S3: - move = (curMove++)->move; + move = (cur++)->move; if (move != ttMove) return move; break; @@ -354,3 +355,10 @@ Move MovePicker::next_move() { } } } + + +/// Version of next_move() to use at split point nodes where the move is grabbed +/// from the split point's shared MovePicker object. This function is not thread +/// safe so should be lock protected by the caller. +template<> +Move MovePicker::next_move() { return ss->sp->mp->next_move(); } diff --git a/DroidFish/jni/stockfish/movepick.h b/DroidFish/jni/stockfish/movepick.h index 03305eb..9459b8c 100644 --- a/DroidFish/jni/stockfish/movepick.h +++ b/DroidFish/jni/stockfish/movepick.h @@ -41,7 +41,7 @@ public: MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value); MovePicker(const Position&, Move, Depth, const History&, Square); MovePicker(const Position&, Move, const History&, PieceType); - Move next_move(); + template Move next_move(); private: void score_captures(); @@ -51,12 +51,13 @@ private: const Position& pos; const History& H; + Search::Stack* ss; Depth depth; Move ttMove; MoveStack killers[2]; Square recaptureSquare; int captureThreshold, phase; - MoveStack *curMove, *lastMove, *lastQuiet, *lastBadCapture; + MoveStack *cur, *end, *endQuiets, *endBadCaptures; MoveStack moves[MAX_MOVES]; }; diff --git a/DroidFish/jni/stockfish/notation.cpp b/DroidFish/jni/stockfish/notation.cpp new file mode 100644 index 0000000..fb65f6f --- /dev/null +++ b/DroidFish/jni/stockfish/notation.cpp @@ -0,0 +1,271 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include + +#include "movegen.h" +#include "notation.h" +#include "position.h" + +using namespace std; + +static const char* PieceToChar = " PNBRQK pnbrqk"; + + +/// score_to_uci() converts a value to a string suitable for use with the UCI +/// protocol specifications: +/// +/// cp The score from the engine's point of view in centipawns. +/// mate Mate in y moves, not plies. If the engine is getting mated +/// use negative values for y. + +string score_to_uci(Value v, Value alpha, Value beta) { + + stringstream s; + + if (abs(v) < VALUE_MATE_IN_MAX_PLY) + s << "cp " << v * 100 / int(PawnValueMg); + else + s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; + + s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); + + return s.str(); +} + + +/// move_to_uci() converts a move to a string in coordinate notation +/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print +/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960 +/// mode. Internally castle moves are always coded as "king captures rook". + +const string move_to_uci(Move m, bool chess960) { + + Square from = from_sq(m); + Square to = to_sq(m); + + if (m == MOVE_NONE) + return "(none)"; + + if (m == MOVE_NULL) + return "0000"; + + if (type_of(m) == CASTLE && !chess960) + to = (to > from ? FILE_G : FILE_C) | rank_of(from); + + string move = square_to_string(from) + square_to_string(to); + + if (type_of(m) == PROMOTION) + move += PieceToChar[make_piece(BLACK, promotion_type(m))]; // Lower case + + return move; +} + + +/// move_from_uci() takes a position and a string representing a move in +/// simple coordinate notation and returns an equivalent legal Move if any. + +Move move_from_uci(const Position& pos, string& str) { + + if (str.length() == 5) // Junior could send promotion piece in uppercase + str[4] = char(tolower(str[4])); + + for (MoveList ml(pos); !ml.end(); ++ml) + if (str == move_to_uci(ml.move(), pos.is_chess960())) + return ml.move(); + + return MOVE_NONE; +} + + +/// move_to_san() takes a position and a legal Move as input and returns its +/// short algebraic notation representation. + +const string move_to_san(Position& pos, Move m) { + + if (m == MOVE_NONE) + return "(none)"; + + if (m == MOVE_NULL) + return "(null)"; + + assert(pos.move_is_legal(m)); + + Bitboard attackers; + bool ambiguousMove, ambiguousFile, ambiguousRank; + string san; + Color us = pos.side_to_move(); + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = pos.piece_on(from); + PieceType pt = type_of(pc); + + if (type_of(m) == CASTLE) + san = to > from ? "O-O" : "O-O-O"; + else + { + if (pt != PAWN) + { + san = PieceToChar[pt]; // Upper case + + // Disambiguation if we have more then one piece with destination 'to' + // note that for pawns is not needed because starting file is explicit. + ambiguousMove = ambiguousFile = ambiguousRank = false; + + attackers = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from; + + while (attackers) + { + Square sq = pop_lsb(&attackers); + + // Pinned pieces are not included in the possible sub-set + if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces())) + continue; + + ambiguousFile |= file_of(sq) == file_of(from); + ambiguousRank |= rank_of(sq) == rank_of(from); + ambiguousMove = true; + } + + if (ambiguousMove) + { + if (!ambiguousFile) + san += file_to_char(file_of(from)); + + else if (!ambiguousRank) + san += rank_to_char(rank_of(from)); + + else + san += square_to_string(from); + } + } + else if (pos.is_capture(m)) + san = file_to_char(file_of(from)); + + if (pos.is_capture(m)) + san += 'x'; + + san += square_to_string(to); + + if (type_of(m) == PROMOTION) + san += string("=") + PieceToChar[promotion_type(m)]; + } + + if (pos.move_gives_check(m, CheckInfo(pos))) + { + StateInfo st; + pos.do_move(m, st); + san += MoveList(pos).size() ? "+" : "#"; + pos.undo_move(m); + } + + return san; +} + + +/// pretty_pv() formats human-readable search information, typically to be +/// appended to the search log file. It uses the two helpers below to pretty +/// format time and score respectively. + +static string time_to_string(int64_t msecs) { + + const int MSecMinute = 1000 * 60; + const int MSecHour = 1000 * 60 * 60; + + int64_t hours = msecs / MSecHour; + int64_t minutes = (msecs % MSecHour) / MSecMinute; + int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000; + + stringstream s; + + if (hours) + s << hours << ':'; + + s << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds; + + return s.str(); +} + +static string score_to_string(Value v) { + + stringstream s; + + if (v >= VALUE_MATE_IN_MAX_PLY) + s << "#" << (VALUE_MATE - v + 1) / 2; + + else if (v <= VALUE_MATED_IN_MAX_PLY) + s << "-#" << (VALUE_MATE + v) / 2; + + else + s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg; + + return s.str(); +} + +string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) { + + const int64_t K = 1000; + const int64_t M = 1000000; + + StateInfo state[MAX_PLY_PLUS_2], *st = state; + Move* m = pv; + string san, padding; + size_t length; + stringstream s; + + s << setw(2) << depth + << setw(8) << score_to_string(value) + << setw(8) << time_to_string(msecs); + + if (pos.nodes_searched() < M) + s << setw(8) << pos.nodes_searched() / 1 << " "; + + else if (pos.nodes_searched() < K * M) + s << setw(7) << pos.nodes_searched() / K << "K "; + + else + s << setw(7) << pos.nodes_searched() / M << "M "; + + padding = string(s.str().length(), ' '); + length = padding.length(); + + while (*m != MOVE_NONE) + { + san = move_to_san(pos, *m); + + if (length + san.length() > 80) + { + s << "\n" + padding; + length = padding.length(); + } + + s << san << ' '; + length += san.length() + 1; + + pos.do_move(*m++, *st++); + } + + while (m != pv) + pos.undo_move(*--m); + + return s.str(); +} diff --git a/DroidFish/jni/stockfish/notation.h b/DroidFish/jni/stockfish/notation.h new file mode 100644 index 0000000..0c5f2fe --- /dev/null +++ b/DroidFish/jni/stockfish/notation.h @@ -0,0 +1,35 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#if !defined(NOTATION_H_INCLUDED) +#define NOTATION_H_INCLUDED + +#include + +#include "types.h" + +class Position; + +std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE); +Move move_from_uci(const Position& pos, std::string& str); +const std::string move_to_uci(Move m, bool chess960); +const std::string move_to_san(Position& pos, Move m); +std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]); + +#endif // !defined(NOTATION_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/pawns.cpp b/DroidFish/jni/stockfish/pawns.cpp index 049e73b..069c8d9 100644 --- a/DroidFish/jni/stockfish/pawns.cpp +++ b/DroidFish/jni/stockfish/pawns.cpp @@ -240,12 +240,12 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) { { // Shelter penalty is higher for the pawn in front of the king b = ourPawns & FileBB[f]; - rkUs = b ? rank_of(Us == WHITE ? first_1(b) : ~last_1(b)) : RANK_1; + rkUs = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1; safety -= ShelterWeakness[f != kf][rkUs]; // Storm danger is smaller if enemy pawn is blocked b = theirPawns & FileBB[f]; - rkThem = b ? rank_of(Us == WHITE ? first_1(b) : ~last_1(b)) : RANK_1; + rkThem = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1; safety -= StormDanger[rkThem == rkUs + 1][rkThem]; } @@ -261,9 +261,14 @@ Score PawnEntry::update_safety(const Position& pos, Square ksq) { kingSquares[Us] = ksq; castleRights[Us] = pos.can_castle(Us); + minKPdistance[Us] = 0; + + Bitboard pawns = pos.pieces(Us, PAWN); + if (pawns) + while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {} if (relative_rank(Us, ksq) > RANK_4) - return kingSafety[Us] = SCORE_ZERO; + return kingSafety[Us] = make_score(0, -16 * minKPdistance[Us]); Value bonus = shelter_storm(pos, ksq); @@ -274,7 +279,7 @@ Score PawnEntry::update_safety(const Position& pos, Square ksq) { if (pos.can_castle(make_castle_right(Us, QUEEN_SIDE))) bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_C1))); - return kingSafety[Us] = make_score(bonus, 0); + return kingSafety[Us] = make_score(bonus, -16 * minKPdistance[Us]); } // Explicit template instantiation diff --git a/DroidFish/jni/stockfish/pawns.h b/DroidFish/jni/stockfish/pawns.h index 4fee931..e6bc2c3 100644 --- a/DroidFish/jni/stockfish/pawns.h +++ b/DroidFish/jni/stockfish/pawns.h @@ -59,6 +59,7 @@ private: Bitboard passedPawns[2]; Bitboard pawnAttacks[2]; Square kingSquares[2]; + int minKPdistance[2]; int castleRights[2]; Score value; int halfOpenFiles[2]; diff --git a/DroidFish/jni/stockfish/platform.h b/DroidFish/jni/stockfish/platform.h index e002d21..ffa00a5 100644 --- a/DroidFish/jni/stockfish/platform.h +++ b/DroidFish/jni/stockfish/platform.h @@ -48,7 +48,7 @@ typedef unsigned __int64 uint64_t; typedef timeval sys_time_t; inline void system_time(sys_time_t* t) { gettimeofday(t, NULL); } -inline uint64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; } +inline int64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; } # include typedef pthread_mutex_t Lock; @@ -74,7 +74,7 @@ typedef void*(*pt_start_fn)(void*); typedef _timeb sys_time_t; inline void system_time(sys_time_t* t) { _ftime(t); } -inline uint64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; } +inline int64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; } #if !defined(NOMINMAX) # define NOMINMAX // disable macros min() and max() diff --git a/DroidFish/jni/stockfish/position.cpp b/DroidFish/jni/stockfish/position.cpp index c26ab1b..4bc8df5 100644 --- a/DroidFish/jni/stockfish/position.cpp +++ b/DroidFish/jni/stockfish/position.cpp @@ -25,6 +25,7 @@ #include "bitcount.h" #include "movegen.h" +#include "notation.h" #include "position.h" #include "psqtab.h" #include "rkiss.h" @@ -35,36 +36,105 @@ using std::string; using std::cout; using std::endl; -Key Position::zobrist[2][8][64]; -Key Position::zobEp[8]; -Key Position::zobCastle[16]; -Key Position::zobSideToMove; -Key Position::zobExclusion; - -Score Position::pieceSquareTable[16][64]; - -// Material values arrays, indexed by Piece -const Value PieceValueMidgame[17] = { - VALUE_ZERO, - PawnValueMidgame, KnightValueMidgame, BishopValueMidgame, - RookValueMidgame, QueenValueMidgame, - VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, - PawnValueMidgame, KnightValueMidgame, BishopValueMidgame, - RookValueMidgame, QueenValueMidgame -}; - -const Value PieceValueEndgame[17] = { - VALUE_ZERO, - PawnValueEndgame, KnightValueEndgame, BishopValueEndgame, - RookValueEndgame, QueenValueEndgame, - VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, - PawnValueEndgame, KnightValueEndgame, BishopValueEndgame, - RookValueEndgame, QueenValueEndgame -}; - -// To convert a Piece to and from a FEN char static const string PieceToChar(" PNBRQK pnbrqk"); +CACHE_LINE_ALIGNMENT + +Score pieceSquareTable[16][64]; // [piece][square] +Value PieceValue[2][18] = { // [Mg / Eg][piece / pieceType] +{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, +{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; + +namespace Zobrist { + +Key psq[2][8][64]; // [color][pieceType][square / piece count] +Key enpassant[8]; // [file] +Key castle[16]; // [castleRight] +Key side; +Key exclusion; + +/// init() initializes at startup the various arrays used to compute hash keys +/// and the piece square tables. The latter is a two-step operation: First, the +/// white halves of the tables are copied from PSQT[] tables. Second, the black +/// halves of the tables are initialized by flipping and changing the sign of +/// the white scores. + +void 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++) + psq[c][pt][s] = rk.rand(); + + for (File f = FILE_A; f <= FILE_H; f++) + enpassant[f] = rk.rand(); + + for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++) + { + Bitboard b = cr; + while (b) + { + Key k = castle[1ULL << pop_lsb(&b)]; + castle[cr] ^= k ? k : rk.rand(); + } + } + + side = rk.rand(); + exclusion = rk.rand(); + + 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++) + { + pieceSquareTable[make_piece(WHITE, pt)][ s] = (v + PSQT[pt][s]); + pieceSquareTable[make_piece(BLACK, pt)][~s] = -(v + PSQT[pt][s]); + } + } +} + +} // namespace Zobrist + + +namespace { + +/// next_attacker() is an helper function used by see() to locate the least +/// valuable attacker for the side to move, remove the attacker we just found +/// from the 'occupied' bitboard and scan for new X-ray attacks behind it. + +template FORCE_INLINE +PieceType next_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers, + Bitboard& occupied, Bitboard& attackers) { + + if (stmAttackers & bb[Pt]) + { + Bitboard b = stmAttackers & bb[Pt]; + occupied ^= b & ~(b - 1); + + if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN) + attackers |= attacks_bb(to, occupied) & (bb[BISHOP] | bb[QUEEN]); + + if (Pt == ROOK || Pt == QUEEN) + attackers |= attacks_bb(to, occupied) & (bb[ROOK] | bb[QUEEN]); + + return (PieceType)Pt; + } + return next_attacker(bb, to, stmAttackers, occupied, attackers); +} + +template<> FORCE_INLINE +PieceType next_attacker(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) { + return KING; // No need to update bitboards, it is the last cycle +} + +} // namespace + /// CheckInfo c'tor @@ -89,7 +159,7 @@ CheckInfo::CheckInfo(const Position& pos) { /// object do not depend on any external data so we detach state pointer from /// the source one. -void Position::operator=(const Position& pos) { +Position& Position::operator=(const Position& pos) { memcpy(this, &pos, sizeof(Position)); startState = *st; @@ -97,6 +167,8 @@ void Position::operator=(const Position& pos) { nodes = 0; assert(pos_is_ok()); + + return *this; } @@ -187,7 +259,7 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) { for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; rsq++) {} else if (token >= 'A' && token <= 'H') - rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); + rsq = File(token - 'A') | relative_rank(c, RANK_1); else continue; @@ -199,7 +271,7 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) { if ( ((fen >> col) && (col >= 'a' && col <= 'h')) && ((fen >> row) && (row == '3' || row == '6'))) { - st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); + st->epSquare = File(col - 'a') | Rank(row - '1'); if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))) st->epSquare = SQ_NONE; @@ -268,7 +340,7 @@ const string Position::to_fen() const { for (File file = FILE_A; file <= FILE_H; file++) { - sq = make_square(file, rank); + sq = file | rank; if (is_empty(sq)) emptyCnt++; @@ -325,6 +397,8 @@ void Position::print(Move move) const { string brd = twoRows + twoRows + twoRows + twoRows + dottedLine; + sync_cout; + if (move) { Position p(*this); @@ -335,7 +409,7 @@ void Position::print(Move move) const { if (piece_on(sq) != NO_PIECE) brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)]; - cout << brd << "\nFen is: " << to_fen() << "\nKey is: " << st->key << endl; + cout << brd << "\nFen is: " << to_fen() << "\nKey is: " << st->key << sync_endl; } @@ -357,7 +431,7 @@ Bitboard Position::hidden_checkers() const { while (pinners) { - b = between_bb(ksq, pop_1st_bit(&pinners)) & pieces(); + b = between_bb(ksq, pop_lsb(&pinners)) & pieces(); if (b && !more_than_one(b) && (b & pieces(sideToMove))) result |= b; @@ -448,7 +522,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { // En passant captures are a tricky special case. Because they are rather // uncommon, we do it simply by testing whether the king is attacked after // the move is made. - if (is_enpassant(m)) + if (type_of(m) == ENPASSANT) { Color them = ~us; Square to = to_sq(m); @@ -469,7 +543,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { // square is attacked by the opponent. Castling moves are checked // for legality during move generation. if (type_of(piece_on(from)) == KING) - return is_castle(m) || !(attackers_to(to_sq(m)) & pieces(~us)); + return type_of(m) == CASTLE || !(attackers_to(to_sq(m)) & pieces(~us)); // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. @@ -485,7 +559,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { bool Position::move_is_legal(const Move m) const { - for (MoveList ml(*this); !ml.end(); ++ml) + for (MoveList ml(*this); !ml.end(); ++ml) if (ml.move() == m) return true; @@ -506,7 +580,7 @@ bool Position::is_pseudo_legal(const Move m) const { Piece pc = piece_moved(m); // Use a slower but simpler function for uncommon cases - if (is_special(m)) + if (type_of(m) != NORMAL) return move_is_legal(m); // Is not a promotion, so promotion piece must be empty @@ -595,7 +669,7 @@ bool Position::is_pseudo_legal(const Move m) const { if (type_of(pc) != KING) { Bitboard b = checkers(); - Square checksq = pop_1st_bit(&b); + Square checksq = pop_lsb(&b); if (b) // double check ? In this case a king move is required return false; @@ -640,23 +714,23 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const { } // Can we skip the ugly special cases ? - if (!is_special(m)) + if (type_of(m) == NORMAL) return false; Color us = sideToMove; Square ksq = king_square(~us); // Promotion with check ? - if (is_promotion(m)) + if (type_of(m) == PROMOTION) return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq; // En passant capture with check ? We have already handled the case // of direct checks and ordinary discovered check, the only case we // need to handle is the unusual case of a discovered check through // the captured pawn. - if (is_enpassant(m)) + if (type_of(m) == ENPASSANT) { - Square capsq = make_square(file_of(to), rank_of(from)); + Square capsq = file_of(to) | rank_of(from); Bitboard b = (pieces() ^ from ^ capsq) | to; return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK)) @@ -664,7 +738,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const { } // Castling with check ? - if (is_castle(m)) + if (type_of(m) == CASTLE) { Square kfrom = from; Square rfrom = to; // 'King captures the rook' notation @@ -706,14 +780,14 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st = &newSt; // Update side to move - k ^= zobSideToMove; + k ^= Zobrist::side; // Increment the 50 moves rule draw counter. Resetting it to zero in the // case of a capture or a pawn move is taken care of later. st->rule50++; st->pliesFromNull++; - if (is_castle(m)) + if (type_of(m) == CASTLE) { st->key = k; do_castle_move(m); @@ -726,7 +800,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI Square to = to_sq(m); Piece piece = piece_on(from); PieceType pt = type_of(piece); - PieceType capture = is_enpassant(m) ? PAWN : type_of(piece_on(to)); + PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); assert(color_of(piece) == us); assert(color_of(piece_on(to)) != us); @@ -740,7 +814,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI // update non-pawn material. if (capture == PAWN) { - if (is_enpassant(m)) + if (type_of(m) == ENPASSANT) { capsq += pawn_push(them); @@ -753,10 +827,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI board[capsq] = NO_PIECE; } - st->pawnKey ^= zobrist[them][PAWN][capsq]; + st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; } else - st->npMaterial[them] -= PieceValueMidgame[capture]; + st->npMaterial[them] -= PieceValue[Mg][capture]; // Remove the captured piece byTypeBB[ALL_PIECES] ^= capsq; @@ -776,8 +850,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE; // Update hash keys - k ^= zobrist[them][capture][capsq]; - st->materialKey ^= zobrist[them][capture][pieceCount[them][capture]]; + k ^= Zobrist::psq[them][capture][capsq]; + st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]]; // Update incremental scores st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq]; @@ -787,12 +861,12 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI } // Update hash key - k ^= zobrist[us][pt][from] ^ zobrist[us][pt][to]; + k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to]; // Reset en passant square if (st->epSquare != SQ_NONE) { - k ^= zobEp[file_of(st->epSquare)]; + k ^= Zobrist::enpassant[file_of(st->epSquare)]; st->epSquare = SQ_NONE; } @@ -800,7 +874,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI if (st->castleRights && (castleRightsMask[from] | castleRightsMask[to])) { int cr = castleRightsMask[from] | castleRightsMask[to]; - k ^= zobCastle[st->castleRights & cr]; + k ^= Zobrist::castle[st->castleRights & cr]; st->castleRights &= ~cr; } @@ -829,10 +903,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI && (attacks_from(from + pawn_push(us), us) & pieces(them, PAWN))) { st->epSquare = Square((from + to) / 2); - k ^= zobEp[file_of(st->epSquare)]; + k ^= Zobrist::enpassant[file_of(st->epSquare)]; } - if (is_promotion(m)) + if (type_of(m) == PROMOTION) { PieceType promotion = promotion_type(m); @@ -854,21 +928,21 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI pieceList[us][promotion][index[to]] = to; // Update hash keys - k ^= zobrist[us][PAWN][to] ^ zobrist[us][promotion][to]; - st->pawnKey ^= zobrist[us][PAWN][to]; - st->materialKey ^= zobrist[us][promotion][pieceCount[us][promotion]++] - ^ zobrist[us][PAWN][pieceCount[us][PAWN]]; + k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; + st->pawnKey ^= Zobrist::psq[us][PAWN][to]; + st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]++] + ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; // Update incremental score st->psqScore += pieceSquareTable[make_piece(us, promotion)][to] - pieceSquareTable[make_piece(us, PAWN)][to]; // Update material - st->npMaterial[us] += PieceValueMidgame[promotion]; + st->npMaterial[us] += PieceValue[Mg][promotion]; } // Update pawn hash key - st->pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to]; + st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; // Reset rule 50 draw counter st->rule50 = 0; @@ -892,7 +966,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI if (moveIsCheck) { - if (is_special(m)) + if (type_of(m) != NORMAL) st->checkersBB = attackers_to(king_square(them)) & pieces(us); else { @@ -927,7 +1001,7 @@ void Position::undo_move(Move m) { sideToMove = ~sideToMove; - if (is_castle(m)) + if (type_of(m) == CASTLE) { do_castle_move(m); return; @@ -945,7 +1019,7 @@ void Position::undo_move(Move m) { assert(color_of(piece) == us); assert(capture != KING); - if (is_promotion(m)) + if (type_of(m) == PROMOTION) { PieceType promotion = promotion_type(m); @@ -988,7 +1062,7 @@ void Position::undo_move(Move m) { { Square capsq = to; - if (is_enpassant(m)) + if (type_of(m) == ENPASSANT) { capsq -= pawn_push(us); @@ -1025,7 +1099,7 @@ template void Position::do_castle_move(Move m) { assert(is_ok(m)); - assert(is_castle(m)); + assert(type_of(m) == CASTLE); Square kto, kfrom, rfrom, rto, kAfter, rAfter; @@ -1086,18 +1160,18 @@ void Position::do_castle_move(Move m) { st->psqScore += psq_delta(rook, rfrom, rto); // Update hash key - st->key ^= zobrist[us][KING][kfrom] ^ zobrist[us][KING][kto]; - st->key ^= zobrist[us][ROOK][rfrom] ^ zobrist[us][ROOK][rto]; + st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto]; + st->key ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; // Clear en passant square if (st->epSquare != SQ_NONE) { - st->key ^= zobEp[file_of(st->epSquare)]; + st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; st->epSquare = SQ_NONE; } // Update castling rights - st->key ^= zobCastle[st->castleRights & castleRightsMask[kfrom]]; + st->key ^= Zobrist::castle[st->castleRights & castleRightsMask[kfrom]]; st->castleRights &= ~castleRightsMask[kfrom]; // Update checkers BB @@ -1138,9 +1212,9 @@ void Position::do_null_move(StateInfo& backupSt) { if (Do) { if (st->epSquare != SQ_NONE) - st->key ^= zobEp[file_of(st->epSquare)]; + st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; - st->key ^= zobSideToMove; + st->key ^= Zobrist::side; prefetch((char*)TT.first_entry(st->key)); st->epSquare = SQ_NONE; @@ -1169,7 +1243,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 (PieceValueMidgame[piece_on(to_sq(m))] >= PieceValueMidgame[piece_moved(m)]) + if (PieceValue[Mg][piece_on(to_sq(m))] >= PieceValue[Mg][piece_moved(m)]) return 1; return see(m); @@ -1178,47 +1252,45 @@ int Position::see_sign(Move m) const { int Position::see(Move m) const { Square from, to; - Bitboard occ, attackers, stmAttackers, b; + Bitboard occupied, attackers, stmAttackers; int swapList[32], slIndex = 1; - PieceType capturedType, pt; + PieceType captured; Color stm; assert(is_ok(m)); - // As castle moves are implemented as capturing the rook, they have - // SEE == RookValueMidgame most of the times (unless the rook is under - // attack). - if (is_castle(m)) - return 0; - from = from_sq(m); to = to_sq(m); - capturedType = type_of(piece_on(to)); - occ = pieces(); + captured = type_of(piece_on(to)); + occupied = pieces() ^ from; // Handle en passant moves - if (is_enpassant(m)) + if (type_of(m) == ENPASSANT) { Square capQq = to - pawn_push(sideToMove); - assert(!capturedType); + assert(!captured); assert(type_of(piece_on(capQq)) == PAWN); // Remove the captured pawn - occ ^= capQq; - capturedType = PAWN; + occupied ^= capQq; + captured = PAWN; } + else if (type_of(m) == CASTLE) + // Castle moves are implemented as king capturing the rook so cannot be + // handled correctly. Simply return 0 that is always the correct value + // unless the rook is ends up under attack. + return 0; // Find all attackers to the destination square, with the moving piece // removed, but possibly an X-ray attacker added behind it. - occ ^= from; - attackers = attackers_to(to, occ); + attackers = attackers_to(to, occupied); // If the opponent has no attackers we are finished stm = ~color_of(piece_on(from)); stmAttackers = attackers & pieces(stm); if (!stmAttackers) - return PieceValueMidgame[capturedType]; + return PieceValue[Mg][captured]; // The destination square is defended, which makes things rather more // difficult to compute. We proceed by building up a "swap list" containing @@ -1226,43 +1298,32 @@ int Position::see(Move m) const { // destination square, where the sides alternately capture, and always // capture with the least valuable piece. After each capture, we look for // new X-ray attacks from behind the capturing piece. - swapList[0] = PieceValueMidgame[capturedType]; - capturedType = type_of(piece_on(from)); + swapList[0] = PieceValue[Mg][captured]; + captured = type_of(piece_on(from)); do { - // Locate the least valuable attacker for the side to move. The loop - // below looks like it is potentially infinite, but it isn't. We know - // that the side to move still has at least one attacker left. - for (pt = PAWN; !(stmAttackers & pieces(pt)); pt++) - assert(pt < KING); - - // Remove the attacker we just found from the 'occupied' bitboard, - // and scan for new X-ray attacks behind the attacker. - b = stmAttackers & pieces(pt); - occ ^= (b & (~b + 1)); - attackers |= (attacks_bb(to, occ) & pieces(ROOK, QUEEN)) - | (attacks_bb(to, occ) & pieces(BISHOP, QUEEN)); - - attackers &= occ; // Cut out pieces we've already done + assert(slIndex < 32); // Add the new entry to the swap list - assert(slIndex < 32); - swapList[slIndex] = -swapList[slIndex - 1] + PieceValueMidgame[capturedType]; + swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[Mg][captured]; slIndex++; - // Remember the value of the capturing piece, and change the side to - // move before beginning the next iteration. - capturedType = pt; + // Locate and remove from 'occupied' the next least valuable attacker + captured = next_attacker(byTypeBB, to, stmAttackers, occupied, attackers); + + attackers &= occupied; // Remove the just found attacker stm = ~stm; stmAttackers = attackers & pieces(stm); - // Stop before processing a king capture - if (capturedType == KING && stmAttackers) + if (captured == KING) { - assert(slIndex < 32); - swapList[slIndex++] = QueenValueMidgame*10; + // Stop before processing a king capture + if (stmAttackers) + swapList[slIndex++] = QueenValueMg * 16; + break; } + } while (stmAttackers); // Having built the swap list, we negamax through it to find the best @@ -1317,19 +1378,19 @@ void Position::put_piece(Piece p, Square s) { Key Position::compute_key() const { - Key k = zobCastle[st->castleRights]; + Key k = Zobrist::castle[st->castleRights]; for (Bitboard b = pieces(); b; ) { - Square s = pop_1st_bit(&b); - k ^= zobrist[color_of(piece_on(s))][type_of(piece_on(s))][s]; + Square s = pop_lsb(&b); + k ^= Zobrist::psq[color_of(piece_on(s))][type_of(piece_on(s))][s]; } if (ep_square() != SQ_NONE) - k ^= zobEp[file_of(ep_square())]; + k ^= Zobrist::enpassant[file_of(ep_square())]; if (sideToMove == BLACK) - k ^= zobSideToMove; + k ^= Zobrist::side; return k; } @@ -1347,8 +1408,8 @@ Key Position::compute_pawn_key() const { for (Bitboard b = pieces(PAWN); b; ) { - Square s = pop_1st_bit(&b); - k ^= zobrist[color_of(piece_on(s))][PAWN][s]; + Square s = pop_lsb(&b); + k ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s]; } return k; @@ -1368,7 +1429,7 @@ Key Position::compute_material_key() const { for (Color c = WHITE; c <= BLACK; c++) for (PieceType pt = PAWN; pt <= QUEEN; pt++) for (int cnt = 0; cnt < piece_count(c, pt); cnt++) - k ^= zobrist[c][pt][cnt]; + k ^= Zobrist::psq[c][pt][cnt]; return k; } @@ -1384,7 +1445,7 @@ Score Position::compute_psq_score() const { for (Bitboard b = pieces(); b; ) { - Square s = pop_1st_bit(&b); + Square s = pop_lsb(&b); score += pieceSquareTable[piece_on(s)][s]; } @@ -1402,7 +1463,7 @@ Value Position::compute_non_pawn_material(Color c) const { Value value = VALUE_ZERO; for (PieceType pt = KNIGHT; pt <= QUEEN; pt++) - value += piece_count(c, pt) * PieceValueMidgame[pt]; + value += piece_count(c, pt) * PieceValue[Mg][pt]; return value; } @@ -1416,11 +1477,11 @@ bool Position::is_draw() const { // Draw by material? if ( !pieces(PAWN) - && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMidgame)) + && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg)) return true; // Draw by the 50 moves rule? - if (st->rule50 > 99 && (!in_check() || MoveList(*this).size())) + if (st->rule50 > 99 && (!in_check() || MoveList(*this).size())) return true; // Draw by repetition? @@ -1452,50 +1513,6 @@ template bool Position::is_draw() const; template bool Position::is_draw() const; -/// Position::init() is a static member function which initializes at startup -/// the various arrays used to compute hash keys and the piece square tables. -/// The latter is a two-step operation: First, the white halves of the tables -/// are copied from PSQT[] tables. Second, the black halves of the tables are -/// initialized by flipping and changing the sign of the white scores. - -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++) - zobrist[c][pt][s] = rk.rand(); - - for (File f = FILE_A; f <= FILE_H; f++) - zobEp[f] = rk.rand(); - - for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++) - { - Bitboard b = cr; - while (b) - { - Key k = zobCastle[1ULL << pop_1st_bit(&b)]; - zobCastle[cr] ^= k ? k : rk.rand(); - } - } - - zobSideToMove = rk.rand(); - zobExclusion = rk.rand(); - - for (PieceType pt = PAWN; pt <= KING; pt++) - { - Score v = make_score(PieceValueMidgame[pt], PieceValueEndgame[pt]); - - for (Square s = SQ_A1; s <= SQ_H8; s++) - { - pieceSquareTable[make_piece(WHITE, pt)][ s] = (v + PSQT[pt][s]); - pieceSquareTable[make_piece(BLACK, pt)][~s] = -(v + PSQT[pt][s]); - } - } -} - - /// Position::flip() flips position with the white and black sides reversed. This /// is only useful for debugging especially for finding evaluation symmetry bugs. diff --git a/DroidFish/jni/stockfish/position.h b/DroidFish/jni/stockfish/position.h index 55dc33f..7418b0d 100644 --- a/DroidFish/jni/stockfish/position.h +++ b/DroidFish/jni/stockfish/position.h @@ -94,10 +94,9 @@ struct ReducedStateInfo { class Position { public: Position() {} - Position(const Position& p) { *this = p; } Position(const Position& p, Thread* t) { *this = p; thisThread = t; } Position(const std::string& f, bool c960, Thread* t) { from_fen(f, c960, t); } - void operator=(const Position&); + Position& operator=(const Position&); // Text input/output void from_fen(const std::string& fen, bool isChess960, Thread* th); @@ -141,6 +140,7 @@ public: // Properties of moves bool move_gives_check(Move m, const CheckInfo& ci) const; bool move_attacks_square(Move m, Square s) const; + bool move_is_legal(const Move m) 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; @@ -189,15 +189,11 @@ public: bool pos_is_ok(int* failedStep = NULL) const; void flip(); - // Global initialization - static void init(); - private: // Initialization helpers (used while setting up a position) void clear(); void put_piece(Piece p, Square s); void set_castle_right(Color c, Square rfrom); - bool move_is_legal(const Move m) const; // Helper template functions template void do_castle_move(Move m); @@ -231,14 +227,6 @@ private: Thread* thisThread; StateInfo* st; int chess960; - - // Static variables - static Score pieceSquareTable[16][64]; // [piece][square] - static Key zobrist[2][8][64]; // [color][pieceType][square]/[piece count] - static Key zobEp[8]; // [file] - static Key zobCastle[16]; // [castleRight] - static Key zobSideToMove; - static Key zobExclusion; }; inline int64_t Position::nodes_searched() const { @@ -367,7 +355,7 @@ inline Key Position::key() const { } inline Key Position::exclusion_key() const { - return st->key ^ zobExclusion; + return st->key ^ Zobrist::exclusion; } inline Key Position::pawn_key() const { @@ -424,14 +412,14 @@ inline bool Position::is_chess960() const { inline bool Position::is_capture_or_promotion(Move m) const { assert(is_ok(m)); - return is_special(m) ? !is_castle(m) : !is_empty(to_sq(m)); + return type_of(m) ? type_of(m) != CASTLE : !is_empty(to_sq(m)); } inline bool Position::is_capture(Move m) const { // Note that castle is coded as "king captures the rook" assert(is_ok(m)); - return (!is_empty(to_sq(m)) && !is_castle(m)) || is_enpassant(m); + return (!is_empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT; } inline PieceType Position::captured_piece_type() const { diff --git a/DroidFish/jni/stockfish/search.cpp b/DroidFish/jni/stockfish/search.cpp index 93014bd..eeca973 100644 --- a/DroidFish/jni/stockfish/search.cpp +++ b/DroidFish/jni/stockfish/search.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -30,6 +29,7 @@ #include "history.h" #include "movegen.h" #include "movepick.h" +#include "notation.h" #include "search.h" #include "timeman.h" #include "thread.h" @@ -42,20 +42,14 @@ namespace Search { LimitsType Limits; std::vector RootMoves; Position RootPosition; - Time SearchTime; + Time::point SearchTime; + StateStackPtr SetupStates; } using std::string; -using std::cout; -using std::endl; using Eval::evaluate; using namespace Search; -// For some reason argument-dependent lookup (ADL) doesn't work for Android's -// STLPort, so explicitly qualify following functions. -using std::count; -using std::find; - namespace { // Set to true to force running with one thread. Used for debugging @@ -144,48 +138,27 @@ namespace { bool connected_threat(const Position& pos, Move m, Move threat); Value refine_eval(const TTEntry* tte, Value ttValue, Value defaultEval); Move do_skill_level(); - string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE); - void pv_info_to_log(Position& pos, int depth, Value score, int time, Move pv[]); - void pv_info_to_uci(const Position& pos, int depth, Value alpha, Value beta); - - // MovePickerExt class template extends MovePicker and allows to choose at - // compile time the proper moves source according to the type of node. In the - // default case we simply create and use a standard MovePicker object. - template struct MovePickerExt : public MovePicker { - - MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b) - : MovePicker(p, ttm, d, h, ss, b) {} - }; - - // In case of a SpNode we use split point's shared MovePicker object as moves source - template<> struct MovePickerExt : public MovePicker { - - MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b) - : MovePicker(p, ttm, d, h, ss, b), mp(ss->sp->mp) {} - - Move next_move() { return mp->next_move(); } - MovePicker* mp; - }; + string uci_pv(const Position& pos, int depth, Value alpha, Value beta); // is_dangerous() checks whether a move belongs to some classes of known // 'dangerous' moves so that we avoid to prune it. FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) { - // Test for a pawn pushed to 7th or a passed pawn move - if (type_of(pos.piece_moved(m)) == PAWN) - { - Color c = pos.side_to_move(); - if ( relative_rank(c, to_sq(m)) == RANK_7 - || pos.pawn_is_passed(c, to_sq(m))) - return true; - } + // Castle move? + if (type_of(m) == CASTLE) + return true; - // Test for a capture that triggers a pawn endgame + // Passed pawn move? + if ( type_of(pos.piece_moved(m)) == PAWN + && pos.pawn_is_passed(pos.side_to_move(), to_sq(m))) + return true; + + // Entering a pawn endgame? if ( captureOrPromotion && type_of(pos.piece_on(to_sq(m))) != PAWN - && !is_special(m) + && type_of(m) == NORMAL && ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) - - PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO)) + - PieceValue[Mg][pos.piece_on(to_sq(m))] == VALUE_ZERO)) return true; return false; @@ -224,24 +197,23 @@ void Search::init() { /// Search::perft() is our utility to verify move generation. All the leaf nodes /// up to the given depth are generated and counted and the sum returned. -int64_t Search::perft(Position& pos, Depth depth) { +size_t Search::perft(Position& pos, Depth depth) { + + // At the last ply just return the number of legal moves (leaf nodes) + if (depth == ONE_PLY) + return MoveList(pos).size(); StateInfo st; - int64_t cnt = 0; - - MoveList ml(pos); - - // At the last ply just return the number of moves (leaf nodes) - if (depth == ONE_PLY) - return ml.size(); - + size_t cnt = 0; CheckInfo ci(pos); - for ( ; !ml.end(); ++ml) + + for (MoveList ml(pos); !ml.end(); ++ml) { pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci)); cnt += perft(pos, depth - ONE_PLY); pos.undo_move(ml.move()); } + return cnt; } @@ -263,8 +235,8 @@ void Search::think() { if (RootMoves.empty()) { - cout << "info depth 0 score " - << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl; + sync_cout << "info depth 0 score " + << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << sync_endl; RootMoves.push_back(MOVE_NONE); goto finalize; @@ -274,9 +246,9 @@ void Search::think() { { Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]); - if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove)) + if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove)) { - std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove)); + std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), bookMove)); goto finalize; } } @@ -298,7 +270,7 @@ void Search::think() { << " time: " << Limits.time[pos.side_to_move()] << " increment: " << Limits.inc[pos.side_to_move()] << " moves to go: " << Limits.movestogo - << endl; + << std::endl; } Threads.wake_up(); @@ -318,16 +290,16 @@ void Search::think() { if (Options["Use Search Log"]) { - int e = SearchTime.elapsed(); + Time::point elapsed = Time::now() - SearchTime + 1; Log log(Options["Search Log Filename"]); log << "Nodes: " << pos.nodes_searched() - << "\nNodes/second: " << (e > 0 ? pos.nodes_searched() * 1000 / e : 0) + << "\nNodes/second: " << pos.nodes_searched() * 1000 / elapsed << "\nBest move: " << move_to_san(pos, RootMoves[0].pv[0]); StateInfo st; pos.do_move(RootMoves[0].pv[0], st); - log << "\nPonder move: " << move_to_san(pos, RootMoves[0].pv[1]) << endl; + log << "\nPonder move: " << move_to_san(pos, RootMoves[0].pv[1]) << std::endl; pos.undo_move(RootMoves[0].pv[0]); } @@ -340,8 +312,8 @@ finalize: pos.this_thread()->wait_for_stop_or_ponderhit(); // Best move could be MOVE_NONE when searching on a stalemate position - cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960) - << " ponder " << move_to_uci(RootMoves[0].pv[1], Chess960) << endl; + sync_cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960) + << " ponder " << move_to_uci(RootMoves[0].pv[1], Chess960) << sync_endl; } @@ -393,7 +365,8 @@ namespace { // Start with a small aspiration window and, in case of fail high/low, // research with bigger window until not failing high/low anymore. - do { + while (true) + { // Search starts from ss+1 to allow referencing (ss-1). This is // needed by update gains and ss copy when splitting at Root. bestValue = search(pos, ss+1, alpha, beta, depth * ONE_PLY); @@ -426,8 +399,8 @@ namespace { // Send full PV info to GUI if we are going to leave the loop or // if we have a fail high/low and we are deep in the search. - if ((bestValue > alpha && bestValue < beta) || SearchTime.elapsed() > 2000) - pv_info_to_uci(pos, depth, alpha, beta); + if ((bestValue > alpha && bestValue < beta) || Time::now() - SearchTime > 2000) + sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; // In case of failing high/low increase aspiration window and // research, otherwise exit the fail high/low loop. @@ -447,9 +420,15 @@ namespace { else break; - assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); + // Search with full window in case we have a win/mate score + if (abs(bestValue) >= VALUE_KNOWN_WIN) + { + alpha = -VALUE_INFINITE; + beta = VALUE_INFINITE; + } - } while (abs(bestValue) < VALUE_KNOWN_WIN); + assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); + } } // Skills: Do we need to pick now the best move ? @@ -457,7 +436,11 @@ namespace { skillBest = do_skill_level(); if (!Signals.stop && Options["Use Search Log"]) - pv_info_to_log(pos, depth, bestValue, SearchTime.elapsed(), &RootMoves[0].pv[0]); + { + Log log(Options["Search Log Filename"]); + log << pretty_pv(pos, depth, bestValue, Time::now() - SearchTime, &RootMoves[0].pv[0]) + << std::endl; + } // Filter out startup noise when monitoring best move stability if (depth > 2 && BestMoveChanges) @@ -475,14 +458,14 @@ namespace { // Stop search if most of available time is already consumed. We // probably don't have enough time to search the first move at the // next iteration anyway. - if (SearchTime.elapsed() > (TimeMgr.available_time() * 62) / 100) + if (Time::now() - SearchTime > (TimeMgr.available_time() * 62) / 100) stop = true; // Stop search early if one move seems to be much better than others if ( depth >= 12 && !stop && ( (bestMoveNeverChanged && pos.captured_piece_type()) - || SearchTime.elapsed() > (TimeMgr.available_time() * 40) / 100)) + || Time::now() - SearchTime > (TimeMgr.available_time() * 40) / 100)) { Value rBeta = bestValue - EasyMoveMargin; (ss+1)->excludedMove = RootMoves[0].pv[0]; @@ -513,7 +496,7 @@ namespace { if (skillBest == MOVE_NONE) // Still unassigned ? skillBest = do_skill_level(); - std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), skillBest)); + std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), skillBest)); } } @@ -661,7 +644,7 @@ namespace { && (ss-1)->eval != VALUE_NONE && ss->eval != VALUE_NONE && !pos.captured_piece_type() - && !is_special(move)) + && type_of(move) == NORMAL) { Square to = to_sq(move); H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval); @@ -708,16 +691,16 @@ namespace { ss->currentMove = MOVE_NULL; // Null move dynamic reduction based on depth - int R = 3 + (depth >= 5 * ONE_PLY ? depth / 8 : 0); + Depth R = 3 * ONE_PLY + depth / 4; // Null move dynamic reduction based on value - if (refinedValue - PawnValueMidgame > beta) - R++; + if (refinedValue - PawnValueMg > beta) + R += ONE_PLY; pos.do_null_move(st); (ss+1)->skipNullMove = true; - nullValue = depth-R*ONE_PLY < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -alpha, depth-R*ONE_PLY); + nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) + : - search(pos, ss+1, -beta, -alpha, depth-R); (ss+1)->skipNullMove = false; pos.do_null_move(st); @@ -732,7 +715,7 @@ namespace { // Do verification search at high depths ss->skipNullMove = true; - Value v = search(pos, ss, alpha, beta, depth-R*ONE_PLY); + Value v = search(pos, ss, alpha, beta, depth-R); ss->skipNullMove = false; if (v >= beta) @@ -777,7 +760,7 @@ namespace { MovePicker mp(pos, ttMove, H, pos.captured_piece_type()); CheckInfo ci(pos); - while ((move = mp.next_move()) != MOVE_NONE) + while ((move = mp.next_move()) != MOVE_NONE) if (pos.pl_move_is_legal(move, ci.pinned)) { ss->currentMove = move; @@ -806,7 +789,7 @@ namespace { split_point_start: // At split points actual search starts from here - MovePickerExt mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta); + MovePicker mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta); CheckInfo ci(pos); futilityBase = ss->eval + ss->evalMargin; singularExtensionNode = !RootNode @@ -820,7 +803,7 @@ split_point_start: // At split points actual search starts from here // Step 11. Loop through moves // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs while ( bestValue < beta - && (move = mp.next_move()) != MOVE_NONE + && (move = mp.next_move()) != MOVE_NONE && !thisThread->cutoff_occurred() && !Signals.stop) { @@ -832,7 +815,7 @@ split_point_start: // At split points actual search starts from here // At root obey the "searchmoves" option and skip moves not listed in Root // Move List, as a consequence any illegal move is also skipped. In MultiPV // mode we also skip PV moves which have been already searched. - if (RootNode && !count(RootMoves.begin() + PVIdx, RootMoves.end(), move)) + if (RootNode && !std::count(RootMoves.begin() + PVIdx, RootMoves.end(), move)) continue; // At PV and SpNode nodes we want all moves to be legal since the beginning @@ -842,7 +825,7 @@ split_point_start: // At split points actual search starts from here if (SpNode) { moveCount = ++sp->moveCount; - lock_release(sp->lock); + sp->mutex.unlock(); } else moveCount++; @@ -851,10 +834,10 @@ split_point_start: // At split points actual search starts from here { Signals.firstRootMove = (moveCount == 1); - if (thisThread == Threads.main_thread() && SearchTime.elapsed() > 2000) - cout << "info depth " << depth / ONE_PLY - << " currmove " << move_to_uci(move, Chess960) - << " currmovenumber " << moveCount + PVIdx << endl; + if (thisThread == Threads.main_thread() && Time::now() - SearchTime > 2000) + sync_cout << "info depth " << depth / ONE_PLY + << " currmove " << move_to_uci(move, Chess960) + << " currmovenumber " << moveCount + PVIdx << sync_endl; } isPvMove = (PvNode && moveCount <= 1); @@ -878,19 +861,18 @@ split_point_start: // At split points actual search starts from here if ( singularExtensionNode && !ext && move == ttMove - && pos.pl_move_is_legal(move, ci.pinned)) + && pos.pl_move_is_legal(move, ci.pinned) + && abs(ttValue) < VALUE_KNOWN_WIN) { - if (abs(ttValue) < VALUE_KNOWN_WIN) - { - Value rBeta = ttValue - int(depth); - ss->excludedMove = move; - ss->skipNullMove = true; - value = search(pos, ss, rBeta - 1, rBeta, depth / 2); - ss->skipNullMove = false; - ss->excludedMove = MOVE_NONE; - if (value < rBeta) - ext = ONE_PLY; - } + Value rBeta = ttValue - int(depth); + ss->excludedMove = move; + ss->skipNullMove = true; + value = search(pos, ss, rBeta - 1, rBeta, depth / 2); + ss->skipNullMove = false; + ss->excludedMove = MOVE_NONE; + + if (value < rBeta) + ext = ONE_PLY; } // Update current move (this must be done after singular extension search) @@ -902,7 +884,6 @@ split_point_start: // At split points actual search starts from here && !inCheck && !dangerous && move != ttMove - && !is_castle(move) && (bestValue > VALUE_MATED_IN_MAX_PLY || bestValue == -VALUE_INFINITE)) { // Move count based pruning @@ -910,7 +891,7 @@ split_point_start: // At split points actual search starts from here && (!threatMove || !connected_threat(pos, move, threatMove))) { if (SpNode) - lock_grab(sp->lock); + sp->mutex.lock(); continue; } @@ -925,7 +906,7 @@ split_point_start: // At split points actual search starts from here if (futilityValue < beta) { if (SpNode) - lock_grab(sp->lock); + sp->mutex.lock(); continue; } @@ -935,7 +916,7 @@ split_point_start: // At split points actual search starts from here && pos.see_sign(move) < 0) { if (SpNode) - lock_grab(sp->lock); + sp->mutex.lock(); continue; } @@ -961,7 +942,6 @@ split_point_start: // At split points actual search starts from here && !isPvMove && !captureOrPromotion && !dangerous - && !is_castle(move) && ss->killers[0] != move && ss->killers[1] != move) { @@ -1000,7 +980,7 @@ split_point_start: // At split points actual search starts from here // Step 18. Check for new best move if (SpNode) { - lock_grab(sp->lock); + sp->mutex.lock(); bestValue = sp->bestValue; alpha = sp->alpha; } @@ -1011,7 +991,7 @@ split_point_start: // At split points actual search starts from here // be trusted, and we don't update the best move and/or PV. if (RootNode && !Signals.stop) { - RootMove& rm = *find(RootMoves.begin(), RootMoves.end(), move); + RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move); // PV move or new best move ? if (isPvMove || value > alpha) @@ -1202,7 +1182,7 @@ split_point_start: // At split points actual search starts from here alpha = bestValue; futilityBase = ss->eval + evalMargin + FutilityMarginQS; - enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame; + enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMg; } // Initialize a MovePicker object for the current position, and prepare @@ -1214,7 +1194,7 @@ split_point_start: // At split points actual search starts from here // Loop through the moves until no moves remain or a beta cutoff occurs while ( bestValue < beta - && (move = mp.next_move()) != MOVE_NONE) + && (move = mp.next_move()) != MOVE_NONE) { assert(is_ok(move)); @@ -1226,12 +1206,12 @@ split_point_start: // At split points actual search starts from here && !givesCheck && move != ttMove && enoughMaterial - && !is_promotion(move) + && type_of(move) != PROMOTION && !pos.is_passed_pawn_push(move)) { futilityValue = futilityBase - + PieceValueEndgame[pos.piece_on(to_sq(move))] - + (is_enpassant(move) ? PawnValueEndgame : VALUE_ZERO); + + PieceValue[Eg][pos.piece_on(to_sq(move))] + + (type_of(move) == ENPASSANT ? PawnValueEg : VALUE_ZERO); if (futilityValue < beta) { @@ -1259,7 +1239,7 @@ split_point_start: // At split points actual search starts from here if ( !PvNode && (!inCheck || evasionPrunable) && move != ttMove - && !is_promotion(move) + && type_of(move) != PROMOTION && pos.see_sign(move) < 0) continue; @@ -1269,7 +1249,7 @@ split_point_start: // At split points actual search starts from here && givesCheck && move != ttMove && !pos.is_capture_or_promotion(move) - && ss->eval + PawnValueMidgame / 4 < beta + && ss->eval + PawnValueMg / 4 < beta && !check_is_dangerous(pos, move, futilityBase, beta)) continue; @@ -1354,7 +1334,7 @@ split_point_start: // At split points actual search starts from here while (b) { // Note that here we generate illegal "double move"! - if (futilityBase + PieceValueEndgame[pos.piece_on(pop_1st_bit(&b))] >= beta) + if (futilityBase + PieceValue[Eg][pos.piece_on(pop_lsb(&b))] >= beta) return true; } @@ -1466,7 +1446,7 @@ split_point_start: // At split points actual search starts from here // Case 2: If the threatened piece has value less than or equal to the // value of the threatening piece, don't prune moves which defend it. if ( pos.is_capture(threat) - && ( PieceValueMidgame[pos.piece_on(tfrom)] >= PieceValueMidgame[pos.piece_on(tto)] + && ( PieceValue[Mg][pos.piece_on(tfrom)] >= PieceValue[Mg][pos.piece_on(tto)] || type_of(pos.piece_on(tfrom)) == KING) && pos.move_attacks_square(m, tto)) return true; @@ -1511,156 +1491,6 @@ split_point_start: // At split points actual search starts from here } - // score_to_uci() converts a value to a string suitable for use with the UCI - // protocol specifications: - // - // cp The score from the engine's point of view in centipawns. - // mate Mate in y moves, not plies. If the engine is getting mated - // use negative values for y. - - string score_to_uci(Value v, Value alpha, Value beta) { - - std::stringstream s; - - if (abs(v) < VALUE_MATE_IN_MAX_PLY) - s << "cp " << v * 100 / int(PawnValueMidgame); - else - s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; - - s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); - - return s.str(); - } - - - // pv_info_to_uci() sends search info to GUI. UCI protocol requires to send all - // the PV lines also if are still to be searched and so refer to the previous - // search score. - - void pv_info_to_uci(const Position& pos, int depth, Value alpha, Value beta) { - - int t = SearchTime.elapsed(); - int selDepth = 0; - - for (int i = 0; i < Threads.size(); i++) - if (Threads[i].maxPly > selDepth) - selDepth = Threads[i].maxPly; - - for (size_t i = 0; i < std::min(UCIMultiPV, RootMoves.size()); i++) - { - bool updated = (i <= PVIdx); - - if (depth == 1 && !updated) - continue; - - int d = (updated ? depth : depth - 1); - Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore); - std::stringstream s; - - for (int j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) - s << " " << move_to_uci(RootMoves[i].pv[j], Chess960); - - cout << "info depth " << d - << " seldepth " << selDepth - << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) - << " nodes " << pos.nodes_searched() - << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0) - << " time " << t - << " multipv " << i + 1 - << " pv" << s.str() << endl; - } - } - - - // pv_info_to_log() writes human-readable search information to the log file - // (which is created when the UCI parameter "Use Search Log" is "true"). It - // uses the two below helpers to pretty format time and score respectively. - - string time_to_string(int millisecs) { - - const int MSecMinute = 1000 * 60; - const int MSecHour = 1000 * 60 * 60; - - int hours = millisecs / MSecHour; - int minutes = (millisecs % MSecHour) / MSecMinute; - int seconds = ((millisecs % MSecHour) % MSecMinute) / 1000; - - std::stringstream s; - - if (hours) - s << hours << ':'; - - s << std::setfill('0') << std::setw(2) << minutes << ':' - << std::setw(2) << seconds; - return s.str(); - } - - string score_to_string(Value v) { - - std::stringstream s; - - if (v >= VALUE_MATE_IN_MAX_PLY) - s << "#" << (VALUE_MATE - v + 1) / 2; - else if (v <= VALUE_MATED_IN_MAX_PLY) - s << "-#" << (VALUE_MATE + v) / 2; - else - s << std::setprecision(2) << std::fixed << std::showpos - << float(v) / PawnValueMidgame; - - return s.str(); - } - - void pv_info_to_log(Position& pos, int depth, Value value, int time, Move pv[]) { - - const int64_t K = 1000; - const int64_t M = 1000000; - - StateInfo state[MAX_PLY_PLUS_2], *st = state; - Move* m = pv; - string san, padding; - size_t length; - std::stringstream s; - - s << std::setw(2) << depth - << std::setw(8) << score_to_string(value) - << std::setw(8) << time_to_string(time); - - if (pos.nodes_searched() < M) - s << std::setw(8) << pos.nodes_searched() / 1 << " "; - - else if (pos.nodes_searched() < K * M) - s << std::setw(7) << pos.nodes_searched() / K << "K "; - - else - s << std::setw(7) << pos.nodes_searched() / M << "M "; - - padding = string(s.str().length(), ' '); - length = padding.length(); - - while (*m != MOVE_NONE) - { - san = move_to_san(pos, *m); - - if (length + san.length() > 80) - { - s << "\n" + padding; - length = padding.length(); - } - - s << san << ' '; - length += san.length() + 1; - - pos.do_move(*m++, *st++); - } - - while (m != pv) - pos.undo_move(*--m); - - Log l(Options["Search Log Filename"]); - l << s.str() << endl; - } - - // When playing with strength handicap choose best move among the MultiPV set // using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen. @@ -1671,12 +1501,12 @@ split_point_start: // At split points actual search starts from here static RKISS rk; // PRNG sequence should be not deterministic - for (int i = Time::current_time().msec() % 50; i > 0; i--) + for (int i = Time::now() % 50; i > 0; i--) rk.rand(); // RootMoves are already sorted by score in descending order size_t size = std::min(MultiPV, RootMoves.size()); - int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMidgame); + int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMg); int weakness = 120 - 2 * SkillLevel; int max_s = -VALUE_INFINITE; Move best = MOVE_NONE; @@ -1705,6 +1535,50 @@ split_point_start: // At split points actual search starts from here return best; } + + // uci_pv() formats PV information according to UCI protocol. UCI requires + // to send all the PV lines also if are still to be searched and so refer to + // the previous search score. + + string uci_pv(const Position& pos, int depth, Value alpha, Value beta) { + + std::stringstream s; + Time::point elaspsed = Time::now() - SearchTime + 1; + int selDepth = 0; + + for (size_t i = 0; i < Threads.size(); i++) + if (Threads[i].maxPly > selDepth) + selDepth = Threads[i].maxPly; + + for (size_t i = 0; i < std::min(UCIMultiPV, RootMoves.size()); i++) + { + bool updated = (i <= PVIdx); + + if (depth == 1 && !updated) + continue; + + int d = (updated ? depth : depth - 1); + Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore); + + if (s.rdbuf()->in_avail()) + s << "\n"; + + s << "info depth " << d + << " seldepth " << selDepth + << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) + << " nodes " << pos.nodes_searched() + << " nps " << pos.nodes_searched() * 1000 / elaspsed + << " time " << elaspsed + << " multipv " << i + 1 + << " pv"; + + for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) + s << " " << move_to_uci(RootMoves[i].pv[j], Chess960); + } + + return s.str(); + } + } // namespace @@ -1775,11 +1649,15 @@ void RootMove::insert_pv_in_tt(Position& pos) { } -/// Thread::idle_loop() is where the thread is parked when it has no work to do. -/// The parameter 'master_sp', if non-NULL, is a pointer to an active SplitPoint -/// object for which the thread is the master. +/// Thread::idle_loop() is where the thread is parked when it has no work to do -void Thread::idle_loop(SplitPoint* sp_master) { +void Thread::idle_loop() { + + // Pointer 'sp_master', if non-NULL, points to the active SplitPoint + // object for which the thread is the master. + const SplitPoint* sp_master = splitPointsCnt ? curSplitPoint : NULL; + + assert(!sp_master || (sp_master->master == this && is_searching)); // If this thread is the master of a split point and all slaves have // finished their work at this split point, return from the idle loop. @@ -1798,12 +1676,12 @@ void Thread::idle_loop(SplitPoint* sp_master) { } // Grab the lock to avoid races with Thread::wake_up() - lock_grab(sleepLock); + mutex.lock(); // If we are master and all slaves have finished don't go to sleep if (sp_master && !sp_master->slavesMask) { - lock_release(sleepLock); + mutex.unlock(); break; } @@ -1812,9 +1690,9 @@ void Thread::idle_loop(SplitPoint* sp_master) { // in the meanwhile, allocated us and sent the wake_up() call before we // had the chance to grab the lock. if (do_sleep || !is_searching) - cond_wait(sleepCond, sleepLock); + sleepCondition.wait(mutex); - lock_release(sleepLock); + mutex.unlock(); } // If this thread has been assigned work, launch a search @@ -1822,12 +1700,12 @@ void Thread::idle_loop(SplitPoint* sp_master) { { assert(!do_sleep && !do_exit); - lock_grab(Threads.splitLock); + Threads.mutex.lock(); assert(is_searching); SplitPoint* sp = curSplitPoint; - lock_release(Threads.splitLock); + Threads.mutex.unlock(); Stack ss[MAX_PLY_PLUS_2]; Position pos(*sp->pos, this); @@ -1835,7 +1713,7 @@ void Thread::idle_loop(SplitPoint* sp_master) { memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); (ss+1)->sp = sp; - lock_grab(sp->lock); + sp->mutex.lock(); if (sp->nodeType == Root) search(pos, ss+1, sp->alpha, sp->beta, sp->depth); @@ -1856,14 +1734,17 @@ void Thread::idle_loop(SplitPoint* sp_master) { // case we are the last slave of the split point. if ( Threads.use_sleeping_threads() && this != sp->master - && !sp->master->is_searching) + && !sp->slavesMask) + { + assert(!sp->master->is_searching); sp->master->wake_up(); + } // After releasing the lock we cannot access anymore any SplitPoint // related data in a safe way becuase it could have been released under // our feet by the sp master. Also accessing other Thread objects is // unsafe because if we are exiting there is a chance are already freed. - lock_release(sp->lock); + sp->mutex.unlock(); } } } @@ -1875,26 +1756,26 @@ void Thread::idle_loop(SplitPoint* sp_master) { void check_time() { - static Time lastInfoTime = Time::current_time(); + static Time::point lastInfoTime = Time::now(); - if (lastInfoTime.elapsed() >= 1000) + if (Time::now() - lastInfoTime >= 1000) { - lastInfoTime.restart(); + lastInfoTime = Time::now(); dbg_print(); } if (Limits.ponder) return; - int e = SearchTime.elapsed(); + Time::point elapsed = Time::now() - SearchTime; bool stillAtFirstMove = Signals.firstRootMove && !Signals.failedLowAtRoot - && e > TimeMgr.available_time(); + && elapsed > TimeMgr.available_time(); - bool noMoreTime = e > TimeMgr.maximum_time() - 2 * TimerResolution + bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerResolution || stillAtFirstMove; if ( (Limits.use_time_management() && noMoreTime) - || (Limits.movetime && e >= Limits.movetime)) + || (Limits.movetime && elapsed >= Limits.movetime)) Signals.stop = true; } diff --git a/DroidFish/jni/stockfish/search.h b/DroidFish/jni/stockfish/search.h index c573807..5f9b74d 100644 --- a/DroidFish/jni/stockfish/search.h +++ b/DroidFish/jni/stockfish/search.h @@ -21,12 +21,14 @@ #define SEARCH_H_INCLUDED #include +#include +#include #include #include "misc.h" +#include "position.h" #include "types.h" -class Position; struct SplitPoint; namespace Search { @@ -91,14 +93,17 @@ struct SignalsType { bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot; }; +typedef std::auto_ptr > StateStackPtr; + extern volatile SignalsType Signals; extern LimitsType Limits; extern std::vector RootMoves; extern Position RootPosition; -extern Time SearchTime; +extern Time::point SearchTime; +extern StateStackPtr SetupStates; extern void init(); -extern int64_t perft(Position& pos, Depth depth); +extern size_t perft(Position& pos, Depth depth); extern void think(); } // namespace Search diff --git a/DroidFish/jni/stockfish/thread.cpp b/DroidFish/jni/stockfish/thread.cpp index a25ae07..aac311f 100644 --- a/DroidFish/jni/stockfish/thread.cpp +++ b/DroidFish/jni/stockfish/thread.cpp @@ -27,7 +27,7 @@ using namespace Search; -ThreadsManager Threads; // Global object +ThreadPool Threads; // Global object namespace { extern "C" { @@ -52,12 +52,6 @@ Thread::Thread(Fn fn) { do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching() - lock_init(sleepLock); - cond_init(sleepCond); - - for (int j = 0; j < MAX_SPLITPOINTS_PER_THREAD; j++) - lock_init(splitPoints[j].lock); - if (!thread_create(handle, start_routine, this)) { std::cerr << "Failed to create thread number " << idx << std::endl; @@ -74,14 +68,7 @@ Thread::~Thread() { do_exit = true; // Search must be already finished wake_up(); - thread_join(handle); // Wait for thread termination - - lock_destroy(sleepLock); - cond_destroy(sleepCond); - - for (int j = 0; j < MAX_SPLITPOINTS_PER_THREAD; j++) - lock_destroy(splitPoints[j].lock); } @@ -93,9 +80,9 @@ void Thread::timer_loop() { while (!do_exit) { - lock_grab(sleepLock); - timed_wait(sleepCond, sleepLock, maxPly ? maxPly : INT_MAX); - lock_release(sleepLock); + mutex.lock(); + sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX); + mutex.unlock(); check_time(); } } @@ -108,18 +95,18 @@ void Thread::main_loop() { while (true) { - lock_grab(sleepLock); + mutex.lock(); do_sleep = true; // Always return to sleep after a search is_searching = false; while (do_sleep && !do_exit) { - cond_signal(Threads.sleepCond); // Wake up UI thread if needed - cond_wait(sleepCond, sleepLock); + Threads.sleepCondition.notify_one(); // Wake up UI thread if needed + sleepCondition.wait(mutex); } - lock_release(sleepLock); + mutex.unlock(); if (do_exit) return; @@ -127,6 +114,8 @@ void Thread::main_loop() { is_searching = true; Search::think(); + + assert(is_searching); } } @@ -136,9 +125,9 @@ void Thread::main_loop() { void Thread::wake_up() { - lock_grab(sleepLock); - cond_signal(sleepCond); - lock_release(sleepLock); + mutex.lock(); + sleepCondition.notify_one(); + mutex.unlock(); } @@ -153,9 +142,9 @@ void Thread::wait_for_stop_or_ponderhit() { Signals.stopOnPonderhit = true; - lock_grab(sleepLock); - while (!Signals.stop) cond_wait(sleepCond, sleepLock); - lock_release(sleepLock); + mutex.lock(); + while (!Signals.stop) sleepCondition.wait(mutex);; + mutex.unlock(); } @@ -199,26 +188,22 @@ bool Thread::is_available_to(Thread* master) const { // a c'tor becuase Threads is a static object and we need a fully initialized // engine at this point due to allocation of endgames in Thread c'tor. -void ThreadsManager::init() { +void ThreadPool::init() { - cond_init(sleepCond); - lock_init(splitLock); timer = new Thread(&Thread::timer_loop); threads.push_back(new Thread(&Thread::main_loop)); read_uci_options(); } -// d'tor cleanly terminates the threads when the program exits. +// exit() cleanly terminates the threads before the program exits. -ThreadsManager::~ThreadsManager() { +void ThreadPool::exit() { - for (int i = 0; i < size(); i++) + for (size_t i = 0; i < threads.size(); i++) delete threads[i]; delete timer; - lock_destroy(splitLock); - cond_destroy(sleepCond); } @@ -227,19 +212,19 @@ ThreadsManager::~ThreadsManager() { // objects are dynamically allocated to avoid creating in advance all possible // threads, with included pawns and material tables, if only few are used. -void ThreadsManager::read_uci_options() { +void ThreadPool::read_uci_options() { maxThreadsPerSplitPoint = Options["Max Threads per Split Point"]; minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY; useSleepingThreads = Options["Use Sleeping Threads"]; - int requested = Options["Threads"]; + size_t requested = Options["Threads"]; assert(requested > 0); - while (size() < requested) + while (threads.size() < requested) threads.push_back(new Thread(&Thread::idle_loop)); - while (size() > requested) + while (threads.size() > requested) { delete threads.back(); threads.pop_back(); @@ -251,9 +236,9 @@ void ThreadsManager::read_uci_options() { // on the sleep condition and to reset maxPly. When useSleepingThreads is set // threads will be woken up at split time. -void ThreadsManager::wake_up() const { +void ThreadPool::wake_up() const { - for (int i = 0; i < size(); i++) + for (size_t i = 0; i < threads.size(); i++) { threads[i]->maxPly = 0; threads[i]->do_sleep = false; @@ -267,19 +252,20 @@ void ThreadsManager::wake_up() const { // sleep() is called after the search finishes to ask all the threads but the // main one to go waiting on a sleep condition. -void ThreadsManager::sleep() const { +void ThreadPool::sleep() const { - for (int i = 1; i < size(); i++) // Main thread will go to sleep by itself - threads[i]->do_sleep = true; // to avoid a race with start_searching() + // Main thread will go to sleep by itself to avoid a race with start_searching() + for (size_t i = 1; i < threads.size(); i++) + threads[i]->do_sleep = true; } // available_slave_exists() tries to find an idle thread which is available as // a slave for the thread 'master'. -bool ThreadsManager::available_slave_exists(Thread* master) const { +bool ThreadPool::available_slave_exists(Thread* master) const { - for (int i = 0; i < size(); i++) + for (size_t i = 0; i < threads.size(); i++) if (threads[i]->is_available_to(master)) return true; @@ -297,9 +283,10 @@ bool ThreadsManager::available_slave_exists(Thread* master) const { // search(). When all threads have returned from search() then split() returns. template -Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta, - Value bestValue, Move* bestMove, Depth depth, - Move threatMove, int moveCount, MovePicker* mp, int nodeType) { +Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, + Value bestValue, Move* bestMove, Depth depth, + Move threatMove, int moveCount, MovePicker* mp, int nodeType) { + assert(pos.pos_is_ok()); assert(bestValue > -VALUE_INFINITE); assert(bestValue <= alpha); @@ -313,41 +300,41 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta, return bestValue; // Pick the next available split point from the split point stack - SplitPoint* sp = &master->splitPoints[master->splitPointsCnt]; + SplitPoint& sp = master->splitPoints[master->splitPointsCnt]; - sp->parent = master->curSplitPoint; - sp->master = master; - sp->cutoff = false; - sp->slavesMask = 1ULL << master->idx; - sp->depth = depth; - sp->bestMove = *bestMove; - sp->threatMove = threatMove; - sp->alpha = alpha; - sp->beta = beta; - sp->nodeType = nodeType; - sp->bestValue = bestValue; - sp->mp = mp; - sp->moveCount = moveCount; - sp->pos = &pos; - sp->nodes = 0; - sp->ss = ss; + sp.parent = master->curSplitPoint; + sp.master = master; + sp.cutoff = false; + sp.slavesMask = 1ULL << master->idx; + sp.depth = depth; + sp.bestMove = *bestMove; + sp.threatMove = threatMove; + sp.alpha = alpha; + sp.beta = beta; + sp.nodeType = nodeType; + sp.bestValue = bestValue; + sp.mp = mp; + sp.moveCount = moveCount; + sp.pos = &pos; + sp.nodes = 0; + sp.ss = ss; assert(master->is_searching); - master->curSplitPoint = sp; + master->curSplitPoint = &sp; int slavesCnt = 0; // Try to allocate available threads and ask them to start searching setting // is_searching flag. This must be done under lock protection to avoid concurrent // allocation of the same slave by another master. - lock_grab(sp->lock); - lock_grab(splitLock); + sp.mutex.lock(); + mutex.lock(); - for (int i = 0; i < size() && !Fake; ++i) + for (size_t i = 0; i < threads.size() && !Fake; ++i) if (threads[i]->is_available_to(master)) { - sp->slavesMask |= 1ULL << i; - threads[i]->curSplitPoint = sp; + sp.slavesMask |= 1ULL << i; + threads[i]->curSplitPoint = &sp; threads[i]->is_searching = true; // Slave leaves idle_loop() if (useSleepingThreads) @@ -359,17 +346,16 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta, master->splitPointsCnt++; - lock_release(splitLock); - lock_release(sp->lock); + mutex.unlock(); + sp.mutex.unlock(); // Everything is set up. The master thread enters the idle loop, from which // it will instantly launch a search, because its is_searching flag is set. - // We pass the split point as a parameter to the idle loop, which means that - // the thread will return from the idle loop when all slaves have finished + // The thread will return from the idle loop when all slaves have finished // their work at this split point. if (slavesCnt || Fake) { - master->idle_loop(sp); + master->idle_loop(); // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. @@ -379,68 +365,69 @@ Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta, // We have returned from the idle loop, which means that all threads are // finished. Note that setting is_searching and decreasing splitPointsCnt is // done under lock protection to avoid a race with Thread::is_available_to(). - lock_grab(sp->lock); // To protect sp->nodes - lock_grab(splitLock); + sp.mutex.lock(); // To protect sp.nodes + mutex.lock(); master->is_searching = true; master->splitPointsCnt--; - master->curSplitPoint = sp->parent; - pos.set_nodes_searched(pos.nodes_searched() + sp->nodes); - *bestMove = sp->bestMove; + master->curSplitPoint = sp.parent; + pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); + *bestMove = sp.bestMove; - lock_release(splitLock); - lock_release(sp->lock); + mutex.unlock(); + sp.mutex.unlock(); - return sp->bestValue; + return sp.bestValue; } // Explicit template instantiations -template Value ThreadsManager::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int); -template Value ThreadsManager::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int); +template Value ThreadPool::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int); +template Value ThreadPool::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int); -// ThreadsManager::set_timer() is used to set the timer to trigger after msec -// milliseconds. If msec is 0 then timer is stopped. +// set_timer() is used to set the timer to trigger after msec milliseconds. +// If msec is 0 then timer is stopped. -void ThreadsManager::set_timer(int msec) { +void ThreadPool::set_timer(int msec) { - lock_grab(timer->sleepLock); + timer->mutex.lock(); timer->maxPly = msec; - cond_signal(timer->sleepCond); // Wake up and restart the timer - lock_release(timer->sleepLock); + timer->sleepCondition.notify_one(); // Wake up and restart the timer + timer->mutex.unlock(); } -// ThreadsManager::wait_for_search_finished() waits for main thread to go to -// sleep, this means search is finished. Then returns. +// wait_for_search_finished() waits for main thread to go to sleep, this means +// search is finished. Then returns. -void ThreadsManager::wait_for_search_finished() { +void ThreadPool::wait_for_search_finished() { Thread* t = main_thread(); - lock_grab(t->sleepLock); - cond_signal(t->sleepCond); // In case is waiting for stop or ponderhit - while (!t->do_sleep) cond_wait(sleepCond, t->sleepLock); - lock_release(t->sleepLock); + t->mutex.lock(); + t->sleepCondition.notify_one(); // In case is waiting for stop or ponderhit + while (!t->do_sleep) sleepCondition.wait(t->mutex); + t->mutex.unlock(); } -// ThreadsManager::start_searching() wakes up the main thread sleeping in -// main_loop() so to start a new search, then returns immediately. +// start_searching() wakes up the main thread sleeping in main_loop() so to start +// a new search, then returns immediately. -void ThreadsManager::start_searching(const Position& pos, const LimitsType& limits, - const std::vector& searchMoves) { +void ThreadPool::start_searching(const Position& pos, const LimitsType& limits, + const std::vector& searchMoves, StateStackPtr& states) { wait_for_search_finished(); - SearchTime.restart(); // As early as possible + SearchTime = Time::now(); // As early as possible Signals.stopOnPonderhit = Signals.firstRootMove = false; Signals.stop = Signals.failedLowAtRoot = false; RootPosition = pos; Limits = limits; + SetupStates = states; // Ownership transfer here RootMoves.clear(); - for (MoveList ml(pos); !ml.end(); ++ml) + for (MoveList ml(pos); !ml.end(); ++ml) if (searchMoves.empty() || std::count(searchMoves.begin(), searchMoves.end(), ml.move())) RootMoves.push_back(RootMove(ml.move())); diff --git a/DroidFish/jni/stockfish/thread.h b/DroidFish/jni/stockfish/thread.h index 3eb8fc9..38a29e8 100644 --- a/DroidFish/jni/stockfish/thread.h +++ b/DroidFish/jni/stockfish/thread.h @@ -31,6 +31,31 @@ const int MAX_THREADS = 32; const int MAX_SPLITPOINTS_PER_THREAD = 8; +struct Mutex { + Mutex() { lock_init(l); } + ~Mutex() { lock_destroy(l); } + + void lock() { lock_grab(l); } + void unlock() { lock_release(l); } + +private: + friend struct ConditionVariable; + + Lock l; +}; + +struct ConditionVariable { + ConditionVariable() { cond_init(c); } + ~ConditionVariable() { cond_destroy(c); } + + void wait(Mutex& m) { cond_wait(c, m.l); } + void wait_for(Mutex& m, int ms) { timed_wait(c, m.l, ms); } + void notify_one() { cond_signal(c); } + +private: + WaitCondition c; +}; + class Thread; struct SplitPoint { @@ -49,7 +74,7 @@ struct SplitPoint { SplitPoint* parent; // Shared data - Lock lock; + Mutex mutex; volatile uint64_t slavesMask; volatile int64_t nodes; volatile Value alpha; @@ -67,20 +92,16 @@ struct SplitPoint { class Thread { - Thread(const Thread&); // Only declared to disable the default ones - Thread& operator=(const Thread&); // that are not suitable in this case. - - typedef void (Thread::* Fn) (); + typedef void (Thread::* Fn) (); // Pointer to member function public: Thread(Fn fn); - ~Thread(); + ~Thread(); void wake_up(); bool cutoff_occurred() const; bool is_available_to(Thread* master) const; - void idle_loop(SplitPoint* sp_master); - void idle_loop() { idle_loop(NULL); } // Hack to allow storing in start_fn + void idle_loop(); void main_loop(); void timer_loop(); void wait_for_stop_or_ponderhit(); @@ -88,10 +109,10 @@ public: SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; MaterialTable materialTable; PawnTable pawnTable; - int idx; + size_t idx; int maxPly; - Lock sleepLock; - WaitCondition sleepCond; + Mutex mutex; + ConditionVariable sleepCondition; NativeHandle handle; Fn start_fn; SplitPoint* volatile curSplitPoint; @@ -102,23 +123,20 @@ public: }; -/// ThreadsManager class handles all the threads related stuff like init, starting, +/// ThreadPool class handles all the threads related stuff like init, starting, /// parking and, the most important, launching a slave thread at a split point. /// All the access to shared thread data is done through this class. -class ThreadsManager { - /* As long as the single ThreadsManager object is defined as a global we don't - need to explicitly initialize to zero its data members because variables with - static storage duration are automatically set to zero before enter main() - */ -public: - void init(); // No c'tor becuase Threads is static and we need engine initialized - ~ThreadsManager(); +class ThreadPool { - Thread& operator[](int id) { return *threads[id]; } +public: + void init(); // No c'tor and d'tor, threads rely on globals that should + void exit(); // be initialized and valid during the whole thread lifetime. + + Thread& operator[](size_t id) { return *threads[id]; } bool use_sleeping_threads() const { return useSleepingThreads; } int min_split_depth() const { return minimumSplitDepth; } - int size() const { return (int)threads.size(); } + size_t size() const { return threads.size(); } Thread* main_thread() { return threads[0]; } void wake_up() const; @@ -127,8 +145,8 @@ public: bool available_slave_exists(Thread* master) const; void set_timer(int msec); void wait_for_search_finished(); - void start_searching(const Position& pos, const Search::LimitsType& limits, - const std::vector& searchMoves); + void start_searching(const Position&, const Search::LimitsType&, + const std::vector&, Search::StateStackPtr&); template Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove, @@ -138,13 +156,13 @@ private: std::vector threads; Thread* timer; - Lock splitLock; - WaitCondition sleepCond; + Mutex mutex; + ConditionVariable sleepCondition; Depth minimumSplitDepth; int maxThreadsPerSplitPoint; bool useSleepingThreads; }; -extern ThreadsManager Threads; +extern ThreadPool Threads; #endif // !defined(THREAD_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/tt.cpp b/DroidFish/jni/stockfish/tt.cpp index 09a74d2..9dbfcb5 100644 --- a/DroidFish/jni/stockfish/tt.cpp +++ b/DroidFish/jni/stockfish/tt.cpp @@ -20,6 +20,7 @@ #include #include +#include "bitboard.h" #include "tt.h" TranspositionTable TT; // Our global transposition table @@ -37,18 +38,13 @@ TranspositionTable::~TranspositionTable() { /// TranspositionTable::set_size() sets the size of the transposition table, -/// measured in megabytes. +/// measured in megabytes. Transposition table consists of a power of 2 number of +/// TTCluster and each cluster consists of ClusterSize number of TTEntries. Each +/// non-empty entry contains information of exactly one position. void TranspositionTable::set_size(size_t mbSize) { - size_t newSize = 1024; - - // Transposition table consists of clusters and each cluster consists - // of ClusterSize number of TTEntries. Each non-empty entry contains - // information of exactly one position and newSize is the number of - // clusters we are going to allocate. - while (2ULL * newSize * sizeof(TTCluster) <= (mbSize << 20)) - newSize *= 2; + size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTCluster)); if (newSize == size) return; @@ -56,13 +52,15 @@ void TranspositionTable::set_size(size_t mbSize) { size = newSize; delete [] entries; entries = new (std::nothrow) TTCluster[size]; + if (!entries) { std::cerr << "Failed to allocate " << mbSize << "MB for transposition table." << std::endl; exit(EXIT_FAILURE); } - clear(); + + clear(); // Operator new is not guaranteed to initialize memory to zero } diff --git a/DroidFish/jni/stockfish/types.h b/DroidFish/jni/stockfish/types.h index 40a2baa..ce73cab 100644 --- a/DroidFish/jni/stockfish/types.h +++ b/DroidFish/jni/stockfish/types.h @@ -119,15 +119,13 @@ enum Move { MOVE_NULL = 65 }; -struct MoveStack { - Move move; - int score; +enum MoveType { + NORMAL = 0, + PROMOTION = 1 << 14, + ENPASSANT = 2 << 14, + CASTLE = 3 << 14 }; -inline bool operator<(const MoveStack& f, const MoveStack& s) { - return f.score < s.score; -} - enum CastleRight { // Defined as in PolyGlot book hash key CASTLES_NONE = 0, WHITE_OO = 1, @@ -168,7 +166,15 @@ enum Value { VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY, VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX, - VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN + VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN, + + Mg = 0, Eg = 1, + + PawnValueMg = 198, PawnValueEg = 258, + KnightValueMg = 817, KnightValueEg = 846, + BishopValueMg = 836, BishopValueEg = 857, + RookValueMg = 1270, RookValueEg = 1278, + QueenValueMg = 2521, QueenValueEg = 2558 }; enum PieceType { @@ -312,20 +318,31 @@ inline Score apply_weight(Score v, Score w) { #undef ENABLE_OPERATORS_ON #undef ENABLE_SAFE_OPERATORS_ON -const Value PawnValueMidgame = Value(198); -const Value PawnValueEndgame = Value(258); -const Value KnightValueMidgame = Value(817); -const Value KnightValueEndgame = Value(846); -const Value BishopValueMidgame = Value(836); -const Value BishopValueEndgame = Value(857); -const Value RookValueMidgame = Value(1270); -const Value RookValueEndgame = Value(1278); -const Value QueenValueMidgame = Value(2521); -const Value QueenValueEndgame = Value(2558); +namespace Zobrist { -extern const Value PieceValueMidgame[17]; // Indexed by Piece or PieceType -extern const Value PieceValueEndgame[17]; -extern int SquareDistance[64][64]; + extern Key psq[2][8][64]; // [color][pieceType][square / piece count] + extern Key enpassant[8]; // [file] + extern Key castle[16]; // [castleRight] + extern Key side; + extern Key exclusion; + + void init(); +} + +CACHE_LINE_ALIGNMENT + +extern Score pieceSquareTable[16][64]; // [piece][square] +extern Value PieceValue[2][18]; // [Mg / Eg][piece / pieceType] +extern int SquareDistance[64][64]; // [square][square] + +struct MoveStack { + Move move; + int score; +}; + +inline bool operator<(const MoveStack& f, const MoveStack& s) { + return f.score < s.score; +} inline Color operator~(Color c) { return Color(c ^ 1); @@ -335,6 +352,10 @@ inline Square operator~(Square s) { return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8 } +inline Square operator|(File f, Rank r) { + return Square((r << 3) | f); +} + inline Value mate_in(int ply) { return VALUE_MATE - ply; } @@ -359,10 +380,6 @@ inline Color color_of(Piece p) { return Color(p >> 3); } -inline Square make_square(File f, Rank r) { - return Square((r << 3) | f); -} - inline bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } @@ -408,10 +425,6 @@ inline int square_distance(Square s1, Square s2) { return SquareDistance[s1][s2]; } -inline char piece_type_to_char(PieceType pt) { - return " PNBRQK"[pt]; -} - inline char file_to_char(File f) { return char(f - FILE_A + int('a')); } @@ -432,20 +445,8 @@ inline Square to_sq(Move m) { return Square(m & 0x3F); } -inline bool is_special(Move m) { - return m & (3 << 14); -} - -inline bool is_promotion(Move m) { - return (m & (3 << 14)) == (1 << 14); -} - -inline int is_enpassant(Move m) { - return (m & (3 << 14)) == (2 << 14); -} - -inline int is_castle(Move m) { - return (m & (3 << 14)) == (3 << 14); +inline MoveType type_of(Move m) { + return MoveType(m & (3 << 14)); } inline PieceType promotion_type(Move m) { @@ -456,16 +457,9 @@ inline Move make_move(Square from, Square to) { return Move(to | (from << 6)); } -inline Move make_promotion(Square from, Square to, PieceType pt) { - return Move(to | (from << 6) | (1 << 14) | ((pt - 2) << 12)) ; -} - -inline Move make_enpassant(Square from, Square to) { - return Move(to | (from << 6) | (2 << 14)); -} - -inline Move make_castle(Square from, Square to) { - return Move(to | (from << 6) | (3 << 14)); +template +inline Move make(Square from, Square to, PieceType pt = KNIGHT) { + return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)) ; } inline bool is_ok(Move m) { diff --git a/DroidFish/jni/stockfish/uci.cpp b/DroidFish/jni/stockfish/uci.cpp index 0ba4de3..3895c7a 100644 --- a/DroidFish/jni/stockfish/uci.cpp +++ b/DroidFish/jni/stockfish/uci.cpp @@ -22,6 +22,7 @@ #include #include "evaluate.h" +#include "notation.h" #include "position.h" #include "search.h" #include "thread.h" @@ -37,9 +38,8 @@ namespace { const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; // Keep track of position keys along the setup moves (from start position to the - // position just before to start searching). This is needed by draw detection - // where, due to 50 moves rule, we need to check at most 100 plies back. - StateInfo StateRingBuf[102], *SetupState = StateRingBuf; + // position just before to start searching). Needed by repetition draw detection. + Search::StateStackPtr SetupStates; void set_option(istringstream& up); void set_position(Position& pos, istringstream& up); @@ -52,7 +52,7 @@ namespace { /// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI /// commands, the function also supports a few debug commands. -void uci_loop(const string& args) { +void UCI::loop(const string& args) { Position pos(StartFEN, false, Threads.main_thread()); // The root position string cmd, token; @@ -96,7 +96,7 @@ void uci_loop(const string& args) { { /* Avoid returning "Unknown command" */ } else if (token == "isready") - cout << "readyok" << endl; + sync_cout << "readyok" << sync_endl; else if (token == "position") set_position(pos, is); @@ -111,20 +111,20 @@ void uci_loop(const string& args) { pos.flip(); else if (token == "eval") - cout << Eval::trace(pos) << endl; + sync_cout << Eval::trace(pos) << sync_endl; else if (token == "bench") benchmark(pos, is); else if (token == "key") - cout << "key: " << hex << pos.key() - << "\nmaterial key: " << pos.material_key() - << "\npawn key: " << pos.pawn_key() << endl; + sync_cout << "key: " << hex << pos.key() + << "\nmaterial key: " << pos.material_key() + << "\npawn key: " << pos.pawn_key() << sync_endl; else if (token == "uci") - cout << "id name " << engine_info(true) - << "\n" << Options - << "\nuciok" << endl; + sync_cout << "id name " << engine_info(true) + << "\n" << Options + << "\nuciok" << sync_endl; else if (token == "perft" && (is >> token)) // Read depth { @@ -137,7 +137,7 @@ void uci_loop(const string& args) { } else - cout << "Unknown command: " << cmd << endl; + sync_cout << "Unknown command: " << cmd << sync_endl; if (!args.empty()) // Command line arguments have one-shot behaviour { @@ -150,10 +150,10 @@ void uci_loop(const string& args) { namespace { - // set_position() is called when engine receives the "position" UCI - // command. The function sets up the position described in the given - // fen string ("fen") or the starting position ("startpos") and then - // makes the moves given in the following move list ("moves"). + // set_position() is called when engine receives the "position" UCI command. + // The function sets up the position described in the given fen string ("fen") + // or the starting position ("startpos") and then makes the moves given in the + // following move list ("moves"). void set_position(Position& pos, istringstream& is) { @@ -174,15 +174,13 @@ namespace { return; pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread()); + SetupStates = Search::StateStackPtr(new std::stack()); // Parse move list (if any) while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE) { - pos.do_move(m, *SetupState); - - // Increment pointer to StateRingBuf circular buffer - if (++SetupState - StateRingBuf >= 102) - SetupState = StateRingBuf; + SetupStates->push(StateInfo()); + pos.do_move(m, SetupStates->top()); } } @@ -207,7 +205,7 @@ namespace { if (Options.count(name)) Options[name] = value; else - cout << "No such option: " << name << endl; + sync_cout << "No such option: " << name << sync_endl; } @@ -248,6 +246,6 @@ namespace { searchMoves.push_back(move_from_uci(pos, token)); } - Threads.start_searching(pos, limits, searchMoves); + Threads.start_searching(pos, limits, searchMoves, SetupStates); } } diff --git a/DroidFish/jni/stockfish/ucioption.cpp b/DroidFish/jni/stockfish/ucioption.cpp index 4761004..bbbbbbf 100644 --- a/DroidFish/jni/stockfish/ucioption.cpp +++ b/DroidFish/jni/stockfish/ucioption.cpp @@ -18,6 +18,8 @@ */ #include +#include +#include #include #include "evaluate.h" @@ -28,71 +30,69 @@ using std::string; -OptionsMap Options; // Global object +UCI::OptionsMap Options; // Global object -namespace { +namespace UCI { /// 'On change' actions, triggered by an option's value change -void on_logger(const UCIOption& opt) { start_logger(opt); } -void on_eval(const UCIOption&) { Eval::init(); } -void on_threads(const UCIOption&) { Threads.read_uci_options(); } -void on_hash_size(const UCIOption& opt) { TT.set_size(opt); } -void on_clear_hash(const UCIOption&) { TT.clear(); } +void on_logger(const Option& o) { start_logger(o); } +void on_eval(const Option&) { Eval::init(); } +void on_threads(const Option&) { Threads.read_uci_options(); } +void on_hash_size(const Option& o) { TT.set_size(o); } +void on_clear_hash(const Option&) { TT.clear(); } + /// Our case insensitive less() function as required by UCI protocol bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); } -} - bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less); } -/// OptionsMap c'tor initializes the UCI options to their hard coded default -/// values and initializes the default value of "Threads" and "Min Split Depth" +/// init() initializes the UCI options to their hard coded default values +/// and initializes the default value of "Threads" and "Min Split Depth" /// parameters according to the number of CPU cores detected. -OptionsMap::OptionsMap() { +void init(OptionsMap& o) { int cpus = std::min(cpu_count(), MAX_THREADS); int msd = cpus < 8 ? 4 : 7; - OptionsMap& o = *this; - o["Use Debug Log"] = UCIOption(false, on_logger); - o["Use Search Log"] = UCIOption(false); - o["Search Log Filename"] = UCIOption("SearchLog.txt"); - o["Book File"] = UCIOption("book.bin"); - o["Best Book Move"] = UCIOption(false); - o["Mobility (Middle Game)"] = UCIOption(100, 0, 200, on_eval); - o["Mobility (Endgame)"] = UCIOption(100, 0, 200, on_eval); - o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200, on_eval); - o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200, on_eval); - o["Space"] = UCIOption(100, 0, 200, on_eval); - o["Aggressiveness"] = UCIOption(100, 0, 200, on_eval); - o["Cowardice"] = UCIOption(100, 0, 200, on_eval); - o["Min Split Depth"] = UCIOption(msd, 4, 7, on_threads); - o["Max Threads per Split Point"] = UCIOption(5, 4, 8, on_threads); - o["Threads"] = UCIOption(cpus, 1, MAX_THREADS, on_threads); - o["Use Sleeping Threads"] = UCIOption(true, on_threads); - o["Hash"] = UCIOption(32, 4, 8192, on_hash_size); - o["Clear Hash"] = UCIOption(on_clear_hash); - o["Ponder"] = UCIOption(true); - o["OwnBook"] = UCIOption(false); - o["MultiPV"] = UCIOption(1, 1, 500); - o["Skill Level"] = UCIOption(20, 0, 20); - o["Emergency Move Horizon"] = UCIOption(40, 0, 50); - o["Emergency Base Time"] = UCIOption(200, 0, 30000); - o["Emergency Move Time"] = UCIOption(70, 0, 5000); - o["Minimum Thinking Time"] = UCIOption(20, 0, 5000); - o["Slow Mover"] = UCIOption(100, 10, 1000); - o["UCI_Chess960"] = UCIOption(false); - o["UCI_AnalyseMode"] = UCIOption(false, on_eval); + o["Use Debug Log"] = Option(false, on_logger); + o["Use Search Log"] = Option(false); + o["Search Log Filename"] = Option("SearchLog.txt"); + o["Book File"] = Option("book.bin"); + o["Best Book Move"] = Option(false); + o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval); + o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); + o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval); + o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval); + o["Space"] = Option(100, 0, 200, on_eval); + o["Aggressiveness"] = Option(100, 0, 200, on_eval); + o["Cowardice"] = Option(100, 0, 200, on_eval); + o["Min Split Depth"] = Option(msd, 4, 7, on_threads); + o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); + o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads); + o["Use Sleeping Threads"] = Option(true, on_threads); + o["Hash"] = Option(32, 4, 8192, on_hash_size); + o["Clear Hash"] = Option(on_clear_hash); + o["Ponder"] = Option(true); + o["OwnBook"] = Option(false); + 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["Minimum Thinking Time"] = Option(20, 0, 5000); + o["Slow Mover"] = Option(100, 10, 1000); + o["UCI_Chess960"] = Option(false); + o["UCI_AnalyseMode"] = Option(false, on_eval); } -/// operator<<() is used to output all the UCI options in chronological insertion -/// order (the idx field) and in the format defined by the UCI protocol. +/// operator<<() is used to print all the options default values in chronological +/// insertion order (the idx field) and in the format defined by the UCI protocol. std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { @@ -100,7 +100,7 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it) if (it->second.idx == idx) { - const UCIOption& o = it->second; + const Option& o = it->second; os << "\noption name " << it->first << " type " << o.type; if (o.type != "button") @@ -115,37 +115,52 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { } -/// UCIOption class c'tors +/// Option c'tors and conversion operators -UCIOption::UCIOption(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f) +Option::Option(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f) { defaultValue = currentValue = v; } -UCIOption::UCIOption(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f) +Option::Option(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f) { defaultValue = currentValue = (v ? "true" : "false"); } -UCIOption::UCIOption(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f) +Option::Option(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f) {} -UCIOption::UCIOption(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f) +Option::Option(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f) { std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); } -/// UCIOption::operator=() updates currentValue. Normally it's up to the GUI to -/// check for option's limits, but we could receive the new value directly from +Option::operator int() const { + assert(type == "check" || type == "spin"); + return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true"); +} + +Option::operator std::string() const { + assert(type == "string"); + return currentValue; +} + + +/// operator=() updates currentValue and triggers on_change() action. It's up to +/// the GUI to check for option's limits, but we could receive the new value from /// the user by console window, so let's check the bounds anyway. -void UCIOption::operator=(const string& v) { +Option& Option::operator=(const string& v) { assert(!type.empty()); - if ( (type == "button" || !v.empty()) - && (type != "check" || (v == "true" || v == "false")) - && (type != "spin" || (atoi(v.c_str()) >= min && atoi(v.c_str()) <= max))) - { - if (type != "button") - currentValue = v; + if ( (type != "button" && v.empty()) + || (type == "check" && v != "true" && v != "false") + || (type == "spin" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max))) + return *this; - if (on_change) - (*on_change)(*this); - } + if (type != "button") + currentValue = v; + + if (on_change) + (*on_change)(*this); + + return *this; } + +} // namespace UCI diff --git a/DroidFish/jni/stockfish/ucioption.h b/DroidFish/jni/stockfish/ucioption.h index c57cf02..df8797f 100644 --- a/DroidFish/jni/stockfish/ucioption.h +++ b/DroidFish/jni/stockfish/ucioption.h @@ -20,35 +20,35 @@ #if !defined(UCIOPTION_H_INCLUDED) #define UCIOPTION_H_INCLUDED -#include -#include #include #include -struct OptionsMap; +namespace UCI { -/// UCIOption class implements an option as defined by UCI protocol -class UCIOption { +class Option; - typedef void (Fn)(const UCIOption&); +/// Custom comparator because UCI options should be case insensitive +struct CaseInsensitiveLess { + bool operator() (const std::string&, const std::string&) const; +}; + +/// Our options container is actually a std::map +typedef std::map OptionsMap; + +/// Option class implements an option as defined by UCI protocol +class Option { + + typedef void (Fn)(const Option&); public: - UCIOption(Fn* = NULL); - UCIOption(bool v, Fn* = NULL); - UCIOption(const char* v, Fn* = NULL); - UCIOption(int v, int min, int max, Fn* = NULL); + Option(Fn* = NULL); + Option(bool v, Fn* = NULL); + Option(const char* v, Fn* = NULL); + Option(int v, int min, int max, Fn* = NULL); - void operator=(const std::string& v); - - operator int() const { - assert(type == "check" || type == "spin"); - return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true"); - } - - operator std::string() const { - assert(type == "string"); - return currentValue; - } + Option& operator=(const std::string& v); + operator int() const; + operator std::string() const; private: friend std::ostream& operator<<(std::ostream&, const OptionsMap&); @@ -59,19 +59,11 @@ private: Fn* on_change; }; +void init(OptionsMap&); +void loop(const std::string&); -/// Custom comparator because UCI options should be case insensitive -struct CaseInsensitiveLess { - bool operator() (const std::string&, const std::string&) const; -}; +} // namespace UCI - -/// Our options container is actually a map with a customized c'tor -struct OptionsMap : public std::map { - OptionsMap(); -}; - -extern std::ostream& operator<<(std::ostream&, const OptionsMap&); -extern OptionsMap Options; +extern UCI::OptionsMap Options; #endif // !defined(UCIOPTION_H_INCLUDED)