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 Pt>
- PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers,
- Bitboard& occupied, Bitboard& attackers) {
-
- Bitboard b = stmAttackers & byTypeBB[Pt];
- if (!b)
- return min_attacker<PieceType(Pt + 1)>(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<BISHOP>(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]);
-
- if (Pt == ROOK || Pt == QUEEN)
- attackers |= attacks_bb<ROOK>(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<KING>(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
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<ALL_PIECES>(~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<PAWN>(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<BISHOP>(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<BISHOP>(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<ROOK>(to, occ) & pieces(ROOK, QUEEN);
+ }
+
+ else if ((bb = stmAttackers & pieces(QUEEN)))
+ {
+ if ((swap = QueenValueMg - swap) < res)
+ break;
+
+ occ ^= lsb(bb);
+ attackers |= (attacks_bb<BISHOP>(to, occ) & pieces(BISHOP, QUEEN))
+ | (attacks_bb<ROOK >(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<LEGAL>(*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<LEGAL>(*this).size()))
+ // counting rules
+ if ( counting_rule()
+ && st->countingLimit
+ && st->countingPly >= st->countingLimit
+ && (!checkers() || MoveList<LEGAL>(*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<ALL_PIECES>(sideToMove) == 2
+ && count<PAWN>(sideToMove) == 1
+ && !checkers())
+ {
+ bool promotionsOnly = true;
+ for (const auto& m : MoveList<LEGAL>(*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<ALL_PIECES>(sideToMove) - count<KING>(sideToMove)))
+ {
+ result = bare_king_value(ply);
+ return true;
+ }
+ if ( bare_king_value() != VALUE_NONE
+ && bare_king_move()
+ && !(count<ALL_PIECES>(~sideToMove) - count<KING>(~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<KING>(~sideToMove) && count<KING>(sideToMove) && !checkers() && Options["TsumeMode"])
+ {
+ result = mate_in(ply);
return true;
+ }
return false;
}