Building a Chess Game in Python

Amit Dhanwani on December 16, 2023

Building a Chess Game in Python

Introduction:

Future employment prospects for youngsters who learn Python Programming might be very diverse. With Python being used in many sophisticated disciplines, many new employment possibilities have emerged and will continue to emerge in the future.

You should start introducing Python programming for kids right off the bat. It would strengthen their academic performance and their chances of securing a prosperous future profession. Even if your children decide not to embrace computer programming in the future, training them in Python today will enable them to develop life skills.

These days, creating video games may be quite lucrative. It can also be utilized as an educational and advertisement tool. Game creation may be really enjoyable and involves a lot of math, logic, physics, AI, and other subjects.

Python can also be used to make games, with features like collision detection, music, backgrounds, and sprites among many others.

PyGame is one of the greatest modules for programming games in Python and is used for game development.

Logo of Pygame

 

What is the Python PyGame Module?

A cross-platform collection of Python modules called Pygame is intended for use in game development. It comes with music and graphics libraries made specifically for use with the Python programming language.

A collection of open-source and free Python modules is called PyGame. It may also be used to create games, as the name would imply. We may code the games and then convert them into an executable file by using certain instructions.

This way, you can show friends what you have been working on. It comes with music and graphics libraries made specifically for use with the Python programming language. The most recent version of PyGame at the time this article was written was 2.0.1.

This blog post will demonstrate how to create a chess game with the Python PyGame package.

Basic Functions Used in the Python PyGame Module:

  1. init(): The PyGame module is started using the command pygame.init().
  2. display.set_mode((width, height)): Using this command, the user may create a window with the specified dimensions (width, height). The object on which we execute various graphical operations is a Surface Object, which is the return value.
  3. display.set_caption(title = “ ”): This command is used to set the title of the window/ board.
  4. event.get(): The event queue is emptied using this. The operating system will see the game as unresponsive if we fail to call this, causing the window messages to build up.
  5. QUIT: This is used to terminate the event when we click on the close button at the corner of the window.

Add Image to the PyGame Window:

Rendering the game item onto the surface is called blitting. We should render the game object when we’ve created it. The black window will appear if the game item is not rendered. Blitting may be done using blit(), a function in PyGame. Since it is among the slowest functions in a game.

we must use caution while utilizing it and avoid blitting too much onto the screen in a single frame.

Syntax:

blit(src, dest)

src: It is the source of the image which we want to display on the screen.

dest: It is the coordinates where we want our image to be displayed.

Program for Building Chess Game Using Python PyGame:

 

# Importing Modules
import pygame
# Initialising pygame module
pygame.init()
# Setting width and height of the Chess Game screen
WIDTH = 800
HEIGHT = 800
screen = pygame.display.set_mode([WIDTH, HEIGHT])
pygame.display.set_caption('Two-Player Chess Game')
font = pygame.font.Font('freesansbold.ttf', 20)
medium_font = pygame.font.Font('freesansbold.ttf', 40)
big_font = pygame.font.Font('freesansbold.ttf', 50)
timer = pygame.time.Clock()
fps = 60
# game variables and images
white_pieces = ['rook', 'knight', 'bishop', 'king', 'queen', 'bishop', 'knight', 'rook',

                'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn']

white_locations = [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0),

                   (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)]

black_pieces = ['rook', 'knight', 'bishop', 'king', 'queen', 'bishop', 'knight', 'rook',

                'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn']

black_locations = [(0, 7), (1, 7), (2, 7), (3, 7), (4, 7), (5, 7), (6, 7), (7, 7),

                   (0, 6), (1, 6), (2, 6), (3, 6), (4, 6), (5, 6), (6, 6), (7, 6)]

captured_pieces_white = []
captured_pieces_black = []

