implemented checkmate
Some checks failed
pre-release / Pre Release (push) Waiting to run
tagged-release / Tagged Release (push) Has been cancelled

This commit is contained in:
Karma Riuk 2025-01-31 18:19:27 +01:00
parent 496207861e
commit 6c0819428e
11 changed files with 73 additions and 11 deletions

View File

@ -55,3 +55,9 @@ class Controller:
else: else:
move = legal_moves_positions[0] move = legal_moves_positions[0]
self._make_move(move) self._make_move(move)
if self._board.is_checkmate_for(self._board._turn):
self._view.notify_checkmate(self._board._turn)
if self._board.is_stalemate_for(self._board._turn):
self._view.notify_stalemate(self._board._turn)

View File

@ -131,11 +131,27 @@ class Board:
if Position.is_within_bounds(x, y): if Position.is_within_bounds(x, y):
possible_pos.append(Position(x, y)) possible_pos.append(Position(x, y))
else: else:
possible_pos += [move.pos for move in piece.legal_moves(self)] possible_pos += [move.pos for move in piece.legal_moves(self, looking_for_check=True)]
if king.pos in possible_pos: if king.pos in possible_pos:
return True return True
return False return False
def is_checkmate_for(self, colour: Colour) -> bool:
""" Is it checkmate for the defending colour passed as parameter """
return self.is_check_for(colour) and self._no_legal_moves_for(colour)
def is_stalemate_for(self, colour: Colour) -> bool:
""" Is it stalemate for the defending colour passed as parameter """
return not self.is_check_for(colour) and self._no_legal_moves_for(colour)
def _no_legal_moves_for(self, colour: Colour) -> bool:
""" Return true if there are indeed no legal moves for the given colour (for checkmate or stalemate)"""
pieces = self._white if colour == Colour.WHITE else self._black
for piece in pieces.values():
if len(piece.legal_moves(self)) > 0:
return False
return True
def castling_writes_for(self, colour: Colour) -> set[CastleSide]: def castling_writes_for(self, colour: Colour) -> set[CastleSide]:
return self._white_castling_write if colour == Colour.WHITE else self._black_castling_write return self._white_castling_write if colour == Colour.WHITE else self._black_castling_write

View File

@ -2,7 +2,7 @@ from logic.move import Move
from .piece import Piece from .piece import Piece
class Bishop(Piece): class Bishop(Piece):
def legal_moves(self, board: "Board") -> list[Move]: def legal_moves(self, board: "Board", / , looking_for_check = False) -> list[Move]:
ret = [] ret = []
# looking north east # looking north east
@ -17,5 +17,8 @@ class Bishop(Piece):
# looking north west # looking north west
ret.extend(self._look_direction(board, -1, 1)) ret.extend(self._look_direction(board, -1, 1))
if not looking_for_check and board.is_check_for(self.colour):
return self.keep_only_blocking(ret, board)
return ret return ret

View File

@ -19,11 +19,13 @@ class King(Piece):
if not board_after_move.is_check_for(self.colour): if not board_after_move.is_check_for(self.colour):
ret.append(move) ret.append(move)
if board.is_check_for(self.colour):
return self.keep_only_blocking(ret, board)
# -- Castles # -- Castles
castling_writes = board.castling_writes_for(self.colour) castling_writes = board.castling_writes_for(self.colour)
if len(castling_writes) == 0: if len(castling_writes) == 0:
return ret return ret
print(castling_writes)
if CastleSide.King in castling_writes: if CastleSide.King in castling_writes:
clear = True clear = True
@ -62,8 +64,6 @@ class King(Piece):
if clear: if clear:
ret.append(Move(self, Position(2, self.pos.y), castle_side=CastleSide.Queen)) ret.append(Move(self, Position(2, self.pos.y), castle_side=CastleSide.Queen))
print(ret)
return ret return ret

View File

