DEV Community

David Newberry
David Newberry

Posted on

Pygame Snake, Pt. 5

Our snake game only has one vital piece missing: food.

Up until this point, there has been no randomness. Now that changes, so start by adding import random at the top of the file.

Now, just above the while loop, we need to create a function that will set a new, random food location. We'll introduce a new food_pos (global) variable, and set it to a new Vector2 object we create with random x and y:

def place_food():
    global food_pos
    food_pos = pygame.Vector2(
        random.randrange(W),
        random.randrange(H)
    )
Enter fullscreen mode Exit fullscreen mode

The variable has to be declared global inside the function, or else Python would create a local variable that exists only inside the function, and then disappears. By using the global keyword, we let the function know to create or update the variable in the global namespace.

This is a good start, but it's not quite done. There is a chance that it will randomly choose a position that the snake occupies. That shouldn't be allowed. So we could say if food_pos in snake: ... and set the variable to a new random location. But it could, however unlikely, again choose a location that the snake is occupying. So, instead of just using an if statement, we can use a while loop:

def place_food():
    global food_pos
    food_pos = pygame.Vector2(
        random.randrange(W),
        random.randrange(H)
    )
    while food_pos in snake:
        food_pos = pygame.Vector2(
            random.randrange(W),
            random.randrange(H)
        )

place_food()
Enter fullscreen mode Exit fullscreen mode

Note: after the function is defined, it is called to set the position of the first food on the board.

Now that the game picks a random food position, we can check to see if the snake is going to get it. Add this check under (or above, it doesn't really matter) the one that checks if the snake is hitting itself.

    if new_head == food_pos:
        grow += 1
        place_food()
Enter fullscreen mode Exit fullscreen mode

And finally, under the for loop that draws the snake, add these two lines to draw the food on the board:

    square = pygame.Rect(food_pos * S, (S, S))
    screen.fill("green", square)
Enter fullscreen mode Exit fullscreen mode

That's the basic game, but there are lots of modifications you can make, such introducing different ways to steer. At this point the player can go outside the bounds of the board. It would make sense to either have this result in death, or wrap-around. To allow wrap-arond, add this code above snake.append(new_head):

    if new_head.x < 0:
        new_head.x = W - 1
    if new_head.x >= W:
        new_head.x = 0

    if new_head.y < 0:
        new_head.y = H - 1
    if new_head.y >= H:
        new_head.y = 0
Enter fullscreen mode Exit fullscreen mode

Full code at this point:

import pygame
import random

W = 30
H = 30
S = 20

# pygame setup
pygame.init()
screen = pygame.display.set_mode((W * S, H * S))
clock = pygame.time.Clock()
running = True

# game setup
snake = [pygame.Vector2(W / 2, H / 2)]
vel = pygame.Vector2(1, 0)

key_vel = {
    pygame.K_RIGHT: pygame.Vector2(1, 0),
    pygame.K_DOWN: pygame.Vector2(0, 1),
    pygame.K_LEFT: pygame.Vector2(-1, 0),
    pygame.K_UP: pygame.Vector2(0, -1)
}

grow = 3

def place_food():
    global food_pos
    food_pos = pygame.Vector2(
        random.randrange(W),
        random.randrange(H)
    )
    while food_pos in snake:
        food_pos = pygame.Vector2(
            random.randrange(W),
            random.randrange(H)
        )

place_food()

while running:
    # poll for events
    for event in pygame.event.get():
        # pygame.QUIT = user closed window
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN and event.key in key_vel:
            vel = key_vel[event.key]

    # fill buffer with white
    screen.fill("white")

    new_head = snake[-1] + vel

    if new_head in snake:
        print("hit self")
        break

    if new_head == food_pos:
        grow += 1
        place_food()

    if new_head.x < 0:
        new_head.x = W - 1
    if new_head.x >= W:
        new_head.x = 0

    if new_head.y < 0:
        new_head.y = H - 1
    if new_head.y >= H:
        new_head.y = 0

    snake.append(new_head)

    if grow > 0:
        grow -= 1
    else:
        snake.pop(0)

    for dot in snake:
        square = pygame.Rect(dot * S, (S, S))
        screen.fill("black", square)

    square = pygame.Rect(food_pos * S, (S, S))
    screen.fill("green", square)

    # copy buffer to screen
    pygame.display.flip()

    # limits FPS
    clock.tick(20)

pygame.quit()
Enter fullscreen mode Exit fullscreen mode

Continues in Part 6

Top comments (0)