Exemplo n.º 1
0
def is_pin_see(fen: str, move: chess.Move) -> bool:
    aug = board.AugBoard(fen)

    if aug.piece_type_at(move.from_square) not in [
            chess.QUEEN,
            chess.ROOK,
            chess.BISHOP,
    ]:
        return False

    attackers_before_move = aug.attacking_pairs(aug.current_color)
    aug.push(move)
    if aug.is_checkmate():
        return False
    attackers_after_move = aug.attacking_pairs(aug.other_color)

    triples = []
    for attacker, attacked in attackers_after_move - attackers_before_move:
        capturers = aug.square_capturers(attacker)
        if capturers and aug.see(attacker, moves_without_stop=1) >= 0:
            continue

        without_attacked = board.AugBoard(aug.fen())
        without_attacked.remove_piece_at(attacked)

        for some_attacker, new_attacked in without_attacked.attacking_pairs(
                aug.other_color):
            if all((
                    some_attacker == attacker,
                    attacked
                    in chess.SquareSet.between(attacker, new_attacked),
                    without_attacked.piece_value_at(new_attacked) >
                    aug.piece_value_at(attacked),
            )):
                triples.append((attacker, attacked, new_attacked))

    for a, b, c in triples:
        # TODO: consider removing this condition and handling it another way since pawns can be also pinned
        if aug.piece_type_at(b) == chess.PAWN:
            continue

        # absolute pin
        if aug.piece_type_at(c) == chess.KING:
            return True

        # relative pin
        without_attacked = board.AugBoard(aug.fen())
        without_attacked.remove_piece_at(b)
        without_attacked.push(chess.Move.null())
        if without_attacked.see(c, attacker=a, moves_without_stop=1) > 0:
            return True

    return False
Exemplo n.º 2
0
def is_fork_simple(fen: str, move: chess.Move) -> bool:
    aug = board.AugBoard(fen)
    return (not aug.gives_checkmate(move) and len(
        aug.move_attacks(move,
                         min_value=aug.piece_value_at(move.from_square) + 1)
        | aug.move_attacks(move, min_value=3, defended=False)) >= 2
            and not aug.can_move_be_captured(move))
Exemplo n.º 3
0
def creates_material_gain_capture(fen: str, move: chess.Move) -> bool:
    aug = board.AugBoard(fen)

    if aug.gives_check(move):
        return False

    if aug.has_positive_see_capture():
        return False

    aug.push(move)
    aug.push(chess.Move.null())
    return aug.has_positive_see_capture()
Exemplo n.º 4
0
def is_sacrifice_see(fen: str, move: chess.Move) -> bool:
    """
    A move is a sacrifice when it loses material.

    NOTE: Wikipedia proposed a division between sham and real sacrifices:
    https://en.wikipedia.org/wiki/Sacrifice_(chess)#Types_of_sacrifice
    maybe something to consider for the future.
    """

    aug = board.AugBoard(fen)
    return aug.see(
        move.to_square, attacker=move.from_square, moves_without_stop=1) < 0
Exemplo n.º 5
0
def creates_mate_threat(fen: str, move: chess.Move) -> bool:
    aug = board.AugBoard(fen)

    if aug.gives_check(move):
        return False

    if aug.has_mate():
        return False

    aug.push(move)
    aug.push(chess.Move.null())
    return aug.has_mate()
Exemplo n.º 6
0
def get_mating_net_piece_type_counters(
        fen: str) -> Optional[Tuple[Counter, Counter]]:
    mating_net = get_mating_net(fen)

    if mating_net is None:
        return None

    maters, cutters = mating_net
    aug = board.AugBoard(fen)
    return (
        Counter(aug.piece_type_at(square) for square in maters),
        Counter(aug.piece_type_at(square) for square in cutters),
    )
Exemplo n.º 7
0
def is_arabian_mate_extended(fen: str, move: chess.Move):
    """
    Same as in Wikipedia definition except that King can be on any border square.
    """
    aug = board.AugBoard(fen)

    king = aug.other_color_king()

    if (chess.square_file(king) not in [0, 7]) and (
        chess.square_rank(king) not in [0, 7]
    ):
        return False

    return is_arabian_mate_extra_extended(fen, move)
