def __init__(self, position, move): resulting_position = position.copy().make_move(move) captured = position._pieces[move.target._x88] piece = position._pieces[move.source._x88] ocolor = Piece.opposite_color(position.fen._to_move) # Pawn moves. enpassant = False if Piece.klass(piece) == PAWN: # En-passant. if move.target.file != move.source.file and not captured: enpassant = True captured = Piece.from_klass_and_color(PAWN, ocolor) # Castling. # TODO: Support Chess960. # TODO: Validate the castling move. if Piece.klass(piece) == KING: self.is_king_side_castle = move.target.x - move.source.x == 2 self.is_queen_side_castle = move.target.x - move.source.x == -2 else: self.is_king_side_castle = self.is_queen_side_castle = False # Checks. self.is_check = resulting_position.is_check() self.is_checkmate = resulting_position.is_checkmate() self.move = move self.piece = piece self.captured = captured self.is_enpassant = enpassant self._set_text(position)
def to_move(cls, position, san): san = str(san) # Castling moves. if san == "O-O" or san == "O-O-O": # TODO: Support Chess960, check the castling moves are valid. rank = 1 if position.fen.turn == "w" else 8 if san == "O-O": return Move( source=Square.from_rank_and_file(rank, 'e'), target=Square.from_rank_and_file(rank, 'g')) else: return Move( source=Square.from_rank_and_file(rank, 'e'), target=Square.from_rank_and_file(rank, 'c')) # Regular moves. else: matches = cls.san_regex.match(san) if not matches: raise ValueError("Invalid SAN: %s." % repr(san)) if matches.group(1): klass = Piece.klass(matches.group(1).lower()) else: klass = PAWN piece = Piece.from_klass_and_color(klass, position.fen._to_move) target = Square(matches.group(4)) source = None for m in position.get_legal_moves(): if position._pieces[m.source._x88] != piece or m.target != target: continue if matches.group(2) and matches.group(2) != m.source.file: continue if matches.group(3) and matches.group(3) != str(m.source.rank): continue # Move matches. Assert it is not ambiguous. if source: raise MoveError( "Move is ambiguous: %s matches %s and %s." % san, source, m) source = m.source if not source: raise MoveError("No legal move matches %s." % san) return Move(source, target, matches.group(5) or None)
def to_move(cls, position, san): san = str(san) # Castling moves. if san == "O-O" or san == "O-O-O": # TODO: Support Chess960, check the castling moves are valid. rank = 1 if position.fen.turn == "w" else 8 if san == "O-O": return Move(source=Square.from_rank_and_file(rank, 'e'), target=Square.from_rank_and_file(rank, 'g')) else: return Move(source=Square.from_rank_and_file(rank, 'e'), target=Square.from_rank_and_file(rank, 'c')) # Regular moves. else: matches = cls.san_regex.match(san) if not matches: raise ValueError("Invalid SAN: %s." % repr(san)) if matches.group(1): klass = Piece.klass(matches.group(1).lower()) else: klass = PAWN piece = Piece.from_klass_and_color(klass, position.fen._to_move) target = Square(matches.group(4)) source = None for m in position.get_legal_moves(): if position._pieces[ m.source._x88] != piece or m.target != target: continue if matches.group(2) and matches.group(2) != m.source.file: continue if matches.group(3) and matches.group(3) != str(m.source.rank): continue # Move matches. Assert it is not ambiguous. if source: raise MoveError( "Move is ambiguous: %s matches %s and %s." % san, source, m) source = m.source if not source: raise MoveError("No legal move matches %s." % san) return Move(source, target, matches.group(5) or None)
def _set_text(self, position): move = self.move piece_klass = Piece.klass(self.piece) # Generate the SAN. san = "" if self.is_king_side_castle: san += "O-O" elif self.is_queen_side_castle: san += "O-O-O" else: if piece_klass != PAWN: san += Piece.from_klass_and_color(piece_klass, WHITE) if position: san += self._get_disambiguator(move, position) if self.captured: if piece_klass == PAWN: san += move.source.file san += "x" san += move.target.name if move.promotion: san += "=" san += move.promotion.upper() if self.is_checkmate: san += "#" elif self.is_check: san += "+" if self.is_enpassant: san += " (e.p.)" self._text = san
def test_from_klass_and_color(self): self.assertEqual('P', Piece.from_klass_and_color(piece.PAWN, piece.WHITE))