-1

I'm trying to re-write a chess game with some improvements and am having problems since i added sprites. In the original game i drew black or white circles with initials to represent the pieces. These were meant to be temporary and were enough to help me test piece movement etc. Now that i'm linking images to the piece objects, when i try to copy the list they are in, i get the following error:

TypeError: cannot pickle 'pygame.surface.Surface' object

I've never used sprites before and frankly, i don't understand what i'm reading when i google this error message. The parts of the code i think are relevant are below.

There is a super class called Piece:

class Piece():
    
    def __init__(self, color):
        # when making a new piece, establish which row, column and colour it is
        self.row = 0
        self.col = 0
        self.color = color
        # drawing co-ordinates of piece
        self.x = 0
        self.y = 0
        self.calculate_position()

    # calculates x and y drawing co-ordinates of a piece based on the row and column it's in  
    def calculate_position(self):
        # find exact centre of the square (as circles are drawn from the centre out)
        self.x = SQUARE_SIZE * self.col + SQUARE_SIZE // 2 + PADDING
        self.y = SQUARE_SIZE * self.row + SQUARE_SIZE // 2 + PADDING
        
    # keep track of piece location when they move
    def assign_location(self, row, col):
        # self.row and col must equal the row and col of the new position
        self.row = row
        self.col = col
        # use the above method to calculate where in the new square to draw the piece
        self.calculate_position()

    # draw image from assets according to piece self.image
    def draw_piece(self, win):
        x = self.x - self.image.get_width()//2
        y = self.y - self.image.get_height()//2
        if self.initial == "P":
            y = self.y - 21
        if self.initial == "R":
            y = self.y - 26
        win.blit(self.image, (x, y))

This is inherited by a King, Queen, Rook etc class which all look something like this:

class King(Piece):
    def __init__(self, color):
        super().__init__(color)
        
        self.row = 0
        self.col = 0
        self.initial = "K"
        self.value = 0
        self.moves_made = 0

        if self.color == WHITE:
            self.image = W_KING
        else:
            self.image = B_KING

The self.image referred to here is imported:

W_KING = pygame.transform.scale(pygame.image.load('assets/w_king.png'), (60, 60))

The Board class creates an array to store the piece objects and is ultimately responsible for moving them around within the array:

class Board():
    def __init__(self, win):
        self.board = []
        self.flipped_board = False
        self.initialize_starting_position(win)
        
    # create initial layout of board with pieces in starting position
    def initialize_starting_position(self, win):
        # create a two-dimensional array comprising 8 lists, each to represent a row on the board
        for _ in range(ROWS):
            self.board.append([])

        # assign starting position of pieces within self.board
        self.board[0] = [Rook(BLACK), Knight(BLACK), Bishop(BLACK), Queen(BLACK), King(BLACK),       Bishop(BLACK), Knight(BLACK), Rook(BLACK)]
        self.board[1] = [Pawn(BLACK), Pawn(BLACK), Pawn(BLACK), Pawn(BLACK), Pawn(BLACK), Pawn(BLACK), Pawn(BLACK), Pawn(BLACK)]
        self.board[2] = [0, 0, 0, 0, 0, 0, 0, 0]
        self.board[3] = [0, 0, 0, 0, 0, 0, 0, 0]
        self.board[4] = [0, 0, 0, 0, 0, 0, 0, 0]
        self.board[5] = [0, 0, 0, 0, 0, 0, 0, 0]
        self.board[6] = [Pawn(WHITE), Pawn(WHITE), Pawn(WHITE), Pawn(WHITE), Pawn(WHITE), Pawn(WHITE), Pawn(WHITE), Pawn(WHITE)]
        self.board[7] = [Rook(WHITE), Knight(WHITE), Bishop(WHITE), Queen(WHITE), King(WHITE), Bishop(WHITE), Knight(WHITE), Rook(WHITE)]

        # iterate through self.board assigning pieces their row and column location 
        for row in range(ROWS):
            for col in range(COLS):
                square = self.board[row][col]
                if square != 0:
                    square.assign_location(row, col)

The draw_board method then uses the index of each list to draw the pieces to the correct part of the board on the screen:

# draw the squares and pieces to the board
    def draw_board(self, win):
        # Draw chequered pattern of board
        win.fill(BACKGROUND)
        # draw a large dark brown rectangle to form base colour of board - co-ordinates (x, y, w, h)
        pygame.draw.rect(win, DARK, (PADDING, PADDING, HEIGHT - (PADDING * 2), HEIGHT - (PADDING * 2)))
        # draw small squares on top of large rectangle to form chequered pattern 
        for row in range(ROWS):
            # when 'row' is even, start drawing light squares from that column, then move accross 2 columns
            for col in range(row % 2, COLS, 2):
                pygame.draw.rect(win, LIGHT, (row * SQUARE_SIZE + PADDING, col * SQUARE_SIZE + PADDING, SQUARE_SIZE, SQUARE_SIZE))

        # Draw notation to squares on board
        letters = ["a", "b", "c", "d", "e", "f", "g", "h"]
        numbers = ["8", "7", "6", "5", "4", "3", "2", "1"]
        # reverse order of number and letter lists if board flipped
        if self.flipped_board == True:
            letters.reverse()
            numbers.reverse()
        # assign location for notation depending on orientation of board
        for row in range(ROWS):
            letter = letters[row]
            letter_img = NOTATION_FONT.render(letter, True, WHITE)
            win.blit(letter_img, (row * SQUARE_SIZE + (SQUARE_SIZE + 6), HEIGHT - (PADDING + 16)))
            number = numbers[row]  
            img = NOTATION_FONT.render(number, True, WHITE)
            win.blit(img, (PADDING + 1, row * SQUARE_SIZE + PADDING + 1))


        # Draw pieces to squares on board
        # if board flipped, copy self.board, reverse its contents and re-assign row and col to copied piece objects
        # for the purpose of obtaining x, y co-ordinates for drawing pieces on flipped board 
        **if self.flipped_board == True:
            board_copy = copy.deepcopy(self.board)
            for row in board_copy:
                row.reverse()
            board_copy.reverse()
            board = board_copy
            # iterate through board_copy assigning pieces their row and column location 
            for row in range(ROWS):
                for col in range(COLS):
                    square = board[row][col]
                    if square != 0:
                        square.assign_location(row, col)**
        else:
            board = self.board
        # iterate through rows and columns drawing pieces as set out in board
        for row in range(ROWS):
            for col in range(COLS):
                square = board[row][col]
                if square != 0:
                    square.draw_piece(win)

It's the section in bold which is causing the problem. I'm trying to achieve a mirror-image of self.board purely for the purpose of displaying the pieces in the reverse order, to replicate viewing the chessboard from the other side. I don't want to affect the piece locations within the actual self.board which is why i'm making a deepcopy.

Could anyone explain what is happening that causes this error?

AndyM
  • 1
  • 3
  • Welcome to [so]. We don't provide debugging services. The following references give advice on debugging your code. [How to debug small programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/), [Six Debugging Techniques for Python Programmers](https://medium.com/techtofreedom/six-debugging-techniques-for-python-programmers-cb25a4baaf4b) or [Ultimate Guide to Python Debugging](https://towardsdatascience.com/ultimate-guide-to-python-debugging-854dea731e1b) – itprorh66 May 18 '23 at 23:16

1 Answers1

0

I found a solution to this problem which works in my case. The issue it seemed was trying to copy the object whilst it was associated with an image. This is not something Pygame was able to do in the way my code was structured. To overcome this, I stored all the sprites in a dictionary, with the piece color as the key. My Piece objects now have a self.image value of None until the point of drawing them to the window. This allows me to make a deepcopy without errors. When drawing the piece, I call a method which assigns their self.image the correct sprite depending on their self.color and self.initial values, draw the piece, then re-assign self.image to None. This avoids the same issue occurring when the screen refreshes. I hope this helps anyone experiencing similar issues.

# iterate through rows and columns drawing pieces as set out in board
    for row in range(ROWS):
        for col in range(COLS):
            square_contents = board[row][col]
            if square_contents != 0:
                self.assign_sprite(square_contents)
                square_contents.draw_piece(win)
                square_contents.image = None
    
def assign_sprite(self, piece):
    if piece.color == WHITE:
        piece.image = SPRITES[f"WHITE_{piece.initial}"]
    else:
        piece.image = SPRITES[f"BLACK_{piece.initial}"]
AndyM
  • 1
  • 3