コード例 #1
0
def can_be_promotion(move: Move) -> bool:
    ktype = KomaType.get(move.koma)
    _, promotion_constrainer = MOVEGEN_FUNCTIONS[ktype]
    komatype_can_promote = (promotion_constrainer == constrain_unpromotable)
    return not move.is_drop and not komatype_can_promote and (
        move.end_sq.is_in_promotion_zone(move.side)
        or move.start_sq.is_in_promotion_zone(move.side))
コード例 #2
0
def _is_move_between_squares_valid(pos: Position, start_sq: Square,
                                   end_sq: Square) -> bool:
    koma = pos.get_koma(start_sq)
    ktype = KomaType.get(koma)
    board = pos.board
    side = koma.side()
    start_idx = MailboxBoard.sq_to_idx(start_sq)
    end_idx = MailboxBoard.sq_to_idx(end_sq)
    dest_generator, _ = MOVEGEN_FUNCTIONS[ktype]
    return end_idx in dest_generator(board, start_idx, side)
コード例 #3
0
 def unmake_move(self, move: Move) -> None:
     """Unplays/retracts a move from the board.
     """
     if move.is_pass():
         self.movenum -= 1
         return
     elif move.is_drop:
         self.set_koma(Koma.NONE, move.end_sq)
         self.inc_hand_koma(move.side, KomaType.get(move.koma))
         self.turn = self.turn.switch()
         self.movenum -= 1
         return
     else:
         if move.captured != Koma.NONE:
             self.dec_hand_koma(move.side,
                                KomaType.get(move.captured).unpromote())
         self.set_koma(move.captured, move.end_sq)
         self.set_koma(move.koma, move.start_sq)
         self.turn = self.turn.switch()
         self.movenum -= 1
         return
コード例 #4
0
def get_ambiguous_moves(pos: Position, move: Move) -> List[Move]:
    start_sq = move.start_sq
    if not _is_move_from_square_available(pos, start_sq):
        return []
    koma = pos.get_koma(start_sq)
    side = koma.side()
    ktype = KomaType.get(koma)
    return [
        mv for mv in generate_valid_moves(pos, side, ktype)
        if (mv.end_sq == move.end_sq) and (
            mv.start_sq != move.start_sq) and is_legal(mv, pos)
    ]
コード例 #5
0
def _disambiguate_japanese_move(
    pos: Position,
    move: Move,
    ambiguous_moves: Iterable[Move],
    aggressive_disambiguation: bool,
) -> str:
    origin_squares = set(
        (amb_move.start_sq for amb_move in ambiguous_moves
         if (aggressive_disambiguation)
         or rules.can_be_promotion(amb_move) == rules.can_be_promotion(move)))
    general_pieces = set((KomaType.GI, KomaType.KI, KomaType.TO, KomaType.NY,
                          KomaType.NK, KomaType.NG))
    side = move.koma.side()
    start_sq = move.start_sq
    end_sq = move.end_sq
    # sugu is special case
    if KomaType.get(move.koma) in general_pieces:
        if end_sq.is_immediately_forward_of(start_sq, side):
            return "直"
    if _is_leftmost(start_sq, origin_squares, side):
        return "左"
    elif _is_rightmost(start_sq, origin_squares, side):
        return "右"
    elif end_sq.is_same_row(start_sq):
        sqs = [sq for sq in origin_squares if end_sq.is_same_row(sq)]
        return _disambiguate_character(start_sq, sqs, side) + "寄"
    elif end_sq.is_forward_of(start_sq, side):
        sqs = []
        for sq in origin_squares:
            if end_sq.is_forward_of(sq, side) and not (
                    KomaType.get(move.koma) in general_pieces
                    and end_sq.is_immediately_forward_of(sq, side)):
                sqs.append(sq)
        return _disambiguate_character(start_sq, sqs, side) + "上"
    elif end_sq.is_backward_of(start_sq, side):
        sqs = [sq for sq in origin_squares if end_sq.is_backward_of(sq, side)]
        return _disambiguate_character(start_sq, sqs, side) + "引"
    raise ValueError(
        f"Disambiguation failed unexpectedly on move: {str(move)}")
