This commit is contained in:
cutsweettea
2025-10-29 10:36:33 -04:00
parent 45d60d1657
commit bbd55f48a0
10 changed files with 147 additions and 32 deletions

6
chess/bishop.py Normal file
View File

@@ -0,0 +1,6 @@
from chess_piece import ChessPiece
from player import Player
class Bishop(ChessPiece):
def __init__(self, piece_color: Player):
super().__init__(piece_color)

View File

@@ -28,8 +28,25 @@ class MoveValidity(Enum):
# TODO: create UndoException
class UndoException(Exception):
pass
# create default board setup (y, x) not (x, y)
# blank spaces are set to none, as seen in the chess_gui_small_view
DEFAULT_BOARD = [
[Rook(Player.BLACK), Knight(Player.BLACK), Bishop(Player.BLACK), Queen(Player.BLACK), King(Player.BLACK), Bishop(Player.BLACK), Knight(Player.BLACK), Rook(Player.BLACK)],
[Pawn(Player.BLACK), Pawn(Player.BLACK), Pawn(Player.BLACK), Pawn(Player.BLACK), Pawn(Player.BLACK), Pawn(Player.BLACK), Pawn(Player.BLACK), Pawn(Player.BLACK)],
[None, None, None, None, None, None, None, None],
[None, None, None, None, None, None, None, None],
[None, None, None, None, None, None, None, None],
[None, None, None, None, None, None, None, None],
[Pawn(Player.WHITE), Pawn(Player.WHITE), Pawn(Player.WHITE), Pawn(Player.WHITE), Pawn(Player.WHITE), Pawn(Player.WHITE), Pawn(Player.WHITE), Pawn(Player.WHITE)],
[Rook(Player.WHITE), Knight(Player.WHITE), Bishop(Player.WHITE), Queen(Player.WHITE), King(Player.WHITE), Bishop(Player.WHITE), Knight(Player.WHITE), Rook(Player.WHITE)]
]
class ChessModel:
# TODO: fill in this class
pass
def __init__(self):
self.__board = DEFAULT_BOARD
# i wanna do easy checking for whether a piece can skip over another piece

View File

@@ -5,11 +5,7 @@ from abc import ABC, abstractmethod
ChessPieceT = TypeVar('ChessPieceT')
# my list of custom exceptions
class PieceOutOfBoundsError(Exception): pass
class StartEndPositionMismatch(Exception): pass
class ChessPiece:
class ChessPiece(ABC):
def __init__(self, piece_color: Player):
self.player = piece_color
@@ -36,17 +32,36 @@ class ChessPiece:
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')
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 = 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
board_orig: ChessPiece | None = board[move.from_col][move.from_row]
board_dest: ChessPiece = board[move.to_col][move.to_row]
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
# 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

6
chess/king.py Normal file
View File

@@ -0,0 +1,6 @@
from chess_piece import ChessPiece
from player import Player
class King(ChessPiece):
def __init__(self, piece_color: Player):
super().__init__(piece_color)

6
chess/knight.py Normal file
View File

@@ -0,0 +1,6 @@
from chess_piece import ChessPiece
from player import Player
class Knight(ChessPiece):
def __init__(self, piece_color: Player):
super().__init__(piece_color)

View File

@@ -68,14 +68,38 @@ def valid_range(max: int) -> list[int]:
# class for static move sets
class StaticMoveSet(MoveSets):
def is_valid_move(self, move: Move) -> bool:
# get the difference between the to and from row / column
from_to_row_diff = move.to_row - move.from_row
from_to_col_diff = move.to_col - move.from_col
# iterate over each move set, checking if it follows any of the move sets passed to the method
for ms in self.move_sets:
if from_to_row_diff == ms[0] and from_to_col_diff == ms[1]:
# checks if the difference in y and x is equal to any available options
if from_to_col_diff == ms[0] and from_to_row_diff == ms[1]:
return True
return False
# in these lists, the move sets are static, so the y and x are a set of combo's of ways to move
"""
pawn move sets are mostly static, but there will be an override for the is_valid_move method,
checking to see if the pawn can move 1 up 1 left / right to take another enemy piece
these values simulate these move sets, with x being the pawn, m being a possible static move,
and p being possible moves (such as the case i explained)
##########
# x #
# pmp #
# m #
# #
# #
# m #
# pmp #
# x #
##########
"""
pawn_valid_move_sets = [(1, 0), (2, 0)]
# in these lists, the move sets are dynamic, so the y and x are a range of times they can move on the x and y
rook_valid_move_sets = [(0, 8), (8, 0), (0, -8), (-8, 0)]
@@ -116,6 +140,6 @@ class DynamicMoveSet(MoveSets):
# create move sets
# static move sets
pawn_move_sets = StaticMoveSet((0, 1), (0, 2))
pawn_move_sets = StaticMoveSet(*pawn_valid_move_sets)
# dynamic move sets

View File

@@ -1,12 +1,17 @@
from chess_piece import ChessPiece
from move import Move
from move import Move, pawn_move_sets
from player import Player
from move_sets import pawn_valid_move_sets
class Pawn(ChessPiece):
def __init__(self, piece_color: Player):
super().__init__(piece_color)
def is_valid_move(self, move: Move, board: list[list[ChessPiece]]) -> bool:
# run original check and other piece specific checks
orig_is_valid = super().is_valid_move(move, board)
# run original check and move set checks
orig_valid = super().is_valid_move(move, board)
pawn_move_set_valid = pawn_move_sets.is_valid_move(move)
# run piece specific check about diagonal taking
print(f'orig_valid={orig_valid}, pawn_move_set_valid={pawn_move_set_valid}')
return orig_valid and pawn_move_set_valid

