Snake

Using Python's Turtle library to make the classic video game.


Rules

The aim of the game is to move the snake around the map, eating as much food as possible to grow the snake longer. Collisions with the boundary or between the head of the snake and its body cause the game to end.


Turtle

In order to generate the snake, food, and 'score' texts, Python's Turtle library is used. The library describes the position of a 'turtle' carrying a pen, whose movement around the screen draws lines, text, and shapes.

By providing the turtle with:

  • Starting position
  • Movement speed
  • Angles through which to turn for 'left', 'right', 'up', 'down'

The classic arcade game comes to life. Classes were used to describe the behaviour of the snake, the food, and the scoreboard.


The Code


The Snake

The snake class functions to initialise the snake with starting positions. It also contains methods for extending the snake (when food is eaten), moving the snake forwards, and maneuvering the snake based on keyboard input.


Food

The food class defines the shape of the food, the colour, and the size. It also features a 'refresh' method for randomly placing new food when some is eaten.


Scoreboard

The scoreboard class initialises a score at the start of each game, displays the highscore, and increments the current score each time the player eats some food.


Code Files

                            
from turtle import Screen
from snake import Snake
from food import Food
from scoreboard import Scoreboard
import time

screen = Screen()
screen.listen()
screen.setup(width= 600, height= 600)
screen.bgcolor("black")

screen.title("Snake")
screen.tracer(0)

game_is_on = True

snake = Snake()
food = Food()
score = Scoreboard()

screen.onkey(snake.left, "Left")
screen.onkey(snake.right, "Right")
screen.onkey(snake.up, "Up")
screen.onkey(snake.down, "Down")

while game_is_on:
    screen.update()
    time.sleep(0.06)
    snake.move()

    # Detect collision of snake with food
    if snake.head.distance(food) < 15:
        food.refresh()                          # If collision between snake and food, move food
        score.increment()                       # Increase and update display for score
        snake.extend()

    # Detect collision with wall
    if abs(snake.head.xcor()) > 290 or abs(snake.head.ycor()) > 290:
        score.reset()
        snake.reset()
    # Detect collision with tail - if tail collides with any tail segment
    for segment in snake.segments[1:]:
        if snake.head.distance(segment) < 10:
            score.reset()
            snake.reset()

screen.exitonclick()
                            
                        
                            
from turtle import Turtle

STARTING_POSITIONS = [(0, 0), (-20, 0), (-40, 0)]
MOVE_DISTANCE = 20
UP = 90
DOWN = 270
LEFT = 180
RIGHT = 0

class Snake:
    def __init__(self):
        self.segments = []
        self.create_snake()
        self.head = self.segments[0]

    def create_snake(self):
        for position in STARTING_POSITIONS:
            self.add_segment(position)

    def extend(self):
        self.add_segment(self.segments[-1].position())       # Get position of last segment in list of segments, use position for new segment
        self.add_segment(self.segments[-1].position())

    def add_segment(self, position):
        new_segment = Turtle(shape="square")
        new_segment.color("white")
        new_segment.penup()
        new_segment.goto(position)
        self.segments.append(new_segment)

    def move(self):
        for seg_num in range(len(self.segments) - 1, 0, -1):  # start, stop, step
            new_x = self.segments[seg_num - 1].xcor()
            new_y = self.segments[seg_num - 1].ycor()
            self.segments[seg_num].goto(new_x, new_y)
        self.segments[0].forward(MOVE_DISTANCE)

    def left(self):
        if self.head.heading() != RIGHT:
            self.head.setheading(LEFT)

    def right(self):
        if self.head.heading() != LEFT:
            self.head.setheading(RIGHT)

    def up(self):
        if self.head.heading() != DOWN:
            self.head.setheading(UP)

    def down(self):
        if self.head.heading() != UP:
            self.head.setheading(DOWN)

    def reset(self):
        for seg in self.segments:
            seg.goto(1000, 1000)
        self.segments.clear()
        self.create_snake()
        self.head = self.segments[0]
                            
                        
                            
from turtle import Turtle
import random

class Food(Turtle):

    def __init__(self):
        super().__init__()
        self.shape("circle")
        self.penup()                                               # So we don't draw lines between food locations
        self.shapesize(stretch_len= 0.5, stretch_wid= 0.5)         # Halve default 20x20 dimensions
        self.color("green")
        self.speed("fastest")                                      # So we don't have to see the food moving across screen
        self.refresh()                                             # Initial position

    def refresh(self):
        rand_x = random.randint(-280, 280)                      # Playing field is -300 to 300 in x and y...
        rand_y = random.randint(-280, 260)                      # ... don't want food right on the edge
        self.goto(rand_x, rand_y)
                            
                        
                            
from turtle import Turtle

ALIGNMENT = "center"
FONT = ('Courier', 22, 'normal')

class Scoreboard(Turtle):

    def __init__(self):
        super().__init__()
        with open("high_score.txt") as file:
            self.high_score = int(file.read())
        # self.high_score = 0
        self.score = -1
        self.hideturtle()
        self.penup()
        self.sety(270)
        self.pencolor("white")
        self.increment()

    def increment(self):
        self.clear()
        self.score += 1
        self.write(arg= f"Score: {self.score}   High Score: {self.high_score}", align= ALIGNMENT, font= FONT)

    # def game_over(self):
    #     self.sety(0)
    #     self.write(arg=f"Game Over", align=ALIGNMENT, font=FONT)

    def reset(self):
        if self.score > self.high_score:
            with open("high_score.txt", mode= "w") as file:
                file.write(str(self.score))
                self.high_score = self.score
        self.clear()
        self.score = 0
        self.write(arg=f"Score: {self.score}   High Score: {self.high_score}", align=ALIGNMENT, font=FONT)
                            
                        
Snake Game image