I am a complete beginner when it comes to programming and writing games and this is my first ever game. I made it with python 3 using pygame
library. I would appreciate any feedback really.
from __future__ import annotations
from typing import Tuple, List
import pygame
import random
import sys
# screen width & height and block size
bg_width = 500
bg_height = 500
block_size = 10
# direction strings
left = "LEFT"
right = "RIGHT"
up = "UP"
down = "DOWN"
# colors (RGB)
bg_color = black = (0, 0, 0)
yellow = (255, 255, 0)
green = (0, 128, 0)
red = (255, 0, 0)
blue = (0, 0, 255)
# pygame & font initialization
pygame.init()
window = pygame.display.set_mode((bg_width, bg_height))
pygame.display.set_caption("Snake")
font = pygame.font.SysFont('Times New Roman', 20)
text_colour = pygame.Color('White')
fps = pygame.time.Clock()
class Snake:
"""This class represents a snake object. Every snake object consists of a head
and its body.
===Attributes===
head: snake head
color: snake color
body: snake body
direction: direction of head
size: size of snake (head and body)
"""
head: List[int, int]
color: Tuple[int, int, int]
body: List[List[int, int]]
direction: str
size: int
def __init__(self, color: Tuple[int, int, int]) -> None:
self.head = [int(10*block_size), int(5*block_size)]
self.color = color
self.body = [self.head, [9*block_size, 5*block_size]]
self.direction = right
self.size = 2
def change_dir(self, direc: str) -> None:
if self.direction != left and direc == right:
self.direction = right
elif self.direction != right and direc == left:
self.direction = left
elif self.direction != down and direc == up:
self.direction = up
elif self.direction != up and direc == down:
self.direction = down
def move(self) -> None:
if self.direction == right:
self.head[0] += block_size
elif self.direction == left:
self.head[0] -= block_size
elif self.direction == up:
self.head[1] -= block_size
elif self.direction == down:
self.head[1] += block_size
self.body.insert(0, list(self.head))
self.body.pop()
if self.body[0] != self.head:
self.head = self.body[0]
def add_to_tail(self) -> None:
new_part = [self.body[-1][0], self.body[-1][1]]
self.body.append(new_part)
self.size += 1
def get_body(self) -> List[List[int, int]]:
return self.body
class Food:
"""This class represents a food object. Each food object has an x
and a y value, a color and a state.
===Attributes===
x: x-coordinate of food object
y: y-coordinate of food object
color: color of food object
state: whether food object is on screen or not
position: x,y-coordinates pair of food object
"""
x: int
y: int
color: Tuple[int, int, int]
state: bool
position: Tuple[int, int]
def __init__(self, color: Tuple[int, int, int]) -> None:
self.x = random.randint(0, bg_width//block_size - 1)*block_size
self.y = random.randint(0, bg_height//block_size - 1)*block_size
self.color = color
self.state = True
self.position = self.x, self.y
def spawn(self) -> Tuple[int, int]:
if self.state:
return self.x, self.y
else:
self.state = True
self.x = random.randint(0, bg_width // block_size-1) * block_size
self.y = random.randint(0, bg_height // block_size-1) * block_size
return self.x, self.y
def update(self, state) -> None:
self.state = state
def collision(snake_: Snake, food_target_x: int, food_target_y: int) -> int:
snake_rect = pygame.Rect(*snake_.head, block_size, block_size)
food_rect = pygame.Rect(food_target_x, food_target_y, block_size,
block_size)
if snake_rect == food_rect:
snake_.add_to_tail()
return 1
return 0
def wall_collision(s: Snake) -> bool:
if (s.head[0] < 0) or (s.head[0] > bg_width-block_size) or (s.head[1] < 0)\
or (s.head[1] > bg_height-block_size):
return True
return False
def game():
# initialize food and snake
food = Food(blue)
snake = Snake(green)
# initialize loop logic
running = True
is_over = False
# initialize score
score = 0
# game loop
while running:
# Game over Screen
while is_over:
text_on_screen = font.render("You scored: " + str(score) +
", Press R to play again or Q to quit",
True, text_colour)
window.blit(text_on_screen, [55, 225])
for event in pygame.event.get():
pressed_key = pygame.key.get_pressed()
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if pressed_key[pygame.K_q]:
pygame.quit()
sys.exit()
if pressed_key[pygame.K_r]:
game()
pygame.display.update()
# check events
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pressed = pygame.key.get_pressed()
if pressed[pygame.K_RIGHT] or pressed[pygame.K_d]:
snake.change_dir(right)
elif pressed[pygame.K_LEFT] or pressed[pygame.K_a]:
snake.change_dir(left)
elif pressed[pygame.K_UP] or pressed[pygame.K_w]:
snake.change_dir(up)
elif pressed[pygame.K_DOWN] or pressed[pygame.K_s]:
snake.change_dir(down)
# fill window and draw snake
window.fill(black)
for item in snake.get_body():
pygame.draw.rect(window, snake.color, [item[0], item[1], block_size,
block_size])
# move snake
snake.move()
# check for collision with wall:
collision_with_wall = wall_collision(snake)
if collision_with_wall:
is_over = True
# check if food is still on screen and draw it
food_pos = food.spawn()
collision_ = collision(snake, *food_pos)
if collision_ == 1:
score += 1
food.update(False)
pygame.draw.rect(window, food.color, [food_pos[0], food_pos[1],
block_size, block_size])
# renders display
pygame.display.flip()
# time delay
pygame.time.delay(60)
fps.tick(30)
pygame.quit()
quit()
game()
body: List [Tuple[int,int]]
be better? You have XY pairs, not a list of variable-length lists. \$\endgroup\$__future__
package from? I can't install it using PyCharm. \$\endgroup\$