# 0 - whites turn no selection: 1-whites turn piece selected: 2- black turn no selection, 3 - black turn piece selected
turn_step = 0
selection = 100
valid_moves = []
# load in game piece images (queen, king, rook, bishop, knight, pawn) x 2
black_queen = pygame.image.load('./images/black queen.png')
black_queen = pygame.transform.scale(black_queen, (80, 80))
black_queen_small = pygame.transform.scale(black_queen, (45, 45))
black_king = pygame.image.load('./images/black king.png')
black_king = pygame.transform.scale(black_king, (80, 80))
black_king_small = pygame.transform.scale(black_king, (45, 45))
black_rook = pygame.image.load('./images/black rook.png')
black_rook = pygame.transform.scale(black_rook, (80, 80))
black_rook_small = pygame.transform.scale(black_rook, (45, 45))
black_bishop = pygame.image.load('./images/black bishop.png')
black_bishop = pygame.transform.scale(black_bishop, (80, 80))
black_bishop_small = pygame.transform.scale(black_bishop, (45, 45))
black_knight = pygame.image.load('./images/black knight.png')
black_knight = pygame.transform.scale(black_knight, (80, 80))
black_knight_small = pygame.transform.scale(black_knight, (45, 45))
black_pawn = pygame.image.load('./images/black pawn.png')
black_pawn = pygame.transform.scale(black_pawn, (65, 65))
black_pawn_small = pygame.transform.scale(black_pawn, (45, 45))
white_queen = pygame.image.load('./images/white queen.png')
white_queen = pygame.transform.scale(white_queen, (80, 80))
white_queen_small = pygame.transform.scale(white_queen, (45, 45))
white_king = pygame.image.load('./images/white king.png')
white_king = pygame.transform.scale(white_king, (80, 80))
white_king_small = pygame.transform.scale(white_king, (45, 45))
white_rook = pygame.image.load('./images/white rook.png')
white_rook = pygame.transform.scale(white_rook, (80, 80))
white_rook_small = pygame.transform.scale(white_rook, (45, 45))
white_bishop = pygame.image.load('./images/white bishop.png')
white_bishop = pygame.transform.scale(white_bishop, (80, 80))
white_bishop_small = pygame.transform.scale(white_bishop, (45, 45))
white_knight = pygame.image.load('./images/white knight.png')
white_knight = pygame.transform.scale(white_knight, (80, 80))
white_knight_small = pygame.transform.scale(white_knight, (45, 45))
white_pawn = pygame.image.load('./images/white pawn.png')
white_pawn = pygame.transform.scale(white_pawn, (65, 65))
white_pawn_small = pygame.transform.scale(white_pawn, (45, 45))

white_images = [white_pawn, white_queen, white_king,

                white_knight, white_rook, white_bishop]

small_white_images = [white_pawn_small, white_queen_small, white_king_small, white_knight_small,

                      white_rook_small, white_bishop_small]

black_images = [black_pawn, black_queen, black_king,

                black_knight, black_rook, black_bishop]

small_black_images = [black_pawn_small, black_queen_small, black_king_small, black_knight_small,

                      black_rook_small, black_bishop_small]

 piece_list = ['pawn', 'queen', 'king', 'knight', 'rook', 'bishop']

 # check variables/ flashing counter
counter = 0
winner = ''
game_over = False

# draw main game board
def draw_board():

    for i in range(32):

        column = i % 4

        row = i // 4

        if row % 2 == 0:

            pygame.draw.rect(screen, 'light gray', [

                             600 - (column * 200), row * 100, 100, 100])

        else:

            pygame.draw.rect(screen, 'light gray', [

                             700 - (column * 200), row * 100, 100, 100])

        pygame.draw.rect(screen, 'gray', [0, 800, WIDTH, 100])

        pygame.draw.rect(screen, 'gold', [0, 800, WIDTH, 100], 5)

        pygame.draw.rect(screen, 'gold', [800, 0, 200, HEIGHT], 5)

        status_text = ['White: Select a Piece to Move!', 'White: Select a Destination!',

                       'Black: Select a Piece to Move!', 'Black: Select a Destination!']

        screen.blit(big_font.render(

            status_text[turn_step], True, 'black'), (20, 820))

        for i in range(9):

            pygame.draw.line(screen, 'black', (0, 100 * i), (800, 100 * i), 2)

            pygame.draw.line(screen, 'black', (100 * i, 0), (100 * i, 800), 2)

        screen.blit(medium_font.render('FORFEIT', True, 'black'), (810, 830))

  