コード例 #6
0
 def _parse_sfen_hands(self, sfen_hands: str) -> None:
     it_hands = re.findall(r"(\d*)([plnsgbrPLNSGBR])", sfen_hands)
     for ch_count, ch in it_hands:
         try:
             koma = KOMA_FROM_SFEN[ch]
         except KeyError as exc:
             raise ValueError(
                 f"SFEN contains unknown character '{ch}'") from exc
         ktype = KomaType.get(koma)
         target_hand = self.hand_sente if ch.isupper() else self.hand_gote
         count = int(ch_count) if ch_count else 1
         target_hand.set_komatype_count(ktype, count)
     return
コード例 #7
0
    def draw(self):
        """Draw complete board with komadai and pieces.
        """
        # Clear board display - could also keep board and just redraw pieces
        self.delete("all")
        position = self.position
        komadai_w = self.measurements.komadai_w
        coords_text_size = self.measurements.coords_text_size
        w_pad = self.measurements.w_pad
        x_sq = self.measurements.x_sq
        y_sq = self.measurements.y_sq

        north_side = (Side.SENTE
                      if self._is_inverted(Side.SENTE) else Side.GOTE)
        south_side = north_side.switch()
        north_hand = position.get_hand_of_side(north_side)
        south_hand = position.get_hand_of_side(south_side)

        self._draw_canvas_base_layer()
        # Draw board
        self.board_artist.draw_board(self)
        self._add_board_onclick_callbacks()
        # Draw board pieces
        for koma, sqset in position.get_koma_sets().items():
            for sq in sqset:
                col_idx, row_idx = self._sq_to_idxs(sq)
                ktype = KomaType.get(koma)
                invert = self._is_inverted(koma.side())
                if self.is_text():
                    text = str(KANJI_FROM_KTYPE[ktype])
                    self.board_artist.draw_text_koma(self, text, invert,
                                                     row_idx, col_idx)
                else:
                    img = self.get_koma_image(ktype, invert)
                    self.board_artist.draw_koma(self, img, row_idx, col_idx)

        # Draw komadai
        self.draw_komadai(w_pad + komadai_w / 2,
                          y_sq(0),
                          north_hand,
                          sente=north_side.is_sente(),
                          align="top")
        self.draw_komadai(x_sq(9) + 2 * coords_text_size + komadai_w / 2,
                          y_sq(9),
                          south_hand,
                          sente=south_side.is_sente(),
                          align="bottom")
        # set focus
        self.set_focus(self.highlighted_sq)
        self.board_artist.lift_click_layer(self)
        return
コード例 #8
0
 def make_move(self, move: Move) -> None:
     """Makes a move on the board.
     """
     if move.is_pass():
         # to account for game terminations or other passing moves
         self.movenum += 1
         return
     elif move.is_drop:
         self.dec_hand_koma(move.side, KomaType.get(move.koma))
         self.set_koma(move.koma, move.end_sq)
         self.turn = self.turn.switch()
         self.movenum += 1
         return
     else:
         self.set_koma(Koma.NONE, move.start_sq)
         if move.captured != Koma.NONE:
             self.inc_hand_koma(move.side,
                                KomaType.get(move.captured).unpromote())
         self.set_koma(
             move.koma.promote() if move.is_promotion else move.koma,
             move.end_sq)
         self.turn = self.turn.switch()
         self.movenum += 1
         return
コード例 #9
0
 def _draw_komadai_focus_tile(
     self,
     canvas: BoardCanvas,
     y_offset: float,
     ktype: KomaType,
 ) -> int:
     id_: int = canvas.create_image(
         self.x_anchor - (self.width / 5),
         self.y_anchor + y_offset,
         image="",
         anchor="center",
         tags=("komadai_focus", ktype.to_csa(),
               "sente" if self.is_sente else "gote"),
     )
     return id_
