from player import Player from move import Move from typing import TypeVar from abc import ABC, abstractmethod ChessPieceT = TypeVar('ChessPieceT') class ChessPiece(ABC): 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) and v != None: raise TypeError(f'each element in each row of the board must be of type ChessPiece or must be None') # create variables for the board dimensions, original spot and destination spot board_dim = len(board) board_orig: ChessPiece | None = board[move.from_col][move.from_row] board_dest: ChessPiece = board[move.to_col][move.to_row] # check if move is within bounds by checking if y and x are within the board dimensions move_col_valid = 0 <= move.to_col <= board_dim move_row_valid = 0 <= move.to_row <= board_dim within_bounds = move_col_valid and move_row_valid # check if player is still at the original position they started at different_position = move.from_col != move.to_col or move.from_row != move.to_row # check if the current piece trying to be moved is in the same spot on the board #print(f'{board_orig} => {self}') #is_current_piece = board_orig is self is_current_piece = True # check if the destination is not empty, if so, do destination specific checks is_piece_class = isinstance(board_dest, ChessPiece) # setting all these values being checked within the conditional to true, cuz if they don't have to be ran, they should still be true to return the true result of all the booleans combined not_taking_friendly_piece = True if is_piece_class: # determines whether an enemy piece is being taken not_taking_friendly_piece = board_dest.player != self.player print(f'within_bounds={within_bounds}, different_position={different_position}, is_current_piece={is_current_piece}, not_taking_friendly_piece={not_taking_friendly_piece}, ({within_bounds and different_position and is_current_piece and not_taking_friendly_piece})') print(f'(other) is_piece_class={is_piece_class}, board_dim={board_dim}, move_col_valid={move_col_valid} ({move.from_col} -> {move.to_col}), move_row_valid={move_row_valid} ({move.from_row} -> {move.to_row})') return within_bounds and different_position and is_current_piece and not_taking_friendly_piece