# draw pieces onto board
def draw_pieces():

    for i in range(len(white_pieces)):

        index = piece_list.index(white_pieces[i])

        if white_pieces[i] == 'pawn':

            screen.blit(

                white_pawn, (white_locations[i][0] * 100 + 22, white_locations[i][1] * 100 + 30))

        else:

            screen.blit(white_images[index], (white_locations[i]

                        [0] * 100 + 10, white_locations[i][1] * 100 + 10))

        if turn_step < 2:

            if selection == i:

                pygame.draw.rect(screen, 'red', [white_locations[i][0] * 100 + 1, white_locations[i][1] * 100 + 1,

                                                 100, 100], 2)

 

    for i in range(len(black_pieces)):

        index = piece_list.index(black_pieces[i])

        if black_pieces[i] == 'pawn':

            screen.blit(

                black_pawn, (black_locations[i][0] * 100 + 22, black_locations[i][1] * 100 + 30))

        else:

            screen.blit(black_images[index], (black_locations[i]

                        [0] * 100 + 10, black_locations[i][1] * 100 + 10))

        if turn_step >= 2:

            if selection == i:

                pygame.draw.rect(screen, 'blue', [black_locations[i][0] * 100 + 1, black_locations[i][1] * 100 + 1,

                                                  100, 100], 2)

 
# function to check all pieces valid options on board
def check_options(pieces, locations, turn):
    moves_list = []

    all_moves_list = []

    for i in range((len(pieces))):

        location = locations[i]

        piece = pieces[i]

        if piece == 'pawn':

            moves_list = check_pawn(location, turn)

        elif piece == 'rook':

            moves_list = check_rook(location, turn)

        elif piece == 'knight':

            moves_list = check_knight(location, turn)

        elif piece == 'bishop':

            moves_list = check_bishop(location, turn)

        elif piece == 'queen':

            moves_list = check_queen(location, turn)

        elif piece == 'king':

            moves_list = check_king(location, turn)

        all_moves_list.append(moves_list)

    return all_moves_list

 # check king valid moves
def check_king(position, color):

    moves_list = []

    if color == 'white':

        enemies_list = black_locations

        friends_list = white_locations

    else:

        friends_list = black_locations

        enemies_list = white_locations

    # 8 squares to check for kings, they can go one square any direction

    targets = [(1, 0), (1, 1), (1, -1), (-1, 0),

               (-1, 1), (-1, -1), (0, 1), (0, -1)]

    for i in range(8):

        target = (position[0] + targets[i][0], position[1] + targets[i][1])

        if target not in friends_list and 0 <= target[0] <= 7 and 0 <= target[1] <= 7:

            moves_list.append(target)

    return moves_list

 # check queen valid moves
def check_queen(position, color):

    moves_list = check_bishop(position, color)

    second_list = check_rook(position, color)

    for i in range(len(second_list)):

        moves_list.append(second_list[i])

    return moves_list

 # check bishop moves
def check_bishop(position, color):

    moves_list = []

    if color == 'white':

        enemies_list = black_locations

        friends_list = white_locations

    else:

        friends_list = black_locations

        enemies_list = white_locations

    for i in range(4):  # up-right, up-left, down-right, down-left

        path = True

        chain = 1

        if i == 0:

            x = 1

            y = -1

        elif i == 1:

            x = -1

            y = -1

        elif i == 2:

            x = 1

            y = 1

        else:

            x = -1

            y = 1

        while path:

            if (position[0] + (chain * x), position[1] + (chain * y)) not in friends_list and \

                    0 <= position[0] + (chain * x) <= 7 and 0 <= position[1] + (chain * y) <= 7:

                moves_list.append(

                    (position[0] + (chain * x), position[1] + (chain * y)))

                if (position[0] + (chain * x), position[1] + (chain * y)) in enemies_list:

                    path = False

                chain += 1

            else:

                path = False

    return moves_list

 # check rook moves