Exemplo n.º 8
0
def is_fork_see(fen, move):
    aug = board.AugBoard(fen)
    if aug.gives_checkmate(move):
        return False

    attacked = aug.move_attacks(
        move, min_value=aug.piece_value_at(move.from_square) +
        1) | aug.move_attacks(move, min_value=3, defended=False)

    if len(attacked) < 2:
        return False

    aug.push(move)
    return (not aug.square_capturers(move.to_square)
            or aug.see(move.to_square, moves_without_stop=1) < 0)
Exemplo n.º 9
0
def get_mating_net(fen) -> Optional[Tuple[chess.SquareSet, chess.SquareSet]]:
    aug = board.AugBoard(fen)
    if not aug.is_checkmate():
        return None

    their_king = aug.current_color_king()
    maters = aug.attackers(aug.other_color, their_king)

    escape_squares = [
        square for square in aug.attacks(their_king)
        if aug.piece_at(square) is None
    ]
    cutters = chess.SquareSet()
    for escape_square in escape_squares:
        cutters.update(aug.attackers(aug.other_color, escape_square))
    cutters -= maters
    return maters, cutters
Exemplo n.º 10
0
def is_smothered_mate(fen, move):
    """
    According to Wikipedia:
    > In chess, a smothered mate is a checkmate delivered by a knight in which the mated king is unable to move
    > because he is surrounded (or smothered) by his own pieces.
    source: https://en.wikipedia.org/wiki/Smothered_mate
    """

    aug = board.AugBoard(fen)
    aug.push(move)
    if not aug.is_checkmate():
        return False
    their_king = aug.current_color_king()
    for square in aug.attacks(their_king):
        piece = aug.piece_at(square)
        if piece is None or piece.color != aug.current_color:
            return False
    return True
Exemplo n.º 11
0
def is_back_rank_mate(fen, move):
    """
    According to Wikipedia:
    > In chess, a back-rank checkmate (also known as the corridor mate) is a checkmate delivered by a rook or queen
    > along a back rank (that is, the row on which the pieces [not pawns] stand at the start of the game) in which
    > the mated king is unable to move up the board because the king is blocked by friendly pieces (usually pawns)
    > on the second rank.
    source: https://en.wikipedia.org/wiki/Back-rank_checkmate
    """

    aug = board.AugBoard(fen)
    their_back_rank = 7 if aug.other_color == chess.BLACK else 0

    their_king = aug.other_color_king()

    if any((
            their_king is None,
            chess.square_rank(their_king) != their_back_rank,
            chess.square_rank(move.to_square) != their_back_rank,
    )):
        return False

    aug.push(move)

    if any((
            not aug.is_checkmate(),
            aug.piece_type_at(move.to_square) not in [chess.QUEEN, chess.ROOK],
    )):
        return False

    num_friendly_blocking_pieces = 0
    for square in aug.attacks(their_king) - aug.attacks(move.to_square):
        if chess.square_rank(square) == their_back_rank:
            continue
        if bool(aug.attackers(aug.other_color, square)):
            continue
        piece = aug.piece_at(square)
        if piece is None or piece.color != aug.current_color:
            return False
        else:
            num_friendly_blocking_pieces += 1
    return num_friendly_blocking_pieces > 0
Exemplo n.º 12
0
def is_arabian_mate_classic(fen: str, move: chess.Move):
    """
    According to Wikipedia:
    > In the Arabian mate, the knight and the rook team up to trap the opposing king on a corner of the board.
    > The rook sits on a square adjacent to the king both to prevent escape along the diagonal and to deliver checkmate
    > while the knight sits two squares away diagonally from the king to prevent escape on the square next to the king
    > and to protect the rook.
    source: https://en.wikipedia.org/wiki/Checkmate_pattern#Arabian_mate
    """

    aug = board.AugBoard(fen)

    their_king = aug.other_color_king()

    if any(
        (
            their_king is None,
            (chess.BB_SQUARES[their_king] & chess.BB_CORNERS) == chess.BB_EMPTY,
            aug.piece_type_at(move.from_square) != chess.ROOK,
        )
    ):
        return False

    aug.push(move)

    if not aug.is_checkmate():
        return False

    patterns = {
        chess.A1: ([chess.A2, chess.B1], chess.C3),
        chess.H1: ([chess.H2, chess.G1], chess.F3),
        chess.A8: ([chess.A7, chess.B8], chess.C6),
        chess.H8: ([chess.H7, chess.G8], chess.F6),
    }

    rook_squares, knight_square = patterns[their_king]
    return move.to_square in rook_squares and aug.piece_at(
        knight_square
    ) == chess.Piece(chess.KNIGHT, aug.other_color)
