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)
)
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()
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()
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)
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
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()
Continues in Part 6
Top comments (0)