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)