The Classic Arcade Game
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)