@ -4,7 +4,7 @@ class Knight(Piece):
def letter(self): def letter(self):
return "n" return "n"
def legal_moves(self, board: "Board") -> list["Move"]: def legal_moves(self, board: "Board", / , looking_for_check = False) -> list["Move"]:
ret = [] ret = []
for dx, dy in [ for dx, dy in [
(+2, +1), (+1, +2), # north east (+2, +1), (+1, +2), # north east
@ -15,5 +15,9 @@ class Knight(Piece):
move = self._move_for_position(board, self.pos.x + dx, self.pos.y + dy) move = self._move_for_position(board, self.pos.x + dx, self.pos.y + dy)
if move is not None: if move is not None:
ret.append(move) ret.append(move)
if not looking_for_check and board.is_check_for(self.colour):
return self.keep_only_blocking(ret, board)
return ret return ret

View File

@ -3,7 +3,7 @@ from logic.pieces.piece import Colour, Piece
from logic.position import Position from logic.position import Position
class Pawn(Piece): class Pawn(Piece):
def legal_moves(self, board) -> list[Move]: def legal_moves(self, board, / , looking_for_check = False) -> list[Move]:
ret = [] ret = []
# can we capture to the left? # can we capture to the left?
@ -37,4 +37,7 @@ class Pawn(Piece):
pos = Position(self.pos.x, self.pos.y - dy) pos = Position(self.pos.x, self.pos.y - dy)
ret.append(Move(self, pos)) ret.append(Move(self, pos))
if not looking_for_check and board.is_check_for(self.colour):
return self.keep_only_blocking(ret, board)
return ret return ret

View File

@ -7,6 +7,9 @@ class Colour(Enum):
WHITE = "white" WHITE = "white"
BLACK = "black" BLACK = "black"
def __str__(self) -> str:
return self.value
class Piece: class Piece:
def __init__(self, pos: Position, colour: Colour) -> None: def __init__(self, pos: Position, colour: Colour) -> None:
self.pos = pos self.pos = pos
@ -16,6 +19,14 @@ class Piece:
def letter(self): def letter(self):
return type(self).__name__[0].lower() return type(self).__name__[0].lower()
def keep_only_blocking(self, candidates: list[Move], board: "Board") -> list[Move]:
ret = []
for move in candidates:
board_after_move = board.make_move(move)
if not board_after_move.is_check_for(self.colour):
ret.append(move)
return ret
def _look_direction(self, board: "Board", mult_dx: int, mult_dy: int): def _look_direction(self, board: "Board", mult_dx: int, mult_dy: int):
ret = [] ret = []
for d in range(1, 8): for d in range(1, 8):
@ -50,5 +61,5 @@ class Piece:
ret = type(self)(pos, self.colour) ret = type(self)(pos, self.colour)
return ret return ret
def legal_moves(self, board: "Board") -> list["Move"]: def legal_moves(self, board: "Board", / , looking_for_check = False) -> list["Move"]:
raise NotImplementedError(f"Can't say what the legal moves are for {type(self).__name__}, the method hasn't been implemented yet") raise NotImplementedError(f"Can't say what the legal moves are for {type(self).__name__}, the method hasn't been implemented yet")

View File

@ -2,7 +2,7 @@ from logic.move import Move
from .piece import Piece from .piece import Piece
class Queen(Piece): class Queen(Piece):
def legal_moves(self, board: "Board") -> list[Move]: def legal_moves(self, board: "Board", / , looking_for_check = False) -> list[Move]:
ret = [] ret = []
# looking north east # looking north east
@ -29,4 +29,7 @@ class Queen(Piece):
# looking north # looking north
ret.extend(self._look_direction(board, 0, 1)) ret.extend(self._look_direction(board, 0, 1))
if not looking_for_check and board.is_check_for(self.colour):
return self.keep_only_blocking(ret, board)
return ret return ret

View File

@ -2,7 +2,7 @@ from logic.move import Move
from .piece import Piece from .piece import Piece
class Rook(Piece): class Rook(Piece):
def legal_moves(self, board: "Board") -> list[Move]: def legal_moves(self, board: "Board", / , looking_for_check = False) -> list[Move]:
ret = [] ret = []
# looking east # looking east
@ -17,4 +17,7 @@ class Rook(Piece):
# looking north # looking north
ret.extend(self._look_direction(board, 0, 1)) ret.extend(self._look_direction(board, 0, 1))
if not looking_for_check and board.is_check_for(self.colour):
return self.keep_only_blocking(ret, board)
return ret return ret

View File

@ -1,4 +1,5 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox
from typing import Type from typing import Type
from PIL import ImageTk, Image from PIL import ImageTk, Image
import os import os
@ -60,6 +61,12 @@ class GUI(View):
self._controller.on_tile_selected(x, y) self._controller.on_tile_selected(x, y)
def notify_checkmate(self, colour: Colour) -> None:
messagebox.showinfo("Checkmate", f"{colour} is currently checkmated")
def notify_stalemate(self, colour: Colour) -> None:
messagebox.showinfo("Stalemate", f"{colour} is currently stalemated")
def update_board(self, board: Board, selected_piece: Piece, legal_moves: list[Move]) -> None: def update_board(self, board: Board, selected_piece: Piece, legal_moves: list[Move]) -> None:
self.canvas.delete("all") self.canvas.delete("all")

View File

@ -1,6 +1,6 @@
from logic.board import Board from logic.board import Board
from logic.move import Move from logic.move import Move
from logic.pieces.piece import Piece from logic.pieces.piece import Colour, Piece
class View: class View:
@ -13,6 +13,12 @@ class View:
def update_board(self, board: Board, selected_piece: Piece, legal_moves: list[Move]) -> None: def update_board(self, board: Board, selected_piece: Piece, legal_moves: list[Move]) -> None:
raise NotImplementedError(f"Can't update the board, the update_board() method of {type(self)} is not implemented") raise NotImplementedError(f"Can't update the board, the update_board() method of {type(self)} is not implemented")
def notify_checkmate(self, colour: Colour) -> None:
raise NotImplementedError(f"Can't notify of the checkmate, the notify_checkmate() method of {type(self)} is not implemented")
def notify_stalemate(self, colour: Colour) -> None:
raise NotImplementedError(f"Can't notify of the stalemate, the notify_stalemate() method of {type(self)} is not implemented")
def set_controller(self, controller: "Controller") -> None: def set_controller(self, controller: "Controller") -> None:
self._controller = controller self._controller = controller