import tkinter as tk from tkinter import messagebox from tkinter import Button from functools import partial class Position: def __init__(self, row, col): self.row = row self.col = col def match(self,list_pos): for pos in list_pos: if pos.row == self.row and pos.col == self.col: return True return False class Piece: def __init__(self, color, board, position=None): self.color = color self.board = board self.has_moved = False self.position = position def __str__(self): pass class King(Piece): def __init__(self,color,board,position=None): super().__init__(color,board,position) self.piece_type = "king" def possible_moves(self): moves = [] offsets = [(1, 0), (0, 1), (-1, 0), (0, -1), (1, 1), (-1, 1), (1, -1), (-1, -1)] for dr, dc in offsets: new_pos = Position(self.position.row + dr, self.position.col + dc) if self.board.is_inside_board(new_pos) and (self.board.is_square_empty(new_pos) or self.board.is_enemy_piece(new_pos, self.color)): moves.append(new_pos) # Castling if not self.board.board[self.position.row][self.position.col].has_moved: # Check kingside castling if self.board.board[self.position.row][7] and not self.board.board[self.position.row][7].has_moved: if all(self.board.is_square_empty(Position(self.position.row, c)) for c in range(self.position.col + 1, 7)): moves.append(Position(self.position.row, self.position.col + 2)) # Check queenside castling if self.board.board[self.position.row][0] and not self.board.board[self.position.row][0].has_moved: if all(self.board.is_square_empty(Position(self.position.row, c)) for c in range(1, self.position.col)): moves.append(Position(self.position.row, self.position.col - 2)) return moves def move(self,end_pos): a = self.possible_moves() for pos in a: if pos.row == end_pos.row and pos.col == end_pos.col: return True return False def __str__(self): if self.color == "White": return "K" return "k" class Bishop(Piece): def __init__(self,color,board,position=None): super().__init__(color,board,position) self.piece_type = "bishop" def possible_moves(self): moves = [] directions = [(-1, -1), (-1, 1), (1, 1), (1, -1)] for dr, dc in directions: temp = self.position while True: new_pos = Position(temp.row + dr, temp.col + dc) temp = new_pos if self.board.is_inside_board(new_pos) == False: break if self.board.is_inside_board(new_pos) and (self.board.is_square_empty(new_pos) or self.board.is_enemy_piece(new_pos, self.color)): moves.append(new_pos) if self.board.is_enemy_piece(new_pos, self.color): break return moves def move(self,end_pos): a = self.possible_moves() for pos in a: if pos.row == end_pos.row and pos.col == end_pos.col: return True return False def __str__(self): if self.color == "White": return "B" return "b" class Pawn(Piece): def __init__(self,color,board,position=None): super().__init__(color,board,position) self.piece_type = "pawn" def possible_moves(self): moves = [] direction = 1 if self.color == "White" else -1 start_row = 1 if self.color == "White" else 6 # Moves for regular pawn advance temp = self.position count = 0 while count < 2: new_pos = Position(temp.row + direction, temp.col) temp = new_pos if self.board.is_inside_board(new_pos) and self.board.is_square_empty(new_pos): moves.append(new_pos) else: break count += 1 # Moves for capturing diagonally temp = self.position new_pos = Position(temp.row + direction, temp.col - 1) if self.board.is_inside_board(new_pos): if self.board.is_enemy_piece(new_pos, self.color): moves.append(new_pos) new_pos = Position(temp.row + direction, temp.col + 1) if self.board.is_inside_board(new_pos): if self.board.is_enemy_piece(new_pos, self.color): moves.append(new_pos) return moves def move(self,end_pos): a = self.possible_moves() for pos in a: if pos.row == end_pos.row and pos.col == end_pos.col: return True return False def __str__(self): if self.color == "White": return "P" return "p" class Rook(Piece): def __init__(self,color,board,positi super().__init__(color,board,position) self.piece_type = "rook" def possible_moves(self): moves = [] directi 0), (-1, 0), (0, 1), (0, -1)] for dr, dc in directions: temp = self.position while True: new_pos = Position(temp.row + dr, temp.col + dc) temp = new_pos if self.board.is_inside_board(new_pos) == False: break if self.board.is_inside_board(new_pos) and (self.board.is_square_empty(new_pos) or self.board.is_enemy_piece(new_pos, self.color)): moves.append(new_pos) if self.board.is_enemy_piece(new_pos, self.color): break return moves def move(self,end_pos): a = self.possible_moves() for pos in a: if pos.row == end_pos.row and pos.col == end_pos.col: return True return False def __str__(self): if self.color == "White": return "R" return "r" class Knight(Piece): def __init__(self,color,board,positi super().__init__(color,board,position) self.piece_type = "knight" def possible_moves(self): moves = [] ends = [(-2, 1), (-2, -1), (2, -1), (2, 1), (1, 2), (-1, 2), (1, -2), (-1, -2)] temp = self.position for er, ec in ends: new_pos = Position(temp.row + er, temp.col + ec) print(new_pos.row, new_pos.col) if self.board.is_inside_board(new_pos) and (self.board.is_square_empty(new_pos) or self.board.is_enemy_piece(new_pos, self.color)): moves.append(new_pos) return moves def move(self,end_pos): a = self.possible_moves() for pos in a: if pos.row == end_pos.row and pos.col == end_pos.col: return True return False def __str__(self): if self.color == "White": return "N" return "n" class Queen(Piece): def __init__(self,color,board,positi super().__init__(color,board,position) self.piece_type = "queen" def possible_moves(self): moves = [] directi -1), (-1, 1), (1, 1), (1, -1), (1, 0), (-1, 0), (0, 1), (0, -1)] for dr, dc in directions: temp = self.position while True: new_pos = Position(temp.row + dr, temp.col + dc) temp = new_pos if self.board.is_inside_board(new_pos): if self.board.is_square_empty(new_pos) or self.board.is_enemy_piece(new_pos, self.color): moves.append(new_pos) if not self.board.is_inside_board(new_pos): break if self.board.is_enemy_piece(new_pos, self.color) or self.board.is_inside_board(new_pos) == False: break return moves def move(self,end_pos): a = self.possible_moves() for pos in a: if pos.row == end_pos.row and pos.col == end_pos.col: return True return False def __str__(self): if self.color == "White": return "Q" return "q" class Board: def __init__(self): self.board = [[None for _ in range(8)] for _ in range(8)] #initialize the board self.butt for _ in range(8)] for _ in range(8)] self.left_click = [[0 for _ in range(8)] for _ in range(8)] self.right_click = [[0 for _ in range(8)] for _ in range(8)] for i in range(8): for j in range(8): bt = Button(master, text = f'{i} {j}', height = 5, width = 10) bt.bind("", partial(self.lft, i, j)) bt.bind("", partial(self.rgt, i, j)) self.buttons[i][j] = bt bt.grid(row = i, column = j) def lft(self, i, j): # global current_player for ii in range(8): for jj in range(8): self.left_click[ii][jj] = 0 # if self.board[i][j].color == current_player: self.left_click[i][j] = 1 def rgt(self, i, j): flag = False for ii in range(8): for jj in range(8): if self.left_click[ii][jj] == 1: flag = True if flag == True: for ii in range(8): for jj in range(8): self.right_click[ii][jj] = 0 self.right_click[i][j] = 1 def place_piece(self, piece, position): self.board[position.row][position.col] = piece self.buttons[position.row][position.col].config(text = f'{piece.piece_type}') piece.position = position def remove_piece(self, piece): self.board[piece.position.row][piece.position.col] = None self.buttons[piece.position.row][piece.position.col].config(text = '') def move_piece(self, start_pos, end_pos): piece = self.board[start_pos.row][start_pos.col] if piece: if piece.move(end_pos): #TODO (remove piece, place the piece at the end_pos and set has_moved to True) self.remove_piece(piece) self.place_piece(piece, end_pos) piece.has_moved = True piece.position = end_pos return True else: print("No piece at the starting position.") return False def is_square_empty(self, position): return self.board[position.row][position.col] is None def is_enemy_piece(self, position, color): if not self.is_square_empty(position): if self.board[position.row][position.col].color == color: return False return True return False def is_inside_board(self, position): if position.row > 7 or position.col > 7 or position.row < 0 or position.col < 0: return False return True def print_board(self): print(" | a b c d e f g h") print("------------------") for i, row in enumerate(self.board): row_str = str(i) + "| " for piece in row: if piece: row_str += f"{piece} " else: row_str += ". " print(row_str) print("\n") class ChessSet: def __init__(self): self.board = Board() self.setup_board() def setup_board(self): # Place white pieces self.board.place_piece(Rook("White",self.board), Position(0, 0)) self.board.place_piece(Knight("White",self.board), Position(0, 1)) self.board.place_piece(Bishop("White",self.board), Position(0, 2)) self.board.place_piece(Queen("White",self.board), Position(0, 3)) self.board.place_piece(King("White",self.board), Position(0, 4)) self.board.place_piece(Bishop("White",self.board), Position(0, 5)) self.board.place_piece(Knight("White",self.board), Position(0, 6)) self.board.place_piece(Rook("White",self.board), Position(0, 7)) for col in range(8): self.board.place_piece(Pawn("White", self.board), Position(1, col)) # Place black pieces self.board.place_piece(Rook("Black",self.board), Position(7, 0)) self.board.place_piece(Knight("Black",self.board), Position(7, 1)) self.board.place_piece(Bishop("Black",self.board), Position(7, 2)) self.board.place_piece(Queen("Black",self.board), Position(7, 3)) self.board.place_piece(King("Black",self.board), Position(7, 4)) self.board.place_piece(Bishop("Black",self.board), Position(7, 5)) self.board.place_piece(Knight("Black",self.board), Position(7, 6)) self.board.place_piece(Rook("Black",self.board), Position(7, 7)) for col in range(8): self.board.place_piece(Pawn("Black", self.board), Position(6, col)) def print_board(self): self.board.print_board() class Chess: def __init__(self): self.chess_set = ChessSet() def start_game(self): # print("Welcome to Chess!\n") current_player = "White" bt = Button(master, text = f"{current_player}'s turn:") bt.grid(row = 4, column = 8) while True: self.chess_set.print_board() if self.is_checkmate(current_player): # print(current_player + " lost") messagebox.showinfo("finish", f'{current_player} lost') break # print(f"\n{current_player}'s turn:") bt.config(text = f"{current_player}'s turn:") while True: # start_pos = input("Enter the position of the piece you want to move (e.g., 'a2'): ") end_pos = None while end_pos == None: for i in range(8): for j in range(8): if self.chess_set.board.right_click[i][j] == 1: end_pos = Position(i, j) for i in range(8): for j in range(8): if self.chess_set.board.left_click[i][j] == 1: start_pos = Position(i, j) # start_pos = Position(1,0) # end_pos = Position(2,0) print("salam") # for i in self.chess_set.board.board[start_pos.row][start_pos.col].possible_moves(): # print(i.col, i.row) if self.chess_set.board.board[start_pos.row][start_pos.col]: if not self.chess_set.board.board[start_pos.row][start_pos.col].move(end_pos): messagebox.showerror("wrong move", "cant move") continue self.chess_set.board.move_piece(start_pos, end_pos) if self.is_check(current_player): self.chess_set.board.move_piece(end_pos, start_pos) continue current_player = "Black" if current_player == "White" else "White" break break #TODO - check if the input is according to the expected format #TODO - move the piece if it is possible, otherwise notify the user to select other moves #TODO - print the board #TODO - check if the king is in checkmate (much simpler than real-world chess) #TODO - check the king is in check #TODO - switch the turns def is_valid_input(self, start_pos, end_pos): #TODO - check each of the inputs have length of two elements and the first letter is an alphabet and the second one is a digit if len(start_pos) != 2 or len(end_pos) != 2: return False c = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] if not (start_pos[0] in c) or not (end_pos[0] in c): return False c = ['0', '1', '2', '3', '4', '5', '6', '7'] if not (start_pos[1] in c) or not (end_pos[1] in c): return False return True def is_check(self, current_player): #TODO - find current_player's king on the board, check if the king is in check enemy = "Black" if current_player == "White" else "White" for i in range(8): for j in range(8): piece = self.chess_set.board.board[i][j] if piece: if piece.color == enemy: if piece.possible_moves(): for pos in piece.possible_moves(): if self.chess_set.board.board[pos.row][pos.col]: if self.chess_set.board.board[pos.row][pos.col].piece_type == "king": return True return False def is_checkmate(self, current_player): # For simplicity, we consider losing the king as checkmate enemy = "Black" if current_player == "White" else "White" for i in range(8): for j in range(8): piece = self.chess_set.board.board[i][j] if piece and piece.piece_type == "king" and piece.color == enemy: return False return True def from_algebraic(self,algebraic_notation): col = ord(algebraic_notation[0]) - ord('a') row = int(algebraic_notation[1]) return Position(row,col) if __name__ == "__main__": master = tk.Tk() master.geometry("850x680") chess_game = Chess() chess_game.start_game() master.mainloop()