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("<Button-1>", partial(self.lft, i, j))
bt.bind("<Button-2>", 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()