Learn Python
Learn about Pygames
Use Python def classes to make a functional game

Table of Content

  1. Setup Python and pygames
  2. Create Screen and grid
  3. Create Snake
  4. Give Snake inputs
  5. Create Apple
  6. Death and Scoring

Image of final product

Snake Image Caption

Download Python and install Pygames and Satoshi Font (because I like it)

Get VS Code, Python and Pygames

Get Visual Studio Code because why use any other IDEs Download VS Code

Get Python from Python website, Click Here!

Make sure you check this box here

Install Python

To install Pygame, in terminal enter
For Windows py -m pip install -U pygame --user
For Mac python3 -m pip install -U pygame --user

Check if they are installed correctly

Check Python
_ For Windows: py --version
_ For Mac: python3 --version

Check Pygame: pip show pygame

Download Satoshi Font (Save in same folder as Pong.py)

If you use another font change Satoshi-Variable.ttf to file name in the code,
Download Font.

Pygame Boilerplate

import pygame
import sys
import random
import time

pygame.init()

SW, SH = 800, 800

BLOCK_SIZE = 50;
FONT = pygame.font.Font("Satoshi-Variable.ttf", BLOCK_SIZE*2)

screen = pygame.display.set_mode((SW,SH))
pygame.display.set_caption("Snake")
clock = pygame.time.Clock()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        pygame.display.update()
        clock.tick(10)      # delay in ms

Creating a grid

def drawGrid():                                        # Nested for loop that draws grid
    for x in range(0, SW, BLOCK_SIZE):
        for y in range(0, SH, BLOCK_SIZE):
            rect = pygame.Rect(x,y,BLOCK_SIZE, BLOCK_SIZE)
            pygame.draw.rect(screen, "#3c3c3b", rect, 1)

Calling drawGrid()

def drawGrid():                                        # Nested for loop that draws grid
    for x in range(0, SW, BLOCK_SIZE):
        for y in range(0, SH, BLOCK_SIZE):
            rect = pygame.Rect(x,y,BLOCK_SIZE, BLOCK_SIZE)
            pygame.draw.rect(screen, "#3c3c3b", rect, 1)

drawGrid()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:

Create a new Snake object

class Snake:
    def __init__(self):
        self.x, self.y = int((SW/4)/BLOCK_SIZE) * BLOCK_SIZE, int((SH/2)/BLOCK_SIZE) * BLOCK_SIZE
        self.xdir = 1   # -1 Left   0 no move   1 Right
        self.ydir = 0   # 1 Down   0 no move   -1 Up
        self.head = pygame.Rect(self.x, self.y, BLOCK_SIZE, BLOCK_SIZE)
        self.body = [pygame.Rect(self.x - BLOCK_SIZE, self.y, BLOCK_SIZE, BLOCK_SIZE)]
        self.dead = False

When initializing a new Snake head object that is facing right on spawn at the center of the screen

Drawing the object on the game screen

First initialize snake object with ‘snake = Snake()'

drawGrid()
snake = Snake()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        # New Code HERE
        pygame.draw.rect(screen, "green", snake.head)

        for square in snake.body:
            pygame.draw.rect(screen, "green", square)
        # New Code END

        pygame.display.update()
        clock.tick(10)      # delay in ms

Updating the snake

def update(self):
    self.body.append(self.head)
    for i in range(len(self.body)-1):
        self.body[i].x = self.body[i+1].x
        self.body[i].y = self.body[i+1].y

    self.head.x += self.xdir * BLOCK_SIZE
    self.head.y += self.ydir * BLOCK_SIZE

    self.body.remove(self.head)

This update method goes in the Snake class

Calling the update

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        snake.update()      # New Code HERE

        pygame.draw.rect(screen, "green", snake.head)

        for square in snake.body:
            pygame.draw.rect(screen, "green", square)

        pygame.display.update()
        clock.tick(10)      # delay in ms

Erasing old snake and redrawing grid

    if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    snake.update()
    # New Code HERE
    screen.fill("black")
    drawGrid()
    # New Code END

    pygame.draw.rect(screen, "green", snake.head)

    for square in snake.body:
        pygame.draw.rect(screen, "green", square)

    pygame.display.update()
    clock.tick(10)      # delay in ms

Getting inputs from pygame

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    # NEW CODE HERE
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_DOWN:
                snake.ydir = 1
                snake.xdir = 0
            elif event.key == pygame.K_UP:
                snake.ydir = -1
                snake.xdir = 0
            elif event.key == pygame.K_RIGHT:
                snake.ydir = 0
                snake.xdir = 1
            elif event.key == pygame.K_LEFT:
                snake.ydir = 0
                snake.xdir = -1
    # NEW CODE END

Solving glitches

