added complete FEN support both for reading and writing

This commit is contained in:
Karma Riuk
2025-02-01 18:34:41 +01:00
parent 92e1ff26fc
commit 4bb068b2a5
5 changed files with 165 additions and 2 deletions

View File

@ -18,6 +18,8 @@ class Board:
self._white_castling_rights = set()
self._black_castling_rights = set()
self._en_passant_target = None
self._n_moves = 0
self._n_half_moves = 0
@staticmethod
def _piece_class_from_char(c: str) -> Type[Piece]:
@ -83,6 +85,8 @@ class Board:
for c in position[index:]:
index += 1
if c == "-" or c == " ":
if c == "-":
index += 1
break
sides = "kq"
@ -98,7 +102,24 @@ class Board:
# -- En passant target
if position[index] != "-":
ret._en_passant_target = position[index:index+2]
pos = Position.from_algebraic(position[index:index+2])
index += 2
if pos.y == 2:
pos.y += 1
assert pos in ret._white, "En passant target is not in the position"
ret._en_passant_target = ret._white[pos]
elif pos.y == 5:
pos.y -= 1
assert pos in ret._black, "En passant target is not in the position"
ret._en_passant_target = ret._black[pos]
else:
raise ValueError("You can't have a en passant target that is not on the third or sixth rank")
else:
index += 1
index += 1
ret._n_half_moves = int(position[index:position.find(" ", index + 1)])
ret._n_moves = int(position[position.find(" ", index)+1:])
return ret
@ -180,6 +201,14 @@ class Board:
if move.pos in other_pieces:
del other_pieces[move.pos]
if piece.colour == Colour.BLACK:
ret._n_moves = self._n_moves + 1
if move.is_capturing or type(piece) == Pawn:
ret._n_half_moves = 0
else:
ret._n_half_moves = self._n_half_moves + 1
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]
@ -232,4 +261,67 @@ class Board:
return ret
INITIAL_BOARD = Board.setup_FEN_position("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
def to_fen_string(self):
ret = ""
for y in range(7, -1, -1):
empty_cell_counter = 0
for x in range(8):
pos = Position(x, y)
piece = None
if pos in self._white:
piece = self._white[pos]
elif pos in self._black:
piece = self._black[pos]
if piece is None:
empty_cell_counter += 1
continue
if empty_cell_counter > 0:
ret += str(empty_cell_counter)
empty_cell_counter = 0
letter = piece.letter()
ret += letter.lower() if piece.colour == Colour.BLACK else letter.upper()
if empty_cell_counter > 0:
ret += str(empty_cell_counter)
if y > 0:
ret += "/"
ret += " "
ret += "w" if self._turn == Colour.WHITE else "b"
ret += " "
if len(self._white_castling_rights) == 0 and len(self._black_castling_rights) == 0:
ret += "-"
else:
if CastleSide.King in self._white_castling_rights:
ret += "K"
if CastleSide.Queen in self._white_castling_rights:
ret += "Q"
if CastleSide.King in self._black_castling_rights:
ret += "k"
if CastleSide.Queen in self._black_castling_rights:
ret += "q"
ret += " "
if self._en_passant_target is not None:
pos = self._en_passant_target.pos
pos.y += -1 if self._en_passant_target.colour == Colour.WHITE else 1
ret += pos.to_algebraic()
else:
ret += "-"
ret += " "
ret += str(self._n_half_moves)
ret += " "
ret += str(self._n_moves)
return ret
_fen_pos = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
_fen_pos = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
INITIAL_BOARD = Board.setup_FEN_position(_fen_pos)

View File

@ -75,3 +75,6 @@ class Pawn(Piece):
return self.keep_only_blocking(ret, board)
return ret
def letter(self):
return "p"

View File

@ -17,6 +17,14 @@ class Position:
return x >= Position._MIN_POS and x <= Position._MAX_POS \
and y >= Position._MIN_POS and y <= Position._MAX_POS
@staticmethod
def from_algebraic(square: str) -> "Position":
assert len(square) == 2, f"'{square}' is malformed"
x = Position._FILES.index(square[0])
y = Position._RANKS.index(int(square[1]))
return Position(x, y)
def to_algebraic(self) -> str:
return f"{Position._FILES[self.x]}{Position._RANKS[self.y]}"