コード例 #10
0
def create_valid_moves_given_squares(pos: Position, start_sq: Square,
                                     end_sq: Square) -> List[Move]:
    if not _is_move_from_square_available(pos, start_sq):
        return []
    if not _is_move_between_squares_valid(pos, start_sq, end_sq):
        return []
    koma = pos.get_koma(start_sq)
    side = pos.turn
    if koma.side() != pos.turn:
        return []
    _, promotion_constrainer = MOVEGEN_FUNCTIONS[KomaType.get(koma)]
    return [
        pos.create_move(start_sq, end_sq, can_promote)
        for can_promote in promotion_constrainer(side, start_sq, end_sq)
    ]
コード例 #11
0
 def _draw_komadai_koma(
     self,
     canvas: BoardCanvas,
     y_offset: float,
     ktype: KomaType,
 ) -> int:
     artist = canvas.make_koma_artist(invert=False, komadai=True)
     id_: int = artist.draw_koma(
         canvas,
         self.x_anchor - (self.width / 5),
         self.y_anchor + y_offset,
         ktype=ktype,
         anchor="center",
         tags=("komadai_koma", ktype.to_csa(),
               "sente" if self.is_sente else "gote"),
     )
     return id_
コード例 #12
0
 def _attempt_move(self, sq: Square) -> Optional[bool]:
     """Check if a legal move can be made. Returns True and sends
     out the move if it is, False if not.
     Returns None if more information is needed (e.g. choice of promotion/nonpromotion.
     """
     mvlist: List[Move] = rules.create_legal_moves_given_squares(
         pos=self.position, start_sq=self.focused_sq, end_sq=sq)
     if not mvlist:
         return False
     elif len(mvlist) == 1:
         self._send_move(mvlist[0])
         return True
     elif len(mvlist) == 2:
         # There is a promotion and nonpromotion option.
         # More info needed, GUI prompts for input.
         koma = self.position.get_koma(self.focused_sq)
         self.board_canvas.prompt_promotion(sq, KomaType.get(koma))
         return None
     else:
         raise RuntimeError("Unexpected mvlist length in _attempt_move()")
コード例 #13
0
 def _set_state(self,
                key: str,
                sq: Square = Square.NONE,
                hand_ktype: KomaType = KomaType.NONE) -> None:
     """Set the state machine's state and take any actions needed.
     """
     if (key == "ready") or (key == "disabled"):
         self.focused_sq = Square.NONE
         self.focused_ktype = KomaType.NONE
         self.board_canvas.set_focus(Square.NONE)
     elif key == "hand":
         self.focused_sq = Square.HAND
         self.focused_ktype = hand_ktype
         self.board_canvas.set_focus(sq, hand_ktype)
     elif key == "board":
         self.focused_sq = sq
         self.focused_ktype = KomaType.get(self.position.get_koma(sq))
         self.board_canvas.set_focus(sq)
     # "wait_for_promotion" currently needs no action
     self.active_state = self.states[key]
     return
コード例 #14
0
    def prompt_promotion(self, sq: Square, ktype: KomaType) -> None:
        """Display the visual cues prompting user to choose promotion
        or non-promotion.
        """
        id_cover = self.board_artist.draw_promotion_cover(self)

        col_idx, row_idx = self._sq_to_idxs(sq)
        invert = self._is_inverted(self.position.turn)

        id_promoted = self.board_artist.draw_promotion_prompt_koma(
            self, ktype.promote(), invert, row_idx, col_idx)
        id_unpromoted = self.board_artist.draw_promotion_prompt_koma(
            self, ktype, invert, row_idx + 1, col_idx)
        callback = functools.partial(self._prompt_promotion_callback,
                                     sq=sq,
                                     ktype=ktype)
        self.tag_bind(id_promoted, "<Button-1>",
                      functools.partial(callback, is_promotion=True))
        self.tag_bind(id_unpromoted, "<Button-1>",
                      functools.partial(callback, is_promotion=False))
        self.tag_bind(id_cover, "<Button-1>",
                      functools.partial(callback, is_promotion=None))
        return
コード例 #15
0
 def write_koma(self, koma: Koma) -> str:
     return KANJI_NOTATION_FROM_KTYPE[KomaType.get(koma)]
コード例 #16
0
 def test_king_is_not_promoted(self):
     self.assertFalse(KomaType.get(Koma.OU).is_promoted())