Create a Chess Game in Python
Here, we have a task to create a chess game in Python. In this article, we will see how to create a chess game in Python.
What is a Chess Game?
Chess Game, Played by two players on an 8×8 grid board with alternate light and dark squares, chess is a time-honored board game of strategy and cunning. Each player deploys 16 pieces at the beginning of the game, placing them in the rows closest to them. Pawns are arranged on the second row, and the first row is occupied by other pieces. The goal of the game is to advance your pieces to a position from which you can capture the king of your opponent and force a “checkmate,” winning the game for you. Every piece has certain movement rules that players must stick to.
Create a Chess Game Using Pygame in Python
Below are the step-by-step procedure and explanation of how to create a Chess Game in Python:
Create a Virtual Environment
First, create the virtual environment using the below commands
python -m venv env
.\env\Scripts\activate.ps1
Install Necessary Library
First, we need to install the Pygame library, which is used for creating games. We are utilizing it to create a chess game. To install Pygame, use the following command:
pip install pygame
File Structure
Writing Python Code (main.py)
Below are the step-by-step explanation of the main.py code that we have used to create a chess game in Python:
Step 1: Importing Modules
Importing necessary modules for the code, including pygame
for game development, requests
for making HTTP requests, rembg
for removing image backgrounds, and BytesIO
for handling byte data.
# Importing Modules
import pygame
import requests
import rembg
from io import BytesIO
Step 2: Initialize Pygame and Set Chess Game Screen
Here, the code initializes the Pygame module using pygame.init()
. This step is crucial for setting up the Pygame environment and enabling the use of its functionalities throughout the script.
# Initialising pygame module
pygame.init()
# Setting Width and height of the Chess Game screen
WIDTH = 1000
HEIGHT = 900
screen = pygame.display.set_mode([WIDTH, HEIGHT])
pygame.display.set_caption('Chess Game')
Step 3: Initialize Fonts and Clock
In below code initializes three different fonts of varying sizes using the pygame.font.Font()
function. These fonts are likely to be used for rendering text within the game. Additionally, the code creates a clock object using pygame.time.Clock()
to control the frame rate of the 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
Step 4: Game Variables and Images
Below, code defines variables related to the game, such as lists representing the locations and types of chess pieces. URLs for chess piece images are stored in image_urls
. These variables play a crucial role in managing the game state, including the positions of pieces and the URLs required to fetch their corresponding images.
# 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 = []
# url for chess pieces images
image_urls = ['https://media.w3wiki.net/wp-content/uploads/20240302025946/black_queen.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025948/black_king.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025345/black_rook.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025951/black_bishop.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025947/black_knight.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025945/black_pawn.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025952/white_queen.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025943/white_king.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025949/white_rook.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025944/white_bishop.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025325/white_knight.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025953/white_pawn.png']
Step 5: Loading Game Piece Images
In below code we are Using the pygame.image.load()
function, the code loads chess piece images from URLs. The rembg
library is employed to remove image backgrounds, enhancing the visual appearance of the pieces. Each image is then scaled using pygame.transform.scale()
to ensure a consistent size within the game.
# load in game piece images (queen, king, rook, bishop, knight, pawn) x 2
black_queen = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[0]).content)))
black_queen = pygame.transform.scale(black_queen, (80, 80))
black_queen_small = pygame.transform.scale(black_queen, (45, 45))
black_king = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[1]).content)))
black_king = pygame.transform.scale(black_king, (80, 80))
black_king_small = pygame.transform.scale(black_king, (45, 45))
black_rook = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[2]).content)))
black_rook = pygame.transform.scale(black_rook, (80, 80))
black_rook_small = pygame.transform.scale(black_rook, (45, 45))
black_bishop = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[3]).content)))
black_bishop = pygame.transform.scale(black_bishop, (80, 80))
black_bishop_small = pygame.transform.scale(black_bishop, (45, 45))
black_knight = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[4]).content)))
black_knight = pygame.transform.scale(black_knight, (80, 80))
black_knight_small = pygame.transform.scale(black_knight, (45, 45))
black_pawn = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[5]).content)))
black_pawn = pygame.transform.scale(black_pawn, (65, 65))
black_pawn_small = pygame.transform.scale(black_pawn, (45, 45))
white_queen = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[6]).content)))
white_queen = pygame.transform.scale(white_queen, (80, 80))
white_queen_small = pygame.transform.scale(white_queen, (45, 45))
white_king = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[7]).content)))
white_king = pygame.transform.scale(white_king, (80, 80))
white_king_small = pygame.transform.scale(white_king, (45, 45))
white_rook = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[8]).content)))
white_rook = pygame.transform.scale(white_rook, (80, 80))
white_rook_small = pygame.transform.scale(white_rook, (45, 45))
white_bishop = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[9]).content)))
white_bishop = pygame.transform.scale(white_bishop, (80, 80))
white_bishop_small = pygame.transform.scale(white_bishop, (45, 45))
white_knight = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[10]).content)))
white_knight = pygame.transform.scale(white_knight, (80, 80))
white_knight_small = pygame.transform.scale(white_knight, (45, 45))
white_pawn = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[11]).content)))
white_pawn = pygame.transform.scale(white_pawn, (65, 65))
white_pawn_small = pygame.transform.scale(white_pawn, (45, 45))
Step 6: Grouping Piece Images
In below code , section organizes the loaded piece images into lists for convenient access and manipulation. Two lists, white_images
and small_white_images
, contain the original and scaled versions of white chess pieces, respectively. Similar lists are created for black pieces. Grouping the images simplifies the process of drawing pieces onto the game board.
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
Step 7: Board Drawing Functions
below code code defines functions responsible for drawing the game board. The draw_board()
function uses Pygame’s drawing functions to create a chessboard with alternating light gray and gray squares. Additionally, lines are drawn to delineate the squares, and status text is displayed at the bottom of the window, indicating the current player’s turn and actions
# 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))
Step 8: Piece Drawing Functions
Below, are the explanation of Piece Drawing Functions for create a chess game in Python.
Piece Rendering on the Board
In below code draw_pieces
function is responsible for visually representing chess pieces on the board. It iterates through both white and black pieces, displaying them at their respective locations. The function adjusts the position for pawn images and outlines the selected piece with a red rectangle during white’s turn and a blue rectangle during black’s turn
# 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)
Piece Movement Validator
In below code the function, check_options
, validates and gathers all possible moves for each piece on the chessboard, distinguishing between different piece types and their respective movements.
# 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
King’s Valid Moves Checker
In below code function check_king
focuses on determining and returning the valid moves for a king, considering its ability to move one square in any direction and distinguishing between friendly and enemy pieces.
# 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
Queen’s Moves Aggregator
In below code check_queen
function combines the valid moves of a queen by integrating the results from both the bishop and rook functions, providing a comprehensive list of valid moves for a queen at a given position and color.
# 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
Bishop’s Diagonal Path Explorer
In below code check_bishop
function calculates and returns the valid moves for a bishop by traversing diagonal paths in all four directions, considering board boundaries and avoiding friendly pieces.
# 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
Rook’s Horizontal and Vertical Navigator
In below code the function, check_rook
, determines the valid moves for a rook by exploring horizontal and vertical paths in all four directions, ensuring the moves stay within the board boundaries and avoid friendly pieces.
# 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
Pawn’s Valid Move Detector
The check_pawn
function identifies and returns the valid moves for a pawn, considering different scenarios such as initial double moves, single moves, and diagonal captures based on the color of the pawn.
# 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
Knight’s L-Shaped Move Generator
The check_knight
function calculates and returns the valid moves for a knight by exploring eight possible positions in an “L” shape, ensuring the moves are within the board boundaries and avoid friendly pieces.
# 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
Current Piece’s Valid Moves Enumerator
The function check_valid_moves
determines the valid moves for the currently selected piece based on the game’s current turn, providing a list of possible moves.
# 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
Valid Moves Visualizer
The draw_valid
function visually represents valid moves on the screen by drawing small circles, using red circles for white’s turn and blue circles for black’s turn.
# 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)
Captured Pieces Display
The draw_captured
function visually displays captured pieces on the side of the screen, utilizing lists of captured pieces for both white and black.
# 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))
Check Indicator
The draw_check
function visually indicates a check condition by drawing a flashing square around the king, with the color and conditions dependent on the current turn.
# 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)
Game Over Display
The draw_game_over
function displays a message on the screen declaring the winner of the game and instructs the player to press ENTER to restart, utilizing the winner variable to determine the winner displayed on the screen.
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))
Step 9: Game Loop and Event Handling
Initialization and Rendering
The game loop begins by calculating valid move options for both black and white pieces using the check_options
function. Subsequently, a counter is incremented, and the screen is filled with a dark gray color, providing a clean canvas for rendering. Functions like draw_board
, draw_pieces
, draw_captured
, and draw_check
are then called to visually represent the chessboard, pieces, captured pieces, and check conditions.
# 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 – Mouse Clicks
The code utilizes a loop to handle pygame events, primarily focusing on mouse clicks (`pygame.MOUSEBUTTONDOWN`). It checks for the left button click (`event.button == 1`) and ensures the game is not over (`not game_over`). The coordinates of the click are determined and translated to grid coordinates. Depending on the turn step, it processes player input for moving pieces, capturing opponents, and updating the game state accordingly.
# event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# Handling left mouse button clicks when the game is not over
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)
# Handling player input during the first two turns
if turn_step <= 1:
# Check if the player clicked on forfeit squares
if click_coords == (8, 8) or click_coords == (9, 8):
winner = 'black'
# Check if the clicked coordinates belong to white pieces
if click_coords in white_locations:
selection = white_locations.index(click_coords)
if turn_step == 0:
turn_step = 1
# Check if the clicked coordinates are valid moves for the selected white piece
if click_coords in valid_moves and selection != 100:
white_locations[selection] = click_coords
# Check for capturing black pieces
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)
# Update move options for both black and white
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 = []
# Handling player input during the last two turns
if turn_step > 1:
# Check if the player clicked on forfeit squares
if click_coords == (8, 8) or click_coords == (9, 8):
winner = 'white'
# Check if the clicked coordinates belong to black pieces
if click_coords in black_locations:
selection = black_locations.index(click_coords)
if turn_step == 2:
turn_step = 3
# Check if the clicked coordinates are valid moves for the selected black piece
if click_coords in valid_moves and selection != 100:
black_locations[selection] = click_coords
# Check for capturing white pieces
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)
# Update move options for both black and white
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 = []
Event Handling – Key Press
Within the event loop, the code checks for a key press (`pygame.KEYDOWN`) when the game is over. Specifically, it looks for the “Enter” key (`event.key == pygame.K_RETURN`). Upon detecting this key press, the game resets to its initial state, including piece positions, captured pieces, turn steps, and valid move lists. This functionality allows players to restart the game seamlessly.
# Handling key press events when the game is over
if event.type == pygame.KEYDOWN and game_over:
# Check if the pressed key is the "Enter" key
if event.key == pygame.K_RETURN:
# Resetting the game state when the "Enter" key is pressed
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 = []
# Update move options for both black and white
black_options = check_options(black_pieces, black_locations, 'black')
white_options = check_options(white_pieces, white_locations, 'white')
Winner Determination and Game Over Display
If a winner is determined, the code sets the `game_over` flag to true and calls the `draw_game_over` function to display a game-over message on the screen. The winner variable influences this decision, considering factors like capturing the opponent’s king or pressing forfeit squares. This aspect enhances the user experience by providing clear feedback on the game’s outcome.
# Checking for a winner and displaying game over message
if winner != '':
game_over = True
draw_game_over()
Complete Code Implementation
Below is the complete code implementation of main.py file that we have used to create a chess game in Python.
main.py
# Importing Modules
import pygame
import requests
import rembg
from io import BytesIO
# Initialising pygame module
pygame.init()
# Setting Width and height of the Chess Game screen
WIDTH = 1000
HEIGHT = 900
screen = pygame.display.set_mode([WIDTH, HEIGHT])
pygame.display.set_caption('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 = []
# url for chess pieces images
image_urls = ['https://media.w3wiki.net/wp-content/uploads/20240302025946/black_queen.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025948/black_king.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025345/black_rook.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025951/black_bishop.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025947/black_knight.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025945/black_pawn.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025952/white_queen.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025943/white_king.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025949/white_rook.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025944/white_bishop.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025325/white_knight.png',
'https://media.w3wiki.net/wp-content/uploads/20240302025953/white_pawn.png']
# load in game piece images (queen, king, rook, bishop, knight, pawn) x 2
black_queen = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[0]).content)))
black_queen = pygame.transform.scale(black_queen, (80, 80))
black_queen_small = pygame.transform.scale(black_queen, (45, 45))
black_king = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[1]).content)))
black_king = pygame.transform.scale(black_king, (80, 80))
black_king_small = pygame.transform.scale(black_king, (45, 45))
black_rook = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[2]).content)))
black_rook = pygame.transform.scale(black_rook, (80, 80))
black_rook_small = pygame.transform.scale(black_rook, (45, 45))
black_bishop = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[3]).content)))
black_bishop = pygame.transform.scale(black_bishop, (80, 80))
black_bishop_small = pygame.transform.scale(black_bishop, (45, 45))
black_knight = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[4]).content)))
black_knight = pygame.transform.scale(black_knight, (80, 80))
black_knight_small = pygame.transform.scale(black_knight, (45, 45))
black_pawn = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[5]).content)))
black_pawn = pygame.transform.scale(black_pawn, (65, 65))
black_pawn_small = pygame.transform.scale(black_pawn, (45, 45))
white_queen = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[6]).content)))
white_queen = pygame.transform.scale(white_queen, (80, 80))
white_queen_small = pygame.transform.scale(white_queen, (45, 45))
white_king = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[7]).content)))
white_king = pygame.transform.scale(white_king, (80, 80))
white_king_small = pygame.transform.scale(white_king, (45, 45))
white_rook = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[8]).content)))
white_rook = pygame.transform.scale(white_rook, (80, 80))
white_rook_small = pygame.transform.scale(white_rook, (45, 45))
white_bishop = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[9]).content)))
white_bishop = pygame.transform.scale(white_bishop, (80, 80))
white_bishop_small = pygame.transform.scale(white_bishop, (45, 45))
white_knight = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[10]).content)))
white_knight = pygame.transform.scale(white_knight, (80, 80))
white_knight_small = pygame.transform.scale(white_knight, (45, 45))
white_pawn = pygame.image.load(
BytesIO(rembg.remove(requests.get(image_urls[11]).content)))
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
Contact Us