def check_rook(position, color):

    moves_list = []

    if color == 'white':

        enemies_list = black_locations

        friends_list = white_locations

    else:

        friends_list = black_locations

        enemies_list = white_locations

    for i in range(4):  # down, up, right, left

        path = True

        chain = 1

        if i == 0:

            x = 0

            y = 1

        elif i == 1:

            x = 0

            y = -1

        elif i == 2:

            x = 1

            y = 0

        else:

            x = -1

            y = 0

        while path:

            if (position[0] + (chain * x), position[1] + (chain * y)) not in friends_list and \

                    0 <= position[0] + (chain * x) <= 7 and 0 <= position[1] + (chain * y) <= 7:

                moves_list.append(

                    (position[0] + (chain * x), position[1] + (chain * y)))

                if (position[0] + (chain * x), position[1] + (chain * y)) in enemies_list:

                    path = False

                chain += 1

            else:

                path = False

    return moves_list

# check valid pawn moves
def check_pawn(position, color):

    moves_list = []

    if color == 'white':

        if (position[0], position[1] + 1) not in white_locations and \

                (position[0], position[1] + 1) not in black_locations and position[1] < 7:

            moves_list.append((position[0], position[1] + 1))

        if (position[0], position[1] + 2) not in white_locations and \

                (position[0], position[1] + 2) not in black_locations and position[1] == 1:

            moves_list.append((position[0], position[1] + 2))

        if (position[0] + 1, position[1] + 1) in black_locations:

            moves_list.append((position[0] + 1, position[1] + 1))

        if (position[0] - 1, position[1] + 1) in black_locations:

            moves_list.append((position[0] - 1, position[1] + 1))

    else:

        if (position[0], position[1] - 1) not in white_locations and \

                (position[0], position[1] - 1) not in black_locations and position[1] > 0:

            moves_list.append((position[0], position[1] - 1))

        if (position[0], position[1] - 2) not in white_locations and \

                (position[0], position[1] - 2) not in black_locations and position[1] == 6:

            moves_list.append((position[0], position[1] - 2))

        if (position[0] + 1, position[1] - 1) in white_locations:

            moves_list.append((position[0] + 1, position[1] - 1))

        if (position[0] - 1, position[1] - 1) in white_locations:

            moves_list.append((position[0] - 1, position[1] - 1))

    return moves_list

 # check valid knight moves
def check_knight(position, color):

    moves_list = []

    if color == 'white':

        enemies_list = black_locations

        friends_list = white_locations

    else:

        friends_list = black_locations

        enemies_list = white_locations

    # 8 squares to check for knights, they can go two squares in one direction and one in another

    targets = [(1, 2), (1, -2), (2, 1), (2, -1),

               (-1, 2), (-1, -2), (-2, 1), (-2, -1)]

    for i in range(8):

        target = (position[0] + targets[i][0], position[1] + targets[i][1])

        if target not in friends_list and 0 <= target[0] <= 7 and 0 <= target[1] <= 7:

            moves_list.append(target)

    return moves_list

# check for valid moves for just selected piece
def check_valid_moves():

    if turn_step < 2:

        options_list = white_options

    else:

        options_list = black_options

    valid_options = options_list[selection]

    return valid_options

# draw valid moves on screen
def draw_valid(moves):

    if turn_step < 2:

        color = 'red'

    else:

        color = 'blue'

    for i in range(len(moves)):

        pygame.draw.circle(

            screen, color, (moves[i][0] * 100 + 50, moves[i][1] * 100 + 50), 5)


# draw captured pieces on side of screen
def draw_captured():

    for i in range(len(captured_pieces_white)):

        captured_piece = captured_pieces_white[i]

        index = piece_list.index(captured_piece)

        screen.blit(small_black_images[index], (825, 5 + 50 * i))

    for i in range(len(captured_pieces_black)):

        captured_piece = captured_pieces_black[i]

        index = piece_list.index(captured_piece)

        screen.blit(small_white_images[index], (925, 5 + 50 * i))

 
