示例#1
0
def parseSAN(board, san, full=True):
    """ Parse a Short/Abbreviated Algebraic Notation string """
    notat = san

    color = board.color

    if notat == "--":
        return newMove(board.kings[color], board.kings[color], NULL_MOVE)

    if notat[-1] in "+#":
        notat = notat[:-1]
        # If '++' was used in place of #
        if notat[-1] == "+":
            notat = notat[:-1]

    flag = NORMAL_MOVE

    # If last char is a piece char, we assue it the promote char
    c = notat[-1]
    if c in "KQRBNSMFkqrbnsmf.":
        c = c.lower()
        if c == "k" and board.variant != SUICIDECHESS:
            raise ParsingError(san, _("invalid promoted piece"), board.asFen())
        elif c == "." and board.variant in (CAMBODIANCHESS, MAKRUKCHESS,
                                            SITTUYINCHESS):
            # temporary hack for xboard bug
            flag = QUEEN_PROMOTION
        else:
            flag = chr2Sign[c] + 2

        if notat[-2] == "=":
            notat = notat[:-2]
        else:
            notat = notat[:-1]

    if len(notat) < 2:
        raise ParsingError(san, _("the move needs a piece and a cord"),
                           board.asFen())

    if notat[0] in "O0o":
        fcord = board.ini_kings[color]
        flag = KING_CASTLE if notat == "O-O" or notat == "0-0" or notat == "o-o" else QUEEN_CASTLE
        side = flag - QUEEN_CASTLE
        if FILE(fcord) == 3 and board.variant in (WILDCASTLECHESS,
                                                  WILDCASTLESHUFFLECHESS):
            side = 0 if side == 1 else 1
        if board.variant == FISCHERRANDOMCHESS:
            tcord = board.ini_rooks[color][side]
        else:
            tcord = board.fin_kings[color][side]
        return newMove(fcord, tcord, flag)

    # LAN is not allowed in pgn spec, but sometimes it occures
    if "-" in notat:
        notat = notat.replace("-", "")

    if "@" in notat:
        tcord = cordDic[notat[-2:]]
        if notat[0].islower():
            # Sjeng-ism
            piece = chr2Sign[notat[0]]
        else:
            piece = chrU2Sign[notat[0]]
        return newMove(piece, tcord, DROP)

    if notat[0] in "QRBKNSMF":
        piece = chrU2Sign[notat[0]]
        notat = notat[1:]
    else:
        piece = PAWN
        if notat[
                -1] in "18" and flag == NORMAL_MOVE and board.variant != SITTUYINCHESS:
            raise ParsingError(
                san, _("promotion move without promoted piece is incorrect"),
                board.asFen())

    if "x" in notat:
        notat, tcord = notat.split("x")
        if tcord not in cordDic:
            raise ParsingError(
                san,
                _("the captured cord (%s) is incorrect") % tcord,
                board.asFen())

        tcord = cordDic[tcord]

        if piece == PAWN:
            # If a pawn is attacking an empty cord, we assue it an enpassant
            if board.arBoard[tcord] == EMPTY:
                if (color == BLACK and 2 * 8 <= tcord < 3 * 8) or (
                        color == WHITE and 5 * 8 <= tcord < 6 * 8):
                    flag = ENPASSANT
                else:
                    raise ParsingError(
                        san, _("pawn capture without target piece is invalid"),
                        board.asFen())
    else:
        if not notat[-2:] in cordDic:
            raise ParsingError(
                san,
                _("the end cord (%s) is incorrect") % notat[-2:],
                board.asFen())

        tcord = cordDic[notat[-2:]]
        notat = notat[:-2]

    # In suicide promoting to king is valid, so
    # more than 1 king per side can exist !
    if board.variant != SUICIDECHESS and piece == KING:
        return newMove(board.kings[color], tcord, flag)

    # If there is any extra location info, like in the move Bexd1 or Nh3f4 we
    # want to know
    frank = None
    ffile = None
    if notat and notat[0] in reprRank:
        frank = int(notat[0]) - 1
        notat = notat[1:]
    if notat and notat[0] in reprFile:
        ffile = ord(notat[0]) - ord("a")
        notat = notat[1:]
    if notat and notat[0] in reprRank:
        frank = int(notat[0]) - 1
        notat = notat[1:]
        # we know all we want
        return newMove(frank * 8 + ffile, tcord, flag)

    if piece == PAWN:
        if (ffile is not None) and ffile != FILE(tcord):
            # capture
            if color == WHITE:
                fcord = tcord - 7 if ffile > FILE(tcord) else tcord - 9
            else:
                fcord = tcord + 7 if ffile < FILE(tcord) else tcord + 9
        else:
            if color == WHITE:
                pawns = board.boards[WHITE][PAWN]
                fcord = tcord - 16 if RANK(tcord) == 3 and not (
                    pawns & fileBits[FILE(tcord)] & rankBits[2]) else tcord - 8
            else:
                pawns = board.boards[BLACK][PAWN]
                fcord = tcord + 16 if RANK(tcord) == 4 and not (
                    pawns & fileBits[FILE(tcord)] & rankBits[5]) else tcord + 8
            if board.variant == SITTUYINCHESS and flag == QUEEN_PROMOTION and \
                    (pawns & fileBits[FILE(tcord)] & rankBits[RANK(tcord)]):
                return newMove(tcord, tcord, flag)
        return newMove(fcord, tcord, flag)
    else:
        if board.pieceCount[color][piece] == 1:
            # we have only one from this kind if piece, so:
            fcord = firstBit(board.boards[color][piece])
            return newMove(fcord, tcord, flag)
        else:
            # We find all pieces who could have done it. (If san was legal, there should
            # never be more than one)
            moves = genPieceMoves(board, piece, tcord)
            if len(moves) == 1:
                return moves.pop()
            else:
                for move in moves:
                    f = FCORD(move)
                    if frank is not None and frank != RANK(f):
                        continue
                    if ffile is not None and ffile != FILE(f):
                        continue
                    board_clone = board.clone(full)
                    board_clone.applyMove(move, full)
                    if board_clone.opIsChecked():
                        continue
                    return move

    errstring = "no %s is able to move to %s" % (reprPiece[piece],
                                                 reprCord[tcord])
    raise ParsingError(san, errstring, board.asFen())
