From: Fabian Fichter Date: Sun, 26 Jan 2020 16:12:57 +0000 (+0100) Subject: Merge official-stockfish/master X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=6fd93fa971f62d147298df3bb3f07a46d762f8e4;p=fairystockfish.git Merge official-stockfish/master --- 6fd93fa971f62d147298df3bb3f07a46d762f8e4 diff --cc src/position.cpp index e52f6d7,9644e02..b33a787 --- a/src/position.cpp +++ b/src/position.cpp @@@ -42,48 -42,16 +42,10 @@@ namespace Zobrist Key enpassant[FILE_NB]; Key castling[CASTLING_RIGHT_NB]; Key side, noPawns; + Key inHand[PIECE_NB][SQUARE_NB]; + Key checks[COLOR_NB][CHECKS_NB]; } --namespace { -- - // min_attacker() is a helper function used by see_ge() to locate the least - // valuable attacker for the side to move, remove the attacker we just found - // from the bitboards and scan for new X-ray attacks behind it. - - template - PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers, - Bitboard& occupied, Bitboard& attackers) { - - Bitboard b = stmAttackers & byTypeBB[Pt]; - if (!b) - return min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); - - occupied ^= lsb(b); // Remove the attacker from occupied - - // Add any X-ray attack behind the just removed piece. For instance with - // rooks in a8 and a7 attacking a1, after removing a7 we add rook in a8. - // Note that new added attackers can be of any color. - if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN) - attackers |= attacks_bb(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]); - - if (Pt == ROOK || Pt == QUEEN) - attackers |= attacks_bb(to, occupied) & (byTypeBB[ROOK] | byTypeBB[QUEEN]); - - // X-ray may add already processed pieces because byTypeBB[] is constant: in - // the rook example, now attackers contains _again_ rook in a7, so remove it. - attackers &= occupied; - return Pt; - } - - template<> - PieceType min_attacker(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) { - return KING; // No need to update bitboards: it is the last cycle - } - - } // namespace - -const string PieceToChar(" PNBRQK pnbrqk"); - -constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, - B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; -} // namespace - /// operator<<(Position) returns an ASCII representation of the position @@@ -1679,259 -1014,112 +1641,286 @@@ bool Position::see_ge(Move m, Value thr assert(is_ok(m)); // Only deal with normal moves, assume others pass a simple see - if (type_of(m) != NORMAL) + if (type_of(m) != NORMAL && type_of(m) != DROP && type_of(m) != PIECE_PROMOTION) return VALUE_ZERO >= threshold; - Bitboard stmAttackers; Square from = from_sq(m), to = to_sq(m); - PieceType nextVictim = type_of(m) == DROP ? dropped_piece_type(m) : type_of(piece_on(from)); - Color us = type_of(m) == DROP ? sideToMove : color_of(piece_on(from)); - Color stm = ~us; // First consider opponent's move - Value balance; // Values of the pieces taken by us minus opponent's ones - - + // nCheck + if (check_counting() && color_of(moved_piece(m)) == sideToMove && gives_check(m)) + return true; + + // Extinction + if ( extinction_value() != VALUE_NONE + && piece_on(to) + && ( ( extinction_piece_types().find(type_of(piece_on(to))) != extinction_piece_types().end() + && pieceCount[piece_on(to)] == 1) + || ( extinction_piece_types().find(ALL_PIECES) != extinction_piece_types().end() + && count(~sideToMove) == 1))) + return extinction_value() < VALUE_ZERO; - // The opponent may be able to recapture so this is the best result - // we can hope for. - balance = PieceValue[MG][piece_on(to)] - threshold; - - if (balance < VALUE_ZERO) + int swap = PieceValue[MG][piece_on(to)] - threshold; + if (swap < 0) return false; - // Now assume the worst possible result: that the opponent can - // capture our piece for free. - balance -= PieceValue[MG][nextVictim]; - - // If it is enough (like in PxQ) then return immediately. Note that - // in case nextVictim == KING we always return here, this is ok - // if the given move is legal. - if (balance >= VALUE_ZERO) - swap = PieceValue[MG][piece_on(from)] - swap; ++ swap = PieceValue[MG][moved_piece(m)] - swap; + if (swap <= 0) return true; - // Find all attackers to the destination square, with the moving piece - // removed, but possibly an X-ray attacker added behind it. - Bitboard occupied = type_of(m) == DROP ? pieces() ^ to : pieces() ^ from ^ to; - Bitboard attackers = attackers_to(to, occupied) & occupied; - Bitboard occ = pieces() ^ from ^ to; - Color stm = color_of(piece_on(from)); ++ Bitboard occ = (type_of(m) != DROP ? pieces() ^ from : pieces()) ^ to; ++ Color stm = color_of(moved_piece(m)); + Bitboard attackers = attackers_to(to, occ); + Bitboard stmAttackers, bb; + int res = 1; + // Flying general rule + if (var->flyingGeneral) + { - if (attackers & pieces(us, KING)) - attackers |= attacks_bb(us, ROOK, to, occupied & ~pieces(ROOK)) & pieces(~us, KING); - if (attackers & pieces(~us, KING)) - attackers |= attacks_bb(~us, ROOK, to, occupied & ~pieces(ROOK)) & pieces(us, KING); ++ if (attackers & pieces(stm, KING)) ++ attackers |= attacks_bb(stm, ROOK, to, occ & ~pieces(ROOK)) & pieces(~stm, KING); ++ if (attackers & pieces(~stm, KING)) ++ attackers |= attacks_bb(~stm, ROOK, to, occ & ~pieces(ROOK)) & pieces(stm, KING); + } + while (true) { - stmAttackers = attackers & pieces(stm); + stm = ~stm; + attackers &= occ; + + // If stm has no more attackers then give up: stm loses + if (!(stmAttackers = attackers & pieces(stm))) + break; // Don't allow pinned pieces to attack (except the king) as long as - // any pinners are on their original square. - if (st->pinners[~stm] & occupied) + // there are pinners on their original square. + if (st->pinners[~stm] & occ) stmAttackers &= ~st->blockersForKing[stm]; if (!stmAttackers) break; + res ^= 1; + // Locate and remove the next least valuable attacker, and add to - // the bitboard 'attackers' the possibly X-ray attackers behind it. - nextVictim = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); + // the bitboard 'attackers' any X-ray attackers behind it. + if ((bb = stmAttackers & pieces(PAWN))) + { + if ((swap = PawnValueMg - swap) < res) + break; + + occ ^= lsb(bb); + attackers |= attacks_bb(to, occ) & pieces(BISHOP, QUEEN); + } + + else if ((bb = stmAttackers & pieces(KNIGHT))) + { + if ((swap = KnightValueMg - swap) < res) + break; - stm = ~stm; // Switch side to move + occ ^= lsb(bb); + } - // Negamax the balance with alpha = balance, beta = balance+1 and - // add nextVictim's value. - // - // (balance, balance+1) -> (-balance-1, -balance) - // - assert(balance < VALUE_ZERO); + else if ((bb = stmAttackers & pieces(BISHOP))) + { + if ((swap = BishopValueMg - swap) < res) + break; - balance = -balance - 1 - PieceValue[MG][nextVictim]; + occ ^= lsb(bb); + attackers |= attacks_bb(to, occ) & pieces(BISHOP, QUEEN); + } - // If balance is still non-negative after giving away nextVictim then we - // win. The only thing to be careful about it is that we should revert - // stm if we captured with the king when the opponent still has attackers. - if (balance >= VALUE_ZERO) + else if ((bb = stmAttackers & pieces(ROOK))) { - if (nextVictim == KING && (attackers & pieces(stm))) - stm = ~stm; - break; + if ((swap = RookValueMg - swap) < res) + break; + + occ ^= lsb(bb); + attackers |= attacks_bb(to, occ) & pieces(ROOK, QUEEN); + } + + else if ((bb = stmAttackers & pieces(QUEEN))) + { + if ((swap = QueenValueMg - swap) < res) + break; + + occ ^= lsb(bb); + attackers |= (attacks_bb(to, occ) & pieces(BISHOP, QUEEN)) + | (attacks_bb(to, occ) & pieces(ROOK , QUEEN)); + } + ++ // fairy pieces ++ // pick next piece without considering value ++ else if ((bb = stmAttackers & ~pieces(KING))) ++ { ++ if ((swap = PieceValue[MG][piece_on(lsb(bb))] - swap) < res) ++ break; ++ ++ occ ^= lsb(bb); + } - assert(nextVictim != KING); ++ + else // KING + // If we "capture" with the king but opponent still has attackers, + // reverse the result. + return (attackers & ~pieces(stm)) ? res ^ 1 : res; } - return us != stm; // We break the above loop when stm loses - } + return res; + } -/// Position::is_draw() tests whether the position is drawn by 50-move rule -/// or by repetition. It does not detect stalemates. +/// Position::is_optinal_game_end() tests whether the position may end the game by +/// 50-move rule, by repetition, or a variant rule that allows a player to claim a game result. + +bool Position::is_optional_game_end(Value& result, int ply) const { + + // n-move rule + if (n_move_rule() && st->rule50 > (2 * n_move_rule() - 1) && (!checkers() || MoveList(*this).size())) + { + result = VALUE_DRAW; + return true; + } + + // n-fold repetition + if (n_fold_rule()) + { + int end = captures_to_hand() ? st->pliesFromNull : std::min(st->rule50, st->pliesFromNull); + + if (end >= 4) + { + StateInfo* stp = st->previous->previous; + int cnt = 0; + bool perpetualThem = st->checkersBB && stp->checkersBB; + bool perpetualUs = st->previous->checkersBB && stp->previous->checkersBB; + + for (int i = 4; i <= end; i += 2) + { + stp = stp->previous->previous; + perpetualThem &= bool(stp->checkersBB); + + // Return a draw score if a position repeats once earlier but strictly + // after the root, or repeats twice before or at the root. + if ( stp->key == st->key + && ++cnt + 1 == (ply > i ? 2 : n_fold_rule())) + { + result = convert_mate_value( var->perpetualCheckIllegal && perpetualThem ? VALUE_MATE + : var->perpetualCheckIllegal && perpetualUs ? -VALUE_MATE + : var->nFoldValueAbsolute && sideToMove == BLACK ? -var->nFoldValue + : var->nFoldValue, ply); + return true; + } -bool Position::is_draw(int ply) const { + if (i + 1 <= end) + perpetualUs &= bool(stp->previous->checkersBB); + } + } + } - if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) + // counting rules + if ( counting_rule() + && st->countingLimit + && st->countingPly >= st->countingLimit + && (!checkers() || MoveList(*this).size())) + { + result = VALUE_DRAW; return true; + } - // Return a draw score if a position repeats once earlier but strictly - // after the root, or repeats twice before or at the root. - if (st->repetition && st->repetition < ply) + // sittuyin stalemate due to optional promotion (3.9 c.7) + if ( sittuyin_promotion() + && count(sideToMove) == 2 + && count(sideToMove) == 1 + && !checkers()) + { + bool promotionsOnly = true; + for (const auto& m : MoveList(*this)) + if (type_of(m) != PROMOTION) + { + promotionsOnly = false; + break; + } + if (promotionsOnly) + { + result = VALUE_DRAW; + return true; + } + } + + return false; +} + +/// Position::is_immediate_game_end() tests whether the position ends the game +/// immediately by a variant rule, i.e., there are no more legal moves. +/// It does not not detect stalemates. + +bool Position::is_immediate_game_end(Value& result, int ply) const { + + // bare king rule + if ( bare_king_value() != VALUE_NONE + && !bare_king_move() + && !(count(sideToMove) - count(sideToMove))) + { + result = bare_king_value(ply); + return true; + } + if ( bare_king_value() != VALUE_NONE + && bare_king_move() + && !(count(~sideToMove) - count(~sideToMove))) + { + result = -bare_king_value(ply); + return true; + } + // extinction + if (extinction_value() != VALUE_NONE) + { + for (PieceType pt : extinction_piece_types()) + if (!count(WHITE, pt) || !count(BLACK, pt)) + { + result = !count(sideToMove, pt) ? extinction_value(ply) : -extinction_value(ply); + return true; + } + } + // capture the flag + if ( capture_the_flag_piece() + && !flag_move() + && (capture_the_flag(~sideToMove) & pieces(~sideToMove, capture_the_flag_piece()))) + { + result = mated_in(ply); + return true; + } + if ( capture_the_flag_piece() + && flag_move() + && (capture_the_flag(sideToMove) & pieces(sideToMove, capture_the_flag_piece()))) + { + result = (capture_the_flag(~sideToMove) & pieces(~sideToMove, capture_the_flag_piece())) + && sideToMove == WHITE ? VALUE_DRAW : mate_in(ply); + return true; + } + // nCheck + if (check_counting() && checks_remaining(~sideToMove) == 0) + { + result = mated_in(ply); + return true; + } + // Connect-n + if (connect_n() > 0) + { + Bitboard b; + for (Direction d : {NORTH, NORTH_EAST, EAST, SOUTH_EAST}) + { + b = pieces(~sideToMove); + for (int i = 1; i < connect_n() && b; i++) + b &= shift(d, b); + if (b) + { + result = mated_in(ply); + return true; + } + } + } + // Tsume mode: Assume that side with king wins when not in check + if (!count(~sideToMove) && count(sideToMove) && !checkers() && Options["TsumeMode"]) + { + result = mate_in(ply); return true; + } return false; }