Exemplo n.º 13
0
def is_arabian_mate_extra_extended(fen: str, move: chess.Move):
    """
    Same as in Wikipedia definition except that King can be on any square.
    """

    aug = board.AugBoard(fen)

    king = aug.other_color_king()

    if any((king is None, aug.piece_type_at(move.from_square) != chess.ROOK,)):
        return False

    aug.push(move)

    if not aug.is_checkmate():
        return False

    rook = move.to_square

    if chess.square_distance(king, rook) > 1 or king not in aug.attacks(rook):
        return False

    knights_defending_rook = [
        square
        for square in aug.attackers(aug.other_color, rook)
        if aug.piece_type_at(square) == chess.KNIGHT
    ]
    if not knights_defending_rook:
        return False

    escape_squares = [
        square for square in aug.attacks(king) if aug.piece_at(square) is None
    ]
    for escape_square in escape_squares:
        if not aug.attackers(aug.other_color, escape_square):
            return False

    return True
Exemplo n.º 14
0
 def __init__(self, fen):
     self.board = board.AugBoard(fen)
     self.moves = tuple(self.board.legal_moves)
     self.their_board = self.board.copy()
     self.their_board.push(chess.Move.null())
     self.their_moves = tuple(self.their_board.legal_moves)
Exemplo n.º 15
0
def is_skewer_see(fen: str, move: chess.Move) -> bool:
    aug = board.AugBoard(fen)

    if aug.piece_type_at(move.from_square) not in [
            chess.QUEEN,
            chess.ROOK,
            chess.BISHOP,
    ]:
        return False

    attackers_before_move = aug.attacking_pairs(aug.current_color)
    aug.push(move)
    if aug.is_checkmate():
        return False
    attackers_after_move = aug.attacking_pairs(aug.other_color)

    triples = []
    for attacker, attacked in attackers_after_move - attackers_before_move:

        if not any(True
                   for _ in aug.generate_legal_moves_from_square(attacked)):
            continue

        if aug.piece_value_at(attacker) > aug.piece_value_at(attacked):
            continue

        if (aug.square_capturers(attacker)
                and aug.see(attacker, moves_without_stop=1) >= 0):
            continue

        without_attacked = board.AugBoard(aug.fen())
        without_attacked.remove_piece_at(attacked)

        for some_attacker, new_attacked in without_attacked.attacking_pairs(
                aug.other_color):
            if aug.piece_value_at(new_attacked) < 3:
                continue

            if all((
                    some_attacker == attacker,
                    attacked
                    in chess.SquareSet.between(attacker, new_attacked),
                    without_attacked.piece_value_at(new_attacked) <
                    aug.piece_value_at(attacked),
            )):
                triples.append((attacker, attacked, new_attacked))

    for a, b, c in triples:
        found_escape = False

        for m in aug.generate_legal_moves_from_square(b):
            m_val = aug.piece_value_at(m.to_square)
            try:
                aug.push(m)
                if m_val - aug.see(c, attacker=a) >= 0:
                    found_escape = True
                    break
            finally:
                aug.pop()

        if not found_escape:
            return True

    return False
Exemplo n.º 16
0
def is_fork_simplest(fen, move):
    aug = board.AugBoard(fen)
    return (not aug.gives_checkmate(move) and len(
        aug.move_attacks(
            move, min_value=aug.piece_value_at(move.from_square) + 1)) >= 2
            and not aug.can_move_be_captured(move))
Exemplo n.º 17
0
def get_move_mating_net_piece_type_counters(
    fen: str, move: chess.Move
) -> Optional[Tuple[Set[chess.PieceType], Set[chess.PieceType]]]:
    aug = board.AugBoard(fen)
    aug.push(move)
    return get_mating_net_piece_type_counters(aug.fen())