Skip to content

Commit

Permalink
Merge pull request #62 from hrushikeshrv/better_opening
Browse files Browse the repository at this point in the history
Fixed SAN parsing errors
  • Loading branch information
hrushikeshrv authored Mar 3, 2023
2 parents 6d27896 + ce8a01e commit 1db7f11
Show file tree
Hide file tree
Showing 8 changed files with 174,113 additions and 893,472 deletions.
93 changes: 59 additions & 34 deletions chessengine/bitboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
change_turn,
)
from chessengine.pgn.parser import PGNParser, SAN_MOVE_REGEX
from chessengine.pgn.utils import best_move_from_tree


class Board:
Expand Down Expand Up @@ -616,7 +617,7 @@ def move_san(self, move: str, side: str) -> None:
:raises MoveError: If an ambiguous or invalid move was passed
:raises PGNParsingError: If the move was invalid SAN
"""
if "0-0-0" in move:
if "0-0-0" in move or "O-O-O" in move:
# queen side castle
if side == "white":
if self.white_queen_side_castle:
Expand All @@ -636,7 +637,7 @@ def move_san(self, move: str, side: str) -> None:
)
self.black_queen_side_castle = False
self.black_king_side_castle = False
elif "0-0" in move:
elif "0-0" in move or "O-O" in move:
# king side castle
if side == "white":
if self.white_king_side_castle:
Expand Down Expand Up @@ -668,47 +669,71 @@ def move_san(self, move: str, side: str) -> None:
else:
piece_moved = san_piece_map[groups[0].upper()]

end_pos = 2 ** coords_to_pos[groups[3].upper()]
end_pos = 2 ** coords_to_pos[groups[2].upper()]
moves = self.get_moves(side, piece_moved)

if groups[1] is None:
# No file provided in the SAN
# No rank or file provided in the SAN
candidate_move = None
for m in moves:
if m[1] == end_pos:
if candidate_move:
raise MoveError(
f"{move} is ambiguous for {side}. Specify a file or rank to move from."
)
candidate_move = m
if candidate_move is None:
raise MoveError(f"{move} is not a valid move for {side}.")
self.move(
start=candidate_move[0],
end=candidate_move[1],
score=candidate_move[2],
)
elif groups[1].isalpha():
# File provided in the SAN
candidate_move = None
for m in moves:
file = get_file(m[0])
if groups[1].upper() == "ABCDEFGH"[file - 1] and m[1] == end_pos:
if candidate_move:
raise MoveError(
f"{move} is ambiguous for {side}. Specify a rank to move from."
)
candidate_move = m
if candidate_move is None:
raise MoveError(f"{move} is not valid for {side}.")
self.move(
start=candidate_move[0],
end=candidate_move[1],
score=candidate_move[2],
)
elif groups[1].isnumeric():
# Rank provided in the SAN
candidate_move = None
for m in moves:
rank = get_rank(m[0])
if groups[1] == "12345678"[rank - 1] and m[1] == end_pos:
if candidate_move:
raise MoveError(
f"{move} is ambiguous for {side}. Specify a file to move from."
)
candidate_move = m
if candidate_move is None:
raise MoveError(f"{move} is not a valid move for {side}.")
self.move(
start=candidate_move[0],
end=candidate_move[1],
score=candidate_move[2],
)
elif groups[1].isalnum():
# Both rank and file provided in the SAN
start_pos = 2 ** coords_to_pos[groups[1].upper()]
for m in moves:
if m[0] == start_pos and m[1] == end_pos:
self.move(start=start_pos, end=end_pos, score=m[2])
break
else:
self.move(start=candidate_move[0], end=candidate_move[1])
else:
if groups[2] is None:
# No rank provided in the SAN
candidate_move = None
for m in moves:
file = get_file(m[0])
if (
groups[1].upper() == "ABCDEFGH"[file - 1]
and m[1] == end_pos
):
if candidate_move:
raise MoveError(
f"{move} is ambiguous for {side}. Specify a file as well as rank to move from."
)
candidate_move = m
if candidate_move is None:
raise MoveError(f"{move} is not a valid move for {side}.")
else:
self.move(start=candidate_move[0], end=candidate_move[1])
else:
# File and rank both present in the SAN
start_pos = 2 ** coords_to_pos[groups[1].upper() + groups[2]]
if (start_pos, end_pos) not in moves:
raise MoveError(f"{move} is not a valid move for {side}.")
self.move(start=start_pos, end=end_pos)
raise MoveError(f"{move} is not a valid move for {side}.")

def make_moves(self, *moves: Iterable[tuple[int, int]]) -> None:
"""
Expand Down Expand Up @@ -751,7 +776,7 @@ def undo_move(self) -> None:

def get_moves(
self, side: str, piece: str = None, position: int = None
) -> list[tuple[int, int]]:
) -> list[tuple[int, int, int]]:
"""
Get all end positions a piece of side can reach starting from position.
``side`` is always required, piece and position are optional.
Expand Down Expand Up @@ -806,7 +831,7 @@ def get_moves(
moves.extend(self.get_moves(side, piece, position))
return moves

def search_forward(self, depth: int = 4) -> tuple[int, tuple[int, int]]:
def search_forward(self, depth: int = 4) -> tuple[int, tuple[int, int, int]]:
"""
Execute an alpha-beta pruned depth-first search to find the optimal move from
the current board state.
Expand Down Expand Up @@ -972,12 +997,12 @@ def play(self, search_depth: int = 4) -> None:
side_to_move = "white"
in_game_tree = parser is not None
current_node = parser.root_node if in_game_tree else None
lines_printed = 20
lines_printed = 11
last_move = ""
while True:
clear_lines(lines_printed)
print(self)
lines_printed = 20
lines_printed = 11
if side_to_move == self.side:
if in_game_tree:
move, node = random.choice(list(current_node.children.items()))
Expand Down
12 changes: 12 additions & 0 deletions chessengine/lookup_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,18 @@
"B": "bishops",
"N": "knights",
"R": "rooks",
"\u2654": "kings",
"\u2655": "queens",
"\u2656": "rooks",
"\u2657": "bishops",
"\u2658": "knights",
"\u2659": "pawns",
"\u265A": "kings",
"\u265B": "queens",
"\u265C": "rooks",
"\u265D": "bishops",
"\u265E": "knights",
"\u265F": "pawns",
}

# fmt: off
Expand Down
Loading

0 comments on commit 1db7f11

Please sign in to comment.