pythonchesspython-chess

Python chess: Check for passed pawns


In a chess position, I wish to check whether any passed pawn exists for white.

Is it possible to do so using the python-chess library? If not, how can I implement it?

def checkForPassedPawn(position: chess.Board, side_to_move: chess.Color):
    # ... check for passed pawn
    # return a boolean value

I could not find any built-in method that detects passed pawns.


Solution

  • You'll have to look at the pawn positions yourself. There are many ways to do that. For instance, you could take the board's string representation as a starting point:

    r n b q k b n r
    p p . . . p p p
    . . . . . . . .
    . . p P p . . .
    . . . . . P . .
    . . . . . . . .
    P P P P . . P P
    R N B Q K B N R
    

    This is the kind of string you get with str(position).

    Then you could put each column in a separate list:

    lines = str(position).replace(" ", "").splitlines()
    columns = list(zip(*lines))
    

    This gives you:

    [
        ('r', 'p', '.', '.', '.', '.', 'P', 'R'),
        ('n', 'p', '.', '.', '.', '.', 'P', 'N'),
        ('b', '.', '.', 'p', '.', '.', 'P', 'B'),
        ('q', '.', '.', 'P', '.', '.', 'P', 'Q'),
        ('k', '.', '.', 'p', '.', '.', '.', 'K'),
        ('b', 'p', '.', '.', 'P', '.', '.', 'B'),
        ('n', 'p', '.', '.', '.', '.', 'P', 'N'),
        ('r', 'p', '.', '.', '.', '.', 'P', 'R')
    ]
    

    It the current player is white, you then can check for the left most "P" in each tuple where it has a "p" more left to it, either in the current tuple, the previous one, or the next one.

    For the black player, you would use a similar logic and it might be useful to first reverse the tuples in that case.

    Here is an implementation of that idea:

    import chess
    
    def checkForPassedPawn(position: chess.Board, side_to_move: chess.Color):
        selfpawn = "pP"[side_to_move]
        otherpawn = "Pp"[side_to_move]
        lines = str(position).replace(" ", "").splitlines()
        if side_to_move == chess.BLACK:
            lines.reverse()
        # turn rows into columns and vice versa
        columns = list(zip(*lines))
        for colnum, col in enumerate(columns):
            if selfpawn in col:
                rownum = col.index(selfpawn)
                if (otherpawn not in col[:rownum]
                        and (colnum == 0 or otherpawn not in columns[colnum-1][:rownum])
                        and (colnum == 7 or otherpawn not in columns[colnum+1][:rownum])):
                    return f"{'abcdefgh'[colnum]}{rownum+1}"
        
    position = chess.Board()
    position.push_san("e4")
    position.push_san("d5")
    position.push_san("f4")
    position.push_san("e5")
    position.push_san("exd5")
    position.push_san("c5")  # Now white pawn at d5 is a passed pawn 
    print(position)
    passedpawn = checkForPassedPawn(position, chess.WHITE)
    print("passed white pawn:", passedpawn)
    position.push_san("d4")
    position.push_san("e4")  # Now black pawn at e4 is a passed pawn
    print(position)
    passedpawn = checkForPassedPawn(position, chess.BLACK)
    print("passed black pawn:", passedpawn)
    

    Output:

    r n b q k b n r
    p p . . . p p p
    . . . . . . . .
    . . p P p . . .
    . . . . . P . .
    . . . . . . . .
    P P P P . . P P
    R N B Q K B N R
    passed white pawn: d4
    r n b q k b n r
    p p . . . p p p
    . . . . . . . .
    . . p P . . . .
    . . . P p P . .
    . . . . . . . .
    P P P . . . P P
    R N B Q K B N R
    passed black pawn: e4