6
chess/queen.py Normal file
View File

@@ -0,0 +1,6 @@
from chess_piece import ChessPiece
from player import Player
class Queen(ChessPiece):
def __init__(self, piece_color: Player):
super().__init__(piece_color)

6
chess/rook.py Normal file
View File

@@ -0,0 +1,6 @@
from chess_piece import ChessPiece
from player import Player
class Rook(ChessPiece):
def __init__(self, piece_color: Player):
super().__init__(piece_color)

View File

@@ -1,11 +1,16 @@
from chess_piece import ChessPiece
from pytest import fixture, mark
from pytest import fixture
from player import Player
from move import StaticMoveSet, Move, DynamicMoveSet, valid_range
from move import StaticMoveSet, Move, DynamicMoveSet, valid_range, pawn_valid_move_sets
from random import randint, choice
from pawn import Pawn
from chess_model import DEFAULT_BOARD
# chess piece tests
# general piece
@fixture
def valid_piece():
return ChessPiece(Player.WHITE)
@@ -19,11 +24,29 @@ def test_update_player(valid_piece: ChessPiece):
def test_repr_str(valid_piece: ChessPiece):
rep = str(valid_piece)
assert 'player='
assert 'player=' in rep
# pawn piece
_INIT_PAWN_ORIGINAL_VAL = (4, 1)
@fixture
def valid_board():
# ask how to define the board
pass
@fixture
def valid_black_pawn():
return Pawn(Player.BLACK)
def test_valid_move_black_pawn_1(valid_black_pawn: Pawn):
# try all valid pawn moves
for ms in pawn_valid_move_sets:
assert valid_black_pawn.is_valid_move(Move(_INIT_PAWN_ORIGINAL_VAL[0], _INIT_PAWN_ORIGINAL_VAL[1], _INIT_PAWN_ORIGINAL_VAL[0]+ms[1], _INIT_PAWN_ORIGINAL_VAL[1]+ms[0]), DEFAULT_BOARD)
# move set testing (kinda separate from main project)
_init_val = 4
_INIT_MOVE_ORIGIN_VAL = 4
# static move sets
@@ -37,13 +60,14 @@ def valid_static_move_set():
def test_valid_static_moves(valid_static_move_set: StaticMoveSet):
for ms in _static_move_sets:
assert valid_static_move_set.is_valid_move(Move(_init_val, _init_val, _init_val+ms[0], _init_val+ms[1]))
print(ms)
assert valid_static_move_set.is_valid_move(Move(_INIT_MOVE_ORIGIN_VAL, _INIT_MOVE_ORIGIN_VAL, _INIT_MOVE_ORIGIN_VAL+ms[0], _INIT_MOVE_ORIGIN_VAL+ms[1]))
# test invalid
def test_invalid_static_moves(valid_static_move_set: StaticMoveSet):
for ms in _static_move_sets:
assert not valid_static_move_set.is_valid_move(Move(_init_val, _init_val, _init_val+ms[0]+(-1 if ms[0] < 0 else 1), _init_val+ms[1]+(-1 if ms[1] < 0 else 1)))
assert not valid_static_move_set.is_valid_move(Move(_INIT_MOVE_ORIGIN_VAL, _INIT_MOVE_ORIGIN_VAL, _INIT_MOVE_ORIGIN_VAL+ms[0]+(-1 if ms[0] < 0 else 1), _INIT_MOVE_ORIGIN_VAL+ms[1]+(-1 if ms[1] < 0 else 1)))
# dynamic move sets
@@ -75,7 +99,7 @@ def test_valid_dynamic_moves(valid_dynamic_move_set: DynamicMoveSet):
rnd_col = choice(valid_range_col)
# test dat thing
assert valid_dynamic_move_set.is_valid_move(Move(_init_val, _init_val, _init_val+rnd_row, _init_val+rnd_col))
assert valid_dynamic_move_set.is_valid_move(Move(_INIT_MOVE_ORIGIN_VAL, _INIT_MOVE_ORIGIN_VAL, _INIT_MOVE_ORIGIN_VAL+rnd_row, _INIT_MOVE_ORIGIN_VAL+rnd_col))
_RND_MIN = 10
_RND_MAX = 20
@@ -101,4 +125,4 @@ def test_invalid_dynamic_moves(valid_dynamic_move_set: DynamicMoveSet):
col_rnd_add = col + (rnd if col > 0 else -rnd)
#print(f'{ms}, ({_init_val}+{row_rnd_add}, {_init_val}+{col_rnd_add}) = ({_init_val+row_rnd_add}, {_init_val+col_rnd_add})')
assert not valid_dynamic_move_set.is_valid_move(Move(_init_val, _init_val, _init_val+row_rnd_add, _init_val+col_rnd_add))
assert not valid_dynamic_move_set.is_valid_move(Move(_INIT_MOVE_ORIGIN_VAL, _INIT_MOVE_ORIGIN_VAL, _INIT_MOVE_ORIGIN_VAL+row_rnd_add, _INIT_MOVE_ORIGIN_VAL+col_rnd_add))