171 lines
6.2 KiB
Python
171 lines
6.2 KiB
Python
import pygame
|
||
import sys
|
||
import random
|
||
import time
|
||
|
||
# Init Pygame because we’re not living in the stone age
|
||
pygame.init()
|
||
|
||
# Constants that will never change except when they do
|
||
WIDTH, HEIGHT = 640, 480
|
||
# Block size: how many pixels per grid cell
|
||
BLOCK_SIZE = 20
|
||
# FPS: frames per second, aka how smooth the snake is
|
||
SPEED = 10
|
||
# Direction mapping: because string comparison is slower than integer comparison
|
||
DIRECTIONS = {'UP': (0, -1), 'DOWN': (0, 1), 'LEFT': (-1, 0), 'RIGHT': (1, 0)}
|
||
|
||
# Colors because RGB values are more readable than magic numbers
|
||
WHITE = (255, 255, 255)
|
||
BLACK = (0, 0, 0)
|
||
GREEN = (0, 255, 0)
|
||
# Red for apple and also for “game over” messages
|
||
RED = (255, 0, 0)
|
||
|
||
class SnakeGame:
|
||
def __init__(self):
|
||
self.display = pygame.display.set_mode((WIDTH, HEIGHT))
|
||
pygame.display.set_caption("Snake Game 2000 Edition") # Title is kino
|
||
self.clock = pygame.time.Clock()
|
||
self.reset()
|
||
|
||
def reset(self):
|
||
# Reset the snake to the starting position
|
||
self.snake = [(200, 200), (220, 200), (240, 200)]
|
||
# Set initial direction to right because why the fuck not
|
||
self.direction = 'RIGHT'
|
||
# Generate an apple somewhere on the grid
|
||
self.apple = self.generate_apple()
|
||
# Keep track of score
|
||
self.score = 0
|
||
# Game over flag
|
||
self.game_over = False
|
||
|
||
def generate_apple(self):
|
||
# Make sure the apple doesn't spawn where the snake already is
|
||
while True:
|
||
x = random.randint(0, WIDTH - BLOCK_SIZE) // BLOCK_SIZE * BLOCK_SIZE
|
||
y = random.randint(0, HEIGHT - BLOCK_SIZE) // BLOCK_SIZE * BLOCK_SIZE
|
||
apple = (x, y)
|
||
if apple not in self.snake:
|
||
return apple
|
||
|
||
def update(self):
|
||
if self.game_over:
|
||
# If game is over, print final score like a based champion
|
||
print(f"\nGame over anon! Final score: {self.score}")
|
||
print("You died like a virgin. Press R to try again.")
|
||
return
|
||
|
||
# Get the head of the snake
|
||
head = self.snake[-1]
|
||
# Calculate new head position
|
||
new_head = (head[0] + DIRECTIONS[self.direction][0] * BLOCK_SIZE,
|
||
head[1] + DIRECTIONS[self.direction][1] * BLOCK_SIZE)
|
||
|
||
# Check for collisions with walls and self
|
||
if ((new_head[0] < 0 or new_head[0] >= WIDTH or
|
||
new_head[1] < 0 or new_head[1] >= HEIGHT) or
|
||
new_head in self.snake):
|
||
# If you hit a wall or yourself, game over
|
||
self.game_over = True
|
||
self.draw() # Draw final frame
|
||
pygame.time.delay(2000) # Wait 2 seconds
|
||
return
|
||
|
||
# Move the snake
|
||
self.snake.append(new_head)
|
||
|
||
# Check if you ate the apple
|
||
if self.apple == new_head:
|
||
# Increase score and spawn new apple
|
||
self.score += 1
|
||
print(f"🍎 Munch! Score: {self.score}") # Emoji for based feedback
|
||
self.apple = self.generate_apple()
|
||
else:
|
||
# Otherwise, remove the tail
|
||
self.snake.pop(0)
|
||
|
||
def draw(self):
|
||
# Fill screen with black like your soul
|
||
self.display.fill(BLACK)
|
||
|
||
# Draw snake
|
||
for pos in self.snake:
|
||
pygame.draw.rect(self.display, GREEN, pygame.Rect(pos[0], pos[1], BLOCK_SIZE, BLOCK_SIZE))
|
||
|
||
# Draw apple
|
||
pygame.draw.rect(self.display, RED, pygame.Rect(self.apple[0], self.apple[1], BLOCK_SIZE, BLOCK_SIZE))
|
||
|
||
# Draw score
|
||
font = pygame.font.Font(None, 36)
|
||
text = font.render(f"Score: {self.score}", True, WHITE)
|
||
self.display.blit(text, (10, 10))
|
||
|
||
# Check if game over and draw “Game Over” text
|
||
if self.game_over:
|
||
text = font.render("Game Over. Press R to restart.", True, RED)
|
||
self.display.blit(text, (10, 50))
|
||
|
||
# Update display like a good boy
|
||
pygame.display.flip()
|
||
|
||
def handle_input(self):
|
||
for event in pygame.event.get():
|
||
if event.type == pygame.QUIT:
|
||
# Exit button pressed, clean up and quit
|
||
pygame.quit()
|
||
sys.exit()
|
||
elif event.type == pygame.KEYDOWN:
|
||
# Handle arrow key presses
|
||
if event.key == pygame.K_UP and self.direction != 'DOWN':
|
||
self.direction = 'UP'
|
||
elif event.key == pygame.K_DOWN and self.direction != 'UP':
|
||
self.direction = 'DOWN'
|
||
elif event.key == pygame.K_LEFT and self.direction != 'RIGHT':
|
||
self.direction = 'LEFT'
|
||
elif event.key == pygame.K_RIGHT and self.direction != 'LEFT':
|
||
self.direction = 'RIGHT'
|
||
elif event.key == pygame.K_r:
|
||
# Reset game
|
||
self.reset()
|
||
self.game_over = False # Clear game over state
|
||
elif event.key == pygame.K_s:
|
||
# Change speed (because speedrunners will always exist)
|
||
try:
|
||
speed = int(input("Enter new speed: "))
|
||
if speed < 1:
|
||
print("Speed must be 1 or higher, you absolute mongoloid")
|
||
else:
|
||
SPEED = speed
|
||
print(f"Speed set to {speed}. Your grandma can handle this.")
|
||
except ValueError:
|
||
print("Invalid input. Default speed remains.")
|
||
|
||
# Bonus feature: press S to show current speed
|
||
elif event.key == pygame.K_c:
|
||
print(f"Current speed: {SPEED}")
|
||
|
||
def run(self):
|
||
# Game loop
|
||
while True:
|
||
self.handle_input()
|
||
|
||
if self.game_over:
|
||
self.draw()
|
||
# Wait 2 seconds before restarting
|
||
pygame.time.delay(2000)
|
||
break
|
||
|
||
self.update()
|
||
self.draw()
|
||
# Cap framerate at SPEED
|
||
self.clock.tick(SPEED)
|
||
|
||
if __name__ == "__main__":
|
||
game = SnakeGame()
|
||
print("Welcome to Snake Game 2000 Edition!")
|
||
print("Use arrow keys to move, R to restart, S to change speed")
|
||
print(f"Current speed: {SPEED}")
|
||
game.run()
|