import sys

# Internal representation: Upper case = White, Lower case = Black
# Empty = '.'
WHITE = "white"
BLACK = "black"

UNICODE = {
    'P': '♙', 'R': '♖', 'N': '♘', 'B': '♗', 'Q': '♕', 'K': '♔',
    'p': '♟', 'r': '♜', 'n': '♞', 'b': '♝', 'q': '♛', 'k': '♚',
    '.': '·'
}

class Chess:
    def __init__(self):
        self.board = [
            ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
            ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
            ['.', '.', '.', '.', '.', '.', '.', '.'],
            ['.', '.', '.', '.', '.', '.', '.', '.'],
            ['.', '.', '.', '.', '.', '.', '.', '.'],
            ['.', '.', '.', '.', '.', '.', '.', '.'],
            ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
            ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']
        ]
        self.turn = WHITE
        self.castling_rights = {
            WHITE: {'k': True, 'q': True},
            BLACK: {'k': True, 'q': True}
        }
        self.en_passant_target = None # (row, col)
        self.game_over = False
        self.result = None

    def print_board(self):
        print("\n  a b c d e f g h")
        for r in range(8):
            print(f"{8-r} ", end="")
            for c in range(8):
                print(UNICODE[self.board[r][c]] + " ", end="")
            print(f"{8-r}")
        print("  a b c d e f g h\n")

    def get_color(self, piece):
        if piece == '.': return None
        return WHITE if piece.isupper() else BLACK

    def is_on_board(self, r, c):
        return 0 <= r < 8 and 0 <= c < 8

    def parse_move(self, s):
        try:
            parts = s.split()
            if len(parts) != 2: return None
            start, end = parts
            s_col, s_row = ord(start[0]) - ord('a'), int(start[1]) - 1
            e_col, e_row = ord(end[0]) - ord('a'), int(end[1]) - 1
            # Convert algebraic to internal (row, col)
            # a1 is (7, 0), a8 is (0, 0)
            s_r, s_c = 8 - s_row, s_col
            e_r, e_c = 8 - e_row, e_col
            if self.is_on_board(s_r, s_c) and self.is_on_board(e_r, e_c):
                return (s_r, s_c), (e_r, e_c)
        except:
            pass
        return None

    def get_pseudo_legal_moves(self, color):
        moves = []
        for r in range(8):
            for c in range(8):
                piece = self.board[r][c]
                if self.get_color(piece) == color:
                    piece_type = piece.upper()
                    # Pawn
                    if piece_type == 'P':
                        direction = -1 if color == WHITE else 1
                        start_row = 6 if color == WHITE else 1
                        
                        # Single push
                        nr, nc = r + direction, c
                        if self.is_on_board(nr, nc) and self.board[nr][nc] == '.':
                            moves.append(((r, c), (nr, nc)))
                            # Double push
                            if r == start_row:
                                nr2, nc2 = r + 2 * direction, c
                                if self.board[nr2][nc2] == '.':
                                    moves.append(((r, c), (nr2, nc2)))
                        
                        # Captures
                        for dc in [-1, 1]:
                            nr, nc = r + direction, c + dc
                            if self.is_on_board(nr, nc):
                                target = self.board[nr][nc]
                                if target != '.' and self.get_color(target) != color:
                                    moves.append(((r, c), (nr, nc)))
                                # En Passant
                                if self.en_passant_target == (nr, nc):
                                    moves.append(((r, c), (nr, nc)))

                    # Knight
                    elif piece_type == 'N':
                        offsets = [(-2, -1), (-2, 1), (-1, -2), (-1, 1), (1, -2), (1, 2), (2, -1), (2, 1)]
                        for dr, dc in offsets:
                            nr, nc = r + dr, c + dc
                            if self.is_on_board(nr, nc):
                                target = self.board[nr][nc]
                                if target == '.' or self.get_color(target) != color:
                                    moves.append(((r, c), (nr, nc)))

                    # Bishop, Rook, Queen (Sliding)
                    elif piece_type in ['B', 'R', 'Q']:
                        directions = []
                        if piece_type in ['B', 'Q']: directions += [(-1, -1), (-1, 1), (1, -1), (1, 1)]
                        if piece_type in ['R', 'Q']: directions += [(-1, 0), (1, 0), (0, -1), (0, 1)]
                        
                        for dr, dc in directions:
                            nr, nc = r + dr, c + dc
                            while self.is_on_board(nr, nc):
                                target = self.board[nr][nc]
                                if target == '.':
                                    moves.append(((r, c), (nr, nc)))
                                else:
                                    if self.get_color(target) != color:
                                        moves.append(((r, c), (nr, nc)))
                                    break
                                nr, nc = nr + dr, nc + dc

                    # King
                    elif piece_type == 'K':
                        offsets = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
                        for dr, dc in offsets:
                            nr, nc = r + dr, c + dc
                            if self.is_on_board(nr, nc):
                                target = self.board[nr][nc]
                                if target == '.' or self.get_color(target) != color:
                                    moves.append(((r, c), (nr, nc)))
                        
                        # Castling
                        if color == WHITE and r == 7 and c == 4:
                            if self.castling_rights[WHITE]['k'] and self.board[7][5] == '.' and self.board[7][6] == '.':
                                moves.append(((7, 4), (7, 6))) # Kingside
                            if self.castling_rights[WHITE]['q'] and self.board[7][3] == '.' and self.board[7][2] == '.' and self.board[7][1] == '.':
                                moves.append(((7, 4), (7, 2))) # Queenside
                        elif color == BLACK and r == 0 and c == 4:
                            if self.castling_rights[BLACK]['k'] and self.board[0][5] == '.' and self.board[0][6] == '.':
                                moves.append(((0, 4), (0, 6)))
                            if self.castling_rights[BLACK]['q'] and self.board[0][3] == '.' and self.board[0][2] == '.' and self.board[0][1] == '.':
                                moves.append(((0, 4), (0, 2)))
        return moves

    def is_square_attacked(self, r, c, attacker_color):
        for tr in range(8):
            for tc in range(8):
                piece = self.board[tr][tc]
                if self.get_color(piece) == attacker_color:
                    p_type = piece.upper()
                    
                    if p_type == 'P':
                        direction = -1 if attacker_color == WHITE else 1
                        if (tr + direction == r) and (abs(tc - c) == 1):
                            return True
                    
                    elif p_type == 'N':
                        if (abs(tr - r) * abs(tc - c) == 2):
                            return True
                            
                    elif p_type in ['B', 'R', 'Q']:
                        directions = []
                        if p_type in ['B', 'Q']: directions += [(-1, -1), (-1, 1), (1, -1), (1, 1)]
                        if p_type in ['R', 'Q']: directions += [(-1, 0), (1, 0), (0, -1), (0, 1)]
                        
                        for dr, dc in directions:
                            cr, cc = tr + dr, tc + dc
                            while self.is_on_board(cr, cc):
                                if cr == r and cc == c: return True
                                if self.board[cr][cc] != '.': break
                                cr, cc = cr + dr, cc + dc
                                
                    elif p_type == 'K':
                        if max(abs(tr - r), abs(tc - c)) == 1:
                            return True
        return False

    def get_legal_moves(self, color):
        pseudo = self.get_pseudo_legal_moves(color)
        legal = []
        king_pos = None
        for r in range(8):
            for c in range(8):
                if self.board[r][c] == ('K' if color == WHITE else 'k'):
                    king_pos = (r, c)
                    break
        
        for start, end in pseudo:
            piece = self.board[start[0]][start[1]]
            target = self.board[end[0]][end[1]]
            
            en_passant_captured = None
            if piece.upper() == 'P' and end == self.en_passant_target:
                ep_dir = -1 if color == WHITE else 1
                en_passant_captured = (end[0] - ep_dir, end[1])
                self.board[en_passant_captured[0]][en_passant_captured[1]] = '.'

            self.board[end[0]][end[1]] = piece
            self.board[start[0]][start[1]] = '.'
            
            is_castling = (piece.upper() == 'K' and abs(start[1] - end[1]) == 2)
            valid = True
            if is_castling:
                step = 1 if end[1] > start[1] else -1
                for c_check in range(start[1], end[1] + step, step):
                    if self.is_square_attacked(start[0], c_check, WHITE if color == BLACK else BLACK):
                        valid = False
                        break
            else:
                curr_king_pos = end if piece.upper() == 'K' else king_pos
                if self.is_square_attacked(curr_king_pos[0], curr_king_pos[1], WHITE if color == BLACK else BLACK):
                    valid = False
            
            if valid:
                legal.append((start, end))
            
            self.board[start[0]][start[1]] = piece
            self.board[end[0]][end[1]] = target
            if en_passant_captured:
                self.board[en_passant_captured[0]][en_passant_captured[1]] = 'P' if color == BLACK else 'p'

        return legal

    def make_move(self, start, end):
        sr, sc = start
        er, ec = end
        piece = self.board[sr][sc]
        color = self.get_color(piece)
        p_type = piece.upper()

        if p_type == 'K':
            self.castling_rights[color]['k'] = False
            self.castling_rights[color]['q'] = False
        elif p_type == 'R':
            if sc == 0: self.castling_rights[color]['q'] = False
            if sc == 7: self.castling_rights[color]['k'] = False
        
        captured = self.board[er][ec]
        if captured != '.':
            c_color = self.get_color(captured)
            if c_color != color:
                if captured.upper() == 'R':
                    if er == 0 and ec == 0: self.castling_rights[BLACK]['q'] = False
                    if er == 0 and ec == 7: self.castling_rights[BLACK]['k'] = False
                    if er == 7 and ec == 0: self.castling_rights[WHITE]['q'] = False
                    if er == 7 and ec == 7: self.castling_rights[WHITE]['k'] = False

        self.en_passant_target = None
        if p_type == 'P':
            if abs(sr - er) == 2:
                self.en_passant_target = ((sr + er) // 2, sc)
            elif (er, ec) == self.en_passant_target:
                dir = -1 if color == WHITE else 1
                self.board[er + dir][ec] = '.'

        if p_type == 'K' and abs(sc - ec) == 2:
            if ec > sc:
                self.board[sr][5] = self.board[sr][7]
                self.board[sr][7] = '.'
            else:
                self.board[sr][3] = self.board[sr][0]
                self.board[sr][0] = '.'

        self.board[er][ec] = piece
        self.board[sr][sc] = '.'

        if p_type == 'P':
            if (color == WHITE and er == 0) or (color == BLACK and er == 7):
                self.board[er][ec] = 'Q' if color == WHITE else 'q'

        self.turn = BLACK if self.turn == WHITE else WHITE

    def is_checkmate(self, color):
        if not self.is_in_check(color): return False
        return len(self.get_legal_moves(color)) == 0

    def is_stalemate(self, color):
        if self.is_in_check(color): return False
        return len(self.get_legal_moves(color)) == 0

    def is_in_check(self, color):
        for r in range(8):
            for c in range(8):
                if self.board[r][c] == ('K' if color == WHITE else 'k'):
                    return self.is_square_attacked(r, c, WHITE if color == BLACK else BLACK)
        return False

    def run(self):
        while not self.game_over:
            self.print_board()
            
            if self.is_checkmate(self.turn):
                print(f"Checkmate! {WHITE if self.turn == BLACK else BLACK} wins.")
                return
            if self.is_stalemate(self.turn):
                print("Stalemate! Draw.")
                return
            
            if self.is_in_check(self.turn):
                print(f"{self.turn.capitalize()} is in check!")

            move_str = input(f"{self.turn.capitalize()}'s move (e.g., e2 e4): ")
            coords = self.parse_move(move_str)
            if not coords:
                print("Invalid format. Use 'e2 e4'.")
                continue
            
            start, end = coords
            legal_moves = self.get_legal_moves(self.turn)
            if (start, end) in legal_moves:
                self.make_move(start, end)
            else:
                print("Illegal move.")

if __name__ == "__main__":
    game = Chess()
    game.run()