# draw a flashing square around king if in check
def draw_check():

    if turn_step < 2:

        if 'king' in white_pieces:

            king_index = white_pieces.index('king')

            king_location = white_locations[king_index]

            for i in range(len(black_options)):

                if king_location in black_options[i]:

                    if counter < 15:

                        pygame.draw.rect(screen, 'dark red', [white_locations[king_index][0] * 100 + 1,

                                                              white_locations[king_index][1] * 100 + 1, 100, 100], 5)

    else:

        if 'king' in black_pieces:

            king_index = black_pieces.index('king')

            king_location = black_locations[king_index]

            for i in range(len(white_options)):

                if king_location in white_options[i]:

                    if counter < 15:

                        pygame.draw.rect(screen, 'dark blue', [black_locations[king_index][0] * 100 + 1,

                                                               black_locations[king_index][1] * 100 + 1, 100, 100], 5)

def draw_game_over():
    pygame.draw.rect(screen, 'black', [200, 200, 400, 70])

    screen.blit(font.render(

        f'{winner} won the game!', True, 'white'), (210, 210))

    screen.blit(font.render(f'Press ENTER to Restart!',

                True, 'white'), (210, 240))

 # main game loop
black_options = check_options(black_pieces, black_locations, 'black')
white_options = check_options(white_pieces, white_locations, 'white')
run = True
while run:

    timer.tick(fps)

    if counter < 30:

        counter += 1

    else:

        counter = 0

    screen.fill('dark gray')

    draw_board()

    draw_pieces()

    draw_captured()

    draw_check()

    if selection != 100:

        valid_moves = check_valid_moves()

        draw_valid(valid_moves)

    # event handling

    for event in pygame.event.get():

        if event.type == pygame.QUIT:

            run = False

        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and not game_over:

            x_coord = event.pos[0] // 100

            y_coord = event.pos[1] // 100

            click_coords = (x_coord, y_coord)

            if turn_step <= 1:

                if click_coords == (8, 8) or click_coords == (9, 8):

                    winner = 'black'

                if click_coords in white_locations:

                    selection = white_locations.index(click_coords)

                    if turn_step == 0:

                        turn_step = 1

                if click_coords in valid_moves and selection != 100:

                    white_locations[selection] = click_coords

                    if click_coords in black_locations:

                        black_piece = black_locations.index(click_coords)

                        captured_pieces_white.append(black_pieces[black_piece])

                        if black_pieces[black_piece] == 'king':

                            winner = 'white'

                        black_pieces.pop(black_piece)

                        black_locations.pop(black_piece)

                    black_options = check_options(

                        black_pieces, black_locations, 'black')

                    white_options = check_options(

                        white_pieces, white_locations, 'white')

                    turn_step = 2

                    selection = 100

                    valid_moves = []

            if turn_step > 1:

                if click_coords == (8, 8) or click_coords == (9, 8):

                    winner = 'white'

                if click_coords in black_locations:

                    selection = black_locations.index(click_coords)

                    if turn_step == 2:

                        turn_step = 3

                if click_coords in valid_moves and selection != 100:

                    black_locations[selection] = click_coords

                    if click_coords in white_locations:

                        white_piece = white_locations.index(click_coords)

                        captured_pieces_black.append(white_pieces[white_piece])

                        if white_pieces[white_piece] == 'king':

                            winner = 'black'

                        white_pieces.pop(white_piece)

                        white_locations.pop(white_piece)

                    black_options = check_options(

                        black_pieces, black_locations, 'black')

                    white_options = check_options(

                        white_pieces, white_locations, 'white')

                    turn_step = 0

                    selection = 100

                    valid_moves = []

        if event.type == pygame.KEYDOWN and game_over:

            if event.key == pygame.K_RETURN:

                game_over = False

                winner = ''

                white_pieces = ['rook', 'knight', 'bishop', 'king', 'queen', 'bishop', 'knight', 'rook',

                                'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn']

                white_locations = [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0),

                                   (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)]

                black_pieces = ['rook', 'knight', 'bishop', 'king', 'queen', 'bishop', 'knight', 'rook',

                                'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn', 'pawn']

                black_locations = [(0, 7), (1, 7), (2, 7), (3, 7), (4, 7), (5, 7), (6, 7), (7, 7),

                                   (0, 6), (1, 6), (2, 6), (3, 6), (4, 6), (5, 6), (6, 6), (7, 6)]

                captured_pieces_white = []

                captured_pieces_black = []

                turn_step = 0

                selection = 100

                valid_moves = []

                black_options = check_options(

                    black_pieces, black_locations, 'black')

                white_options = check_options(

                    white_pieces, white_locations, 'white')

 

    if winner != '':

        game_over = True

        draw_game_over()

 

    pygame.display.flip()
