diff --git a/DroidFish/jni/stockfish/benchmark.cpp b/DroidFish/jni/stockfish/benchmark.cpp index b43f0ee..ba05ac7 100644 --- a/DroidFish/jni/stockfish/benchmark.cpp +++ b/DroidFish/jni/stockfish/benchmark.cpp @@ -28,7 +28,6 @@ #include "ucioption.h" using namespace std; -using namespace Search; static const char* Defaults[] = { "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", @@ -61,7 +60,7 @@ static const char* Defaults[] = { void benchmark(int argc, char* argv[]) { vector fens; - LimitsType limits; + Search::LimitsType limits; int time; int64_t nodes = 0; @@ -115,14 +114,14 @@ void benchmark(int argc, char* argv[]) { if (valType == "perft") { - int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY); + int64_t cnt = Search::perft(pos, limits.maxDepth * ONE_PLY); cerr << "\nPerft " << limits.maxDepth << " leaf nodes: " << cnt << endl; nodes += cnt; } else { - Threads.start_thinking(pos, limits, vector(), false); - nodes += RootPosition.nodes_searched(); + Threads.start_thinking(pos, limits); + nodes += Search::RootPosition.nodes_searched(); } } diff --git a/DroidFish/jni/stockfish/bitboard.cpp b/DroidFish/jni/stockfish/bitboard.cpp index e4e4095..5c6b6e0 100644 --- a/DroidFish/jni/stockfish/bitboard.cpp +++ b/DroidFish/jni/stockfish/bitboard.cpp @@ -49,9 +49,7 @@ Bitboard SquaresInFrontMask[2][64]; Bitboard PassedPawnMask[2][64]; Bitboard AttackSpanMask[2][64]; -Bitboard BishopPseudoAttacks[64]; -Bitboard RookPseudoAttacks[64]; -Bitboard QueenPseudoAttacks[64]; +Bitboard PseudoAttacks[6][64]; uint8_t BitCount8Bit[256]; int SquareDistance[64][64]; @@ -203,7 +201,7 @@ void bitboards_init() { Bitboard b = 1ULL << i; b ^= b - 1; b ^= b >> 32; - BSFTable[uint32_t(b * 0x783A9B23) >> 26] = i; + BSFTable[(uint32_t)(b * 0x783A9B23) >> 26] = i; } else BSFTable[((1ULL << i) * 0x218A392CD3D5DBFULL) >> 58] = i; @@ -227,14 +225,14 @@ void bitboards_init() { for (Square s = SQ_A1; s <= SQ_H8; s++) { - BishopPseudoAttacks[s] = bishop_attacks_bb(s, 0); - RookPseudoAttacks[s] = rook_attacks_bb(s, 0); - QueenPseudoAttacks[s] = queen_attacks_bb(s, 0); + PseudoAttacks[BISHOP][s] = bishop_attacks_bb(s, 0); + PseudoAttacks[ROOK][s] = rook_attacks_bb(s, 0); + PseudoAttacks[QUEEN][s] = queen_attacks_bb(s, 0); } for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) - if (bit_is_set(QueenPseudoAttacks[s1], s2)) + if (bit_is_set(PseudoAttacks[QUEEN][s1], s2)) { Square delta = (s2 - s1) / square_distance(s1, s2); diff --git a/DroidFish/jni/stockfish/bitboard.h b/DroidFish/jni/stockfish/bitboard.h index cb4c9fa..ce198c2 100644 --- a/DroidFish/jni/stockfish/bitboard.h +++ b/DroidFish/jni/stockfish/bitboard.h @@ -49,9 +49,7 @@ extern int BShifts[64]; extern Bitboard BMasks[64]; extern Bitboard* BAttacks[64]; -extern Bitboard BishopPseudoAttacks[64]; -extern Bitboard RookPseudoAttacks[64]; -extern Bitboard QueenPseudoAttacks[64]; +extern Bitboard PseudoAttacks[6][64]; extern uint8_t BitCount8Bit[256]; diff --git a/DroidFish/jni/stockfish/endgame.cpp b/DroidFish/jni/stockfish/endgame.cpp index 67e1218..27fbe44 100644 --- a/DroidFish/jni/stockfish/endgame.cpp +++ b/DroidFish/jni/stockfish/endgame.cpp @@ -19,14 +19,13 @@ #include #include -#include #include "bitcount.h" #include "endgame.h" #include "pawns.h" using std::string; -using std::transform; +using namespace std; extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm); @@ -207,10 +206,10 @@ Value Endgame::operator()(const Position& pos) const { } else { - wksq = flip(pos.king_square(BLACK)); - bksq = flip(pos.king_square(WHITE)); - wpsq = flip(pos.piece_list(BLACK, PAWN)[0]); - stm = flip(pos.side_to_move()); + wksq = ~pos.king_square(BLACK); + bksq = ~pos.king_square(WHITE); + wpsq = ~pos.piece_list(BLACK, PAWN)[0]; + stm = ~pos.side_to_move(); } if (file_of(wpsq) >= FILE_E) @@ -253,10 +252,10 @@ Value Endgame::operator()(const Position& pos) const { if (strongerSide == BLACK) { - wksq = flip(wksq); - wrsq = flip(wrsq); - bksq = flip(bksq); - bpsq = flip(bpsq); + wksq = ~wksq; + wrsq = ~wrsq; + bksq = ~bksq; + bpsq = ~bpsq; } Square queeningSq = make_square(file_of(bpsq), RANK_1); @@ -493,11 +492,11 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // pawn is on the left half of the board. if (strongerSide == BLACK) { - wksq = flip(wksq); - wrsq = flip(wrsq); - wpsq = flip(wpsq); - bksq = flip(bksq); - brsq = flip(brsq); + wksq = ~wksq; + wrsq = ~wrsq; + wpsq = ~wpsq; + bksq = ~bksq; + brsq = ~brsq; } if (file_of(wpsq) > FILE_D) { @@ -869,10 +868,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if (strongerSide == BLACK) { - wksq = flip(wksq); - bksq = flip(bksq); - wpsq = flip(wpsq); - stm = flip(stm); + wksq = ~wksq; + bksq = ~bksq; + wpsq = ~wpsq; + stm = ~stm; } if (file_of(wpsq) >= FILE_E) diff --git a/DroidFish/jni/stockfish/endgame.h b/DroidFish/jni/stockfish/endgame.h index d91ec40..70f795c 100644 --- a/DroidFish/jni/stockfish/endgame.h +++ b/DroidFish/jni/stockfish/endgame.h @@ -82,7 +82,7 @@ struct EndgameBase { template::type> struct Endgame : public EndgameBase { - explicit Endgame(Color c) : strongerSide(c), weakerSide(flip(c)) {} + explicit Endgame(Color c) : strongerSide(c), weakerSide(~c) {} Color color() const { return strongerSide; } T operator()(const Position&) const; diff --git a/DroidFish/jni/stockfish/evaluate.cpp b/DroidFish/jni/stockfish/evaluate.cpp index a48dc13..6f652c0 100644 --- a/DroidFish/jni/stockfish/evaluate.cpp +++ b/DroidFish/jni/stockfish/evaluate.cpp @@ -714,7 +714,7 @@ namespace { b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them); // Consider only squares where the enemy rook gives check - b &= RookPseudoAttacks[ksq]; + b &= PseudoAttacks[ROOK][ksq]; if (b) { @@ -887,7 +887,7 @@ namespace { for (c = WHITE; c <= BLACK; c++) { // Skip if other side has non-pawn pieces - if (pos.non_pawn_material(flip(c))) + if (pos.non_pawn_material(~c)) continue; b = ei.pi->passed_pawns(c); @@ -900,7 +900,7 @@ namespace { // Compute plies to queening and check direct advancement movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2); - oppMovesToGo = square_distance(pos.king_square(flip(c)), queeningSquare) - int(c != pos.side_to_move()); + oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move()); pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath); if (movesToGo >= oppMovesToGo && !pathDefended) @@ -928,7 +928,7 @@ namespace { return SCORE_ZERO; winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK); - loserSide = flip(winnerSide); + loserSide = ~winnerSide; // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss? b = candidates = pos.pieces(PAWN, loserSide); diff --git a/DroidFish/jni/stockfish/lock.h b/DroidFish/jni/stockfish/lock.h index 3b3de1a..7f74fcf 100644 --- a/DroidFish/jni/stockfish/lock.h +++ b/DroidFish/jni/stockfish/lock.h @@ -45,26 +45,9 @@ typedef pthread_cond_t WaitCondition; #undef WIN32_LEAN_AND_MEAN #undef NOMINMAX -// Default fast and race free locks and condition variables -#if !defined(OLD_LOCKS) - -typedef SRWLOCK Lock; -typedef CONDITION_VARIABLE WaitCondition; - -# define lock_init(x) InitializeSRWLock(x) -# define lock_grab(x) AcquireSRWLockExclusive(x) -# define lock_release(x) ReleaseSRWLockExclusive(x) -# define lock_destroy(x) (x) -# define cond_destroy(x) (x) -# define cond_init(x) InitializeConditionVariable(x) -# define cond_signal(x) WakeConditionVariable(x) -# define cond_wait(x,y) SleepConditionVariableSRW(x,y,INFINITE,0) -# define cond_timedwait(x,y,z) SleepConditionVariableSRW(x,y,z,0) - -// Fallback solution to build for Windows XP and older versions, note that -// cond_wait() is racy between lock_release() and WaitForSingleObject(). -#else - +// 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; @@ -80,6 +63,4 @@ typedef HANDLE WaitCondition; #endif -#endif - #endif // !defined(LOCK_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/material.cpp b/DroidFish/jni/stockfish/material.cpp index 313a9cf..7369bca 100644 --- a/DroidFish/jni/stockfish/material.cpp +++ b/DroidFish/jni/stockfish/material.cpp @@ -203,13 +203,13 @@ MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const { // No pawns makes it difficult to win, even with a material advantage if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame) { - mi->factor[WHITE] = uint8_t + mi->factor[WHITE] = (uint8_t) (npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]); } if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame) { - mi->factor[BLACK] = uint8_t + mi->factor[BLACK] = (uint8_t) (npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]); } @@ -231,7 +231,7 @@ MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const { { pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } }; - mi->value = int16_t((imbalance(pieceCount) - imbalance(pieceCount)) / 16); + mi->value = (int16_t)((imbalance(pieceCount) - imbalance(pieceCount)) / 16); return mi; } diff --git a/DroidFish/jni/stockfish/misc.cpp b/DroidFish/jni/stockfish/misc.cpp index 2a0c95b..8b8976e 100644 --- a/DroidFish/jni/stockfish/misc.cpp +++ b/DroidFish/jni/stockfish/misc.cpp @@ -55,7 +55,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 = "2.2.1"; +static const string Version = "2.2.2"; static const string Tag = ""; diff --git a/DroidFish/jni/stockfish/move.cpp b/DroidFish/jni/stockfish/move.cpp index 8e937ae..1bf9b6a 100644 --- a/DroidFish/jni/stockfish/move.cpp +++ b/DroidFish/jni/stockfish/move.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include "movegen.h" #include "position.h" @@ -148,13 +147,13 @@ const string move_to_san(Position& pos, Move m) { } } - // The move gives check? We don't use pos.move_gives_check() here - // because we need to test for a mate after the move is done. - StateInfo st; - pos.do_move(m, st); - if (pos.in_check()) - san += pos.is_mate() ? "#" : "+"; - pos.undo_move(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 eb235b1..0ec1fce 100644 --- a/DroidFish/jni/stockfish/movegen.cpp +++ b/DroidFish/jni/stockfish/movegen.cpp @@ -17,50 +17,238 @@ along with this program. If not, see . */ -#include #include +#include -#include "bitcount.h" #include "movegen.h" #include "position.h" -// Simple macro to wrap a very common while loop, no facny, no flexibility, -// hardcoded list name 'mlist' and from square 'from'. -#define SERIALIZE_MOVES(b) while (b) (*mlist++).move = make_move(from, pop_1st_bit(&b)) - -// Version used for pawns, where the 'from' square is given as a delta from the 'to' square -#define SERIALIZE_MOVES_D(b, d) while (b) { to = pop_1st_bit(&b); (*mlist++).move = make_move(to + (d), to); } +/// 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)) +/// 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); \ + (*mlist++).move = make_move(to + (d), to); } namespace { - enum CastlingSide { - KING_SIDE, - QUEEN_SIDE - }; + enum CastlingSide { KING_SIDE, QUEEN_SIDE }; - template - MoveStack* generate_castle_moves(const Position&, MoveStack*, Color us); + template + MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) { - template - MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square); + const CastleRight CR[] = { Side ? WHITE_OOO : WHITE_OO, + Side ? BLACK_OOO : BLACK_OO }; - template - inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) { + if (!pos.can_castle(CR[us])) + return mlist; - assert(Pt != QUEEN && Pt != PAWN); + // After castling, the rook and king final positions are the same in Chess960 + // as they would be in standard chess. + Square kfrom = pos.king_square(us); + Square rfrom = pos.castle_rook_square(CR[us]); + Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1); + Square rto = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1); + Bitboard enemies = pos.pieces(~us); - Bitboard b = pos.attacks_from(from) & pos.empty_squares(); + assert(!pos.in_check()); + assert(pos.piece_on(kfrom) == make_piece(us, KING)); + assert(pos.piece_on(rfrom) == make_piece(us, ROOK)); - if (Pt == KING) - b &= ~QueenPseudoAttacks[pos.king_square(flip(pos.side_to_move()))]; + // Unimpeded rule: All the squares between the king's initial and final squares + // (including the final square), and all the squares between the rook's initial + // and final squares (including the final square), must be vacant except for + // the king and castling rook. + for (Square s = std::min(rfrom, rto), e = std::max(rfrom, rto); s <= e; s++) + if (s != kfrom && s != rfrom && !pos.square_is_empty(s)) + return mlist; + + for (Square s = std::min(kfrom, kto), e = std::max(kfrom, kto); s <= e; s++) + if ( (s != kfrom && s != rfrom && !pos.square_is_empty(s)) + ||(pos.attackers_to(s) & enemies)) + return mlist; + + // Because we generate only legal castling moves we need to verify that + // when moving the castling rook we do not discover some hidden checker. + // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. + if (pos.is_chess960()) + { + Bitboard occ = pos.occupied_squares(); + clear_bit(&occ, rfrom); + if (pos.attackers_to(kto, occ) & enemies) + return mlist; + } + + (*mlist++).move = make_castle(kfrom, rfrom); + + if (OnlyChecks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos))) + mlist--; - SERIALIZE_MOVES(b); return mlist; } + + template + inline Bitboard move_pawns(Bitboard p) { + + return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 : + Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 : + Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p; + } + + + template + inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) { + + const Bitboard TFileABB = ( Delta == DELTA_NE + || Delta == DELTA_SE ? FileABB : FileHBB); + + Bitboard b = move_pawns(pawns) & target & ~TFileABB; + SERIALIZE_PAWNS(b, -Delta); + return mlist; + } + + + template + inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7, Bitboard target, Square ksq) { + + const Bitboard TFileABB = ( Delta == DELTA_NE + || Delta == DELTA_SE ? FileABB : FileHBB); + + Bitboard b = move_pawns(pawnsOn7) & target; + + if (Delta != DELTA_N && Delta != DELTA_S) + b &= ~TFileABB; + + while (b) + { + Square to = pop_1st_bit(&b); + + if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) + (*mlist++).move = make_promotion(to - Delta, to, QUEEN); + + if (Type == MV_NON_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) + { + (*mlist++).move = make_promotion(to - Delta, to, ROOK); + (*mlist++).move = make_promotion(to - Delta, to, BISHOP); + (*mlist++).move = make_promotion(to - Delta, to, KNIGHT); + } + + // Knight-promotion is the only one that can give a check (direct or + // discovered) not already included in the queen-promotion. + if (Type == MV_NON_CAPTURE_CHECK && bit_is_set(StepAttacksBB[W_KNIGHT][to], ksq)) + (*mlist++).move = make_promotion(to - Delta, to, KNIGHT); + else + (void)ksq; // Silence a warning under MSVC + } + return mlist; + } + + + template + MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) { + + // Calculate our parametrized parameters at compile time, named according to + // the point of view of white side. + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); + const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); + const Square UP = (Us == WHITE ? DELTA_N : DELTA_S); + const Square RIGHT = (Us == WHITE ? DELTA_NE : DELTA_SW); + const Square LEFT = (Us == WHITE ? DELTA_NW : DELTA_SE); + + Bitboard b1, b2, dc1, dc2, emptySquares; + + Bitboard pawnsOn7 = pos.pieces(PAWN, Us) & TRank7BB; + Bitboard pawnsNotOn7 = pos.pieces(PAWN, Us) & ~TRank7BB; + + Bitboard enemies = (Type == MV_EVASION ? pos.pieces(Them) & target: + Type == MV_CAPTURE ? target : pos.pieces(Them)); + + // Single and double pawn pushes, no promotions + if (Type != MV_CAPTURE) + { + emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares()); + + b1 = move_pawns(pawnsNotOn7) & emptySquares; + b2 = move_pawns(b1 & TRank3BB) & emptySquares; + + if (Type == MV_EVASION) // Consider only blocking squares + { + b1 &= target; + b2 &= target; + } + + if (Type == MV_NON_CAPTURE_CHECK) + { + // Consider only direct checks + b1 &= pos.attacks_from(ksq, Them); + b2 &= pos.attacks_from(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 + { + dc1 = move_pawns(pawnsNotOn7 & target) & emptySquares & ~file_bb(ksq); + dc2 = move_pawns(dc1 & TRank3BB) & emptySquares; + + b1 |= dc1; + b2 |= dc2; + } + } + + SERIALIZE_PAWNS(b1, -UP); + SERIALIZE_PAWNS(b2, -UP -UP); + } + + // Promotions and underpromotions + if (pawnsOn7) + { + if (Type == MV_CAPTURE) + emptySquares = pos.empty_squares(); + + if (Type == MV_EVASION) + emptySquares &= target; + + mlist = generate_promotions(mlist, pawnsOn7, enemies, ksq); + mlist = generate_promotions(mlist, pawnsOn7, enemies, ksq); + mlist = generate_promotions(mlist, pawnsOn7, emptySquares, ksq); + } + + // Standard and en-passant captures + if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) + { + mlist = generate_pawn_captures(mlist, pawnsNotOn7, enemies); + mlist = generate_pawn_captures(mlist, pawnsNotOn7, enemies); + + if (pos.ep_square() != SQ_NONE) + { + assert(rank_of(pos.ep_square()) == (Us == WHITE ? RANK_6 : RANK_3)); + + // 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 && !bit_is_set(target, pos.ep_square() - UP)) + return mlist; + + b1 = pawnsNotOn7 & pos.attacks_from(pos.ep_square(), Them); + + assert(b1); + + while (b1) + (*mlist++).move = make_enpassant(pop_1st_bit(&b1), pos.ep_square()); + } + } + + return mlist; + } + + template - inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us, - Bitboard dc, Square ksq) { + inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, + Color us, const CheckInfo& ci) { assert(Pt != KING && Pt != PAWN); Bitboard checkSqs, b; @@ -70,41 +258,44 @@ namespace { if ((from = *pl++) == SQ_NONE) return mlist; - checkSqs = pos.attacks_from(ksq) & pos.empty_squares(); + checkSqs = ci.checkSq[Pt] & pos.empty_squares(); do { - if ( (Pt == QUEEN && !(QueenPseudoAttacks[from] & checkSqs)) - || (Pt == ROOK && !(RookPseudoAttacks[from] & checkSqs)) - || (Pt == BISHOP && !(BishopPseudoAttacks[from] & checkSqs))) + if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) + && !(PseudoAttacks[Pt][from] & checkSqs)) continue; - if (dc && bit_is_set(dc, from)) + if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from)) continue; b = pos.attacks_from(from) & checkSqs; - SERIALIZE_MOVES(b); + SERIALIZE(b); } while ((from = *pl++) != SQ_NONE); return mlist; } - template<> - FORCE_INLINE MoveStack* generate_direct_checks(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) { - return (us == WHITE ? generate_pawn_moves(p, m, dc, ksq) - : generate_pawn_moves(p, m, dc, ksq)); + template<> + FORCE_INLINE MoveStack* generate_direct_checks(const Position& p, MoveStack* m, + Color us, const CheckInfo& ci) { + + return us == WHITE ? generate_pawn_moves(p, m, ci.dcCandidates, ci.ksq) + : generate_pawn_moves(p, m, ci.dcCandidates, ci.ksq); } + template FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) { assert(Pt == PAWN); - return (us == WHITE ? generate_pawn_moves(p, m, t, SQ_NONE) - : generate_pawn_moves(p, m, t, SQ_NONE)); + return us == WHITE ? generate_pawn_moves(p, m, t, SQ_NONE) + : generate_pawn_moves(p, m, t, SQ_NONE); } + template FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) { @@ -117,24 +308,23 @@ namespace { do { from = *pl; b = pos.attacks_from(from) & target; - SERIALIZE_MOVES(b); + SERIALIZE(b); } while (*++pl != SQ_NONE); } return mlist; } + template<> FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) { - Bitboard b; Square from = pos.king_square(us); - - b = pos.attacks_from(from) & target; - SERIALIZE_MOVES(b); + Bitboard b = pos.attacks_from(from) & target; + SERIALIZE(b); return mlist; } -} +} // namespace /// generate generates all pseudo-legal captures and queen @@ -156,13 +346,13 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { Bitboard target; if (Type == MV_CAPTURE) - target = pos.pieces(flip(us)); + target = pos.pieces(~us); else if (Type == MV_NON_CAPTURE) target = pos.empty_squares(); else if (Type == MV_NON_EVASION) - target = pos.pieces(flip(us)) | pos.empty_squares(); + target = pos.pieces(~us) | pos.empty_squares(); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); @@ -173,11 +363,8 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { if (Type != MV_CAPTURE && pos.can_castle(us)) { - if (pos.can_castle(us == WHITE ? WHITE_OO : BLACK_OO)) - mlist = generate_castle_moves(pos, mlist, us); - - if (pos.can_castle(us == WHITE ? WHITE_OOO : BLACK_OOO)) - mlist = generate_castle_moves(pos, mlist, us); + mlist = generate_castle_moves(pos, mlist, us); + mlist = generate_castle_moves(pos, mlist, us); } return mlist; @@ -196,36 +383,39 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) assert(!pos.in_check()); - Bitboard b, dc; - Square from; Color us = pos.side_to_move(); - Square ksq = pos.king_square(flip(us)); + CheckInfo ci(pos); + Bitboard dc = ci.dcCandidates; - assert(pos.piece_on(ksq) == make_piece(flip(us), KING)); - - // Discovered non-capture checks - b = dc = pos.discovered_check_candidates(); - - while (b) + while (dc) { - from = pop_1st_bit(&b); - switch (type_of(pos.piece_on(from))) - { - case PAWN: /* Will be generated togheter with pawns direct checks */ break; - case KNIGHT: mlist = generate_discovered_checks(pos, mlist, from); break; - case BISHOP: mlist = generate_discovered_checks(pos, mlist, from); break; - case ROOK: mlist = generate_discovered_checks(pos, mlist, from); break; - case KING: mlist = generate_discovered_checks(pos, mlist, from); break; - default: assert(false); break; - } + Square from = pop_1st_bit(&dc); + PieceType pt = type_of(pos.piece_on(from)); + + if (pt == PAWN) + continue; // Will be generated togheter with direct checks + + Bitboard b = pos.attacks_from(Piece(pt), from) & pos.empty_squares(); + + if (pt == KING) + b &= ~PseudoAttacks[QUEEN][ci.ksq]; + + SERIALIZE(b); } - // Direct non-capture checks - mlist = generate_direct_checks(pos, mlist, us, dc, ksq); - mlist = generate_direct_checks(pos, mlist, us, dc, ksq); - mlist = generate_direct_checks(pos, mlist, us, dc, ksq); - mlist = generate_direct_checks(pos, mlist, us, dc, ksq); - return generate_direct_checks(pos, mlist, us, dc, 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); + mlist = generate_direct_checks(pos, mlist, us, ci); + + if (pos.can_castle(us)) + { + mlist = generate_castle_moves(pos, mlist, us); + mlist = generate_castle_moves(pos, mlist, us); + } + + return mlist; } @@ -241,38 +431,37 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { int checkersCnt = 0; Color us = pos.side_to_move(); Square ksq = pos.king_square(us); - Bitboard checkers = pos.checkers(); Bitboard sliderAttacks = 0; + Bitboard checkers = pos.checkers(); - assert(pos.piece_on(ksq) == make_piece(us, KING)); assert(checkers); - // Find squares attacked by slider checkers, we will remove - // them from the king evasions set so to early skip known - // illegal moves and avoid an useless legality check later. + // 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); - assert(color_of(pos.piece_on(checksq)) == flip(us)); + assert(color_of(pos.piece_on(checksq)) == ~us); switch (type_of(pos.piece_on(checksq))) { - case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break; - case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break; + case BISHOP: sliderAttacks |= PseudoAttacks[BISHOP][checksq]; break; + case ROOK: sliderAttacks |= PseudoAttacks[ROOK][checksq]; break; case QUEEN: - // If queen and king are far we can safely remove all the squares attacked - // in the other direction becuase are not reachable by the king anyway. - if (squares_between(ksq, checksq) || (RookPseudoAttacks[checksq] & (1ULL << ksq))) - sliderAttacks |= QueenPseudoAttacks[checksq]; + // If queen and king are far or not on a diagonal line we can safely + // remove all the squares attacked in the other direction becuase are + // not reachable by the king anyway. + if (squares_between(ksq, checksq) || !bit_is_set(PseudoAttacks[BISHOP][checksq], ksq)) + sliderAttacks |= PseudoAttacks[QUEEN][checksq]; - // Otherwise, if king and queen are adjacent and on a diagonal line, we need to - // use real rook attacks to check if king is safe to move in the other direction. - // For example: king in B2, queen in A1 a knight in B1, and we can safely move to C1. + // Otherwise we need to use real rook attacks to check if king is safe + // to move in the other direction. For example: king in B2, queen in A1 + // a knight in B1, and we can safely move to C1. else - sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from(checksq); + sliderAttacks |= PseudoAttacks[BISHOP][checksq] | pos.attacks_from(checksq); default: break; @@ -282,14 +471,13 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { // Generate evasions for king, capture and non capture moves b = pos.attacks_from(ksq) & ~pos.pieces(us) & ~sliderAttacks; from = ksq; - SERIALIZE_MOVES(b); + SERIALIZE(b); - // Generate evasions for other pieces only if not double check + // Generate evasions for other pieces only if not under a double check if (checkersCnt > 1) return mlist; - // Find squares where a blocking evasion or a capture of the - // checker piece is possible. + // Blocking evasions or captures of the checking piece target = squares_between(checksq, ksq) | checkers; mlist = generate_piece_moves(pos, mlist, us, target); @@ -300,7 +488,7 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { } -/// generate computes a complete list of legal moves in the current position +/// generate generates all the legal moves in the given position template<> MoveStack* generate(const Position& pos, MoveStack* mlist) { @@ -310,8 +498,6 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { last = pos.in_check() ? generate(pos, mlist) : generate(pos, mlist); - - // Remove illegal moves from the list while (cur != last) if (!pos.pl_move_is_legal(cur->move, pinned)) cur->move = (--last)->move; @@ -320,218 +506,3 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { return last; } - - -namespace { - - template - inline Bitboard move_pawns(Bitboard p) { - - return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 : - Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 : - Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p; - } - - template - inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) { - - const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB); - - Bitboard b; - Square to; - - // Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black) - b = move_pawns(pawns) & target & ~TFileABB; - SERIALIZE_MOVES_D(b, -Delta); - return mlist; - } - - template - inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) { - - const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB); - - Bitboard b; - Square to; - - // Promotions and under-promotions, both captures and non-captures - b = move_pawns(pawnsOn7) & target; - - if (Delta != DELTA_N && Delta != DELTA_S) - b &= ~TFileABB; - - while (b) - { - to = pop_1st_bit(&b); - - if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) - (*mlist++).move = make_promotion(to - Delta, to, QUEEN); - - if (Type == MV_NON_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) - { - (*mlist++).move = make_promotion(to - Delta, to, ROOK); - (*mlist++).move = make_promotion(to - Delta, to, BISHOP); - (*mlist++).move = make_promotion(to - Delta, to, KNIGHT); - } - - // This is the only possible under promotion that can give a check - // not already included in the queen-promotion. - if ( Type == MV_CHECK - && bit_is_set(pos.attacks_from(to), pos.king_square(Delta > 0 ? BLACK : WHITE))) - (*mlist++).move = make_promotion(to - Delta, to, KNIGHT); - else (void)pos; // Silence a warning under MSVC - } - return mlist; - } - - template - MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) { - - // Calculate our parametrized parameters at compile time, named - // according to the point of view of white side. - const Color Them = (Us == WHITE ? BLACK : WHITE); - const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); - const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - const Square UP = (Us == WHITE ? DELTA_N : DELTA_S); - const Square RIGHT_UP = (Us == WHITE ? DELTA_NE : DELTA_SW); - const Square LEFT_UP = (Us == WHITE ? DELTA_NW : DELTA_SE); - - Square to; - Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares; - Bitboard pawns = pos.pieces(PAWN, Us); - Bitboard pawnsOn7 = pawns & TRank7BB; - Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces(Them)); - - // Pre-calculate pawn pushes before changing emptySquares definition - if (Type != MV_CAPTURE) - { - emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares()); - pawnPushes = move_pawns(pawns & ~TRank7BB) & emptySquares; - } - - if (Type == MV_EVASION) - { - emptySquares &= target; // Only blocking squares - enemyPieces &= target; // Capture only the checker piece - } - - // Promotions and underpromotions - if (pawnsOn7) - { - if (Type == MV_CAPTURE) - emptySquares = pos.empty_squares(); - - pawns &= ~TRank7BB; - mlist = generate_promotions(pos, mlist, pawnsOn7, enemyPieces); - mlist = generate_promotions(pos, mlist, pawnsOn7, enemyPieces); - mlist = generate_promotions(pos, mlist, pawnsOn7, emptySquares); - } - - // Standard captures - if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) - { - mlist = generate_pawn_captures(mlist, pawns, enemyPieces); - mlist = generate_pawn_captures(mlist, pawns, enemyPieces); - } - - // Single and double pawn pushes - if (Type != MV_CAPTURE) - { - b1 = (Type != MV_EVASION ? pawnPushes : pawnPushes & emptySquares); - b2 = move_pawns(pawnPushes & TRank3BB) & emptySquares; - - if (Type == MV_CHECK) - { - // Consider only pawn moves which give direct checks - b1 &= pos.attacks_from(ksq, Them); - b2 &= pos.attacks_from(ksq, Them); - - // Add pawn moves which gives 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. - if (pawns & target) // For CHECK type target is dc bitboard - { - dc1 = move_pawns(pawns & target & ~file_bb(ksq)) & emptySquares; - dc2 = move_pawns(dc1 & TRank3BB) & emptySquares; - - b1 |= dc1; - b2 |= dc2; - } - } - SERIALIZE_MOVES_D(b1, -UP); - SERIALIZE_MOVES_D(b2, -UP -UP); - } - - // En passant captures - if ( (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) - && pos.ep_square() != SQ_NONE) - { - assert(Us != WHITE || rank_of(pos.ep_square()) == RANK_6); - assert(Us != BLACK || rank_of(pos.ep_square()) == RANK_3); - - // 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 && !bit_is_set(target, pos.ep_square() - UP)) - return mlist; - - b1 = pawns & pos.attacks_from(pos.ep_square(), Them); - - assert(b1); - - while (b1) - { - to = pop_1st_bit(&b1); - (*mlist++).move = make_enpassant(to, pos.ep_square()); - } - } - return mlist; - } - - template - MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) { - - CastleRight f = CastleRight((Side == KING_SIDE ? WHITE_OO : WHITE_OOO) << us); - Color them = flip(us); - - // After castling, the rook and king's final positions are exactly the same - // in Chess960 as they would be in standard chess. - Square kfrom = pos.king_square(us); - Square rfrom = pos.castle_rook_square(f); - Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1); - Square rto = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1); - - assert(!pos.in_check()); - assert(pos.piece_on(kfrom) == make_piece(us, KING)); - assert(pos.piece_on(rfrom) == make_piece(us, ROOK)); - - // Unimpeded rule: All the squares between the king's initial and final squares - // (including the final square), and all the squares between the rook's initial - // and final squares (including the final square), must be vacant except for - // the king and castling rook. - for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++) - if ( (s != kfrom && s != rfrom && !pos.square_is_empty(s)) - ||(pos.attackers_to(s) & pos.pieces(them))) - return mlist; - - for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); s++) - if (s != kfrom && s != rfrom && !pos.square_is_empty(s)) - return mlist; - - // Because we generate only legal castling moves we need to verify that - // when moving the castling rook we do not discover some hidden checker. - // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - if (pos.is_chess960()) - { - Bitboard occ = pos.occupied_squares(); - clear_bit(&occ, rfrom); - if (pos.attackers_to(kto, occ) & pos.pieces(them)) - return mlist; - } - - (*mlist++).move = make_castle(kfrom, rfrom); - - return mlist; - } - -} // namespace diff --git a/DroidFish/jni/stockfish/movegen.h b/DroidFish/jni/stockfish/movegen.h index f0feab1..c5c6e3e 100644 --- a/DroidFish/jni/stockfish/movegen.h +++ b/DroidFish/jni/stockfish/movegen.h @@ -25,7 +25,6 @@ enum MoveType { MV_CAPTURE, MV_NON_CAPTURE, - MV_CHECK, MV_NON_CAPTURE_CHECK, MV_EVASION, MV_NON_EVASION, diff --git a/DroidFish/jni/stockfish/movepick.cpp b/DroidFish/jni/stockfish/movepick.cpp index 9ada404..8a7e9ae 100644 --- a/DroidFish/jni/stockfish/movepick.cpp +++ b/DroidFish/jni/stockfish/movepick.cpp @@ -255,7 +255,7 @@ void MovePicker::score_captures() { { m = cur->move; cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))] - - type_of(pos.piece_on(from_sq(m))); + - type_of(pos.piece_moved(m)); if (is_promotion(m)) cur->score += PieceValueMidgame[Piece(promotion_piece_type(m))]; @@ -294,9 +294,9 @@ void MovePicker::score_evasions() { cur->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_on(from_sq(m))) + History::MaxValue; + - type_of(pos.piece_moved(m)) + History::MaxValue; else - cur->score = H.value(pos.piece_on(from_sq(m)), to_sq(m)); + cur->score = H.value(pos.piece_moved(m), to_sq(m)); } } diff --git a/DroidFish/jni/stockfish/position.cpp b/DroidFish/jni/stockfish/position.cpp index d2c0a63..ccc29ac 100644 --- a/DroidFish/jni/stockfish/position.cpp +++ b/DroidFish/jni/stockfish/position.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include "bitcount.h" #include "movegen.h" @@ -79,8 +78,8 @@ namespace { CheckInfo::CheckInfo(const Position& pos) { - Color them = flip(pos.side_to_move()); - Square ksq = pos.king_square(them); + Color them = ~pos.side_to_move(); + ksq = pos.king_square(them); pinned = pos.pinned_pieces(); dcCandidates = pos.discovered_check_candidates(); @@ -166,11 +165,11 @@ void Position::from_fen(const string& fenStr, bool isChess960) { // 1. Piece placement while ((fen >> token) && !isspace(token)) { - if (token == '/') - sq -= Square(16); // Jump back of 2 rows + if (isdigit(token)) + sq += Square(token - '0'); // Advance the given number of files - else if (isdigit(token)) - sq += Square(token - '0'); // Skip the given number of files + else if (token == '/') + sq = make_square(FILE_A, rank_of(sq) - Rank(2)); else if ((p = PieceToChar.find(token)) != string::npos) { @@ -193,15 +192,14 @@ void Position::from_fen(const string& fenStr, bool isChess960) { { Square rsq; Color c = islower(token) ? BLACK : WHITE; - Piece rook = make_piece(c, ROOK); token = char(toupper(token)); if (token == 'K') - for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; rsq--) {} + for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; rsq--) {} else if (token == 'Q') - for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; rsq++) {} + for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; rsq++) {} else if (token >= 'A' && token <= 'H') rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); @@ -209,7 +207,7 @@ void Position::from_fen(const string& fenStr, bool isChess960) { else continue; - set_castle_right(king_square(c), rsq); + set_castle_right(c, rsq); } // 4. En passant square. Ignore if no pawn capture is possible @@ -235,7 +233,7 @@ void Position::from_fen(const string& fenStr, bool isChess960) { st->value = compute_value(); st->npMaterial[WHITE] = compute_non_pawn_material(WHITE); st->npMaterial[BLACK] = compute_non_pawn_material(BLACK); - st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(flip(sideToMove)); + st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); chess960 = isChess960; assert(pos_is_ok()); @@ -243,14 +241,14 @@ void Position::from_fen(const string& fenStr, bool isChess960) { /// Position::set_castle_right() is an helper function used to set castling -/// rights given the corresponding king and rook starting squares. +/// rights given the corresponding color and the rook starting square. -void Position::set_castle_right(Square ksq, Square rsq) { +void Position::set_castle_right(Color c, Square rsq) { - int f = (rsq < ksq ? WHITE_OOO : WHITE_OO) << color_of(piece_on(ksq)); + int f = (rsq < king_square(c) ? WHITE_OOO : WHITE_OO) << c; st->castleRights |= f; - castleRightsMask[ksq] ^= f; + castleRightsMask[king_square(c)] ^= f; castleRightsMask[rsq] ^= f; castleRookSquare[f] = rsq; } @@ -358,12 +356,12 @@ Bitboard Position::hidden_checkers() const { // Pinned pieces protect our king, dicovery checks attack the enemy king Bitboard b, result = 0; - Bitboard pinners = pieces(FindPinned ? flip(sideToMove) : sideToMove); - Square ksq = king_square(FindPinned ? sideToMove : flip(sideToMove)); + Bitboard pinners = pieces(FindPinned ? ~sideToMove : sideToMove); + Square ksq = king_square(FindPinned ? sideToMove : ~sideToMove); // Pinners are sliders, that give check when candidate pinned is removed - pinners &= (pieces(ROOK, QUEEN) & RookPseudoAttacks[ksq]) - | (pieces(BISHOP, QUEEN) & BishopPseudoAttacks[ksq]); + pinners &= (pieces(ROOK, QUEEN) & PseudoAttacks[ROOK][ksq]) + | (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq]); while (pinners) { @@ -451,7 +449,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { assert(is_ok(m)); assert(pinned == pinned_pieces()); - Color us = side_to_move(); + Color us = sideToMove; Square from = from_sq(m); assert(color_of(piece_on(from)) == us); @@ -462,7 +460,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { // the move is made. if (is_enpassant(m)) { - Color them = flip(us); + Color them = ~us; Square to = to_sq(m); Square capsq = to + pawn_push(them); Square ksq = king_square(us); @@ -485,7 +483,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(flip(us))); + return is_castle(m) || !(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. @@ -516,7 +514,7 @@ bool Position::move_is_legal(const Move m) const { bool Position::is_pseudo_legal(const Move m) const { Color us = sideToMove; - Color them = flip(sideToMove); + Color them = ~sideToMove; Square from = from_sq(m); Square to = to_sq(m); Piece pc = piece_on(from); @@ -614,7 +612,7 @@ bool Position::is_pseudo_legal(const Move m) const { { Bitboard b = occupied_squares(); clear_bit(&b, from); - if (attackers_to(to_sq(m), b) & pieces(flip(us))) + if (attackers_to(to_sq(m), b) & pieces(~us)) return false; } else @@ -642,7 +640,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const { assert(is_ok(m)); assert(ci.dcCandidates == discovered_check_candidates()); - assert(color_of(piece_on(from_sq(m))) == side_to_move()); + assert(color_of(piece_moved(m)) == sideToMove); Square from = from_sq(m); Square to = to_sq(m); @@ -657,7 +655,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const { { // For pawn and king moves we need to verify also direction if ( (pt != PAWN && pt != KING) - || !squares_aligned(from, to, king_square(flip(side_to_move())))) + || !squares_aligned(from, to, king_square(~sideToMove))) return true; } @@ -665,9 +663,9 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const { if (!is_special(m)) return false; - Color us = side_to_move(); + Color us = sideToMove; Bitboard b = occupied_squares(); - Square ksq = king_square(flip(us)); + Square ksq = king_square(~us); // Promotion with check ? if (is_promotion(m)) @@ -765,8 +763,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI return; } - Color us = side_to_move(); - Color them = flip(us); + Color us = sideToMove; + Color them = ~us; Square from = from_sq(m); Square to = to_sq(m); Piece piece = piece_on(from); @@ -959,7 +957,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI } // Finish - sideToMove = flip(sideToMove); + sideToMove = ~sideToMove; st->value += (sideToMove == WHITE ? TempoValue : -TempoValue); assert(pos_is_ok()); @@ -973,7 +971,7 @@ void Position::undo_move(Move m) { assert(is_ok(m)); - sideToMove = flip(sideToMove); + sideToMove = ~sideToMove; if (is_castle(m)) { @@ -981,8 +979,8 @@ void Position::undo_move(Move m) { return; } - Color us = side_to_move(); - Color them = flip(us); + Color us = sideToMove; + Color them = ~us; Square from = from_sq(m); Square to = to_sq(m); Piece piece = piece_on(to); @@ -1077,7 +1075,7 @@ void Position::do_castle_move(Move m) { Square kto, kfrom, rfrom, rto, kAfter, rAfter; - Color us = side_to_move(); + Color us = sideToMove; Square kBefore = from_sq(m); Square rBefore = to_sq(m); @@ -1161,10 +1159,10 @@ void Position::do_castle_move(Move m) { st->rule50 = 0; // Update checkers BB - st->checkersBB = attackers_to(king_square(flip(us))) & pieces(us); + st->checkersBB = attackers_to(king_square(~us)) & pieces(us); // Finish - sideToMove = flip(sideToMove); + sideToMove = ~sideToMove; st->value += (sideToMove == WHITE ? TempoValue : -TempoValue); } else @@ -1195,7 +1193,7 @@ void Position::do_null_move(StateInfo& backupSt) { dst->rule50 = src->rule50; dst->pliesFromNull = src->pliesFromNull; - sideToMove = flip(sideToMove); + sideToMove = ~sideToMove; if (Do) { @@ -1265,9 +1263,9 @@ int Position::see(Move m) const { // Handle en passant moves if (is_enpassant(m)) { - Square capQq = to - pawn_push(side_to_move()); + Square capQq = to - pawn_push(sideToMove); - assert(capturedType == NO_PIECE_TYPE); + assert(!capturedType); assert(type_of(piece_on(capQq)) == PAWN); // Remove the captured pawn @@ -1281,7 +1279,7 @@ int Position::see(Move m) const { attackers = attackers_to(to, occ); // If the opponent has no attackers we are finished - stm = flip(color_of(piece_on(from))); + stm = ~color_of(piece_on(from)); stmAttackers = attackers & pieces(stm); if (!stmAttackers) return PieceValueMidgame[capturedType]; @@ -1319,7 +1317,7 @@ int Position::see(Move m) const { // Remember the value of the capturing piece, and change the side to // move before beginning the next iteration. capturedType = pt; - stm = flip(stm); + stm = ~stm; stmAttackers = attackers & pieces(stm); // Stop before processing a king capture @@ -1403,7 +1401,7 @@ Key Position::compute_key() const { if (ep_square() != SQ_NONE) result ^= zobEp[ep_square()]; - if (side_to_move() == BLACK) + if (sideToMove == BLACK) result ^= zobSideToMove; return result; @@ -1467,7 +1465,7 @@ Score Position::compute_value() const { result += pst(make_piece(c, pt), pop_1st_bit(&b)); } - result += (side_to_move() == WHITE ? TempoValue / 2 : -TempoValue / 2); + result += (sideToMove == WHITE ? TempoValue / 2 : -TempoValue / 2); return result; } @@ -1500,7 +1498,7 @@ bool Position::is_draw() const { return true; // Draw by the 50 moves rule? - if (st->rule50 > 99 && !is_mate()) + if (st->rule50 > 99 && (!in_check() || MoveList(*this).size())) return true; // Draw by repetition? @@ -1532,15 +1530,6 @@ template bool Position::is_draw() const; template bool Position::is_draw() const; -/// Position::is_mate() returns true or false depending on whether the -/// side to move is checkmated. - -bool Position::is_mate() const { - - return in_check() && !MoveList(*this).size(); -} - - /// 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 @@ -1572,7 +1561,7 @@ void Position::init() { for (Square s = SQ_A1; s <= SQ_H8; s++) { pieceSquareTable[p][s] = ps + PSQT[p][s]; - pieceSquareTable[p+8][flip(s)] = -pieceSquareTable[p][s]; + pieceSquareTable[p+8][~s] = -pieceSquareTable[p][s]; } } } @@ -1592,27 +1581,27 @@ void Position::flip_me() { // Board for (Square s = SQ_A1; s <= SQ_H8; s++) if (!pos.square_is_empty(s)) - put_piece(Piece(pos.piece_on(s) ^ 8), flip(s)); + put_piece(Piece(pos.piece_on(s) ^ 8), ~s); // Side to move - sideToMove = flip(pos.side_to_move()); + sideToMove = ~pos.side_to_move(); // Castling rights if (pos.can_castle(WHITE_OO)) - set_castle_right(king_square(BLACK), flip(pos.castle_rook_square(WHITE_OO))); + set_castle_right(BLACK, ~pos.castle_rook_square(WHITE_OO)); if (pos.can_castle(WHITE_OOO)) - set_castle_right(king_square(BLACK), flip(pos.castle_rook_square(WHITE_OOO))); + set_castle_right(BLACK, ~pos.castle_rook_square(WHITE_OOO)); if (pos.can_castle(BLACK_OO)) - set_castle_right(king_square(WHITE), flip(pos.castle_rook_square(BLACK_OO))); + set_castle_right(WHITE, ~pos.castle_rook_square(BLACK_OO)); if (pos.can_castle(BLACK_OOO)) - set_castle_right(king_square(WHITE), flip(pos.castle_rook_square(BLACK_OOO))); + set_castle_right(WHITE, ~pos.castle_rook_square(BLACK_OOO)); // En passant square if (pos.st->epSquare != SQ_NONE) - st->epSquare = flip(pos.st->epSquare); + st->epSquare = ~pos.st->epSquare; // Checkers - st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(flip(sideToMove)); + st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); // Hash keys st->key = compute_key(); @@ -1654,7 +1643,7 @@ bool Position::pos_is_ok(int* failedStep) const { if (failedStep) *failedStep = 1; // Side to move OK? - if (side_to_move() != WHITE && side_to_move() != BLACK) + if (sideToMove != WHITE && sideToMove != BLACK) return false; // Are the king squares in the position correct? @@ -1683,8 +1672,8 @@ bool Position::pos_is_ok(int* failedStep) const { if (failedStep) (*failedStep)++; if (debugKingCapture) { - Color us = side_to_move(); - Color them = flip(us); + Color us = sideToMove; + Color them = ~us; Square ksq = king_square(them); if (attackers_to(ksq) & pieces(us)) return false; @@ -1721,7 +1710,7 @@ bool Position::pos_is_ok(int* failedStep) const { { // The en passant square must be on rank 6, from the point of view of the // side to move. - if (relative_rank(side_to_move(), ep_square()) != RANK_6) + if (relative_rank(sideToMove, ep_square()) != RANK_6) return false; } diff --git a/DroidFish/jni/stockfish/position.h b/DroidFish/jni/stockfish/position.h index 36d8869..c391d82 100644 --- a/DroidFish/jni/stockfish/position.h +++ b/DroidFish/jni/stockfish/position.h @@ -37,6 +37,7 @@ struct CheckInfo { Bitboard dcCandidates; Bitboard pinned; Bitboard checkSq[8]; + Square ksq; }; @@ -100,6 +101,7 @@ public: // The piece on a given square Piece piece_on(Square s) const; + Piece piece_moved(Move m) const; bool square_is_empty(Square s) const; // Side to move @@ -183,14 +185,9 @@ public: Value non_pawn_material(Color c) const; Score pst_delta(Piece piece, Square from, Square to) const; - // Game termination checks - bool is_mate() const; - template bool is_draw() const; - - // Plies from start position to the beginning of search - int startpos_ply_counter() const; - // Other properties of the position + template bool is_draw() const; + int startpos_ply_counter() const; bool opposite_colored_bishops() const; bool has_pawn_on_7th(Color c) const; bool is_chess960() const; @@ -213,7 +210,7 @@ private: // Initialization helper functions (used while setting up a position) void clear(); void put_piece(Piece p, Square s); - void set_castle_right(Square ksq, Square rsq); + void set_castle_right(Color c, Square rsq); bool move_is_legal(const Move m) const; // Helper template functions @@ -277,6 +274,10 @@ inline Piece Position::piece_on(Square s) const { return board[s]; } +inline Piece Position::piece_moved(Move m) const { + return board[from_sq(m)]; +} + inline bool Position::square_is_empty(Square s) const { return board[s] == NO_PIECE; } @@ -391,7 +392,7 @@ inline Bitboard Position::pinned_pieces() const { } inline bool Position::pawn_is_passed(Color c, Square s) const { - return !(pieces(PAWN, flip(c)) & passed_pawn_mask(c, s)); + return !(pieces(PAWN, ~c) & passed_pawn_mask(c, s)); } inline Key Position::key() const { diff --git a/DroidFish/jni/stockfish/search.cpp b/DroidFish/jni/stockfish/search.cpp index 058b56b..1006541 100644 --- a/DroidFish/jni/stockfish/search.cpp +++ b/DroidFish/jni/stockfish/search.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include "book.h" #include "evaluate.h" @@ -42,15 +41,14 @@ namespace Search { volatile SignalsType Signals; LimitsType Limits; - std::vector SearchMoves; + std::vector RootMoves; Position RootPosition; } using std::string; using std::cout; using std::endl; -using std::count; -using std::find; +using namespace std; using namespace Search; namespace { @@ -61,33 +59,6 @@ namespace { // Different node types, used as template parameter enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV }; - // RootMove struct is used for moves at the root of the tree. For each root - // move we store a score, a node count, and a PV (really a refutation in the - // case of moves which fail low). Score is normally set at -VALUE_INFINITE for - // all non-pv moves. - struct RootMove { - - RootMove(){} - RootMove(Move m) { - score = prevScore = -VALUE_INFINITE; - pv.push_back(m); - pv.push_back(MOVE_NONE); - } - - bool operator<(const RootMove& m) const { return score < m.score; } - bool operator==(const Move& m) const { return pv[0] == m; } - - void extract_pv_from_tt(Position& pos); - void insert_pv_in_tt(Position& pos); - - Value score; - Value prevScore; - std::vector pv; - }; - - - /// Constants - // Lookup table to check if a Piece is a slider and its access function const bool Slidings[18] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1 }; inline bool piece_is_slider(Piece p) { return Slidings[p]; } @@ -137,17 +108,14 @@ namespace { return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)]; } - // Easy move margin. An easy move candidate must be at least this much - // better than the second best move. + // Easy move margin. An easy move candidate must be at least this much better + // than the second best move. const Value EasyMoveMargin = Value(0x150); // This is the minimum interval in msec between two check_time() calls const int TimerResolution = 5; - /// Namespace variables - - std::vector RootMoves; size_t MultiPV, UCIMultiPV, PVIdx; TimeManager TimeMgr; int BestMoveChanges; @@ -156,8 +124,6 @@ namespace { History H; - /// Local functions - template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); @@ -202,7 +168,7 @@ namespace { 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_on(from_sq(m))) == PAWN) + if (type_of(pos.piece_moved(m)) == PAWN) { Color c = pos.side_to_move(); if ( relative_rank(c, to_sq(m)) == RANK_7 @@ -284,29 +250,29 @@ void Search::think() { static Book book; // Defined static to initialize the PRNG only once + Move bm; Position& pos = RootPosition; Chess960 = pos.is_chess960(); elapsed_time(true); TimeMgr.init(Limits, pos.startpos_ply_counter()); TT.new_search(); H.clear(); - RootMoves.clear(); - // Populate RootMoves with all the legal moves (default) or, if a SearchMoves - // is given, with the subset of legal moves to search. - for (MoveList ml(pos); !ml.end(); ++ml) - if (SearchMoves.empty() || count(SearchMoves.begin(), SearchMoves.end(), ml.move())) - RootMoves.push_back(RootMove(ml.move())); - - if (Options["OwnBook"]) + if (RootMoves.empty()) { - Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]); + cout << "info depth 0 score " + << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl; - if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove)) - { - std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove)); - goto finalize; - } + RootMoves.push_back(MOVE_NONE); + goto finalize; + } + + if ( Options["OwnBook"] + && (bm = book.probe(pos, Options["Book File"], Options["Best Book Move"])) != MOVE_NONE + && count(RootMoves.begin(), RootMoves.end(), bm)) + { + std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bm)); + goto finalize; } // Read UCI options: GUI could change UCI parameters during the game @@ -377,9 +343,9 @@ void Search::think() { finalize: - // When we reach max depth we arrive here even without a StopRequest, but if - // we are pondering or in infinite search, we shouldn't print the best move - // before we are told to do so. + // When we reach max depth we arrive here even without Signals.stop is raised, + // but if we are pondering or in infinite search, we shouldn't print the best + // move before we are told to do so. if (!Signals.stop && (Limits.ponder || Limits.infinite)) Threads.wait_for_stop_or_ponderhit(); @@ -408,16 +374,6 @@ namespace { bestValue = delta = -VALUE_INFINITE; ss->currentMove = MOVE_NULL; // Hack to skip update gains - // Handle the special case of a mated/stalemate position - if (RootMoves.empty()) - { - cout << "info depth 0 score " - << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl; - - RootMoves.push_back(MOVE_NONE); - return; - } - // Iterative deepening loop until requested to stop or target depth reached while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.maxDepth || depth <= Limits.maxDepth)) { @@ -533,15 +489,15 @@ namespace { stop = true; // Stop search early if one move seems to be much better than others - if ( depth >= 10 + if ( depth >= 12 && !stop - && ( bestMoveNeverChanged + && ( (bestMoveNeverChanged && pos.captured_piece_type()) || elapsed_time() > (TimeMgr.available_time() * 40) / 100)) { Value rBeta = bestValue - EasyMoveMargin; (ss+1)->excludedMove = RootMoves[0].pv[0]; (ss+1)->skipNullMove = true; - Value v = search(pos, ss+1, rBeta - 1, rBeta, (depth * ONE_PLY) / 2); + Value v = search(pos, ss+1, rBeta - 1, rBeta, (depth - 3) * ONE_PLY); (ss+1)->skipNullMove = false; (ss+1)->excludedMove = MOVE_NONE; @@ -703,7 +659,7 @@ namespace { if ( (move = (ss-1)->currentMove) != MOVE_NULL && (ss-1)->eval != VALUE_NONE && ss->eval != VALUE_NONE - && pos.captured_piece_type() == NO_PIECE_TYPE + && !pos.captured_piece_type() && !is_special(move)) { Square to = to_sq(move); @@ -972,7 +928,7 @@ split_point_start: // At split points actual search starts from here // but fixing this made program slightly weaker. Depth predictedDepth = newDepth - reduction(depth, moveCount); futilityValue = futilityBase + futility_margin(predictedDepth, moveCount) - + H.gain(pos.piece_on(from_sq(move)), to_sq(move)); + + H.gain(pos.piece_moved(move), to_sq(move)); if (futilityValue < beta) { @@ -1156,13 +1112,13 @@ split_point_start: // At split points actual search starts from here // Increase history value of the cut-off move Value bonus = Value(int(depth) * int(depth)); - H.add(pos.piece_on(from_sq(move)), to_sq(move), bonus); + H.add(pos.piece_moved(move), to_sq(move), bonus); // Decrease history of all the other played non-capture moves for (int i = 0; i < playedMoveCount - 1; i++) { Move m = movesSearched[i]; - H.add(pos.piece_on(from_sq(m)), to_sq(m), -bonus); + H.add(pos.piece_moved(m), to_sq(m), -bonus); } } } @@ -1395,7 +1351,7 @@ split_point_start: // At split points actual search starts from here from = from_sq(move); to = to_sq(move); - them = flip(pos.side_to_move()); + them = ~pos.side_to_move(); ksq = pos.king_square(them); kingAtt = pos.attacks_from(ksq); pc = pos.piece_on(from); @@ -1803,76 +1759,76 @@ split_point_start: // At split points actual search starts from here return best; } - - // extract_pv_from_tt() builds a PV by adding moves from the transposition table. - // We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This - // allow to always have a ponder move even when we fail high at root and also a - // long PV to print that is important for position analysis. - - void RootMove::extract_pv_from_tt(Position& pos) { - - StateInfo state[MAX_PLY_PLUS_2], *st = state; - TTEntry* tte; - int ply = 1; - Move m = pv[0]; - - assert(m != MOVE_NONE && pos.is_pseudo_legal(m)); - - pv.clear(); - pv.push_back(m); - pos.do_move(m, *st++); - - while ( (tte = TT.probe(pos.key())) != NULL - && tte->move() != MOVE_NONE - && pos.is_pseudo_legal(tte->move()) - && pos.pl_move_is_legal(tte->move(), pos.pinned_pieces()) - && ply < MAX_PLY - && (!pos.is_draw() || ply < 2)) - { - pv.push_back(tte->move()); - pos.do_move(tte->move(), *st++); - ply++; - } - pv.push_back(MOVE_NONE); - - do pos.undo_move(pv[--ply]); while (ply); - } - - - // insert_pv_in_tt() is called at the end of a search iteration, and inserts - // the PV back into the TT. This makes sure the old PV moves are searched - // first, even if the old TT entries have been overwritten. - - void RootMove::insert_pv_in_tt(Position& pos) { - - StateInfo state[MAX_PLY_PLUS_2], *st = state; - TTEntry* tte; - Key k; - Value v, m = VALUE_NONE; - int ply = 0; - - assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply])); - - do { - k = pos.key(); - tte = TT.probe(k); - - // Don't overwrite existing correct entries - if (!tte || tte->move() != pv[ply]) - { - v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m)); - TT.store(k, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[ply], v, m); - } - pos.do_move(pv[ply], *st++); - - } while (pv[++ply] != MOVE_NONE); - - do pos.undo_move(pv[--ply]); while (ply); - } - } // namespace +/// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table. +/// We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes so +/// to allow to always have a ponder move even when we fail high at root, and +/// a long PV to print that is important for position analysis. + +void RootMove::extract_pv_from_tt(Position& pos) { + + StateInfo state[MAX_PLY_PLUS_2], *st = state; + TTEntry* tte; + int ply = 1; + Move m = pv[0]; + + assert(m != MOVE_NONE && pos.is_pseudo_legal(m)); + + pv.clear(); + pv.push_back(m); + pos.do_move(m, *st++); + + while ( (tte = TT.probe(pos.key())) != NULL + && tte->move() != MOVE_NONE + && pos.is_pseudo_legal(tte->move()) + && pos.pl_move_is_legal(tte->move(), pos.pinned_pieces()) + && ply < MAX_PLY + && (!pos.is_draw() || ply < 2)) + { + pv.push_back(tte->move()); + pos.do_move(tte->move(), *st++); + ply++; + } + pv.push_back(MOVE_NONE); + + do pos.undo_move(pv[--ply]); while (ply); +} + + +/// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and +/// inserts the PV back into the TT. This makes sure the old PV moves are searched +/// first, even if the old TT entries have been overwritten. + +void RootMove::insert_pv_in_tt(Position& pos) { + + StateInfo state[MAX_PLY_PLUS_2], *st = state; + TTEntry* tte; + Key k; + Value v, m = VALUE_NONE; + int ply = 0; + + assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply])); + + do { + k = pos.key(); + tte = TT.probe(k); + + // Don't overwrite existing correct entries + if (!tte || tte->move() != pv[ply]) + { + v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m)); + TT.store(k, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[ply], v, m); + } + pos.do_move(pv[ply], *st++); + + } while (pv[++ply] != MOVE_NONE); + + do pos.undo_move(pv[--ply]); while (ply); +} + + /// Thread::idle_loop() is where the thread is parked when it has no work to do. /// The parameter 'sp', if non-NULL, is a pointer to an active SplitPoint object /// for which the thread is the master. diff --git a/DroidFish/jni/stockfish/search.h b/DroidFish/jni/stockfish/search.h index 8290b41..a4cba6b 100644 --- a/DroidFish/jni/stockfish/search.h +++ b/DroidFish/jni/stockfish/search.h @@ -48,13 +48,36 @@ struct Stack { }; +/// RootMove struct is used for moves at the root of the tree. For each root +/// move we store a score, a node count, and a PV (really a refutation in the +/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for +/// all non-pv moves. +struct RootMove { + + RootMove(){} // Needed by sort() + RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) { + pv.push_back(m); pv.push_back(MOVE_NONE); + } + + bool operator<(const RootMove& m) const { return score < m.score; } + bool operator==(const Move& m) const { return pv[0] == m; } + + void extract_pv_from_tt(Position& pos); + void insert_pv_in_tt(Position& pos); + + Value score; + Value prevScore; + std::vector pv; +}; + + /// The LimitsType struct stores information sent by GUI about available time /// to search the current move, maximum depth/time, if we are in analysis mode /// or if we have to ponder while is our opponent's side to move. struct LimitsType { - LimitsType() { memset(this, 0, sizeof(LimitsType)); } + LimitsType() { memset(this, 0, sizeof(LimitsType)); } bool use_time_management() const { return !(maxTime | maxDepth | maxNodes | infinite); } int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder; @@ -70,13 +93,13 @@ struct SignalsType { extern volatile SignalsType Signals; extern LimitsType Limits; -extern std::vector SearchMoves; +extern std::vector RootMoves; extern Position RootPosition; extern void init(); extern int64_t perft(Position& pos, Depth depth); extern void think(); -} // namespace +} // namespace Search #endif // !defined(SEARCH_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/thread.cpp b/DroidFish/jni/stockfish/thread.cpp index cb0ab5e..1db61a2 100644 --- a/DroidFish/jni/stockfish/thread.cpp +++ b/DroidFish/jni/stockfish/thread.cpp @@ -19,6 +19,7 @@ #include +#include "movegen.h" #include "search.h" #include "thread.h" #include "ucioption.h" @@ -420,7 +421,7 @@ void Thread::main_loop() { if (do_terminate) return; - think(); // This is the search entry point + Search::think(); } } @@ -431,7 +432,7 @@ void Thread::main_loop() { // the search to finish. void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limits, - const std::vector& searchMoves, bool asyncMode) { + const std::set& searchMoves, bool async) { Thread& main = threads[0]; lock_grab(&main.sleepLock); @@ -443,15 +444,22 @@ void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limit // Copy input arguments to initialize the search RootPosition.copy(pos, 0); Limits = limits; - SearchMoves = searchMoves; + RootMoves.clear(); + + // Populate RootMoves with all the legal moves (default) or, if a searchMoves + // set is given, with the subset of legal moves to search. + for (MoveList ml(pos); !ml.end(); ++ml) + if (searchMoves.empty() || searchMoves.count(ml.move())) + RootMoves.push_back(RootMove(ml.move())); // Reset signals before to start the new search - memset((void*)&Signals, 0, sizeof(Signals)); + Signals.stopOnPonderhit = Signals.firstRootMove = false; + Signals.stop = Signals.failedLowAtRoot = false; main.do_sleep = false; cond_signal(&main.sleepCond); // Wake up main thread and start searching - if (!asyncMode) + if (!async) while (!main.do_sleep) cond_wait(&sleepCond, &main.sleepLock); diff --git a/DroidFish/jni/stockfish/thread.h b/DroidFish/jni/stockfish/thread.h index 48da805..cc49063 100644 --- a/DroidFish/jni/stockfish/thread.h +++ b/DroidFish/jni/stockfish/thread.h @@ -21,6 +21,7 @@ #define THREAD_H_INCLUDED #include +#include #include "lock.h" #include "material.h" @@ -120,7 +121,7 @@ public: void wait_for_stop_or_ponderhit(); void stop_thinking(); void start_thinking(const Position& pos, const Search::LimitsType& limits, - const std::vector& searchMoves, bool asyncMode); + const std::set& = std::set(), bool async = false); template Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, diff --git a/DroidFish/jni/stockfish/types.h b/DroidFish/jni/stockfish/types.h index fb938a3..487343a 100644 --- a/DroidFish/jni/stockfish/types.h +++ b/DroidFish/jni/stockfish/types.h @@ -34,14 +34,10 @@ /// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works /// | only in 64-bit mode. For compiling requires hardware with /// | popcnt support. -/// -/// -DOLD_LOCKS | Under Windows are used the fast Slim Reader/Writer (SRW) -/// | Locks and Condition Variables: these are not supported by -/// | Windows XP and older, to compile for those platforms you -/// | should enable OLD_LOCKS. #include #include +#include #if defined(_MSC_VER) @@ -339,6 +335,14 @@ extern const Value PieceValueMidgame[17]; extern const Value PieceValueEndgame[17]; extern int SquareDistance[64][64]; +inline Color operator~(Color c) { + return Color(c ^ 1); +} + +inline Square operator~(Square s) { + return Square(s ^ 56); +} + inline Value mate_in(int ply) { return VALUE_MATE - ply; } @@ -359,10 +363,6 @@ inline Color color_of(Piece p) { return Color(p >> 3); } -inline Color flip(Color c) { - return Color(c ^ 1); -} - inline Square make_square(File f, Rank r) { return Square((r << 3) | f); } @@ -379,10 +379,6 @@ inline Rank rank_of(Square s) { return Rank(s >> 3); } -inline Square flip(Square s) { - return Square(s ^ 56); -} - inline Square mirror(Square s) { return Square(s ^ 7); } diff --git a/DroidFish/jni/stockfish/uci.cpp b/DroidFish/jni/stockfish/uci.cpp index 6bc387c..ceb04dc 100644 --- a/DroidFish/jni/stockfish/uci.cpp +++ b/DroidFish/jni/stockfish/uci.cpp @@ -201,7 +201,7 @@ namespace { string token; Search::LimitsType limits; - std::vector searchMoves; + std::set searchMoves; int time[] = { 0, 0 }, inc[] = { 0, 0 }; while (is >> token) @@ -228,7 +228,7 @@ namespace { is >> limits.maxTime; else if (token == "searchmoves") while (is >> token) - searchMoves.push_back(move_from_uci(pos, token)); + searchMoves.insert(move_from_uci(pos, token)); } limits.time = time[pos.side_to_move()]; diff --git a/DroidFish/jni/stockfish/ucioption.cpp b/DroidFish/jni/stockfish/ucioption.cpp index 4c78276..897326e 100644 --- a/DroidFish/jni/stockfish/ucioption.cpp +++ b/DroidFish/jni/stockfish/ucioption.cpp @@ -19,14 +19,13 @@ #include #include -#include #include "misc.h" #include "thread.h" #include "ucioption.h" using std::string; -using std::lexicographical_compare; +using namespace std; OptionsMap Options; // Global object diff --git a/DroidFish/res/values-de/strings.xml b/DroidFish/res/values-de/strings.xml index 54e2206..59d8c35 100644 --- a/DroidFish/res/values-de/strings.xml +++ b/DroidFish/res/values-de/strings.xml @@ -175,7 +175,7 @@ \ Information\n\ DroidFish ist ein Schachprogramm mit vielfältigen Funktionen \ -und der sehr spielstarken Schach-Engine Stockfish 2.2.1. \n\ +und der sehr spielstarken Schach-Engine Stockfish 2.2.2. \n\ \n\ Funktionen\n\ * Eröffnungsbuch\n\ diff --git a/DroidFish/res/values-es/strings.xml b/DroidFish/res/values-es/strings.xml index 70d30e2..cee6ba8 100644 --- a/DroidFish/res/values-es/strings.xml +++ b/DroidFish/res/values-es/strings.xml @@ -175,7 +175,7 @@ \ About\n\ DroidFish incorpora una interfaz gráfica de ajedrez de usuario muy completa, \ -combinada con el muy potente motor de ajedrez Stockfish 2.2.1. +combinada con el muy potente motor de ajedrez Stockfish 2.2.2. \n\ Características\n\ * Libro de Aperturas\n\ diff --git a/DroidFish/res/values/strings.xml b/DroidFish/res/values/strings.xml index e9e5181..6c6027e 100644 --- a/DroidFish/res/values/strings.xml +++ b/DroidFish/res/values/strings.xml @@ -175,7 +175,7 @@ \ About\n\ DroidFish is a feature-rich graphical chess user interface, \ -combined with the very strong Stockfish 2.2.1 chess engine.\n\ +combined with the very strong Stockfish 2.2.2 chess engine.\n\ \n\ Features\n\ * Opening book\n\