# NEW CODE HERE
input_cooldown = 0.01
last_input_time = time.time()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            current_time = time.time()                                          # NEW CODE HERE
            if current_time - last_input_time > input_cooldown:                 # NEW CODE HERE
                if event.key == pygame.K_DOWN and snake.ydir != -1:             # MODIFIED CODE HERE
                    snake.ydir = 1
                    snake.xdir = 0
                elif event.key == pygame.K_UP and snake.ydir != 1:              # MODIFIED CODE HERE
                    snake.ydir = -1
                    snake.xdir = 0
                elif event.key == pygame.K_RIGHT and snake.xdir != -1:          # MODIFIED CODE HERE
                    snake.ydir = 0
                    snake.xdir = 1
                elif event.key == pygame.K_LEFT and snake.xdir != 1:            # MODIFIED CODE HERE
                    snake.ydir = 0
                    snake.xdir = -1
                last_input_time = current_time                                  # NEW CODE HERE

Making a new Apple Class

class Snake: ...

class Apple:
    def __init__(self):
        self.x = random.randint(0, SW)
        self.y = random.randint(0, SH)
        self.rect = pygame.Rect(self.x, self.y, BLOCK_SIZE, BLOCK_SIZE)

    def update(self):
        pygame.draw.rect(screen, "red", self.rect)

Initializing Apple and updating it

drawGrid()
snake = Snake()
apple = Apple()     #NEW CODE HERE

while True:
    for event in pygame.event.get():

Scroll down to drawing section

    snake.update()
    screen.fill("black")
    drawGrid()
    apple.update()  #NEW CODE HERE

    pygame.draw.rect(screen, "green", snake.head)

    for square in snake.body:
        pygame.draw.rect(screen, "green", square)

Solving not on grid problem

class Apple:
    def __init__(self):
        self.x = int(random.randint(0, SW)/BLOCK_SIZE) * BLOCK_SIZE          # CODE UPDATE
        self.y = int(random.randint(0, SH)/BLOCK_SIZE) * BLOCK_SIZE          # CODE UPDATE
        self.rect = pygame.Rect(self.x, self.y, BLOCK_SIZE, BLOCK_SIZE)

    def update(self):
        pygame.draw.rect(screen, "red", self.rect)

Apple Collision

pygame.draw.rect(screen, "green", snake.head)

for square in snake.body:
    pygame.draw.rect(screen, "green", square)

screen.blit(score,score_rect)       # Display Score

# NEW CODE HERE
if snake.head.x == apple.x and snake.head.y == apple.y:
    snake.body.append(pygame.Rect(square.x, square.y, BLOCK_SIZE, BLOCK_SIZE))      # square exists here because Python is built different (so square == the last piece of body from previous frame)
    apple = Apple()
# NEW CODE END

pygame.display.update()
clock.tick(10)      # delay in ms

Add new checkDeath() function under Snake Class

class Snake: ...
    ...
    def checkDeath(self):
        for square in self.body:
            if self.head.x == square.x and self.head.y == square.y:
                self.dead = True
            if self.head.x not in range(0, SW) or self.head.y not in range(0, SH):      # Demorgans law >:)
                self.dead = True

Call new checkDeath() function in update

class Snake: ...
    def update(self):
        # NEW CODE HERE
        global apple

        self.checkDeath()

        if self.dead:
            self.x, self.y = int((SW/4)/BLOCK_SIZE) * BLOCK_SIZE, int((SH/2)/BLOCK_SIZE) * BLOCK_SIZE       
            self.xdir = 1
            self.ydir = 0
            self.head = pygame.Rect(self.x, self.y, BLOCK_SIZE, BLOCK_SIZE)
            self.body = [pygame.Rect(self.x - BLOCK_SIZE, self.y, BLOCK_SIZE, BLOCK_SIZE)]
            self.dead = False
            apple = Apple()
        # NEW CODE END

        self.body.append(self.head)
        for i in range(len(self.body)-1):
            self.body[i].x = self.body[i+1].x
            self.body[i].y = self.body[i+1].y

Creating a Score

FONT.render("text", anti aliasing, Color)

class Apple:...

def drawGrid:...

# NEW CODE HERE
score = FONT.render("0", True, "white")
score_rect = score.get_rect(center=(SW/2, SH/16))
# NEW CODE END

drawGrid()
snake = Snake()
apple = Apple()

Rendering and updating Score on apple collision

snake.update()
    screen.fill('black')
    drawGrid()

    apple.update()

    score = FONT.render(f"{len(snake.body)-1}", True, "white")      # NEW CODE HERE
    screen.blit(score,score_rect)                                   # NEW CODE HERE

    pygame.draw.rect(screen, "green", snake.head)

Soly Image Caption

Thanks for participating please don't forget to check in, this is the only way we can argue for funding from the college next year

FYI CoD club got like $10k 2 years ago and we have nothing ;~;
so please we need the money