示例#2
0
def parseSAN(board, san):
    """ Parse a Short/Abbreviated Algebraic Notation string """
    notat = san

    color = board.color

    if notat == "--":
        return newMove(board.kings[color], board.kings[color], NULL_MOVE)

    if notat[-1] in "+#":
        notat = notat[:-1]
        # If '++' was used in place of #
        if notat[-1] == "+":
            notat = notat[:-1]

    flag = NORMAL_MOVE

    # If last char is a piece char, we assue it the promote char
    c = notat[-1]
    if c in "KQRBNSMFkqrbnsmf.":
        c = c.lower()
        if c == "k" and board.variant != SUICIDECHESS and board.variant != GIVEAWAYCHESS:
            raise ParsingError(san, _("invalid promoted piece"), board.asFen())
        elif c == ".":
            if board.variant in (CAMBODIANCHESS, MAKRUKCHESS, SITTUYINCHESS):
                # temporary hack for xboard bug
                flag = QUEEN_PROMOTION
            else:
                raise ParsingError(san, "invalid san", board.asFen())
        else:
            flag = chr2Sign[c] + 2

        if notat[-2] == "=":
            notat = notat[:-2]
        else:
            notat = notat[:-1]

    if len(notat) < 2:
        raise ParsingError(san, _("the move needs a piece and a cord"),
                           board.asFen())

    if notat[0] in "O0o":
        fcord = board.ini_kings[color]
        flag = KING_CASTLE if notat in ("O-O", "0-0", "o-o", "OO", "00",
                                        "oo") else QUEEN_CASTLE
        side = flag - QUEEN_CASTLE
        if FILE(fcord) == 3 and board.variant in (WILDCASTLECHESS,
                                                  WILDCASTLESHUFFLECHESS):
            side = 0 if side == 1 else 1
        if board.variant == FISCHERRANDOMCHESS:
            tcord = board.ini_rooks[color][side]
        else:
            tcord = board.fin_kings[color][side]
        return newMove(fcord, tcord, flag)

    # LAN is not allowed in pgn spec, but sometimes it occures
    if "-" in notat:
        notat = notat.replace("-", "")

    if "@" in notat:
        tcord = cordDic[notat[-2:]]
        if notat[0].islower():
            # Sjeng-ism
            piece = chr2Sign[notat[0]]
        else:
            piece = chrU2Sign[notat[0]]
        return newMove(piece, tcord, DROP)

    # standard piece letters
    if notat[0] in "QRBKNSMF":
        piece = chrU2Sign[notat[0]]
        notat = notat[1:]
    # unambigious lowercase piece letters
    elif notat[0] in "qrknsm":
        piece = chr2Sign[notat[0]]
        notat = notat[1:]
    # a lowercase bishop letter or a pawn capture
    elif notat[0] == "b" and len(notat) > 2 and board.variant == NORMALCHESS:
        tcord = cordDic[notat[-2:]]
        trank = int(notat[-1])
        # if from and to lines are not neighbours -> Bishop
        if abs(ord(notat[0]) - ord(notat[-2])) > 1:
            piece = chr2Sign[notat[0]]
            notat = notat[1:]
        # if from and to lines are neighbours (or the same) but to is an empty square
        # which can't be en-passant square target -> Bishop
        elif board.arBoard[tcord] == EMPTY and (
            (color == BLACK and trank != 3) or
            (color == WHITE and trank != 6)):
            piece = chr2Sign[notat[0]]
            notat = notat[1:]
        # elif "ba3", "bc3" ,"ba6", "bc6"
        # these can be Bishop or Pawn moves, but we don't try to introspect them (sorry)
        else:
            piece = PAWN
    else:
        piece = PAWN
        if notat[
                -1] in "18" and flag == NORMAL_MOVE and board.variant != SITTUYINCHESS:
            flag = QUEEN_PROMOTION

    if "x" in notat:
        notat, tcord = notat.split("x")
        if tcord not in cordDic:
            raise ParsingError(
                san,
                _("the captured cord (%s) is incorrect") % tcord,
                board.asFen())

        tcord = cordDic[tcord]

        if piece == PAWN:
            # If a pawn is attacking an empty cord, we assue it an enpassant
            if board.arBoard[tcord] == EMPTY:
                if (color == BLACK and 2 * 8 <= tcord < 3 * 8) or (
                        color == WHITE and 5 * 8 <= tcord < 6 * 8):
                    flag = ENPASSANT
                else:
                    raise ParsingError(
                        san, _("pawn capture without target piece is invalid"),
                        board.asFen())
    else:
        if not notat[-2:] in cordDic:
            raise ParsingError(
                san,
                _("the end cord (%s) is incorrect") % notat[-2:],
                board.asFen())

        tcord = cordDic[notat[-2:]]
        notat = notat[:-2]

    # In suicide promoting to king is valid, so
    # more than 1 king per side can exist !
    if board.variant != SUICIDECHESS and board.variant != GIVEAWAYCHESS and piece == KING:
        return newMove(board.kings[color], tcord, flag)

    # If there is any extra location info, like in the move Bexd1 or Nh3f4 we
    # want to know
    frank = None
    ffile = None
    if notat and notat[0] in reprRank:
        frank = int(notat[0]) - 1
        notat = notat[1:]
    if notat and notat[0] in reprFile:
        ffile = ord(notat[0]) - ord("a")
        notat = notat[1:]
    if notat and notat[0] in reprRank:
        frank = int(notat[0]) - 1
        notat = notat[1:]
        # we know all we want
        return newMove(frank * 8 + ffile, tcord, flag)

    if piece == PAWN:
        if (ffile is not None) and ffile != FILE(tcord):
            # capture
            if color == WHITE:
                fcord = tcord - 7 if ffile > FILE(tcord) else tcord - 9
            else:
                fcord = tcord + 7 if ffile < FILE(tcord) else tcord + 9
        else:
            if color == WHITE:
                pawns = board.boards[WHITE][PAWN]
                # In horde white pawns on first rank may move two squares also
                if board.variant == HORDECHESS and RANK(tcord) == 2 and not (
                        pawns & fileBits[FILE(tcord)] & rankBits[1]):
                    fcord = tcord - 16
                else:
                    fcord = tcord - 16 if RANK(tcord) == 3 and not (
                        pawns & fileBits[FILE(tcord)]
                        & rankBits[2]) else tcord - 8
            else:
                pawns = board.boards[BLACK][PAWN]
                fcord = tcord + 16 if RANK(tcord) == 4 and not (
                    pawns & fileBits[FILE(tcord)] & rankBits[5]) else tcord + 8

            if board.variant == SITTUYINCHESS and flag == QUEEN_PROMOTION:
                if pawns & fileBits[FILE(tcord)] & rankBits[RANK(tcord)]:
                    # in place promotion
                    return newMove(tcord, tcord, flag)
                else:
                    # queen move promotion (fcord have to be the closest cord of promotion zone)
                    fcord = sittuyin_promotion_fcord(board, tcord)
                    return newMove(fcord, tcord, flag)
        return newMove(fcord, tcord, flag)
    else:
        if board.pieceCount[color][piece] == 1:
            # we have only one from this kind if piece, so:
            fcord = firstBit(board.boards[color][piece])
            return newMove(fcord, tcord, flag)
        else:
            # We find all pieces who could have done it. (If san was legal, there should
            # never be more than one)
            moves = genPieceMoves(board, piece, tcord)
            if len(moves) == 1:
                return moves.pop()
            else:
                for move in moves:
                    f = FCORD(move)
                    if frank is not None and frank != RANK(f):
                        continue
                    if ffile is not None and ffile != FILE(f):
                        continue
                    board_clone = board.clone()
                    board_clone.applyMove(move)
                    if board_clone.opIsChecked():
                        continue
                    return move

    errstring = _("no %(piece)s is able to move to %(cord)s") % {
        "piece": reprPiece[piece],
        "cord": reprCord[tcord]
    }
    raise ParsingError(san, errstring, board.asFen())
