From 6016153ee1b54138583e2f9cc533ad1361de57e6 Mon Sep 17 00:00:00 2001 From: HarshilPatel007 Date: Tue, 20 Feb 2024 14:41:44 +0530 Subject: [PATCH 1/9] feat(move pieces): DONE - add ability to move pieces on the chessboard - add pawn promotion --- README.md | 2 + chessboard.py | 95 +++++++++++++++++++++++++++---------- pieces.py => chesspieces.py | 10 +++- main.py | 8 ++-- movemanager.py | 82 ++++++++++++++++++++++++++++++++ vars.py | 1 + 6 files changed, 166 insertions(+), 32 deletions(-) rename pieces.py => chesspieces.py (90%) create mode 100644 movemanager.py diff --git a/README.md b/README.md index d9855ef..4588cdb 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ # YACS - Yet Another Chess Software + +> warning : currently in development. diff --git a/chessboard.py b/chessboard.py index 4f746cc..53b2ba3 100644 --- a/chessboard.py +++ b/chessboard.py @@ -1,16 +1,19 @@ import random -import chess as chess -import pieces as ChessPieces -import vars as vars +import chess from PySide6 import QtCore, QtGui, QtSvg, QtSvgWidgets, QtWidgets +import vars +from chesspieces import ChessPieces +from movemanager import MoveManager + class ChessBoard: def __init__(self): - self.fischer_random = True + self.fischer_random = False self.board = chess.Board(chess960=self.fischer_random) self.is_board_flipped = False + self.move_manager = MoveManager(self) def set_chess960_board(self): self.board.set_chess960_pos(random.randint(1, 959)) @@ -27,6 +30,40 @@ def get_square_coordinates(self, square): row = 7 - chess.square_rank(square) return col, row, col * vars.SQUARE_SIZE, row * vars.SQUARE_SIZE + def get_selected_square_number(self, event): + """ + return the selected square number + """ + pos = event.position().toPoint() + mapped_pos = self.mapToScene(pos) + col = int(mapped_pos.x() / vars.SQUARE_SIZE) + row = int(mapped_pos.y() / vars.SQUARE_SIZE) + if self.is_board_flipped: + return chess.square(7 - col, row) + return chess.square(col, 7 - row) + + +class ChessBoardEvents: + def __init__(self, chessboard): + self.chessboard = chessboard + + def mousePress(self, event): + if event.buttons() == QtCore.Qt.LeftButton: + square_number = self.chessboard.get_selected_square_number(event) + + if self.chessboard.move_manager.selected_square is None: + self.chessboard.move_manager.selected_square = square_number + else: + if square_number == self.chessboard.move_manager.selected_square: + self.chessboard.move_manager.selected_square = None + return + + self.chessboard.move_manager.move_piece(square_number) + if self.chessboard.move_manager.is_piece_moved is True: + self.chessboard.move_manager.selected_square = None + self.chessboard.chess_pieces.delete_pieces() + self.chessboard.chess_pieces.draw_pieces() + class DrawChessBoard(QtWidgets.QGraphicsView, ChessBoard): @@ -34,13 +71,16 @@ def __init__(self): super().__init__() self.scene = QtWidgets.QGraphicsScene() self.setScene(self.scene) - self.chess_pieces = ChessPieces.ChessPieces(self, self.is_board_flipped, self.scene, "cardinal") + self.chess_pieces = ChessPieces( + self, self.is_board_flipped, self.scene, "cardinal" + ) self.chess_pieces.load_chess_piece_images() self.show_labels = True + self.events = ChessBoardEvents(self) def draw_squares(self): """ - draws a squares forming a chessboard + draws squares forming a chessboard """ for square in chess.SQUARES: col, row, x, y = self.get_square_coordinates(square) @@ -58,7 +98,7 @@ def draw_squares(self): def draw_labels(self): """ - draws rank and file label. a-h,1-8 + draws rank (1-8) & file (a-h) label """ for square in chess.SQUARES: col, row, x, y = self.get_square_coordinates(square) @@ -76,28 +116,31 @@ def draw_labels(self): else vars.THEME_COLORS["dark_square"] ) - if self.show_labels: - # Add label for the first set of columns (a-h) - if row == 7: - if self.is_board_flipped: - label = self.scene.addText(f'{chr(ord("h")-col)}') - else: - label = self.scene.addText(f'{chr(ord("a")+col)}') - label.setDefaultTextColor(QtGui.QColor(label_color)) - label.setPos(col_label_x, col_label_y) - - # Add label for the first set of rows (1-8) - if col == 0: - if self.is_board_flipped: - label = self.scene.addText(f"{row+1}") - else: - label = self.scene.addText(f"{8-row}") - label.setDefaultTextColor(QtGui.QColor(label_color)) - label.setPos(row_label_x, row_label_y) + # Add label for the first set of columns (a-h) + if row == 7: + if self.is_board_flipped: + label = self.scene.addText(f'{chr(ord("h")-col)}') + else: + label = self.scene.addText(f'{chr(ord("a")+col)}') + label.setDefaultTextColor(QtGui.QColor(label_color)) + label.setPos(col_label_x, col_label_y) + + # Add label for the first set of rows (1-8) + if col == 0: + if self.is_board_flipped: + label = self.scene.addText(f"{row+1}") + else: + label = self.scene.addText(f"{8-row}") + label.setDefaultTextColor(QtGui.QColor(label_color)) + label.setPos(row_label_x, row_label_y) def draw_chessboard(self): if self.fischer_random: self.set_chess960_board() self.draw_squares() - self.draw_labels() + if self.show_labels: + self.draw_labels() self.chess_pieces.draw_pieces() + + def mousePressEvent(self, event): + self.events.mousePress(event) diff --git a/pieces.py b/chesspieces.py similarity index 90% rename from pieces.py rename to chesspieces.py index 38e0c37..a5ee890 100644 --- a/pieces.py +++ b/chesspieces.py @@ -1,5 +1,5 @@ -import vars as vars -import chess as chess +import vars +import chess from PySide6 import QtCore, QtGui, QtSvg, QtSvgWidgets, QtWidgets class ChessPieces: @@ -51,3 +51,9 @@ def draw_pieces(self): self.scene.addItem(piece_item) square += 1 + + def delete_pieces(self): + items = self.scene.items() + for item in items: + if isinstance(item, QtWidgets.QGraphicsPixmapItem): + self.scene.removeItem(item) diff --git a/main.py b/main.py index 87b533c..2a7991e 100644 --- a/main.py +++ b/main.py @@ -1,16 +1,16 @@ -import chessboard as cb +from chessboard import DrawChessBoard import sys from PySide6 import QtWidgets -import vars as vars +import vars class ApplicationWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() - self.__version__ = "0.1-alpha" + self.__version__ = vars.VERSION self.setWindowTitle(f"YACS - Yet Another Chess Software ({self.__version__})") - self.chess_board = cb.DrawChessBoard() + self.chess_board = DrawChessBoard() self.setCentralWidget(self.chess_board) self.chess_board.draw_chessboard() self.chess_board.setFixedSize(vars.SQUARE_SIZE * 8.5, vars.SQUARE_SIZE * 8.5) diff --git a/movemanager.py b/movemanager.py new file mode 100644 index 0000000..da318e6 --- /dev/null +++ b/movemanager.py @@ -0,0 +1,82 @@ +import chess +from PySide6 import QtWidgets + + +class MoveManager: + + def __init__(self, chessboard): + self.chessboard = chessboard + self.selected_square = None + self.is_piece_moved = False + + def move_piece(self, target_square): + if self.selected_square is not None: + for move in self.chessboard.board.legal_moves: + if ( + move.from_square == self.selected_square + and move.to_square == target_square + ): + if self._is_pawn_promotion(target_square): + self._show_pawn_promotion_dialog(move) + + self.chessboard.board.push(move) + self.is_piece_moved = True + break + + def _is_pawn_promotion(self, target_square): + """ + check if the pawn reached at the last rank or not + """ + return self.chessboard.board.piece_type_at( + self.selected_square + ) == chess.PAWN and ( + ( + self.chessboard.board.turn == chess.WHITE + and chess.square_rank(target_square) == 7 + ) + or ( + self.chessboard.board.turn == chess.BLACK + and chess.square_rank(target_square) == 0 + ) + ) + + def _show_pawn_promotion_dialog(self, move): + pawn_promotion = PawnPromotion(self.chessboard) + pawn_promotion.pawn_promotion_dialog(move) + + +class PawnPromotion: + def __init__(self, chessboard): + self.chessboard = chessboard + + def pawn_promotion_dialog(self, move): + piece_options = ["Queen", "Rook", "Knight", "Bishop"] + + dialog = QtWidgets.QDialog(self.chessboard) + dialog.setModal(True) + dialog.setWindowTitle("Promote Pawn") + dialog.setFixedWidth(300) + + layout = QtWidgets.QVBoxLayout(dialog) + dialog.setLayout(layout) + + # Create a button for each piece option + for piece in piece_options: + button = QtWidgets.QPushButton(piece) + button.clicked.connect( + lambda move=move, piece=piece: self.promote_pawn(dialog, move, piece) + ) + layout.addWidget(button) + + dialog.exec() + + def promote_pawn(self, dialog, move, piece): + piece_map = { + "Queen": chess.QUEEN, + "Rook": chess.ROOK, + "Knight": chess.KNIGHT, + "Bishop": chess.BISHOP, + } + move.promotion = piece_map[piece] + + dialog.accept() # Close the dialog after promoting the pawn diff --git a/vars.py b/vars.py index 4f14755..54bd79d 100644 --- a/vars.py +++ b/vars.py @@ -1,3 +1,4 @@ +VERSION = "0.2 Alpha" SQUARE_SIZE = 70 THEME_COLORS = { "dark_square": "#769656", From b3d730e041b9b2e1c23214c1e8976073c017a854 Mon Sep 17 00:00:00 2001 From: HarshilPatel007 Date: Wed, 21 Feb 2024 17:33:15 +0530 Subject: [PATCH 2/9] feat(update only source and destination square): WIP --- .gitignore | 2 ++ chessboard.py | 48 +++++++++++++++++++++++++++++++++++++++++++++--- chesspieces.py | 36 +++++++++++++++++++++++++++++++++++- movemanager.py | 10 ++++++++-- 4 files changed, 90 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 61dd7a1..2fd7334 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ __pycache__/ !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json + +test.py diff --git a/chessboard.py b/chessboard.py index 53b2ba3..eee0533 100644 --- a/chessboard.py +++ b/chessboard.py @@ -42,6 +42,37 @@ def get_selected_square_number(self, event): return chess.square(7 - col, row) return chess.square(col, 7 - row) + def get_source_square_from_move(self, move): + """ + returns a square where piece is moved from + """ + if self.is_board_flipped: + return ( + 7 - chess.square_file(move.from_square), + chess.square_rank(move.from_square), + ) + return chess.square_file(move.from_square), 7 - chess.square_rank( + move.from_square + ) + + def get_destination_square_from_move(self, move): + """ + returns a square where piece is moved to + """ + if self.is_board_flipped: + return ( + 7 - chess.square_file(move.to_square), + chess.square_rank(move.to_square), + ) + return chess.square_file(move.to_square), 7 - chess.square_rank(move.to_square) + + def get_selected_piece_color_and_name(self, selected_square): + piece = self.board.piece_at(selected_square) + if piece is not None: + piece_color = "w" if piece.color == chess.WHITE else "b" + piece_name = piece.symbol().upper() + return piece_color, piece_name + class ChessBoardEvents: def __init__(self, chessboard): @@ -50,7 +81,6 @@ def __init__(self, chessboard): def mousePress(self, event): if event.buttons() == QtCore.Qt.LeftButton: square_number = self.chessboard.get_selected_square_number(event) - if self.chessboard.move_manager.selected_square is None: self.chessboard.move_manager.selected_square = square_number else: @@ -61,8 +91,20 @@ def mousePress(self, event): self.chessboard.move_manager.move_piece(square_number) if self.chessboard.move_manager.is_piece_moved is True: self.chessboard.move_manager.selected_square = None - self.chessboard.chess_pieces.delete_pieces() - self.chessboard.chess_pieces.draw_pieces() + last_move = self.chessboard.board.move_stack[-1] + source_square = self.chessboard.get_source_square_from_move( + last_move + ) + destination_square = ( + self.chessboard.get_destination_square_from_move(last_move) + ) + pc, pn = self.chessboard.get_selected_piece_color_and_name( + square_number + ) + self.chessboard.chess_pieces.delete_piece(source_square) + self.chessboard.chess_pieces.draw_moved_piece( + pn, pc, destination_square + ) class DrawChessBoard(QtWidgets.QGraphicsView, ChessBoard): diff --git a/chesspieces.py b/chesspieces.py index a5ee890..1d4b0e1 100644 --- a/chesspieces.py +++ b/chesspieces.py @@ -1,7 +1,9 @@ -import vars import chess from PySide6 import QtCore, QtGui, QtSvg, QtSvgWidgets, QtWidgets +import vars + + class ChessPieces: def __init__(self, chessboard, is_chessboard_flipped, scene, piece_set="staunty"): @@ -57,3 +59,35 @@ def delete_pieces(self): for item in items: if isinstance(item, QtWidgets.QGraphicsPixmapItem): self.scene.removeItem(item) + + def delete_piece(self, square): + items = self.scene.items() + for item in items: + if isinstance( + item, QtWidgets.QGraphicsPixmapItem + ) and item.pos() == QtCore.QPointF( + square[0] * vars.SQUARE_SIZE + 5, square[1] * vars.SQUARE_SIZE + 5 + ): + self.scene.removeItem(item) + + def draw_moved_piece(self, piece_name, piece_color, destination_square): + x = destination_square[0] * vars.SQUARE_SIZE + 5 + y = destination_square[1] * vars.SQUARE_SIZE + 5 + + piece_item = QtWidgets.QGraphicsPixmapItem( + self.piece_images[(piece_color, piece_name)] + ) + piece_item.setPos(x, y) + self.scene.addItem(piece_item) + captured_pawn_square = ( + destination_square[0], + destination_square[1] + 1 if piece_color == "w" else destination_square[1] - 1 + ) + + # Check if the move is an en-passant capture + if self.chessboard.move_manager.is_ep: + print( + f"{self.chessboard.move_manager.is_ep} -{destination_square} - {captured_pawn_square}" + ) + self.chessboard.move_manager.is_ep = False + self.delete_piece(captured_pawn_square) diff --git a/movemanager.py b/movemanager.py index da318e6..8361a9b 100644 --- a/movemanager.py +++ b/movemanager.py @@ -1,5 +1,6 @@ import chess -from PySide6 import QtWidgets +from PySide6 import QtWidgets, QtCore +import vars class MoveManager: @@ -8,6 +9,7 @@ def __init__(self, chessboard): self.chessboard = chessboard self.selected_square = None self.is_piece_moved = False + self.is_ep = False def move_piece(self, target_square): if self.selected_square is not None: @@ -18,7 +20,8 @@ def move_piece(self, target_square): ): if self._is_pawn_promotion(target_square): self._show_pawn_promotion_dialog(move) - + if self.chessboard.board.is_en_passant(move): + self.is_ep = True self.chessboard.board.push(move) self.is_piece_moved = True break @@ -44,6 +47,9 @@ def _show_pawn_promotion_dialog(self, move): pawn_promotion = PawnPromotion(self.chessboard) pawn_promotion.pawn_promotion_dialog(move) + def get_last_move(self): + return self.chessboard.board.peek() + class PawnPromotion: def __init__(self, chessboard): From 441d2f244bf19301bc6058298bc82ec76b7cddb4 Mon Sep 17 00:00:00 2001 From: HarshilPatel007 Date: Thu, 22 Feb 2024 15:04:06 +0530 Subject: [PATCH 3/9] feat(highlight legal moves): DONE - highlight legal moves of selected piece fix(delete captured piece): DONE - set is_piece_moved to False after move - delete opponent's piece from the scene at the destination square - remove the need of is_chessboard_flipped parameter from ChessPieces class --- chessboard.py | 68 +++++++++++++++++++++++++++++++++++++++++--------- chesspieces.py | 33 ++++++++++++++---------- movemanager.py | 10 ++++++++ 3 files changed, 86 insertions(+), 25 deletions(-) diff --git a/chessboard.py b/chessboard.py index eee0533..dd2907e 100644 --- a/chessboard.py +++ b/chessboard.py @@ -67,11 +67,49 @@ def get_destination_square_from_move(self, move): return chess.square_file(move.to_square), 7 - chess.square_rank(move.to_square) def get_selected_piece_color_and_name(self, selected_square): + """ + returns the color and name of the piece at the selected square + """ piece = self.board.piece_at(selected_square) - if piece is not None: + if piece: piece_color = "w" if piece.color == chess.WHITE else "b" piece_name = piece.symbol().upper() return piece_color, piece_name + return None, None + + def get_board_turn(self): + """ + returns which player's turn to play + """ + return "w" if self.board.turn == chess.WHITE else "b" + + def highlight_legal_moves(self, scene, square): + """ + highlights the legal moves of a selected piece + """ + legal_moves = self.move_manager.get_legal_moves(square) + + for target_square in set(move.to_square for move in legal_moves): + col, row, x, y = self.get_square_coordinates(target_square) + + # Add a circle in the center of the square + circle = scene.addEllipse( + x + vars.SQUARE_SIZE / 3, + y + vars.SQUARE_SIZE / 3, + vars.SQUARE_SIZE / 3, + vars.SQUARE_SIZE / 3, + ) + circle.setPen(QtCore.Qt.NoPen) + circle.setBrush(QtGui.QColor(vars.THEME_COLORS["highlight_legal_moves"])) + circle.setOpacity(0.45) + + def delete_highlighted_legal_moves(self, scene): + items = scene.items() + for item in items: + if isinstance(item, QtWidgets.QGraphicsEllipseItem): + brush_color = item.brush().color() + if brush_color == QtGui.QColor(vars.THEME_COLORS["highlight_legal_moves"]): + scene.removeItem(item) class ChessBoardEvents: @@ -79,32 +117,39 @@ def __init__(self, chessboard): self.chessboard = chessboard def mousePress(self, event): + square_number = self.chessboard.get_selected_square_number(event) if event.buttons() == QtCore.Qt.LeftButton: - square_number = self.chessboard.get_selected_square_number(event) if self.chessboard.move_manager.selected_square is None: self.chessboard.move_manager.selected_square = square_number + self.chessboard.highlight_legal_moves( + self.chessboard.scene, self.chessboard.move_manager.selected_square + ) else: if square_number == self.chessboard.move_manager.selected_square: self.chessboard.move_manager.selected_square = None + self.chessboard.delete_highlighted_legal_moves(self.chessboard.scene) return self.chessboard.move_manager.move_piece(square_number) + if self.chessboard.move_manager.is_piece_moved is True: - self.chessboard.move_manager.selected_square = None - last_move = self.chessboard.board.move_stack[-1] + piece_color, piece_name = self.chessboard.get_selected_piece_color_and_name( + square_number + ) + last_move = self.chessboard.move_manager.get_last_move() source_square = self.chessboard.get_source_square_from_move( last_move ) destination_square = ( self.chessboard.get_destination_square_from_move(last_move) ) - pc, pn = self.chessboard.get_selected_piece_color_and_name( - square_number - ) self.chessboard.chess_pieces.delete_piece(source_square) - self.chessboard.chess_pieces.draw_moved_piece( - pn, pc, destination_square + self.chessboard.chess_pieces.draw_piece( + piece_name, piece_color, destination_square ) + self.chessboard.move_manager.is_piece_moved = False + self.chessboard.move_manager.selected_square = None + self.chessboard.delete_highlighted_legal_moves(self.chessboard.scene) class DrawChessBoard(QtWidgets.QGraphicsView, ChessBoard): @@ -113,9 +158,8 @@ def __init__(self): super().__init__() self.scene = QtWidgets.QGraphicsScene() self.setScene(self.scene) - self.chess_pieces = ChessPieces( - self, self.is_board_flipped, self.scene, "cardinal" - ) + self.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform) + self.chess_pieces = ChessPieces(self, self.scene, "cardinal") self.chess_pieces.load_chess_piece_images() self.show_labels = True self.events = ChessBoardEvents(self) diff --git a/chesspieces.py b/chesspieces.py index 1d4b0e1..1d84a90 100644 --- a/chesspieces.py +++ b/chesspieces.py @@ -6,9 +6,8 @@ class ChessPieces: - def __init__(self, chessboard, is_chessboard_flipped, scene, piece_set="staunty"): + def __init__(self, chessboard, scene, piece_set="staunty"): self.chessboard = chessboard - self.is_chessboard_flipped = is_chessboard_flipped self.scene = scene self.piece_images = {} self.piece_set = piece_set @@ -39,7 +38,7 @@ def draw_pieces(self): piece_name = char.upper() piece_color = "w" if char.isupper() else "b" - if self.is_chessboard_flipped: + if self.chessboard.is_board_flipped: x = (7 - square % 8) * vars.SQUARE_SIZE + 5 y = (7 - square // 8) * vars.SQUARE_SIZE + 5 else: @@ -62,32 +61,40 @@ def delete_pieces(self): def delete_piece(self, square): items = self.scene.items() + x = square[0] * vars.SQUARE_SIZE + 5 + y = square[1] * vars.SQUARE_SIZE + 5 for item in items: if isinstance( item, QtWidgets.QGraphicsPixmapItem - ) and item.pos() == QtCore.QPointF( - square[0] * vars.SQUARE_SIZE + 5, square[1] * vars.SQUARE_SIZE + 5 - ): + ) and item.pos() == QtCore.QPointF(x, y): self.scene.removeItem(item) - def draw_moved_piece(self, piece_name, piece_color, destination_square): + def draw_piece(self, piece_name, piece_color, destination_square): x = destination_square[0] * vars.SQUARE_SIZE + 5 y = destination_square[1] * vars.SQUARE_SIZE + 5 + # delete opponent's piece from the scene at the destination square + self.delete_piece(destination_square) + piece_item = QtWidgets.QGraphicsPixmapItem( self.piece_images[(piece_color, piece_name)] ) piece_item.setPos(x, y) self.scene.addItem(piece_item) - captured_pawn_square = ( - destination_square[0], - destination_square[1] + 1 if piece_color == "w" else destination_square[1] - 1 - ) + ep_pawn_square = ( + destination_square[0], + ( + destination_square[1] + 1 + if piece_color == "w" + else destination_square[1] - 1 + ), + ) # Check if the move is an en-passant capture if self.chessboard.move_manager.is_ep: print( - f"{self.chessboard.move_manager.is_ep} -{destination_square} - {captured_pawn_square}" + f"{self.chessboard.move_manager.is_ep} -{destination_square} - {ep_pawn_square}" ) self.chessboard.move_manager.is_ep = False - self.delete_piece(captured_pawn_square) + # delete opponent's pawn from the scene at the square + self.delete_piece(ep_pawn_square) diff --git a/movemanager.py b/movemanager.py index 8361a9b..aefeb57 100644 --- a/movemanager.py +++ b/movemanager.py @@ -10,6 +10,7 @@ def __init__(self, chessboard): self.selected_square = None self.is_piece_moved = False self.is_ep = False + self.is_kingside_castling = False def move_piece(self, target_square): if self.selected_square is not None: @@ -22,6 +23,8 @@ def move_piece(self, target_square): self._show_pawn_promotion_dialog(move) if self.chessboard.board.is_en_passant(move): self.is_ep = True + if self.chessboard.board.is_kingside_castling(move): + self.is_kingside_castling = True self.chessboard.board.push(move) self.is_piece_moved = True break @@ -50,6 +53,13 @@ def _show_pawn_promotion_dialog(self, move): def get_last_move(self): return self.chessboard.board.peek() + def get_legal_moves(self, square): + moves = [] + for move in self.chessboard.board.legal_moves: + if move.from_square == square: + moves.append(move) + return moves + class PawnPromotion: def __init__(self, chessboard): From f61a0cb45a7ac01ab23edeea260c1b6b7c0a760d Mon Sep 17 00:00:00 2001 From: HarshilPatel007 Date: Fri, 23 Feb 2024 12:52:55 +0530 Subject: [PATCH 4/9] feat(update only source & destination square) : WIP - add castling case - only remove piece image at destination square if it is capture --- chesspieces.py | 60 +++++++++++++++++++++++++++++++++++++++++++++----- movemanager.py | 6 +++++ 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/chesspieces.py b/chesspieces.py index 1d84a90..3bc8416 100644 --- a/chesspieces.py +++ b/chesspieces.py @@ -74,13 +74,16 @@ def draw_piece(self, piece_name, piece_color, destination_square): y = destination_square[1] * vars.SQUARE_SIZE + 5 # delete opponent's piece from the scene at the destination square - self.delete_piece(destination_square) + if self.chessboard.move_manager.is_capture: + self.delete_piece(destination_square) + self.chessboard.move_manager.is_capture = False piece_item = QtWidgets.QGraphicsPixmapItem( self.piece_images[(piece_color, piece_name)] ) piece_item.setPos(x, y) self.scene.addItem(piece_item) + ep_pawn_square = ( destination_square[0], ( @@ -90,11 +93,56 @@ def draw_piece(self, piece_name, piece_color, destination_square): ), ) - # Check if the move is an en-passant capture + # check if the move is an en-passant capture if self.chessboard.move_manager.is_ep: - print( - f"{self.chessboard.move_manager.is_ep} -{destination_square} - {ep_pawn_square}" - ) - self.chessboard.move_manager.is_ep = False # delete opponent's pawn from the scene at the square self.delete_piece(ep_pawn_square) + self.chessboard.move_manager.is_ep = False + + kingside_castling_rook_before = ( + destination_square[0] + 1, + destination_square[1], + ) + kingside_castling_rook_after = ( + destination_square[0] - 1, + destination_square[1], + ) + + queenside_castling_rook_before = ( + destination_square[0] - 2, + destination_square[1], + ) + queenside_castling_rook_after = ( + destination_square[0] + 1, + destination_square[1], + ) + + # check if the move is kingside castling + if self.chessboard.move_manager.is_kingside_castling == True: + piece_item = QtWidgets.QGraphicsPixmapItem( + self.piece_images[ + ("w" if piece_color == "w" else "b", "R") + ] + ) + piece_item.setPos( + kingside_castling_rook_after[0] * vars.SQUARE_SIZE + 5, + kingside_castling_rook_after[1] * vars.SQUARE_SIZE + 5, + ) + self.scene.addItem(piece_item) + self.delete_piece(kingside_castling_rook_before) + self.chessboard.move_manager.is_kingside_castling = False + + # check if the move is queenside castling + if self.chessboard.move_manager.is_queenside_castling == True: + piece_item = QtWidgets.QGraphicsPixmapItem( + self.piece_images[ + ("w" if piece_color == "w" else "b", "R") + ] + ) + piece_item.setPos( + queenside_castling_rook_after[0] * vars.SQUARE_SIZE + 5, + queenside_castling_rook_after[1] * vars.SQUARE_SIZE + 5, + ) + self.scene.addItem(piece_item) + self.delete_piece(queenside_castling_rook_before) + self.chessboard.move_manager.is_queenside_castling = False diff --git a/movemanager.py b/movemanager.py index aefeb57..1b1da37 100644 --- a/movemanager.py +++ b/movemanager.py @@ -9,8 +9,10 @@ def __init__(self, chessboard): self.chessboard = chessboard self.selected_square = None self.is_piece_moved = False + self.is_capture = False self.is_ep = False self.is_kingside_castling = False + self.is_queenside_castling = False def move_piece(self, target_square): if self.selected_square is not None: @@ -21,10 +23,14 @@ def move_piece(self, target_square): ): if self._is_pawn_promotion(target_square): self._show_pawn_promotion_dialog(move) + if self.chessboard.board.is_capture(move): + self.is_capture = True if self.chessboard.board.is_en_passant(move): self.is_ep = True if self.chessboard.board.is_kingside_castling(move): self.is_kingside_castling = True + if self.chessboard.board.is_queenside_castling(move): + self.is_queenside_castling = True self.chessboard.board.push(move) self.is_piece_moved = True break From a1a34f783cdee3c9bbec947a2487e279a300bfd0 Mon Sep 17 00:00:00 2001 From: HarshilPatel007 Date: Sat, 24 Feb 2024 08:23:03 +0530 Subject: [PATCH 5/9] refactor(update only source & destination square): WIP - new way of getting square coordinates of a given piece name and color --- chessboard.py | 41 +++++++++++++++++++----- chesspieces.py | 85 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 85 insertions(+), 41 deletions(-) diff --git a/chessboard.py b/chessboard.py index dd2907e..f08d9c9 100644 --- a/chessboard.py +++ b/chessboard.py @@ -18,9 +18,28 @@ def __init__(self): def set_chess960_board(self): self.board.set_chess960_pos(random.randint(1, 959)) + def get_pieces_squares(self, piece_name, piece_color): + """ + returns the list of squares coordinates where the given pieces are. + get_pieces_squares(chess.ROOK, chess.WHITE) => [(0, 7), (7, 7)] + """ + squares = [] + for square in self.board.pieces(piece_name, piece_color): + if self.is_board_flipped: + squares.append( + (7 - chess.square_file(square), chess.square_rank(square)) + ) + else: + squares.append( + (chess.square_file(square), 7 - chess.square_rank(square)) + ) + + return squares + def get_square_coordinates(self, square): """ - col, row, x, y = self.get_square_coordinates(square) + returns the coordinates of given square number + col, row, x, y = self.get_square_coordinates(20) """ if self.is_board_flipped: col = 7 - chess.square_file(square) @@ -108,7 +127,9 @@ def delete_highlighted_legal_moves(self, scene): for item in items: if isinstance(item, QtWidgets.QGraphicsEllipseItem): brush_color = item.brush().color() - if brush_color == QtGui.QColor(vars.THEME_COLORS["highlight_legal_moves"]): + if brush_color == QtGui.QColor( + vars.THEME_COLORS["highlight_legal_moves"] + ): scene.removeItem(item) @@ -127,14 +148,16 @@ def mousePress(self, event): else: if square_number == self.chessboard.move_manager.selected_square: self.chessboard.move_manager.selected_square = None - self.chessboard.delete_highlighted_legal_moves(self.chessboard.scene) + self.chessboard.delete_highlighted_legal_moves( + self.chessboard.scene + ) return self.chessboard.move_manager.move_piece(square_number) if self.chessboard.move_manager.is_piece_moved is True: - piece_color, piece_name = self.chessboard.get_selected_piece_color_and_name( - square_number + piece_color, piece_name = ( + self.chessboard.get_selected_piece_color_and_name(square_number) ) last_move = self.chessboard.move_manager.get_last_move() source_square = self.chessboard.get_source_square_from_move( @@ -149,7 +172,9 @@ def mousePress(self, event): ) self.chessboard.move_manager.is_piece_moved = False self.chessboard.move_manager.selected_square = None - self.chessboard.delete_highlighted_legal_moves(self.chessboard.scene) + self.chessboard.delete_highlighted_legal_moves( + self.chessboard.scene + ) class DrawChessBoard(QtWidgets.QGraphicsView, ChessBoard): @@ -158,7 +183,9 @@ def __init__(self): super().__init__() self.scene = QtWidgets.QGraphicsScene() self.setScene(self.scene) - self.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform) + self.setRenderHints( + QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform + ) self.chess_pieces = ChessPieces(self, self.scene, "cardinal") self.chess_pieces.load_chess_piece_images() self.show_labels = True diff --git a/chesspieces.py b/chesspieces.py index 3bc8416..a780724 100644 --- a/chesspieces.py +++ b/chesspieces.py @@ -69,6 +69,22 @@ def delete_piece(self, square): ) and item.pos() == QtCore.QPointF(x, y): self.scene.removeItem(item) + def get_piece_position(self, piece_name, fen): + rows = fen.split("/") + + piece_positions = [] + for y, row in enumerate(rows): + x = 0 + for char in row: + if char in piece_name: + piece_positions.append((x, y)) + if char.isdigit(): + x += int(char) + else: + x += 1 + + return piece_positions + def draw_piece(self, piece_name, piece_color, destination_square): x = destination_square[0] * vars.SQUARE_SIZE + 5 y = destination_square[1] * vars.SQUARE_SIZE + 5 @@ -84,6 +100,16 @@ def draw_piece(self, piece_name, piece_color, destination_square): piece_item.setPos(x, y) self.scene.addItem(piece_item) + # [(0, 7), (7, 7)] before + # [(0, 7), (5, 7)] after + white_rooks_positions = self.chessboard.get_pieces_squares( + chess.ROOK, chess.WHITE + ) + black_rooks_positions = self.chessboard.get_pieces_squares( + chess.ROOK, chess.BLACK + ) + print(white_rooks_positions) + print(black_rooks_positions) ep_pawn_square = ( destination_square[0], ( @@ -99,50 +125,41 @@ def draw_piece(self, piece_name, piece_color, destination_square): self.delete_piece(ep_pawn_square) self.chessboard.move_manager.is_ep = False - kingside_castling_rook_before = ( - destination_square[0] + 1, - destination_square[1], - ) - kingside_castling_rook_after = ( - destination_square[0] - 1, - destination_square[1], - ) - - queenside_castling_rook_before = ( - destination_square[0] - 2, - destination_square[1], - ) - queenside_castling_rook_after = ( - destination_square[0] + 1, - destination_square[1], - ) - # check if the move is kingside castling if self.chessboard.move_manager.is_kingside_castling == True: piece_item = QtWidgets.QGraphicsPixmapItem( - self.piece_images[ - ("w" if piece_color == "w" else "b", "R") - ] - ) - piece_item.setPos( - kingside_castling_rook_after[0] * vars.SQUARE_SIZE + 5, - kingside_castling_rook_after[1] * vars.SQUARE_SIZE + 5, + self.piece_images[("w" if piece_color == "w" else "b", "R")] ) + if piece_color == "w": + x = white_rooks_positions[1][0] * vars.SQUARE_SIZE + 5 + y = white_rooks_positions[1][1] * vars.SQUARE_SIZE + 5 + if piece_color == "b": + x = black_rooks_positions[1][0] * vars.SQUARE_SIZE + 5 + y = black_rooks_positions[1][1] * vars.SQUARE_SIZE + 5 + + piece_item.setPos(x, y) self.scene.addItem(piece_item) - self.delete_piece(kingside_castling_rook_before) + # if piece_color == "w": + # self.delete_piece(white_rooks_positions[1]) + # if piece_color == "b": + # self.delete_piece(black_rooks_positions[1]) self.chessboard.move_manager.is_kingside_castling = False # check if the move is queenside castling if self.chessboard.move_manager.is_queenside_castling == True: piece_item = QtWidgets.QGraphicsPixmapItem( - self.piece_images[ - ("w" if piece_color == "w" else "b", "R") - ] - ) - piece_item.setPos( - queenside_castling_rook_after[0] * vars.SQUARE_SIZE + 5, - queenside_castling_rook_after[1] * vars.SQUARE_SIZE + 5, + self.piece_images[("w" if piece_color == "w" else "b", "R")] ) + if piece_color == "w": + x = white_rooks_positions[0][0] * vars.SQUARE_SIZE + 5 + y = white_rooks_positions[0][1] * vars.SQUARE_SIZE + 5 + if piece_color == "b": + x = black_rooks_positions[0][0] * vars.SQUARE_SIZE + 5 + y = black_rooks_positions[0][1] * vars.SQUARE_SIZE + 5 + piece_item.setPos(x, y) self.scene.addItem(piece_item) - self.delete_piece(queenside_castling_rook_before) + # if piece_color == "w": + # self.delete_piece(white_rooks_positions[0]) + # if piece_color == "b": + # self.delete_piece(black_rooks_positions[0]) self.chessboard.move_manager.is_queenside_castling = False From 450060ace5de7a1ac423e7e3fc6e3c28ed109371 Mon Sep 17 00:00:00 2001 From: HarshilPatel007 Date: Sat, 24 Feb 2024 10:12:59 +0530 Subject: [PATCH 6/9] chores(add requirements.txt): DONE docs(update README.md): DONE --- README.md | 12 +++++++++++- chessboard.py | 30 +++++++++++++++++------------- requirements.txt | 5 +++++ 3 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 requirements.txt diff --git a/README.md b/README.md index 4588cdb..1d6c8d6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ # YACS - Yet Another Chess Software -> warning : currently in development. +> warning : currently in development + +## Getting Started + +#### For Developers +- clone this repo +- go to the directory where you cloned this repo +- pip install -r requirements. txt + +#### For Users +> build file(s) yet to be created diff --git a/chessboard.py b/chessboard.py index f08d9c9..3ac446b 100644 --- a/chessboard.py +++ b/chessboard.py @@ -20,7 +20,7 @@ def set_chess960_board(self): def get_pieces_squares(self, piece_name, piece_color): """ - returns the list of squares coordinates where the given pieces are. + returns the list of squares coordinates of the given piece name & color get_pieces_squares(chess.ROOK, chess.WHITE) => [(0, 7), (7, 7)] """ squares = [] @@ -36,17 +36,17 @@ def get_pieces_squares(self, piece_name, piece_color): return squares - def get_square_coordinates(self, square): + def get_square_coordinates(self, square_number): """ returns the coordinates of given square number col, row, x, y = self.get_square_coordinates(20) """ if self.is_board_flipped: - col = 7 - chess.square_file(square) - row = chess.square_rank(square) + col = 7 - chess.square_file(square_number) + row = chess.square_rank(square_number) else: - col = chess.square_file(square) - row = 7 - chess.square_rank(square) + col = chess.square_file(square_number) + row = 7 - chess.square_rank(square_number) return col, row, col * vars.SQUARE_SIZE, row * vars.SQUARE_SIZE def get_selected_square_number(self, event): @@ -63,7 +63,9 @@ def get_selected_square_number(self, event): def get_source_square_from_move(self, move): """ - returns a square where piece is moved from + returns a square coordinates where piece is moved from + source_square = get_source_square_from_move(e2e4) + => (4, 6) """ if self.is_board_flipped: return ( @@ -76,7 +78,9 @@ def get_source_square_from_move(self, move): def get_destination_square_from_move(self, move): """ - returns a square where piece is moved to + returns a square coordinates where piece is moved to + destination_square = get_destination_square_from_move(e2e4) + => (4, 4) """ if self.is_board_flipped: return ( @@ -85,11 +89,11 @@ def get_destination_square_from_move(self, move): ) return chess.square_file(move.to_square), 7 - chess.square_rank(move.to_square) - def get_selected_piece_color_and_name(self, selected_square): + def get_selected_piece_color_and_name(self, square_number): """ returns the color and name of the piece at the selected square """ - piece = self.board.piece_at(selected_square) + piece = self.board.piece_at(square_number) if piece: piece_color = "w" if piece.color == chess.WHITE else "b" piece_name = piece.symbol().upper() @@ -102,11 +106,11 @@ def get_board_turn(self): """ return "w" if self.board.turn == chess.WHITE else "b" - def highlight_legal_moves(self, scene, square): + def highlight_legal_moves(self, scene, square_number): """ - highlights the legal moves of a selected piece + highlights the legal moves of a selected piece/square """ - legal_moves = self.move_manager.get_legal_moves(square) + legal_moves = self.move_manager.get_legal_moves(square_number) for target_square in set(move.to_square for move in legal_moves): col, row, x, y = self.get_square_coordinates(target_square) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..780eb5b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +chess==1.10.0 +PySide6==6.6.2 +PySide6_Addons==6.6.2 +PySide6_Essentials==6.6.2 +shiboken6==6.6.2 From ec5ab35e805010fc97ef0b9577ae1dc62538d19b Mon Sep 17 00:00:00 2001 From: HarshilPatel007 Date: Sat, 24 Feb 2024 13:51:16 +0530 Subject: [PATCH 7/9] feat(update only source & destination square): WIP - delete rooks at old location (only works in normal chess variant) --- .github/workflows/pylint.yml | 30 +++++++++++++++++++++++ chessboard.py | 6 ++++- chesspieces.py | 46 ++++++++++++++++++++++++++---------- 3 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/pylint.yml diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml new file mode 100644 index 0000000..9d7178a --- /dev/null +++ b/.github/workflows/pylint.yml @@ -0,0 +1,30 @@ +name: Pylint + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Analysing the code with pylint + run: | + pylint $(git ls-files '*.py') diff --git a/chessboard.py b/chessboard.py index 3ac446b..bbb8630 100644 --- a/chessboard.py +++ b/chessboard.py @@ -12,8 +12,9 @@ class ChessBoard: def __init__(self): self.fischer_random = False self.board = chess.Board(chess960=self.fischer_random) - self.is_board_flipped = False + self.is_board_flipped = True self.move_manager = MoveManager(self) + self.starting_board_position_fen = None def set_chess960_board(self): self.board.set_chess960_pos(random.randint(1, 959)) @@ -258,6 +259,9 @@ def draw_chessboard(self): if self.show_labels: self.draw_labels() self.chess_pieces.draw_pieces() + self.starting_board_position_fen = ( + self.board.fen().split()[0] + ) # hack to get the fen of only starting position def mousePressEvent(self, event): self.events.mousePress(event) diff --git a/chesspieces.py b/chesspieces.py index a780724..356fa2d 100644 --- a/chesspieces.py +++ b/chesspieces.py @@ -77,7 +77,10 @@ def get_piece_position(self, piece_name, fen): x = 0 for char in row: if char in piece_name: - piece_positions.append((x, y)) + if self.chessboard.is_board_flipped: + piece_positions.append((7 - x, 7 - y)) + else: + piece_positions.append((x, y)) if char.isdigit(): x += int(char) else: @@ -100,16 +103,33 @@ def draw_piece(self, piece_name, piece_color, destination_square): piece_item.setPos(x, y) self.scene.addItem(piece_item) - # [(0, 7), (7, 7)] before - # [(0, 7), (5, 7)] after white_rooks_positions = self.chessboard.get_pieces_squares( chess.ROOK, chess.WHITE ) black_rooks_positions = self.chessboard.get_pieces_squares( chess.ROOK, chess.BLACK ) - print(white_rooks_positions) - print(black_rooks_positions) + white_king_position = self.chessboard.get_pieces_squares( + chess.KING, chess.WHITE + ) + black_king_position = self.chessboard.get_pieces_squares( + chess.KING, chess.BLACK + ) + + white_rooks_positions_before_castling = self.get_piece_position( + ["R"], self.chessboard.starting_board_position_fen + ) + black_rooks_positions_before_castling = self.get_piece_position( + ["r"], self.chessboard.starting_board_position_fen + ) + + white_king_positions_before_castling = self.get_piece_position( + ["K"], self.chessboard.starting_board_position_fen + ) + black_king_positions_before_castling = self.get_piece_position( + ["k"], self.chessboard.starting_board_position_fen + ) + ep_pawn_square = ( destination_square[0], ( @@ -139,10 +159,10 @@ def draw_piece(self, piece_name, piece_color, destination_square): piece_item.setPos(x, y) self.scene.addItem(piece_item) - # if piece_color == "w": - # self.delete_piece(white_rooks_positions[1]) - # if piece_color == "b": - # self.delete_piece(black_rooks_positions[1]) + if piece_color == "w": + self.delete_piece(white_rooks_positions_before_castling[1]) + if piece_color == "b": + self.delete_piece(black_rooks_positions_before_castling[1]) self.chessboard.move_manager.is_kingside_castling = False # check if the move is queenside castling @@ -158,8 +178,8 @@ def draw_piece(self, piece_name, piece_color, destination_square): y = black_rooks_positions[0][1] * vars.SQUARE_SIZE + 5 piece_item.setPos(x, y) self.scene.addItem(piece_item) - # if piece_color == "w": - # self.delete_piece(white_rooks_positions[0]) - # if piece_color == "b": - # self.delete_piece(black_rooks_positions[0]) + if piece_color == "w": + self.delete_piece(white_rooks_positions_before_castling[0]) + if piece_color == "b": + self.delete_piece(black_rooks_positions_before_castling[0]) self.chessboard.move_manager.is_queenside_castling = False From bdb49c3514e6e930be1de2edcaea4431dc883f9b Mon Sep 17 00:00:00 2001 From: HarshilPatel007 Date: Sun, 25 Feb 2024 16:58:14 +0530 Subject: [PATCH 8/9] feat(update only source & destination square): DONE - deleting & drawing pieces at source & destination square is working now with chess960 variant --- chessboard.py | 31 ++++++++++++++--- chesspieces.py | 94 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 97 insertions(+), 28 deletions(-) diff --git a/chessboard.py b/chessboard.py index bbb8630..38e756a 100644 --- a/chessboard.py +++ b/chessboard.py @@ -12,12 +12,13 @@ class ChessBoard: def __init__(self): self.fischer_random = False self.board = chess.Board(chess960=self.fischer_random) - self.is_board_flipped = True + self.is_board_flipped = False self.move_manager = MoveManager(self) self.starting_board_position_fen = None def set_chess960_board(self): self.board.set_chess960_pos(random.randint(1, 959)) + # self.board.set_chess960_pos(665) #342 def get_pieces_squares(self, piece_name, piece_color): """ @@ -161,9 +162,20 @@ def mousePress(self, event): self.chessboard.move_manager.move_piece(square_number) if self.chessboard.move_manager.is_piece_moved is True: - piece_color, piece_name = ( - self.chessboard.get_selected_piece_color_and_name(square_number) - ) + # this if condition is here because, in chess960 variant, user + # have to click on a rook to do castling and don't know why the + # `get_selected_piece_color_and_name` method returns None if user + # try to click on a rook to do castling. + # (tested on chess960 position number 665, 342 or similar positions) + if self.chessboard.fischer_random and (self.chessboard.move_manager.is_queenside_castling or self.chessboard.move_manager.is_kingside_castling): + piece_color = ( + "b" if self.chessboard.board.turn == chess.WHITE else "w" + ) + piece_name = "R" + else: + piece_color, piece_name = ( + self.chessboard.get_selected_piece_color_and_name(square_number) + ) last_move = self.chessboard.move_manager.get_last_move() source_square = self.chessboard.get_source_square_from_move( last_move @@ -171,15 +183,25 @@ def mousePress(self, event): destination_square = ( self.chessboard.get_destination_square_from_move(last_move) ) + self.chessboard.chess_pieces.delete_piece(source_square) + + # delete opponent's piece from the scene at the destination square + # if it's a capture + if self.chessboard.move_manager.is_capture: + self.chessboard.chess_pieces.delete_piece(destination_square) + self.chessboard.move_manager.is_capture = False + self.chessboard.chess_pieces.draw_piece( piece_name, piece_color, destination_square ) + self.chessboard.move_manager.is_piece_moved = False self.chessboard.move_manager.selected_square = None self.chessboard.delete_highlighted_legal_moves( self.chessboard.scene ) + square_number = None class DrawChessBoard(QtWidgets.QGraphicsView, ChessBoard): @@ -262,6 +284,7 @@ def draw_chessboard(self): self.starting_board_position_fen = ( self.board.fen().split()[0] ) # hack to get the fen of only starting position + # print(self.board.chess960_pos()) def mousePressEvent(self, event): self.events.mousePress(event) diff --git a/chesspieces.py b/chesspieces.py index 356fa2d..a22c604 100644 --- a/chesspieces.py +++ b/chesspieces.py @@ -92,17 +92,19 @@ def draw_piece(self, piece_name, piece_color, destination_square): x = destination_square[0] * vars.SQUARE_SIZE + 5 y = destination_square[1] * vars.SQUARE_SIZE + 5 - # delete opponent's piece from the scene at the destination square - if self.chessboard.move_manager.is_capture: - self.delete_piece(destination_square) - self.chessboard.move_manager.is_capture = False - piece_item = QtWidgets.QGraphicsPixmapItem( self.piece_images[(piece_color, piece_name)] ) piece_item.setPos(x, y) self.scene.addItem(piece_item) + self.handle_special_cases(piece_color, destination_square) + + def handle_special_cases(self, piece_color, destination_square): + """ + handle special cases such as, castling & en-passant + for drawing and removing pieces at source & destination square + """ white_rooks_positions = self.chessboard.get_pieces_squares( chess.ROOK, chess.WHITE ) @@ -122,11 +124,10 @@ def draw_piece(self, piece_name, piece_color, destination_square): black_rooks_positions_before_castling = self.get_piece_position( ["r"], self.chessboard.starting_board_position_fen ) - - white_king_positions_before_castling = self.get_piece_position( + white_king_position_before_castling = self.get_piece_position( ["K"], self.chessboard.starting_board_position_fen ) - black_king_positions_before_castling = self.get_piece_position( + black_king_position_before_castling = self.get_piece_position( ["k"], self.chessboard.starting_board_position_fen ) @@ -139,15 +140,16 @@ def draw_piece(self, piece_name, piece_color, destination_square): ), ) - # check if the move is an en-passant capture - if self.chessboard.move_manager.is_ep: - # delete opponent's pawn from the scene at the square - self.delete_piece(ep_pawn_square) - self.chessboard.move_manager.is_ep = False - # check if the move is kingside castling if self.chessboard.move_manager.is_kingside_castling == True: - piece_item = QtWidgets.QGraphicsPixmapItem( + # delete the rook from old square + if piece_color == "w": + self.delete_piece(white_rooks_positions_before_castling[1]) + if piece_color == "b": + self.delete_piece(black_rooks_positions_before_castling[1]) + + # draw the rook to new square + rook = QtWidgets.QGraphicsPixmapItem( self.piece_images[("w" if piece_color == "w" else "b", "R")] ) if piece_color == "w": @@ -156,18 +158,40 @@ def draw_piece(self, piece_name, piece_color, destination_square): if piece_color == "b": x = black_rooks_positions[1][0] * vars.SQUARE_SIZE + 5 y = black_rooks_positions[1][1] * vars.SQUARE_SIZE + 5 + rook.setPos(x, y) + self.scene.addItem(rook) - piece_item.setPos(x, y) - self.scene.addItem(piece_item) + # draw the king to new square + king = QtWidgets.QGraphicsPixmapItem( + self.piece_images[("w" if piece_color == "w" else "b", "K")] + ) if piece_color == "w": - self.delete_piece(white_rooks_positions_before_castling[1]) + x = white_king_position[0][0] * vars.SQUARE_SIZE + 5 + y = white_king_position[0][1] * vars.SQUARE_SIZE + 5 if piece_color == "b": - self.delete_piece(black_rooks_positions_before_castling[1]) + x = black_king_position[0][0] * vars.SQUARE_SIZE + 5 + y = black_king_position[0][1] * vars.SQUARE_SIZE + 5 + + if white_king_position == white_king_position_before_castling: + pass + elif black_king_position == black_king_position_before_castling: + pass + else: + king.setPos(x, y) + self.scene.addItem(king) + self.chessboard.move_manager.is_kingside_castling = False # check if the move is queenside castling if self.chessboard.move_manager.is_queenside_castling == True: - piece_item = QtWidgets.QGraphicsPixmapItem( + # delete the rook from old square + if piece_color == "w": + self.delete_piece(white_rooks_positions_before_castling[0]) + if piece_color == "b": + self.delete_piece(black_rooks_positions_before_castling[0]) + + # draw the rook to new square + rook = QtWidgets.QGraphicsPixmapItem( self.piece_images[("w" if piece_color == "w" else "b", "R")] ) if piece_color == "w": @@ -176,10 +200,32 @@ def draw_piece(self, piece_name, piece_color, destination_square): if piece_color == "b": x = black_rooks_positions[0][0] * vars.SQUARE_SIZE + 5 y = black_rooks_positions[0][1] * vars.SQUARE_SIZE + 5 - piece_item.setPos(x, y) - self.scene.addItem(piece_item) + rook.setPos(x, y) + self.scene.addItem(rook) + + # draw the king to new square + king = QtWidgets.QGraphicsPixmapItem( + self.piece_images[("w" if piece_color == "w" else "b", "K")] + ) if piece_color == "w": - self.delete_piece(white_rooks_positions_before_castling[0]) + x = white_king_position[0][0] * vars.SQUARE_SIZE + 5 + y = white_king_position[0][1] * vars.SQUARE_SIZE + 5 if piece_color == "b": - self.delete_piece(black_rooks_positions_before_castling[0]) + x = black_king_position[0][0] * vars.SQUARE_SIZE + 5 + y = black_king_position[0][1] * vars.SQUARE_SIZE + 5 + + if white_king_position == white_king_position_before_castling: + pass + elif black_king_position == black_king_position_before_castling: + pass + else: + king.setPos(x, y) + self.scene.addItem(king) + self.chessboard.move_manager.is_queenside_castling = False + + # check if the move is an en-passant capture + if self.chessboard.move_manager.is_ep: + # delete opponent's pawn from the scene at the square + self.delete_piece(ep_pawn_square) + self.chessboard.move_manager.is_ep = False From 312e69f8c9ad01bb68f97b1d15cfa59441bab20b Mon Sep 17 00:00:00 2001 From: HarshilPatel007 Date: Mon, 26 Feb 2024 14:49:45 +0530 Subject: [PATCH 9/9] refactor(get fen of starting board pos): DONE --- chessboard.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/chessboard.py b/chessboard.py index 38e756a..d9e4a8a 100644 --- a/chessboard.py +++ b/chessboard.py @@ -18,7 +18,6 @@ def __init__(self): def set_chess960_board(self): self.board.set_chess960_pos(random.randint(1, 959)) - # self.board.set_chess960_pos(665) #342 def get_pieces_squares(self, piece_name, piece_color): """ @@ -167,14 +166,19 @@ def mousePress(self, event): # `get_selected_piece_color_and_name` method returns None if user # try to click on a rook to do castling. # (tested on chess960 position number 665, 342 or similar positions) - if self.chessboard.fischer_random and (self.chessboard.move_manager.is_queenside_castling or self.chessboard.move_manager.is_kingside_castling): + if self.chessboard.fischer_random and ( + self.chessboard.move_manager.is_queenside_castling + or self.chessboard.move_manager.is_kingside_castling + ): piece_color = ( "b" if self.chessboard.board.turn == chess.WHITE else "w" ) piece_name = "R" else: piece_color, piece_name = ( - self.chessboard.get_selected_piece_color_and_name(square_number) + self.chessboard.get_selected_piece_color_and_name( + square_number + ) ) last_move = self.chessboard.move_manager.get_last_move() source_square = self.chessboard.get_source_square_from_move( @@ -186,8 +190,6 @@ def mousePress(self, event): self.chessboard.chess_pieces.delete_piece(source_square) - # delete opponent's piece from the scene at the destination square - # if it's a capture if self.chessboard.move_manager.is_capture: self.chessboard.chess_pieces.delete_piece(destination_square) self.chessboard.move_manager.is_capture = False @@ -282,9 +284,8 @@ def draw_chessboard(self): self.draw_labels() self.chess_pieces.draw_pieces() self.starting_board_position_fen = ( - self.board.fen().split()[0] + self.board.board_fen() ) # hack to get the fen of only starting position - # print(self.board.chess960_pos()) def mousePressEvent(self, event): self.events.mousePress(event)