Compare commits
3 Commits
6c0819428e
...
e6fafc8081
Author | SHA1 | Date | |
---|---|---|---|
|
e6fafc8081 | ||
|
1be71bf203 | ||
|
e862ab6b0b |
@ -15,8 +15,8 @@ class Board:
|
|||||||
self._white: dict[Position, Piece] = {}
|
self._white: dict[Position, Piece] = {}
|
||||||
self._black: dict[Position, Piece] = {}
|
self._black: dict[Position, Piece] = {}
|
||||||
self._turn = None
|
self._turn = None
|
||||||
self._white_castling_write = set()
|
self._white_castling_rights = set()
|
||||||
self._black_castling_write = set()
|
self._black_castling_rights = set()
|
||||||
self._en_passant_target = None
|
self._en_passant_target = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -88,13 +88,13 @@ class Board:
|
|||||||
sides = "kq"
|
sides = "kq"
|
||||||
assert c in sides or c in sides.upper(), f"The FEN position is malformed, the castling rights should be either k or q (both either lower- or upper-case), instead is '{c}'"
|
assert c in sides or c in sides.upper(), f"The FEN position is malformed, the castling rights should be either k or q (both either lower- or upper-case), instead is '{c}'"
|
||||||
if c == "K":
|
if c == "K":
|
||||||
ret._white_castling_write.add(CastleSide.King)
|
ret._white_castling_rights.add(CastleSide.King)
|
||||||
if c == "Q":
|
if c == "Q":
|
||||||
ret._white_castling_write.add(CastleSide.Queen)
|
ret._white_castling_rights.add(CastleSide.Queen)
|
||||||
if c == "k":
|
if c == "k":
|
||||||
ret._black_castling_write.add(CastleSide.King)
|
ret._black_castling_rights.add(CastleSide.King)
|
||||||
if c == "q":
|
if c == "q":
|
||||||
ret._black_castling_write.add(CastleSide.Queen)
|
ret._black_castling_rights.add(CastleSide.Queen)
|
||||||
|
|
||||||
# -- En passant target
|
# -- En passant target
|
||||||
if position[index] != "-":
|
if position[index] != "-":
|
||||||
@ -152,8 +152,8 @@ class Board:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def castling_writes_for(self, colour: Colour) -> set[CastleSide]:
|
def castling_rights_for(self, colour: Colour) -> set[CastleSide]:
|
||||||
return self._white_castling_write if colour == Colour.WHITE else self._black_castling_write
|
return self._white_castling_rights if colour == Colour.WHITE else self._black_castling_rights
|
||||||
|
|
||||||
def make_move(self, move: Move) -> "Board":
|
def make_move(self, move: Move) -> "Board":
|
||||||
dest_piece = self.piece_at(move.pos.x, move.pos.y)
|
dest_piece = self.piece_at(move.pos.x, move.pos.y)
|
||||||
@ -161,15 +161,18 @@ class Board:
|
|||||||
if dest_piece:
|
if dest_piece:
|
||||||
assert dest_piece.colour != move.piece.colour, "A piece cannot cannot eat another piece of the same colour"
|
assert dest_piece.colour != move.piece.colour, "A piece cannot cannot eat another piece of the same colour"
|
||||||
|
|
||||||
|
# -- Copy current state
|
||||||
ret = Board()
|
ret = Board()
|
||||||
ret._white = self._white.copy()
|
ret._white = self._white.copy()
|
||||||
ret._black = self._black.copy()
|
ret._black = self._black.copy()
|
||||||
ret._turn = Colour.WHITE if self._turn == Colour.BLACK else Colour.BLACK
|
ret._turn = Colour.WHITE if self._turn == Colour.BLACK else Colour.BLACK
|
||||||
ret._white_castling_write = self._white_castling_write.copy()
|
ret._white_castling_rights = self._white_castling_rights.copy()
|
||||||
ret._black_castling_write = self._black_castling_write.copy()
|
ret._black_castling_rights = self._black_castling_rights.copy()
|
||||||
ret._en_passant_target = self._en_passant_target
|
|
||||||
|
|
||||||
piece = move.piece
|
piece = move.piece
|
||||||
|
|
||||||
|
# -- Actually make the move
|
||||||
pieces_moving, other_pieces = (ret._white, ret._black) if piece.colour == Colour.WHITE else (ret._black, ret._white)
|
pieces_moving, other_pieces = (ret._white, ret._black) if piece.colour == Colour.WHITE else (ret._black, ret._white)
|
||||||
|
|
||||||
del pieces_moving[piece.pos]
|
del pieces_moving[piece.pos]
|
||||||
@ -177,6 +180,17 @@ class Board:
|
|||||||
if move.pos in other_pieces:
|
if move.pos in other_pieces:
|
||||||
del other_pieces[move.pos]
|
del other_pieces[move.pos]
|
||||||
|
|
||||||
|
if move.en_passant:
|
||||||
|
pos_to_remove = Position(move.pos.x, move.pos.y + (1 if self._turn == Colour.BLACK else -1))
|
||||||
|
del other_pieces[pos_to_remove]
|
||||||
|
|
||||||
|
# -- Set en passant target if needed
|
||||||
|
if move.becomes_en_passant_target:
|
||||||
|
ret._en_passant_target = pieces_moving[move.pos]
|
||||||
|
else:
|
||||||
|
ret._en_passant_target = None
|
||||||
|
|
||||||
|
# -- Handle castling (just move the rook over)
|
||||||
if move.castle_side == CastleSide.King:
|
if move.castle_side == CastleSide.King:
|
||||||
rook_pos = Position(7, piece.pos.y)
|
rook_pos = Position(7, piece.pos.y)
|
||||||
assert rook_pos in pieces_moving and type(pieces_moving[rook_pos]) == Rook, "Either rook is absent from the king side or you are trying to castle with something else than a rook..."
|
assert rook_pos in pieces_moving and type(pieces_moving[rook_pos]) == Rook, "Either rook is absent from the king side or you are trying to castle with something else than a rook..."
|
||||||
@ -191,24 +205,25 @@ class Board:
|
|||||||
new_rook_pos = Position(3, piece.pos.y)
|
new_rook_pos = Position(3, piece.pos.y)
|
||||||
pieces_moving[new_rook_pos] = Rook(new_rook_pos, piece.colour)
|
pieces_moving[new_rook_pos] = Rook(new_rook_pos, piece.colour)
|
||||||
|
|
||||||
|
# -- Check for castling rights
|
||||||
if piece.colour == Colour.WHITE:
|
if piece.colour == Colour.WHITE:
|
||||||
if type(piece) == King:
|
if type(piece) == King:
|
||||||
ret._white_castling_write = set()
|
ret._white_castling_rights = set()
|
||||||
|
|
||||||
if type(piece) == Rook:
|
if type(piece) == Rook:
|
||||||
if piece.pos.x == 0 and CastleSide.Queen in ret._white_castling_write:
|
if piece.pos.x == 0 and CastleSide.Queen in ret._white_castling_rights:
|
||||||
ret._white_castling_write.remove(CastleSide.Queen)
|
ret._white_castling_rights.remove(CastleSide.Queen)
|
||||||
elif piece.pos.x == 7 and CastleSide.King in ret._white_castling_write:
|
elif piece.pos.x == 7 and CastleSide.King in ret._white_castling_rights:
|
||||||
ret._white_castling_write.remove(CastleSide.King)
|
ret._white_castling_rights.remove(CastleSide.King)
|
||||||
else:
|
else:
|
||||||
if type(piece) == King:
|
if type(piece) == King:
|
||||||
ret._black_castling_write = set()
|
ret._black_castling_rights = set()
|
||||||
|
|
||||||
if type(piece) == Rook:
|
if type(piece) == Rook:
|
||||||
if piece.pos.x == 0 and CastleSide.Queen in ret._black_castling_write:
|
if piece.pos.x == 0 and CastleSide.Queen in ret._black_castling_rights:
|
||||||
ret._black_castling_write.remove(CastleSide.Queen)
|
ret._black_castling_rights.remove(CastleSide.Queen)
|
||||||
elif piece.pos.x == 7 and CastleSide.King in ret._black_castling_write:
|
elif piece.pos.x == 7 and CastleSide.King in ret._black_castling_rights:
|
||||||
ret._black_castling_write.remove(CastleSide.King)
|
ret._black_castling_rights.remove(CastleSide.King)
|
||||||
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -8,11 +8,13 @@ class CastleSide(Enum):
|
|||||||
Queen = "O-O-O"
|
Queen = "O-O-O"
|
||||||
|
|
||||||
class Move:
|
class Move:
|
||||||
def __init__(self, piece: "Piece", pos: Position,/, is_capturing: bool = False, castle_side: CastleSide = CastleSide.Neither) -> None:
|
def __init__(self, piece: "Piece", pos: Position,/, is_capturing: bool = False, castle_side: CastleSide = CastleSide.Neither, en_passant: bool = False, becomes_en_passant_target: bool = False) -> None:
|
||||||
self.piece = piece
|
self.piece = piece
|
||||||
self.pos = pos
|
self.pos = pos
|
||||||
self.is_capturing = is_capturing
|
self.is_capturing = is_capturing
|
||||||
self.castle_side = castle_side
|
self.castle_side = castle_side
|
||||||
|
self.becomes_en_passant_target = becomes_en_passant_target
|
||||||
|
self.en_passant = en_passant
|
||||||
|
|
||||||
def to_algebraic(self) -> str:
|
def to_algebraic(self) -> str:
|
||||||
raise NotImplementedError("The move can't be translated to algbraic notation, as it was not implemented")
|
raise NotImplementedError("The move can't be translated to algbraic notation, as it was not implemented")
|
||||||
|
@ -23,11 +23,11 @@ class King(Piece):
|
|||||||
return self.keep_only_blocking(ret, board)
|
return self.keep_only_blocking(ret, board)
|
||||||
|
|
||||||
# -- Castles
|
# -- Castles
|
||||||
castling_writes = board.castling_writes_for(self.colour)
|
castling_rights = board.castling_rights_for(self.colour)
|
||||||
if len(castling_writes) == 0:
|
if len(castling_rights) == 0:
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
if CastleSide.King in castling_writes:
|
if CastleSide.King in castling_rights:
|
||||||
clear = True
|
clear = True
|
||||||
for dx in range(1, 3):
|
for dx in range(1, 3):
|
||||||
x = self.pos.x + dx
|
x = self.pos.x + dx
|
||||||
@ -45,7 +45,7 @@ class King(Piece):
|
|||||||
if clear:
|
if clear:
|
||||||
ret.append(Move(self, Position(6, self.pos.y), castle_side=CastleSide.King))
|
ret.append(Move(self, Position(6, self.pos.y), castle_side=CastleSide.King))
|
||||||
|
|
||||||
if CastleSide.Queen in castling_writes:
|
if CastleSide.Queen in castling_rights:
|
||||||
clear = True
|
clear = True
|
||||||
for dx in range(1, 3):
|
for dx in range(1, 3):
|
||||||
x = self.pos.x - dx
|
x = self.pos.x - dx
|
||||||
|
@ -24,18 +24,30 @@ class Pawn(Piece):
|
|||||||
if capturable_piece.colour != self.colour:
|
if capturable_piece.colour != self.colour:
|
||||||
ret.append(Move(self, capturable_piece.pos, is_capturing = True))
|
ret.append(Move(self, capturable_piece.pos, is_capturing = True))
|
||||||
|
|
||||||
|
# -- Can we capture en passant?
|
||||||
|
if board._en_passant_target is not None and \
|
||||||
|
board._en_passant_target.pos.y == self.pos.y and (
|
||||||
|
board._en_passant_target.pos.x == self.pos.x - 1
|
||||||
|
or board._en_passant_target.pos.x == self.pos.x + 1
|
||||||
|
):
|
||||||
|
if board._en_passant_target.colour != self.colour:
|
||||||
|
old_pos = board._en_passant_target.pos
|
||||||
|
new_pos = Position(old_pos.x, old_pos.y + (1 if self.colour == Colour.WHITE else -1))
|
||||||
|
ret.append(Move(self, new_pos, is_capturing = True, en_passant = True))
|
||||||
|
|
||||||
|
# -- Normal moves
|
||||||
if self.colour == Colour.WHITE:
|
if self.colour == Colour.WHITE:
|
||||||
for dy in range(1, 3 if self.pos.y == 1 else 2):
|
for dy in range(1, 3 if self.pos.y == 1 else 2):
|
||||||
if self.pos.y + dy > 7 or board.piece_at(self.pos.x, self.pos.y + dy):
|
if self.pos.y + dy > 7 or board.piece_at(self.pos.x, self.pos.y + dy):
|
||||||
break
|
break
|
||||||
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, becomes_en_passant_target=dy==2))
|
||||||
else:
|
else:
|
||||||
for dy in range(1, 3 if self.pos.y == 6 else 2):
|
for dy in range(1, 3 if self.pos.y == 6 else 2):
|
||||||
if self.pos.y - dy < 0 or board.piece_at(self.pos.x, self.pos.y - dy):
|
if self.pos.y - dy < 0 or board.piece_at(self.pos.x, self.pos.y - dy):
|
||||||
break
|
break
|
||||||
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, becomes_en_passant_target=dy==2))
|
||||||
|
|
||||||
if not looking_for_check and board.is_check_for(self.colour):
|
if not looking_for_check and board.is_check_for(self.colour):
|
||||||
return self.keep_only_blocking(ret, board)
|
return self.keep_only_blocking(ret, board)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user