Moving Chessboard Pieces with Pygame

I am working in a small team of friends learning Python. To learn the basics of it we decided to build a chess game. This is the third post about it. You can see part 1 and part 2 here.

In this post, I will cover how I move the chess pieces. I do not know if this is the correct way, but for now, it works. I need to create a chessboard class and also move some of the properties of the board to there such as the squares, but for now, the chessboard is just a method in main.py. The code below shows part of the main loop that deals with mouse clicks.

if event.type == MOUSEBUTTONUP:
    x, y = pygame.mouse.get_pos()
    clicked_square = get_square_for_position(x, y)

    # Checking the clicked squares length to determine if first or second click.
    if first_clicked_square is None and clicked_square.piece is not None:
        # We have a square with a piece and it's the first click
        first_clicked_square = clicked_square
    elif first_clicked_square is not None and clicked_square.piece is not None:
        # We have a piece and it's the second click.
        print("Cannot move here. Space occupied by", clicked_square.piece.piece_type)
        first_clicked_square = None
    elif first_clicked_square is not None and clicked_square.piece is None:
        # We do not have a piece and it's the second click. Move the piece
        print("Moving", first_clicked_square.piece.piece_type, "From", first_clicked_square.position, "To", clicked_square.position)
        clicked_square.piece = first_clicked_square.piece
        first_clicked_square.piece = None
        first_clicked_square = None
        redraw_board()

Line 1 checks for mouse button up which is an event.

Line 2 gets the position of the cursor when mouse button up occurred, and stores the x position in x, and y in y.

Line 3 passes the x and y coordinates to a method that finds which square was clicked on. That method returns the square object. You can see more of how that works in part 2 from yesterday.

The if/elif statements on lines 6-19 do some basic logic. If it’s the first mouse click, and the square contains a piece, then the square, which encompasses the piece as a property, is stored in a variable from out of the loop called first_clicked_square.

The first elif checks if first_clicked_square is not empty. If it contains a square, then this must be the second click. It also checks if the square selected for that second click contains a piece. If it does, then it prints a message saying that the move is not valid. The first_clicked_square is then emptied allowing the user to select another piece to move.

The final elif is used for the second click. It checks if the first_clicked_square contains a square and also if the second square selected is empty. If both true then it prints a message to say what piece is moving and where it is moving to. It then copies the piece from the first square to the second square and then erases the piece from the first square.

The last line calls a method that redraws the board. The method is below:

def redraw_board():
    for row in chess_board:
        for square in row:
            redraw_surf = pygame.Surface((square.width_height, square.width_height))

            if square.chess_colour == ChessColour.WHITE:
                redraw_surf.fill((203, 206, 145))
            else:
                redraw_surf.fill((118, 82, 139))

            screen.blit(redraw_surf, (square.x_start, square.y_start))
            if square.piece is not None:
                screen.blit(square.piece.piece_image, (square.x_start, square.y_start))
    pygame.display.flip()
    pygame.display.update()

It is similar to the initial board drawer, but instead of the logic that figures out if a chess piece needs adding to a square, it skips over that because the board already contains the chess pieces in the correct location. For that reason, it just draws the square, and if a piece is within the square, that is also drawn to the screen.

I need to study more on surfaces, display, flip, update, etc… as some of this might be redundant and I might be doing it wrong, but performance is just fine with no flickering.

Challenges

One challenge I have at the moment is with the draw init board and redraw board methods. Both of them do a similar thing. I need to put these into another class and then have methods that each of them shares, such as the square colour to use which is repeated in both methods.

Of course, the board and pieces have no rules associated with them. I think the next step for the team will be to start looking at limiting where pieces can move, such as a rook not being able to move up or down the board if a pawn is in the way, and a knight only being able to move 2 across and 1 up, for example.

When the rules are in place we will probably investigate how to take opponent pieces and when that is done, look at implementing rules for check and checkmate, castling, promoting, etc…

After that, it will likely move on to making it to a two-player game, perhaps with AI, although I am not too familiar with AI, so that will be something fun to scratch the surface of.

Leave a comment