In this post, we will create the Snake Game using Python and OpenCV as shown in the video above.
Snake Game
In the Snake Game, we have a digital snake that we control using the arrows keys.
An apple randomly appears on the screen and our goal is to move the snake so that it eats the apple. The snake grows in size after eating an apple and our objective is to make the snake as long as possible.
However, the game ends if the snake crashes into a wall or if it crashes into itself.
Now, let’s see how we can implement Snake in Python. Ha ha! See the joke?
Implementing Snake Game with OpenCV
First, we will import some libraries for the display and for generating random positions for the apple.
import cv2
import numpy as np
from random import randint
from random import choice
Let’s make a few classes to represent the game.
SnakePart
: As shown in Figure 1, this represents the body of the snake (excluding the head).Head
: This class represents the head of the snake (shown in blue in Figure 1).
The SnakePart
class has x
and y
position coordinates, and a reference to another SnakePart
or Head
in front of it.
class SnakePart:
def __init__(self, front, x, y):
self.front = front
self.x = x
self.y = y
def move(self):
# Moves by following the part in front of it
self.x = self.front.x
self.y = self.front.y
The move()
method simply copies the location of the part in front of it.
The Head
class also has an x and y. In addition, it has a direction
variable that specifies the direction of movement.
class Head:
def __init__(self, direction, x, y):
self.direction = direction
self.x = x
self.y = y
def move(self):
# Checks what its current direction is and moves accordingly
if self.direction == 0:
self.x += 1
elif self.direction == 1:
self.y += 1
elif self.direction == 2:
self.x -= 1
elif self.direction == 3:
self.y -= 1
Now, let’s move to the main function to see how the game is implemented. We first define a few constants that will come in handy.
CELL_SIZE
specifies the width of each cell of the board in pixels.
BOARD_SIZE
is the number of cells along the width of the board. In our example, CELL_SIZE is 20 and BOARD_SIZE is 45. So, the size of the display is 20 x 45 = 900 pixels.
SPEED
dictates how fast or slow the snake is. Use a smaller number if the game is too fast for you.
GROWTH
specifies the number of units by which the snake grows after eating one apple.
if __name__ == '__main__' :
# Size of each cell in the board game
CELL_SIZE = 20
# Number of cells along the width in the game
BOARD_SIZE = 45
# Change SPEED to make the game go faster
SPEED = 12
# After eating an apple the snake grows by GROWTH units
GROWTH = 3
Next, we define some variables that will come in handy later on.
# Variable to track if apple is eaten
eaten = True
# Variable to check if the game should quit
quit = False
# Variable to track growth.
grow = 0
Now, let’s create an empty array to store the head and snakeparts.
# Array for storing snake
snake = []
#snake's head starts at the center of the board.
head = Head(0, int((BOARD_SIZE - 1)/2), int((BOARD_SIZE - 1)/2))
snake.append(head)
Before, we move on to the main while loop of the program, let’s look at the display
function that displays the current game.
In the code below, we do the following.
- We first create a board and fill it with zeros (black)
- We color the cells occupied by the snake green.
- We color the single cell occupied by the apple red.
- If a key is pressed on the keyboard, return the id of the key.
def display():
# Create a blank image
board = np.zeros([BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE, 3])
# Color the snake green
for part in snake:
x = part.x * CELL_SIZE
y = part.y * CELL_SIZE
board[y:y + CELL_SIZE, x:x + CELL_SIZE] = [0, 255, 0]
# Color the apple red
x = applex * CELL_SIZE
y = appley * CELL_SIZE
board[y:y + CELL_SIZE, x:x + CELL_SIZE] = [0, 0, 255]
# Display board
cv2.imshow("Snake Game", np.uint8(board))
key = cv2.waitKey(int(1000/SPEED))
# Return the key pressed. It is -1 if no key is pressed.
return key
We are almost ready to enter the main while loop of the game. Before we do that we print some instructions.
# Start the game by printing instructions
print('w = top, a = left, s = down, d = right')
# Ugly trick to bring the window in focus
win_focus()
You will notice a function win_focus()
. Depending on which windowing method OpenCV is using on your system, when you run this game, the display window may not be in focus. To fix that problem, we have used this ugly hack to bring our game window in focus so the user does not have to click on the window.
In the code below we are doing the following
- We open a window
- Display the game window
- Set the window to fullscreen. This brings the window in focus.
- We resize the window back to normal size.
def win_focus():
# Ugly trick to get the window in focus.
# Opens an image in fullscreen and then back to normal window
cv2.namedWindow("Snake Game", cv2.WINDOW_AUTOSIZE);
board = np.zeros([BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE, 3])
cv2.imshow("Snake Game", board);
cv2.setWindowProperty("Snake Game", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN);
cv2.waitKey(2000)
cv2.setWindowProperty("Snake Game", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_AUTOSIZE)
If you encounter any glitches, you can comment out win_focus()
in the main function.
For the main part of the code, we will use a while loop where each iteration is one move for the snake. In the loop below, we accomplish two things
- Check if the apple has been eaten.
- If yes, generate a new apple. At the time of generating the apple, we make sure its position does not overlap the snake.
while True:
# Checks if the apple is eaten and generates a new one
if eaten:
# Create a list of all possible locations
s = list(range(0, BOARD_SIZE ** 2))
# Delete cells that are part of the snake
for part in snake:
s.remove(part.x * BOARD_SIZE + part.y)
# Randomly pick from one of the remaining cells
a = choice(s)
applex = int(a/BOARD_SIZE)
appley = a % BOARD_SIZE
eaten = False
Next, we display the board, and look for keyboard input.
The if/elif statement checks if ‘w’, ‘a’,’s’, ‘d’, escape, or delete keys have been pressed. Based on the keyboard input, the snake changes direction or we stop the program.
# Makes and displays the board
key = display()
# Gets key presses and moves accordingly
# 8 and 27 are delete and escape keys
# Arrow keys are tricky in OpenCV. So we use
# keys 'w', 'a','s','d' for movement.
# w = top, a = left, s = down, d = right
if key == 8 or key == 27:
break
elif key == ord("d") :
head.direction = 0
elif key == ord("s") :
head.direction = 1
elif key == ord("a") :
head.direction = 2
elif key == ord("w") :
head.direction = 3
Based on the keyboard input, we move the snake. As you may remember, the move
method of SnakePart
simply copies the location of the snake part in front of it.
# Moving the snake
for part in snake[::-1]:
part.move()
Next, we implement the check for collisions. The code below implements two rules
- If the head of the snake moves outside the board, the game ends.
- If the head of the snake collides with a snake part (i.e. body of the snake), the game ends.
# Collision rules
if head.x < 0 or head.x > BOARD_SIZE - 1 or head.y < 0 or head.y > BOARD_SIZE - 1:
break
for part in snake[1:]:
if head.x == part.x and head.y == part.y:
quit = True
break
if quit:
break
Lastly, we code the rule for when the snake eats the apple and grows. The grow
variable is incremented by the constant GROWTH when the apple is eaten, and the snake grows by one cell per frame until it is fully grown.
# The snake grows gradually over multiple frames
if grow > 0:
snake.append(SnakePart(snake[-1], subx, suby))
grow -= 1
# Grows the snake when it eats an apple
if applex == head.x and appley == head.y:
subx = snake[-1].x
suby = snake[-1].y
eaten = True
grow += GROWTH
That is all! Give this a try and let me know how you like the game in the comments section.
Author’s Note
I am Rohan Nayak Mallick, and I am 10 years old.
I received help in writing this post from by dad Dr. Satya Mallick.
However, the idea of implementing the Snake Game was mine, and I wrote the code entirely by myself with minor inputs on style from my dad.
I have been programming in Python for a couple of years and started taking AI courses recently. Currently I am on week 6 on Computer Vision I and week 5 on Deep Learning with PyTorch.
If you like this post, please leave a comment. It will help me earn a chance to write more posts.