Skip to content

Commit

Permalink
Detect castling, en-passant and captures
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasf committed May 29, 2015
1 parent 82296d0 commit 025a749
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 39 deletions.
29 changes: 24 additions & 5 deletions chess/__init__.py
Expand Up @@ -2182,10 +2182,6 @@ def san(self, move):

if piece == PAWN:
san = ""

# Detect en-passant.
if not BB_SQUARES[move.to_square] & self.occupied:
en_passant = abs(move.from_square - move.to_square) in (7, 9)
else:
# Get ambigous move candidates.
if piece == KNIGHT:
Expand Down Expand Up @@ -2229,7 +2225,7 @@ def san(self, move):
san += str(rank_index(move.from_square) + 1)

# Captures.
if BB_SQUARES[move.to_square] & self.occupied or en_passant:
if self.is_capture(move):
if piece == PAWN:
san += FILE_NAMES[file_index(move.from_square)]
san += "x"
Expand All @@ -2249,6 +2245,29 @@ def san(self, move):

return san

def is_en_passant(self, move):
"""Checks if the given pseudo-legal move is an en-passant capture."""
diff = abs(move.to_square - move.from_square)

if not diff in (7, 9):
return False

if not self.pawns & BB_SQUARES[move.from_square]:
return False

if self.occupied & BB_SQUARES[move.to_square]:
return False

return True

def is_capture(self, move):
"""Checks if the given pseudo-legal move is a capture."""
return BB_SQUARES[move.to_square] & self.occupied_co[self.turn ^ 1] or self.is_en_passant(move)

def is_castling(self, move):
"""Checks if the given pseudo-legal move is a castling move."""
return bool(BB_SQUARES[move.to_square] & self.occupied_co[self.turn])

def status(self, allow_chess960=True):
"""
Gets a bitmask of possible problems with the position.
Expand Down
43 changes: 9 additions & 34 deletions chess/syzygy.py
Expand Up @@ -1376,8 +1376,7 @@ def probe_wdl(self, board):
# Look at least at all legal en-passant captures.
for move in board.generate_legal_moves(castling=False, pawns=True, knights=False, bishops=False, rooks=False, queens=False, king=False):
# Filter out non-en-passant moves.
diff = abs(move.to_square - move.from_square)
if not ((diff == 7 or diff == 9) and not board.occupied & chess.BB_SQUARES[move.to_square]):
if not board.is_en_passant(move):
continue

# Do the move.
Expand All @@ -1398,21 +1397,10 @@ def probe_wdl(self, board):
if v1 >= v:
v = v1
elif v == 0:
# Check whether there is at least one legal non-ep move.
found_move = False
for move in board.generate_legal_moves():
if board.piece_type_at(move.from_square) != chess.PAWN:
found_move = True
break

diff = abs(move.to_square - move.from_square)
if not ((diff == 7 or diff == 9) and not board.occupied & chess.BB_SQUARES[move.to_square]):
found_move = True
break

# If not, then we are forced to play the losing ep capture.
if not found_move:
v = v1
# If there is not at least one legal non-en-passant move we are
# forced to play the losing en-passant cature.
if all(board.is_en_passant(move) for move in board.generate_legal_moves()):
v = v1

return v

Expand All @@ -1436,10 +1424,9 @@ def probe_dtz_no_ep(self, board):
return 1 if wdl == 2 else 101

if wdl > 0:
# Generate all legal non capturing pawn moves.
# Generate all legal non-capturing pawn moves.
for move in board.generate_legal_moves(castling=False, pawns=True, knights=False, bishops=False, rooks=False, queens=False, king=False):
diff = abs(move.to_square - move.from_square)
if diff == 7 or diff == 9:
if board.is_capture(move):
continue

board.push(move)
Expand Down Expand Up @@ -1552,8 +1539,7 @@ def probe_dtz(self, board):

for move in board.generate_legal_moves(castling=False, pawns=True, knights=False, bishops=False, rooks=False, queens=False, king=False):
# Filter out non-en-passant moves.
diff = abs(move.to_square - move.from_square)
if not ((diff == 7 or diff == 9) and not board.occupied & chess.BB_SQUARES[move.to_square]):
if not board.is_en_passant(move):
continue

board.push(move)
Expand Down Expand Up @@ -1586,18 +1572,7 @@ def probe_dtz(self, board):
elif v1 >= 0:
v = v1
else:
found_move = False
for move in board.generate_legal_moves():
if board.piece_type_at(move.from_square) != chess.PAWN:
found_move = True
break

diff = abs(move.to_square - move.from_square)
if not ((diff == 7 or diff == 9) and not board.occupied & chess.BB_SQUARES[move.to_square]):
found_move = True
break

if not found_move:
if all(board.is_en_passant(move) for move in board.generate_legal_moves()):
v = v1

return v
Expand Down
23 changes: 23 additions & 0 deletions test.py
Expand Up @@ -877,6 +877,29 @@ def test_string_conversion(self):
self.assertFalse(u"♜" in html)
self.assertFalse(u"♖" in html)

def test_move_info(self):
board = chess.Board("r1bqkb1r/p3np2/2n1p2p/1p4pP/2pP4/4PQ1N/1P2BPP1/RNB1K2R w KQkq g6 0 11")

self.assertTrue(board.is_capture(board.parse_san("Qxf7+")))
self.assertFalse(board.is_en_passant(board.parse_san("Qxf7+")))
self.assertFalse(board.is_castling(board.parse_san("Qxf7+")))

self.assertTrue(board.is_capture(board.parse_san("hxg6")))
self.assertTrue(board.is_en_passant(board.parse_san("hxg6")))
self.assertFalse(board.is_castling(board.parse_san("hxg6")))

self.assertFalse(board.is_capture(board.parse_san("b3")))
self.assertFalse(board.is_en_passant(board.parse_san("b3")))
self.assertFalse(board.is_castling(board.parse_san("b3")))

self.assertFalse(board.is_capture(board.parse_san("Ra6")))
self.assertFalse(board.is_en_passant(board.parse_san("Ra6")))
self.assertFalse(board.is_castling(board.parse_san("Ra6")))

self.assertFalse(board.is_capture(board.parse_san("O-O")))
self.assertFalse(board.is_en_passant(board.parse_san("O-O")))
self.assertTrue(board.is_castling(board.parse_san("O-O")))


class LegalMoveGeneratorTestCase(unittest.TestCase):

Expand Down

0 comments on commit 025a749

Please sign in to comment.