示例#3
0
文件: lmove.py 项目: btrent/knave
def parseSAN (board, san):
    """ Parse a Short/Abbreviated Algebraic Notation string """
    notat = san

    color = board.color
    
    if notat == "--":
        return newMove(board.kings[color], board.kings[color], NULL_MOVE)

    if notat[-1] in ("+", "#"):
        notat = notat[:-1]
        # If '++' was used in place of #
        if notat[-1] == "+":
            notat = notat[:-1]
    
    flag = NORMAL_MOVE
    
    # If last char is a piece char, we assue it the promote char
    c = notat[-1]
    if c in ("K", "Q", "R", "B", "N", "k", "q", "r", "b", "n"):
        c = c.lower()
        if c == "k" and board.variant != SUICIDECHESS:
            raise ParsingError, (san, "invalid promoted piece", board.asFen())
            
        flag = chr2Sign[c] + 2
        if notat[-2] == "=":
            notat = notat[:-2]
        else: notat = notat[:-1]
    
    if len(notat) < 2:
        raise ParsingError, (san, _("the move needs a piece and a cord"), board.asFen())
    
    if notat[0] in "O0o":
        fcord = board.ini_kings[color]
        flag = KING_CASTLE if notat == "O-O" or notat == "0-0" or notat == "o-o" else QUEEN_CASTLE
        side = flag -QUEEN_CASTLE
        if FILE(fcord) == 3 and board.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS):
            side = 0 if side == 1 else 1
        if board.variant == FISCHERRANDOMCHESS:
            tcord = board.ini_rooks[color][side]
        else:
            tcord = board.fin_kings[color][side]
        return newMove (fcord, tcord, flag)

    # LAN is not allowed in pgn spec, but sometimes it occures
    if "-" in notat:
        notat = notat.replace("-", "")

    if "@" in notat:
        tcord = cordDic[notat[-2:]]
        if notat[0].islower():
            # Sjeng-ism
            piece = chr2Sign[notat[0]]
        else:
            piece = chrU2Sign[notat[0]]
        return newMove(piece, tcord, DROP)
    
    if notat[0] in ("Q", "R", "B", "K", "N"):
        piece = chrU2Sign[notat[0]]
        notat = notat[1:]
    else:
        piece = PAWN
        if notat[-1] in ("1", "8") and flag == NORMAL_MOVE:
            raise ParsingError, (
                    san, _("promotion move without promoted piece is incorrect"), board.asFen())
    
    if "x" in notat:
        notat, tcord = notat.split("x")
        if not tcord in cordDic:
            raise ParsingError, (
                    san, _("the captured cord (%s) is incorrect") % tcord, board.asFen())

        tcord = cordDic[tcord]

        if piece == PAWN:
            # If a pawn is attacking an empty cord, we assue it an enpassant
            if board.arBoard[tcord] == EMPTY:
                flag = ENPASSANT
    else:
        if not notat[-2:] in cordDic:
            raise ParsingError, (
                    san, "the end cord (%s) is incorrect" % notat[-2:], board.asFen())
        
        tcord = cordDic[notat[-2:]]
        notat = notat[:-2]
    
    # In suicide promoting to king is valid, so
    # more than 1 king per side can exist !
    if board.variant != SUICIDECHESS and piece == KING:
        return newMove(board.kings[color], tcord, flag)

    # If there is any extra location info, like in the move Bexd1 or Nh3f4 we
    # want to know
    frank = None
    ffile = None
    if notat and notat[0] in reprRank:
        frank = int(notat[0])-1
        notat = notat[1:]
    if notat and notat[0] in reprFile:
        ffile = ord(notat[0]) - ord("a")
        notat = notat[1:]
    if notat and notat[0] in reprRank:
        frank = int(notat[0])-1
        notat = notat[1:]
        # we know all we want
        return newMove(frank*8+ffile, tcord, flag)

    if piece == PAWN:
        if (ffile is not None) and ffile != FILE(tcord):
            # capture
            if color == WHITE:
                fcord = tcord-7 if ffile > FILE(tcord) else tcord-9
            else:
                fcord = tcord+7 if ffile < FILE(tcord) else tcord+9
        else:
            if color == WHITE:
                pawns = board.boards[WHITE][PAWN]
                fcord = tcord-16 if RANK(tcord)==3 and not (pawns & fileBits[FILE(tcord)] & rankBits[2]) else tcord-8
            else:
                pawns = board.boards[BLACK][PAWN]
                fcord = tcord+16 if RANK(tcord)==4 and not (pawns & fileBits[FILE(tcord)] & rankBits[5]) else tcord+8
        return newMove(fcord, tcord, flag)
    else:
        if board.pieceCount[color][piece] == 1:
            # we have only one from this kind if piece, so:
            fcord = firstBit(board.boards[color][piece])
            return newMove(fcord, tcord, flag)
        else:
            # We find all pieces who could have done it. (If san was legal, there should
            # never be more than one)
            moves = genPieceMoves(board, piece, tcord)
            if len(moves) == 1:
                return moves.pop()
            else:
                for move in moves:
                    f = FCORD(move)
                    if frank != None and frank != RANK(f):
                        continue
                    if ffile != None and ffile != FILE(f):
                        continue
                    board_clone = board.clone()
                    board_clone.applyMove(move)
                    if board_clone.opIsChecked():
                        continue
                    return move
    
    errstring = "no %s is able to move to %s" % (reprPiece[piece], reprCord[tcord])
    raise ParsingError, (san, errstring, board.asFen())
