Search code examples
pythondictionarychess

How write correct condition? (FEN chess)


I'm trying to build a simple chess game in python, I've created a dictionary with positions on the pawns. I want to change dictionary into Forsyth–Edwards Notation (FEN) (the position of the pawns).

curr_position = {'a8': 'r', 'b8': 'n', 'c8': 'b', 'd8': 'q', 'e8': 'k', 'f8': 'b', 'g8': 'n', 'h8': 'r', 'a7': 'p', 'b7': 'p', 'c7': 'p', 'd7': 'p', 'e7': 'p', 'f7': 'p', 'g7': 'p', 'h7': 'p', 'a6': None, 'b6': None, 'c6': None, 'd6': None, 'e6': None, 'f6': None, 'g6': None, 'h6': None, 'a5': None, 'b5': None, 'c5': None, 'd5': None, 'e5': None, 'f5': None, 'g5': None, 'h5': None, 'a4': None, 'b4': None, 'c4': None, 'd4': None, 'e4': None, 'f4': None, 'g4': None, 'h4': None, 'a3': None, 'b3': None, 'c3': None, 'd3': None, 'e3': None, 'f3': None, 'g3': None, 'h3': None, 'a2': 'P', 'b2': 'P', 'c2': 'P', 'd2': 'P', 'e2': 'P', 'f2': 'P', 'g2': 'P', 'h2': 'P', 'a1': 'R', 'b1': 'N', 'c1': 'B', 'd1': 'Q', 'e1': 'K', 'f1': 'B', 'g1': 'N', 'h1': 'R'}

the above dictionary should return something like this in FEN:

"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"

I wrote

def curr_fen():
    positions2 = ""
    pos_temp = 0
    prev_value = 0
    for ix, value in enumerate(curr_position.values()):
        if ix % 8 == 0 and ix != 0:
            positions2 += "/"
            pos_temp = 0
        if not value:
            pos_temp += 1
            positions2 += str(pos_temp)
        else:
            positions2 += value
    return positions2

print(curr_fen())

which returns

"nbqkbnr/pppppppp/12345678/12345678/12345678/12345678/PPPPPPPP/RNBQKBNR" 

which is incorrect. How can I adjust my function to return the desired output?


Solution

  • As you step through the board positions you are keeping track of consecutive empty squares with pos_temp.

    The number of consecutive empty squares should be added to the string whenever you find a square with a piece or you move to the next rank (if the previous rank was completely empty).

    pos_temp needs to be reset to zero whenever it is added to the string.

    I've made some additions and one deletion to your code.

    def curr_fen():
        positions2 = ""
        pos_temp = 0
        prev_value = 0
        for ix, value in enumerate(curr_position.values()):
            if ix % 8 == 0 and ix != 0:
                if pos_temp > 0:
                    positions2 = positions2 + str(pos_temp)
                positions2 += "/"
                pos_temp = 0
            if not value:
                pos_temp += 1
                # positions2 += str(pos_temp)
            else:
                if pos_temp > 0:
                    positions2 = positions2 + str(pos_temp)
                    pos_temp = 0
                positions2 += value
    
        return positions2
    

    Your solution relies on the dictionary being constructed in a specific order. Here are a couple of alternatives that don't rely on the order of the original dictionary. They both have an intermediate step of transforming the original dictionary.

    import collections
    def f(d):
        rank_order = '87654321'
        file_order = 'abcdefgh'
        new = collections.defaultdict(dict)
        for (file,rank),piece in d.items():
            new[rank].update({file:piece})
        board = []
        for rank in rank_order:
            s = ''
            temp = 0
            for file in file_order:
                piece = new[rank][file]
                if piece is None:
                    temp += 1
                else:
                    if temp > 0:
                        s = f'{s}{temp}'
                        temp = 0
                    s = f'{s}{piece}'
            if temp > 0:
                s = f'{s}{temp}'
            board.append(s)
        return '/'.join(board)
    
    import itertools
    def g(d):
        rank_order = '87654321'
        file_order = 'abcdefgh'
        new1 = collections.defaultdict(list)
        for (file,rank),piece in d.items():
            new1[rank].append((file,piece))
        board = []
        for rank in rank_order:
            s = ''
            positions = new1[rank]
            positions.sort()
            for piece,items in itertools.groupby(positions,key=lambda x:x[1]):
                n_pieces = len(list(items))
                if piece is None:
                    s = f'{s}{n_pieces}'
                else:
                    s = f'{s}{piece * n_pieces}'
            board.append(s)
        return '/'.join(board)