(self, san)
return matched_move
Uses the current position as the context to parse a move in standard
        algebraic notation and return the corresponding move object.

        The returned move is guaranteed to be either legal or a null move.

        :raises ValueError: If the SAN is invalid or ambigous.
Uses the current position as the context to parse a move in standard
        algebraic notation and return the corresponding move object.
Uses the current position as the context to parse a move in standard algebraic notation and return the corresponding move object .
def parse_san(self, san):
        """
        Uses the current position as the context to parse a move in standard
        algebraic notation and return the corresponding move object.

        The returned move is guaranteed to be either legal or a null move.

        :raises ValueError: If the SAN is invalid or ambigous.
        """
        # Null moves.
        if san == "--":
            return Move.null()

        # Castling.
        if san in ("O-O", "O-O+", "O-O#"):
            move = Move(E1, G1) if self.turn == WHITE else Move(E8, G8)
            if self.kings & self.occupied_co[self.turn] & BB_SQUARES[move.from_square] and self.is_legal(move):
                return move
            else:
                raise ValueError("Invalid SAN: Can not castle short.")
        elif san in ("O-O-O", "O-O-O+", "O-O-O#"):
            move = Move(E1, C1) if self.turn == WHITE else Move(E8, C8)
            if self.kings & self.occupied_co[self.turn] & BB_SQUARES[move.from_square] and self.is_legal(move):
                return move
            else:
                raise ValueError("Invalid SAN: Can not castle long.")

        # Match normal moves.
        match = SAN_REGEX.match(san)
        if not match:
            raise ValueError("Invalid SAN.")

        # Get target square.
        to_square = SQUARE_NAMES.index(match.group(4))

        # Get the promotion type.
        if not match.group(5):
            promotion = NONE
        else:
            promotion = PIECE_SYMBOLS.index(match.group(5)[1].lower())

        # Filter by piece type.
        if match.group(1) == "N":
            from_mask = self.knights
        elif match.group(1) == "B":
            from_mask = self.bishops
        elif match.group(1) == "K":
            from_mask = self.kings
        elif match.group(1) == "R":
            from_mask = self.rooks
        elif match.group(1) == "Q":
            from_mask = self.queens
        else:
            from_mask = self.pawns

        # Filter by turn.
        from_mask &= self.occupied_co[self.turn]

        # Filter by source file.
        if match.group(2):
            from_mask &= BB_FILES[FILE_NAMES.index(match.group(2))]

        # Filter by source rank.
        if match.group(3):
            from_mask &= BB_RANKS[int(match.group(3)) - 1]

        # Match legal moves.
        matched_move = None
        for move in self.generate_pseudo_legal_moves():
            if move.to_square != to_square:
                continue

            if move.promotion != promotion:
                continue

            if not BB_SQUARES[move.from_square] & from_mask:
                continue

            if self.is_into_check(move):
                continue

            if matched_move:
                raise ValueError("Invalid SAN: Move is ambigous.")

            matched_move = move

        if not matched_move:
            raise ValueError("Invalid SAN: No matching legal move found.")

        return matched_move
def parse_san ( self , san ) : # Null moves. if san == "--" : return Move . null ( ) # Castling. if san in ( "O-O" , "O-O+" , "O-O#" ) : move = Move ( E1 , G1 ) if self . turn == WHITE else Move ( E8 , G8 ) if self . kings & self . occupied_co [ self . turn ] & BB_SQUARES [ move . from_square ] and self . is_legal ( move ) : return move else : raise ValueError ( "Invalid SAN: Can not castle short." ) elif san in ( "O-O-O" , "O-O-O+" , "O-O-O#" ) : move = Move ( E1 , C1 ) if self . turn == WHITE else Move ( E8 , C8 ) if self . kings & self . occupied_co [ self . turn ] & BB_SQUARES [ move . from_square ] and self . is_legal ( move ) : return move else : raise ValueError ( "Invalid SAN: Can not castle long." ) # Match normal moves. match = SAN_REGEX . match ( san ) if not match : raise ValueError ( "Invalid SAN." ) # Get target square. to_square = SQUARE_NAMES . index ( match . group ( 4 ) ) # Get the promotion type. if not match . group ( 5 ) : promotion = NONE else : promotion = PIECE_SYMBOLS . index ( match . group ( 5 ) [ 1 ] . lower ( ) ) # Filter by piece type. if match . group ( 1 ) == "N" : from_mask = self . knights elif match . group ( 1 ) == "B" : from_mask = self . bishops elif match . group ( 1 ) == "K" : from_mask = self . kings elif match . group ( 1 ) == "R" : from_mask = self . rooks elif match . group ( 1 ) == "Q" : from_mask = self . queens else : from_mask = self . pawns # Filter by turn. from_mask &= self . occupied_co [ self . turn ] # Filter by source file. if match . group ( 2 ) : from_mask &= BB_FILES [ FILE_NAMES . index ( match . group ( 2 ) ) ] # Filter by source rank. if match . group ( 3 ) : from_mask &= BB_RANKS [ int ( match . group ( 3 ) ) - 1 ] # Match legal moves. matched_move = None for move in self . generate_pseudo_legal_moves ( ) : if move . to_square != to_square : continue if move . promotion != promotion : continue if not BB_SQUARES [ move . from_square ] & from_mask : continue if self . is_into_check ( move ) : continue if matched_move : raise ValueError ( "Invalid SAN: Move is ambigous." ) matched_move = move if not matched_move : raise ValueError ( "Invalid SAN: No matching legal move found." ) return matched_move