def get_theoretical_ep_right(self, x): """Checks if a player could have an ep-move in theory from looking just at the piece positions. :param file: The file to check as a letter between `"a"` and `"h"`. :return: A boolean indicating whether the player could theoretically have that en-passant move. """ if x < 0 or x > 7: raise ValueError(x) ''' 3 states of en-passant p. pP .. .. .. .p .P .. .. ''' # Check there is a pawn on the right rank for e.p. y = 3 if self.fen._to_move == WHITE else 4 x88 = X88.from_x_and_y(x, y) piece = self._pieces[x88] if not piece: return False # If the square is not an opposite colored pawn then its not possible. ocolor = Piece.opposite_color(self.fen._to_move) if not Piece.is_klass_and_color(piece, PAWN, ocolor): return False # If the square below the pawn is not empty then it not possible. y = 2 if self.fen.turn == WHITE else 5 x88 = X88.from_x_and_y(x, y) if self[x88]: return False # If there is not pawn of opposite color on a neighboring file then its not possible. xs = [_x for _x in range(8) if _x >= 0 and _x < 8 and abs(x - _x) == 1] for _x in xs: x88 = X88.from_x_and_y(_x, y) piece = self._pieces[x88] if Piece.is_klass_and_color( piece, PAWN, Piece.opposite_color(self.fen._to_move)): return True # Else its just not possible. return False
def get_theoretical_ep_right(self, x): """Checks if a player could have an ep-move in theory from looking just at the piece positions. :param file: The file to check as a letter between `"a"` and `"h"`. :return: A boolean indicating whether the player could theoretically have that en-passant move. """ if x < 0 or x > 7: raise ValueError(x) ''' 3 states of en-passant p. pP .. .. .. .p .P .. .. ''' # Check there is a pawn on the right rank for e.p. y = 3 if self.fen._to_move == WHITE else 4 x88 = X88.from_x_and_y(x, y) piece = self._pieces[x88] if not piece: return False # If the square is not an opposite colored pawn then its not possible. ocolor = Piece.opposite_color(self.fen._to_move) if not Piece.is_klass_and_color(piece, PAWN, ocolor): return False # If the square below the pawn is not empty then it not possible. y = 2 if self.fen.turn == WHITE else 5 x88 = X88.from_x_and_y(x, y) if self[x88]: return False # If there is not pawn of opposite color on a neighboring file then its not possible. xs = [_x for _x in range(8) if _x>=0 and _x<8 and abs(x-_x) == 1] for _x in xs: x88 = X88.from_x_and_y(_x, y) piece = self._pieces[x88] if Piece.is_klass_and_color(piece, PAWN, Piece.opposite_color(self.fen._to_move)): return True # Else its just not possible. return False
def __init__(self, label): if len(label) != 2: raise ValueError(square) try: x = self.files.index(label[0]) y = self.ranks.index(label[1]) except ValueError: raise ValueError(label) self._x88 = X88.from_x_and_y(x, y)
def test_to_index(self): self.assertEqual(X88.to_index(0), 0)
def test_is_backrank(self): self.assertEqual(X88.is_backrank(0, piece.BLACK), True)
def file(self): """The file as a character in %s""" % self.files return self.files[X88.to_x(self._x88)]
def y(self): """The y-coordinate, starting with 0 for the first rank.""" return X88.to_y(self._x88)
def x(self): """The x-coordinate, starting with 0 for the a-file.""" return X88.to_x(self._x88)
def test_from_index(self): self.assertEqual(X88.from_index(0), 0)
def test_to_y(self): self.assertEqual(X88.to_y(0), 0)
def test_is_secondrank(self): self.assertEqual(X88.is_secondrank(8, piece.WHITE), True)
def rank(self): """The rank as a character in %s""" % self.ranks return self.ranks[X88.to_y(self._x88)]
def index(self): """An integer between 0 and %s where 0 represents Square('a1').""" % ( len(self.ranks) * len(self.files) - 1) return X88.to_index(self._x88)
def test_from_x_and_y(self): self.assertEqual(X88.from_x_and_y(0, 0), 0)
def get_pseudo_legal_moves(self, source=None): """:yield: Pseudo legal moves in the current position. :param source: The source square to limit moves or None for all possible moves. """ tomove = self.fen._to_move for x88 in [ x88 for x88 in Square._x88_squares.keys() if self._pieces[x88] and Piece.color(self._pieces[x88]) == tomove and (source is None or x88 == source._x88)]: piece = self._pieces[x88] klass = Piece.klass(piece) # pawn moves if klass == PAWN: single, double, capleft, capright = X88.PAWN_OFFSETS[tomove] # Single square ahead. Do not capture. offset = x88 + single if not self._pieces[offset]: # Promotion. if X88.is_backrank(offset, tomove): for promote_to in Piece.promote_to: yield Move.from_x88(x88, offset, promote_to) else: yield Move.from_x88(x88, offset) # Two squares ahead. Do not capture. if X88.is_secondrank(x88, tomove): offset = x88 + double if not self._pieces[offset]: yield Move.from_x88(x88, offset) # Pawn captures. for cap in [capleft, capright]: offset = x88 + cap if offset & X88.X88: continue target = self._pieces[offset] if target and Piece.color(target) != tomove: # Promotion. if X88.is_backrank(offset, tomove): for promote_to in Piece.promote_to: yield Move.from_x88(x88, offset, promote_to) else: yield Move.from_x88(x88, offset) # En-passant. elif not target and offset == self.fen._ep: yield Move.from_x88(target, self.fen._ep) #piece moves else: # for each a direction a piece moves in for offset in X88.PIECE_OFFSETS[Piece.klass(piece)]: t_x88 = x88 + offset # while we do not fall off the board while not t_x88 & 0x88: # if there was not piece to attack then yield a quiet move if not self._pieces[t_x88]: yield Move.from_x88(x88, t_x88) # do not break out # else there is a piece there else: # if we can attack generate a move if Piece.color(self._pieces[t_x88]) != tomove: yield Move.from_x88(x88, t_x88) # we hit something so break out break # Knight and king do not go multiple times in their direction. if klass in [KNIGHT, KING]: break # travel down the board in the direction t_x88 += offset # castling moves opponent = Piece.opposite_color(tomove) ok = True # get possible castling for the side to move for castle in [c for c in self.fen._castle_rights if Piece.color(c) == tomove]: (square, enum), _ = Piece.castle_squares[castle] king = Square(square) if Piece.klass(castle) == KING: direc = 1 else: direc = -1 # for offset in the squares the king will travel for offset in range(0, 3): s = Square.from_x88(king._x88 + (offset * direc)) # if we are not the king square and we are occuppied if offset and self._pieces[s._x88]: ok = False break # if we are trying to travel through check if self.is_attacked(opponent, s): ok = False break # kludge: we have to check occupancy for one more square on the queen side if direc == -1 and self._pieces[s._x88 - 1]: ok = False if ok: yield Move(king, s)
def get_pseudo_legal_moves(self, source=None): """:yield: Pseudo legal moves in the current position. :param source: The source square to limit moves or None for all possible moves. """ tomove = self.fen._to_move for x88 in [ x88 for x88 in Square._x88_squares.keys() if self._pieces[x88] and Piece.color(self._pieces[x88]) == tomove and (source is None or x88 == source._x88) ]: piece = self._pieces[x88] klass = Piece.klass(piece) # pawn moves if klass == PAWN: single, double, capleft, capright = X88.PAWN_OFFSETS[tomove] # Single square ahead. Do not capture. offset = x88 + single if not self._pieces[offset]: # Promotion. if X88.is_backrank(offset, tomove): for promote_to in Piece.promote_to: yield Move.from_x88(x88, offset, promote_to) else: yield Move.from_x88(x88, offset) # Two squares ahead. Do not capture. if X88.is_secondrank(x88, tomove): offset = x88 + double if not self._pieces[offset]: yield Move.from_x88(x88, offset) # Pawn captures. for cap in [capleft, capright]: offset = x88 + cap if offset & X88.X88: continue target = self._pieces[offset] if target and Piece.color(target) != tomove: # Promotion. if X88.is_backrank(offset, tomove): for promote_to in Piece.promote_to: yield Move.from_x88(x88, offset, promote_to) else: yield Move.from_x88(x88, offset) # En-passant. elif not target and offset == self.fen._ep: yield Move.from_x88(target, self.fen._ep) #piece moves else: # for each a direction a piece moves in for offset in X88.PIECE_OFFSETS[Piece.klass(piece)]: t_x88 = x88 + offset # while we do not fall off the board while not t_x88 & 0x88: # if there was not piece to attack then yield a quiet move if not self._pieces[t_x88]: yield Move.from_x88(x88, t_x88) # do not break out # else there is a piece there else: # if we can attack generate a move if Piece.color(self._pieces[t_x88]) != tomove: yield Move.from_x88(x88, t_x88) # we hit something so break out break # Knight and king do not go multiple times in their direction. if klass in [KNIGHT, KING]: break # travel down the board in the direction t_x88 += offset # castling moves opponent = Piece.opposite_color(tomove) ok = True # get possible castling for the side to move for castle in [ c for c in self.fen._castle_rights if Piece.color(c) == tomove ]: (square, enum), _ = Piece.castle_squares[castle] king = Square(square) if Piece.klass(castle) == KING: direc = 1 else: direc = -1 # for offset in the squares the king will travel for offset in range(0, 3): s = Square.from_x88(king._x88 + (offset * direc)) # if we are not the king square and we are occuppied if offset and self._pieces[s._x88]: ok = False break # if we are trying to travel through check if self.is_attacked(opponent, s): ok = False break # kludge: we have to check occupancy for one more square on the queen side if direc == -1 and self._pieces[s._x88 - 1]: ok = False if ok: yield Move(king, s)
def test_from_x_and_y(self): self.assertEqual(X88.from_x_and_y(0,0), 0)