from player import Player from move import Move from typing import TypeVar from abc import ABC, abstractmethod ChessPieceT = TypeVar('ChessPieceT') # my list of custom exceptions class PieceOutOfBoundsError(Exception): pass class StartEndPositionMismatch(Exception): pass class ChessPiece: def __init__(self, piece_color: Player): self.player = piece_color @property def player(self): return self.__player @player.setter def player(self, new_val): if not isinstance(new_val, Player): raise TypeError(f'new value for player is not of type Player') self.__player = new_val def __str__(self): # im not making this abstract, attributes amongst each piece are the same, str repr is also dynamic for the class name return f'[{self.__class__.__name__} player={self.player}]' def is_valid_move(self, move: Move, board: list[list[ChessPieceT]]) -> bool: if not isinstance(board, list): raise TypeError(f'board must be a list') for arr in board: if not isinstance(arr, list): raise TypeError(f'each element in the board list bust be another list') for v in arr: if not isinstance(v, ChessPiece): raise TypeError(f'each element in each row of the board must be of type ChessPiece') board_dim = len(board) board_orig: ChessPiece = board[move.to_row][move.to_col] board_dest: ChessPiece = board[move.from_row][move.from_col] within_bounds = board_dim <= move.to_col <= board_dim and board_dim <= move.to_row <= board_dim different_position = move.from_col != move.to_col and move.from_row != move.to_row at_position = board_orig == self is_piece_class = isinstance(board_dest, ChessPiece) taking_friendly_piece = board_dest.player != self.player print(f'within_bounds={within_bounds}, different_position={different_position}, at_position={at_position}, is_piece_class={is_piece_class}, taking_friendly_piece={taking_friendly_piece}') return within_bounds and different_position and at_position and is_piece_class and taking_friendly_piece