In this blog post, we will create the Snake Game using Python and OpenCV as shown in the video above.
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).
SnakePart class has
y position coordinates, and a reference to another
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
move() method simply copies the location of the part in front of it.
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.
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 on this blog.