18. Snake in Python
By Bernd Klein. Last modified: 28 Jan 2022.
As a 90’s kid, I’ve witnessed many technological advancements that rapidly changed the world I was born into. One of those changes was the “Mobile Phone”. Back in the day, when Nokia was the hype, some people would buy SMS packages and being frugal, they would make compromises to fit everything into one message, while others would go crazy with the new polyphonic ringtones and were always ready to pay more to show off when their phone rang. It also brought an interesting fashion, the phone belts, a nice accessory to the over-sized clothes of the time. However, for me and for many other kids and adults, the biggest thing about this invention was the games one could play, rather than all the other practicalities it offered. I would always look forward to playing the “Snake”.
Of course, Snake has actually been around since 1976, introduced with Blockade. However, one could say that the game’s popularity has significantly increased after being implemented in the Nokia phones. Programmed by Taneli Armanto in 1997, the first phone to offer Snake was the 6610.
In this article, you will find how we recreated the famous “Snake” using Python, how you could modify it according to your wishes and what kind of thinking strategy one should follow when making / recreating games. This article will encourage you to test and modify your code, rather than just explaining the end product we made.
You can download the complete code by using these links:
We used Pygames, a free library for creating video games, for recreating Snake.
Where do we begin?
Snake, borders, food, score… We all know that these are the main elements of the game, however, where should we really begin?
After opening your preferred code editor, the first step you need to take is:
<pre> import pygame as pg </pre>
After all, we are going to be using some of the modules from the Pygame library.
Afterwards, don’t forget to name your file “game.py”.
Prepare yourself to work with multiple files, and travel between the multiple tabs open on your code editor. Let’s begin!
The Game Display
Let’s construct the game display in game.py. For this, we first need to define a class, called “Game” . For now, it will only contain the initializer “init_()”, and “run()” methods.
Inside initializer we need to set our width and height, we preferred to make it 800 * 600 pixels, however, these are arbitrary values. We preferred to save it in self.width and self.height correspondingly to make our code more readable, and for the changes to be easier later on.
import pygame as pg
class Game:
def ```__init__```(self):
self.width = 800
self.height = 600
self.gameDisplay = pg.display.set_mode((self.width, self.height))
The Game Display is constructed with display.set_mode() function of pygames, and takes the width and height as arguments.
In order to be able to get the pygames modules running though, we need to initalize them first (i.e. before we set the game display), which is taken care of by the function pg.init(). Let’s call:
pg.init()
Meanwhile, we shouldn’t forget to name our game, which of course will be “Snake”. For this, after deciding on our height and width and calling pg.init(), we use the display.set_caption() function of pygames.
pg.display.set_caption( ' Snake ')
Wonderful. Now we need to set our clock, we will be using the time.clock() function of pygames. This will help us later on with the food spawning(i.e. how many seconds does it take for new food to appear), and as well as computing how many milliseconds have passed since the previous call. This function limits the runtime speed of a game, i.e. the program wouldn’t run more than the given number of frames per second.
self.clock = pg.time.Clock()
We now need to define another method, run(), to get our game running and to stop it. Here, the pygames function event.get() helps us track the events. Currently, we are only tracking if the player is trying to quit the game. This is represented with pg.QUIT.
def run(self):
running = True
while running:
events = pg.event.get()
for event in events:
if event.type == pg.QUIT:
running = False
self.clock.tick(100) # limits the game to 100 frames a second
pg.quit()
Now our game display is ready to run! Let’s call the run() on Game.
g = Game() g.run()
At this stage, your code should look like the following image.
Creating the Snake
As we mentioned earlier, we’ll be traveling between files, and now it is time to open a new file and name it “snake.py”. It will contain everything about the Snake, like its name suggests.
Setting the Directions
What all of us can certainly remember about Snake was that, it could move towards 4 directions, up, down, left and right. As Pygames library suggests, we determine the directions as:
class Direction:
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
With this, we will be able to move the snake using our arrow keys.
Live Python training
See our Python training courses
Where are we?
It is important to determine where the snake is in the canvas. That’s why we will construct another class called “Point”.
We need to imagine the canvas like the coordinate system, so we will have x and y as locations. We need to make sure we add them as arguments in the initializer. However, this canvas has (0,0) on the top left, so we need to think accordingly.
In our code we decided to move the snake by one grid, which in our case is 10 pixels, so we increment or decrement accordingly when it goes up or down and left and right. For each movement, we need a specific method.
We used the magic method __eq__ check points from Points class.
At the end class Point should look like this:
class Point:
def ```__init__```(self, x, y):
self.x = x
self.y = y
def ```__eq__```(self, other):
if self.x == other.x and self.y == other.y:
return True
return False
def move_left(self):
self.x -= 1
def move_right(self):
self.x += 1
def move_up(self):
self.y -= 1
def move_down(self):
self.y += 1
The Snake
Before we construct the snake, we need to think about everything that the snake entails. We need to know its position, length, direction and the borders of the canvas (if any). So these are going to be the arguments that the initializer will contain.
The snake consists of tiles, even though this was more apparent in the small Nokia screens than the computer screen you are going to be playing with. So, we need to make an empty list for the snake’s tiles, and append tiles as it gets longer.
The first thing we should construct is a method for getting the head position, which should be the tile at the position 0 i.e. the first tile. We also need to get the tiles, to see how big the snake is. After that we also need to get the directions, to see where we are heading.
def get_head_pos(self):
return self.tiles[0]
def get_tiles(self):
return self.tiles
def get_directions(self):
return self.direction
The next method we should construct is a method for moving the snake. We set borders to up and down, however feel free to experiment on this. We thought that if the snake is above the ground, it’d be discovered, and if it was too deep in the ground it’d find nothing but rocks so it’d starve. After the move, we change its position to where its head is. After that, should be able to determine our location, that’s why we return self.check_position().
def move(self):
for i in range(len(self.tiles) - 1, 0, -1):
x = self.tiles[i-1].x
y = self.tiles[i-1].y
self.tiles[i] = Point(x, y)
if self.direction == Direction.UP:
self.tiles[0].move_up()
if self.direction == Direction.DOWN:
self.tiles[0].move_down()
if self.direction == Direction.LEFT:
self.tiles[0].move_left()
if self.tiles[0].x < 0:
self.tiles[0].x = self.borders[0]
if self.direction == Direction.RIGHT:
self.tiles[0].move_right()
if self.tiles[0].x > self.borders[0]:
self.tiles[0].x = 0
self.pos = self.tiles[0]
return self.check_position()
Wait! We didn’t write a method about checking the position, what are we going to return? Let’s do that now. This method checks if the snake’s head touches its body, and if it is out of the borders. If everything is OK, i.e. True, the game continues. Otherwise, the game is over.
def check_position(self):
if self.tiles[0].y < 0:
return False
if self.tiles[0].y > self.borders[1]:
return False
# check if the snake’s head touches the body
for i in range(1, len(self.tiles)):
if self.tiles[0] == self.tiles[i]:
return False
return True
The main goal of this game is to grow the snake as much as possible, so we need to construct a method called eat. In our code, each food has the value 2 and therefore increments the length of the snake by 2. Feel free to experiment on this in your recreation. This is also the reason why our score increments by 2.
def eat(self, value = 2):
self.length += value
x= self.tiles[-1].x
y= self.tiles[-1].y
self.tiles.append(Point(x,y))
Last but not least, the snake eats, but how about the food? We construct a class called Food with a position and a value. We are going to visualize it in our game file.
class Food:
def __init__(self, pos, value = 2):
self.pos = pos
self.value = value
Setting boundaries
So far so good, however, what if we press up when the snake is moving down or vice versa? This is very problematic, so we need to set some limits as to where the snake can move to. In the method below, you see an empty return statement, which is for doing nothing. In other words, by doing nothing we prevent the action from taking place.
def change_direction(self, direction):
if direction == Direction.UP and self.direction == Direction.DOWN:
return
if direction == Direction.DOWN and self.direction == Direction.UP:
return
if direction == Direction.LEFT and self.direction == Direction.RIGHT:
return
if direction == Direction.RIGHT and self.direction == Direction.LEFT:
return
self.direction = direction
Live Python training
Upcoming online Courses
