Skip to content

Commit

Permalink
Dev - update only source and destination square (#2)
Browse files Browse the repository at this point in the history
* feat(move pieces): DONE
- add ability to move pieces on the chessboard
- add pawn promotion

* feat(update only source and destination square): WIP

* 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

* feat(update only source & destination square) : WIP
- add castling case
- only remove piece image at destination square if it is capture

* refactor(update only source & destination square): WIP
- new way of getting square coordinates of a given piece name and color

* chores(add requirements.txt): DONE
docs(update README.md): DONE

* feat(update only source & destination square): WIP
- delete rooks at old location (only works in normal chess variant)

* feat(update only source & destination square): DONE
- deleting & drawing pieces at source & destination square is working now with chess960 variant

* refactor(get fen of starting board pos): DONE
  • Loading branch information
HarshilPatel007 authored Feb 26, 2024
1 parent a13615d commit 34cc561
Show file tree
Hide file tree
Showing 7 changed files with 405 additions and 19 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
@@ -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')
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ __pycache__/
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

test.py
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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
169 changes: 157 additions & 12 deletions chessboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,40 @@ def __init__(self):
self.board = chess.Board(chess960=self.fischer_random)
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))

def get_square_coordinates(self, square):
def get_pieces_squares(self, piece_name, piece_color):
"""
col, row, x, y = self.get_square_coordinates(square)
returns the list of squares coordinates of the given piece name & color
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_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):
Expand All @@ -42,27 +62,148 @@ 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 coordinates where piece is moved from
source_square = get_source_square_from_move(e2e4)
=> (4, 6)
"""
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 coordinates where piece is moved to
destination_square = get_destination_square_from_move(e2e4)
=> (4, 4)
"""
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, square_number):
"""
returns the color and name of the piece at the 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()
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_number):
"""
highlights the legal moves of a selected piece/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)

# 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:
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:
# 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
)
destination_square = (
self.chessboard.get_destination_square_from_move(last_move)
)

self.chessboard.chess_pieces.delete_piece(source_square)

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.chess_pieces.delete_pieces()
self.chessboard.chess_pieces.draw_pieces()
self.chessboard.delete_highlighted_legal_moves(
self.chessboard.scene
)
square_number = None


class DrawChessBoard(QtWidgets.QGraphicsView, ChessBoard):
Expand All @@ -71,9 +212,10 @@ 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)
Expand Down Expand Up @@ -141,6 +283,9 @@ def draw_chessboard(self):
if self.show_labels:
self.draw_labels()
self.chess_pieces.draw_pieces()
self.starting_board_position_fen = (
self.board.board_fen()
) # hack to get the fen of only starting position

def mousePressEvent(self, event):
self.events.mousePress(event)
Loading

0 comments on commit 34cc561

Please sign in to comment.