示例#4
0
def parseSAN(board, san):
    """ Parse a Short/Abbreviated Algebraic Notation string """
    notat = san

    color = board.color

    if notat == "--":
        return newMove(board.kings[color], board.kings[color], NULL_MOVE)

    if notat[-1] in "+#":
        notat = notat[:-1]
        # If '++' was used in place of #
        if notat[-1] == "+":
            notat = notat[:-1]

    flag = NORMAL_MOVE

    # If last char is a piece char, we assue it the promote char
    c = notat[-1]
    if c in "KQRBNSMFkqrbnsmf.":
        c = c.lower()
        if c == "k" and board.variant != SUICIDECHESS and board.variant != GIVEAWAYCHESS:
            raise ParsingError(san, _("invalid promoted piece"), board.asFen())
        elif c == "." and board.variant in (CAMBODIANCHESS, MAKRUKCHESS,
                                            SITTUYINCHESS):
            # temporary hack for xboard bug
            flag = QUEEN_PROMOTION
        else:
            flag = chr2Sign[c] + 2

        if notat[-2] == "=":
            notat = notat[:-2]
        else:
            notat = notat[:-1]

    if len(notat) < 2:
        raise ParsingError(san, _("the move needs a piece and a cord"),
                           board.asFen())

    if notat[0] in "O0o":
        fcord = board.ini_kings[color]
        flag = KING_CASTLE if notat in ("O-O", "0-0", "o-o", "OO", "00", "oo") else QUEEN_CASTLE
        side = flag - QUEEN_CASTLE
        if FILE(fcord) == 3 and board.variant in (WILDCASTLECHESS,
                                                  WILDCASTLESHUFFLECHESS):
            side = 0 if side == 1 else 1
        if board.variant == FISCHERRANDOMCHESS:
            tcord = board.ini_rooks[color][side]
        else:
            tcord = board.fin_kings[color][side]
        return newMove(fcord, tcord, flag)

    # LAN is not allowed in pgn spec, but sometimes it occures
    if "-" in notat:
        notat = notat.replace("-", "")

    if "@" in notat:
        tcord = cordDic[notat[-2:]]
        if notat[0].islower():
            # Sjeng-ism
            piece = chr2Sign[notat[0]]
        else:
            piece = chrU2Sign[notat[0]]
        return newMove(piece, tcord, DROP)

    # standard piece letters
    if notat[0] in "QRBKNSMF":
        piece = chrU2Sign[notat[0]]
        notat = notat[1:]
    # unambigious lowercase piece letters
    elif notat[0] in "qrknsm":
        piece = chr2Sign[notat[0]]
        notat = notat[1:]
    # a lowercase bishop letter or a pawn capture
    elif notat[0] == "b" and len(notat) > 2 and board.variant == NORMALCHESS:
        tcord = cordDic[notat[-2:]]
        trank = int(notat[-1])
        # if from and to lines are not neighbours -> Bishop
        if abs(ord(notat[0]) - ord(notat[-2])) > 1:
            piece = chr2Sign[notat[0]]
            notat = notat[1:]
        # if from and to lines are neighbours (or the same) but to is an empty square
        # which can't be en-passant square target -> Bishop
        elif board.arBoard[tcord] == EMPTY and ((color == BLACK and trank != 3) or (color == WHITE and trank != 6)):
            piece = chr2Sign[notat[0]]
            notat = notat[1:]
        # elif "ba3", "bc3" ,"ba6", "bc6"
        # these can be Bishop or Pawn moves, but we don't try to introspect them (sorry)
        else:
            piece = PAWN
    else:
        piece = PAWN
        if notat[-1] in "18" and flag == NORMAL_MOVE and board.variant != SITTUYINCHESS:
            raise ParsingError(
                san, _("promotion move without promoted piece is incorrect"),
                board.asFen())

    if "x" in notat:
        notat, tcord = notat.split("x")
        if tcord not in cordDic:
            raise ParsingError(san, _("the captured cord (%s) is incorrect") %
                               tcord, board.asFen())

        tcord = cordDic[tcord]

        if piece == PAWN:
            # If a pawn is attacking an empty cord, we assue it an enpassant
            if board.arBoard[tcord] == EMPTY:
                if (color == BLACK and 2 * 8 <= tcord < 3 * 8) or (color == WHITE and 5 * 8 <= tcord < 6 * 8):
                    flag = ENPASSANT
                else:
                    raise ParsingError(
                        san, _("pawn capture without target piece is invalid"),
                        board.asFen())
    else:
        if not notat[-2:] in cordDic:
            raise ParsingError(san, _("the end cord (%s) is incorrect") %
                               notat[-2:], board.asFen())

        tcord = cordDic[notat[-2:]]
        notat = notat[:-2]

    # In suicide promoting to king is valid, so
    # more than 1 king per side can exist !
    if board.variant != SUICIDECHESS and board.variant != GIVEAWAYCHESS and piece == KING:
        return newMove(board.kings[color], tcord, flag)

    # If there is any extra location info, like in the move Bexd1 or Nh3f4 we
    # want to know
    frank = None
    ffile = None
    if notat and notat[0] in reprRank:
        frank = int(notat[0]) - 1
        notat = notat[1:]
    if notat and notat[0] in reprFile:
        ffile = ord(notat[0]) - ord("a")
        notat = notat[1:]
    if notat and notat[0] in reprRank:
        frank = int(notat[0]) - 1
        notat = notat[1:]
        # we know all we want
        return newMove(frank * 8 + ffile, tcord, flag)

    if piece == PAWN:
        if (ffile is not None) and ffile != FILE(tcord):
            # capture
            if color == WHITE:
                fcord = tcord - 7 if ffile > FILE(tcord) else tcord - 9
            else:
                fcord = tcord + 7 if ffile < FILE(tcord) else tcord + 9
        else:
            if color == WHITE:
                pawns = board.boards[WHITE][PAWN]
                # In horde white pawns on first rank may move two squares also
                if board.variant == HORDECHESS and RANK(tcord) == 2 and not (
                        pawns & fileBits[FILE(tcord)] & rankBits[1]):
                    fcord = tcord - 16
                else:
                    fcord = tcord - 16 if RANK(tcord) == 3 and not (
                        pawns & fileBits[FILE(tcord)] & rankBits[2]) else tcord - 8
            else:
                pawns = board.boards[BLACK][PAWN]
                fcord = tcord + 16 if RANK(tcord) == 4 and not (
                    pawns & fileBits[FILE(tcord)] & rankBits[5]) else tcord + 8
            if board.variant == SITTUYINCHESS and flag == QUEEN_PROMOTION and \
                    (pawns & fileBits[FILE(tcord)] & rankBits[RANK(tcord)]):
                    return newMove(tcord, tcord, flag)
        return newMove(fcord, tcord, flag)
    else:
        if board.pieceCount[color][piece] == 1:
            # we have only one from this kind if piece, so:
            fcord = firstBit(board.boards[color][piece])
            return newMove(fcord, tcord, flag)
        else:
            # We find all pieces who could have done it. (If san was legal, there should
            # never be more than one)
            moves = genPieceMoves(board, piece, tcord)
            if len(moves) == 1:
                return moves.pop()
            else:
                for move in moves:
                    f = FCORD(move)
                    if frank is not None and frank != RANK(f):
                        continue
                    if ffile is not None and ffile != FILE(f):
                        continue
                    board_clone = board.clone()
                    board_clone.applyMove(move)
                    if board_clone.opIsChecked():
                        continue
                    return move

    errstring = "no %s is able to move to %s" % (reprPiece[piece],
                                                 reprCord[tcord])
    raise ParsingError(san, errstring, board.asFen())