pygame.quit()

Output of the code: 

 

Fig 2: Chess PyGame output.

Working of Code:

This Python code is for a two-player chess game implemented using the PyGame library.

Importing Libraries:

Importing the PyGame library is the first step in the code, which creates the chess game’s graphical user interface.

Initializing PyGame:

PyGame is initialized using pygame.init(), which sets up the Pygame environment.

Setting Up Game Window:

The code sets 800 pixels for both width and height as the game window’s size. The window caption is changed to “Two-Player Chess Game.”

Defining Fonts and Clock:

To present text at varying sizes, three distinct fonts are defined. To manage the game’s frame rate (60 frames per second), a clock object (timer) is made.

Defining Game Variables and Images:

There are set-up lists of the starting places of the white and black pieces on the chessboard. Both players’ captured pieces are observed. The game state and player movements are controlled by the variables (turn_step, selection, and valid_moves). The chess piece images (such as the king, queen, and rook) are loaded and resized appropriately.

Defining Chess Piece Movement Functions:

To verify the legal movements for every kind of chess piece—such as pawns, rooks, knights, bishops, queens, and kings—several functions are defined. For a given piece at a particular position, these routines produce a list of feasible move locations.

Drawing the Game Board:

The chessboard grid, status text, and “FORFEIT” button are all drawn using the draw_board() method.

Drawing Chess Pieces:

The chess pieces are drawn on the board based on their present locations using the draw_pieces() method. A red or blue rectangle indicates which piece the player has picked.

Checking Valid Moves and Drawing Highlights:

After determining if the chosen piece may be moved, the check_valid_moves() method gives a list of possible move locations. The picked piece can be moved to the valid move positions indicated by the little circles shown by the draw_valid() function.

Handling Game Events:

The code is always looking for PyGame events, such as keystrokes and mouse clicks. Moves and piece selection are done using mouse clicks. You can stop the game by clicking the “FORFEIT” button. The “ENTER” key is used to resume the game once it has ended.

Drawing Captured Pieces:

The captured pieces are shown to both players on the side of the game window via the draw_captured() function.

Checking for Check and Game Over:

The king of the player who is in check is surrounded by a flashing square drawn by the draw_check() function. To decide the winner and put a stop to the game, the checkmate or stalemate condition is checked for.

Main Game Loop:

Rendering, event management, and game updates are handled by the main game loop (during the run:).
It also controls player turns, the game’s finish, and the overall condition of the game.

Exiting the Game:

PyGame may be terminated using pygame.quit() to end the game loop, which lasts until the player exits the game window.

You can find the images used in the activity here.

Conclusion:

PyGame is an excellent option for individuals who are new to game creation as it is easy to use. It is accessible due to its simplicity and Python syntax. We can customize the chessboard’s appearance by modifying the graphics and colors in your game. Learn Python for kids online with Codingal. Whether you are a beginner exploring the basics or an experienced developer aiming to add depth to your projects, learning Python online with Codingal proves to be a transformative journey.

Best Coding Course for K12 Kids

Sign up to our newsletter

Get the latest blogs, articles, and updates delivered straight to your inbox.

Share with your friends

Try a free class