The code of the classic Snake game. First version. Version 2 in the next post.
The Gameplay
I would like to make some smooth movements, instead of going forwand of one “cell” at the time. Maybe in the next posts we could try to do that.
The code of Snake
import pygame
from pygame import gfxdraw
from random import randrange
# Define Constants
BOARD_SIZE = 20 # Size of the board, in block
BLOCK_SIZE = 20 # Size of 1 block, in pixel
GAME_SPEED = 8 # Game speed (Normal = 10), The bigger, the faster
SIZE = (BOARD_SIZE * BLOCK_SIZE, BOARD_SIZE * BLOCK_SIZE) # 400 x 400
# Surface
window = pygame.display.set_mode(SIZE)
pygame.display.set_caption("Python Snake")
score = 0
# ============================ THE SNAKE POSITION AND BEHAVIOUR ======
class Snake():
def __init__(self):
self.starting_position()
def starting_position(self):
"The coordinates of the start and direction are here"
self.head = [
# self.head[0] = x = 5
int(BOARD_SIZE / 4),
# self.head[1] = x = 5
int(BOARD_SIZE / 4)]
self.body = [[self.head[0], self.head[1]],
[self.head[0] - 1, self.head[1]],
[self.head[0] - 2, self.head[1]]
]
# [ ][ ][ ] => right
self.direction = "RIGHT"
# Conditions to not go in the opposite direction
def direction_to(self, direction):
"When you hit a key in the while loop; avoid going backwards"
opposites = [("RIGHT", "LEFT"),("UP", "DOWN")]
for a, z in opposites:
if self.direction == a:
if not direction == z:
self.direction = direction
break
if self.direction == z:
if not direction == a:
self.direction = direction
break
def move(self, food_pos):
moves = {
"RIGHT": (0, 1),
"LEFT": (0, -1),
"UP" : (1, -1),
"DOWN": (1, 1)
}
for k in moves:
if self.direction == k:
self.head[moves[k][0]] += moves[k][1]
self.body.insert(0, list(self.head))
if self.head == food_pos:
return 1
else:
"If do not eat... same size"
self.body.pop()
return 0
def check_collision(self):
# Checks collision with border or himself
conditions = (
# x
self.head[0] >= 20 or self.head[0] < 0,
# y
self.head[1] > 19 or self.head[1] < 0,
# self
[x for x in self.body[1:] if self.head == x]
)
if any(conditions):
return 1
else:
return 0
# ============================= SPAWN FOOD =======================
class FoodSpawner():
def __init__(self):
self.food_pos = self.randompos()
self.there_is_food = True
def spawn_food(self):
if self.there_is_food == False:
self.food_pos = self.randompos()
self.there_is_food = True
return self.food_pos
def set_food_on_screen(self, bool_value):
self.there_is_food = bool_value
def randompos(self):
return [randrange(1, 20), randrange(1, 20)]
# ===================== DRAW HEAD, BODY and FOOD ================
class Draw:
def draw_head(pos):
pygame.draw.rect(
window,
(0, 255, 0),
pygame.Rect(
pos[0] * BLOCK_SIZE,
pos[1] * BLOCK_SIZE,
BLOCK_SIZE,
BLOCK_SIZE))
def draw_body(pos):
pygame.draw.rect(window, (0, 128, 0), pygame.Rect(pos[0] * BLOCK_SIZE, pos[1] * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
def delete_tail(pos):
pygame.draw.rect(window, (0, 0, 0), pygame.Rect(snake.body[-1][0] * BLOCK_SIZE, snake.body[-1][1] * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE))
def delete_fruit(pos, food_pos):
x = food_pos[0] * BLOCK_SIZE + 10
y = food_pos[1] * BLOCK_SIZE + 10
r = 9
gfxdraw.filled_circle(window, x, y, r, (0, 0, 0))
def draw_fruit(food_pos):
gfxdraw.filled_circle(window, food_pos[0] * BLOCK_SIZE + 10, food_pos[1] * BLOCK_SIZE + 10, 9, (255, 0, 0))
def text_surface(text_to_show, x=0, y=0, middle="both"):
"It write in the middle by default, if not middle='both' middle='x'"
text = font.render(text_to_show, 1, pygame.Color("Coral"))
if middle == "x":
text_rect = text.get_rect(center=((SIZE[0] // 2, y)))
window.blit(text, text_rect)
elif middle == "both":
text_rect = text.get_rect(center=((SIZE[0] // 2, SIZE[1] // 2)))
window.blit(text, text_rect)
else:
window.blit(text, (x, y))
pygame.display.update()
# ================================= MANAGE GAME PART =================
class Game:
clock = pygame.time.Clock()
def restart():
global GAME_SPEED
GAME_SPEED = 8
window.fill((0, 0, 0))
snake.starting_position()
Game.start()
def press_to_start():
"Initial menu"
global loop, snake, food
global font, size
pygame.init()
font = pygame.font.SysFont("Arial", 24)
snake = Snake()
food = FoodSpawner()
Draw.text_surface("Python vs Snake", y=30, middle="x")
Draw.text_surface("Press s to start")
while True:
event = pygame.event.wait()
if event.type == pygame.QUIT:
loop = 0
break
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
loop = 0
break
if event.key == pygame.K_s:
Game.restart()
break
pygame.quit()
def start():
global GAME_SPEED, score, loop
food_pos = food.spawn_food()
loop = 1
while loop:
if pygame.event.get(pygame.QUIT):
loop = 0
pygame.event.pump()
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]:
loop = 0
if keys[pygame.K_UP]:
snake.direction_to("UP")
if keys[pygame.K_DOWN]:
snake.direction_to("DOWN")
if keys[pygame.K_RIGHT]:
snake.direction_to("RIGHT")
if keys[pygame.K_LEFT]:
snake.direction_to("LEFT")
if snake.move(food_pos) == 1:
# delete_fruit(pos, food_pos)
score += 1
food.set_food_on_screen(False)
GAME_SPEED += 1
food_pos = food.spawn_food()
head = 1
for pos in snake.body:
if head == 1:
Draw.draw_head(pos)
head = 0
else:
Draw.draw_body(pos)
Draw.delete_tail(pos)
Draw.draw_fruit(food_pos)
if snake.check_collision() == 1:
loop = 0
Game.press_to_start()
pygame.display.update()
Game.clock.tick(GAME_SPEED)
pygame.quit()
Game.press_to_start()