Skip to content

Instantly share code, notes, and snippets.

@ThomasRohde
Last active March 10, 2025 10:09
Show Gist options
  • Save ThomasRohde/922f22a5b8abdee758686ead06eb9bdf to your computer and use it in GitHub Desktop.
Save ThomasRohde/922f22a5b8abdee758686ead06eb9bdf to your computer and use it in GitHub Desktop.
This script implements a snazzy version of the cla...
# /// script
# description = 'A snazzy game of Snake with two AI-controlled snakes competing'
# authors = [
# 'Script-Magic AI Generator',
# ]
# date = '2024-07-14'
# requires-python = '>=3.9'
# dependencies = [
# 'pygame>=2.1.0',
# ]
# tags = [
# 'generated',
# 'script-magic',
# 'game',
# 'snake',
# 'pygame',
# 'ai',
# 'competition',
# 'edited',
# ]
# ///
# Generated from the prompt: "Create a snazzy game of snake using pygame"
# Modified to have two AI-controlled snakes compete against each other
import pygame
import random
import sys
import time
# Initialize Pygame
pygame.init()
# Set up the game window
WIDTH, HEIGHT = 800, 600
GRID_SIZE = 20
GRID_WIDTH = WIDTH // GRID_SIZE
GRID_HEIGHT = HEIGHT // GRID_SIZE
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("AI Snake Competition")
# Define colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
PURPLE = (128, 0, 128)
ORANGE = (255, 165, 0)
# Define snake properties
# Snake 1 (Green)
snake1_pos = [(GRID_WIDTH // 4, GRID_HEIGHT // 2)]
snake1_dir = (1, 0)
snake1_length = 1
# Snake 2 (Purple)
snake2_pos = [(3 * GRID_WIDTH // 4, GRID_HEIGHT // 2)]
snake2_dir = (-1, 0)
snake2_length = 1
# Define food properties
food_pos = (random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1))
# Set up the game clock
clock = pygame.time.Clock()
# Initialize scores
snake1_score = 0
snake2_score = 0
# Set game speed (FPS)
GAME_SPEED = 10
# Set up fonts
font = pygame.font.Font(None, 36)
def draw_grid():
for x in range(0, WIDTH, GRID_SIZE):
pygame.draw.line(SCREEN, (40, 40, 40), (x, 0), (x, HEIGHT))
for y in range(0, HEIGHT, GRID_SIZE):
pygame.draw.line(SCREEN, (40, 40, 40), (0, y), (WIDTH, y))
def draw_snake(snake_pos, base_color):
for i, (x, y) in enumerate(snake_pos):
color = pygame.Color(*base_color)
# Create a gradient effect for each snake
color.hsva = (color.hsva[0] + i * 5 % 360, 100, 100, 100)
pygame.draw.rect(SCREEN, color, (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE))
def draw_food():
x, y = food_pos
pygame.draw.circle(SCREEN, RED, (x * GRID_SIZE + GRID_SIZE // 2, y * GRID_SIZE + GRID_SIZE // 2), GRID_SIZE // 2)
def ai_decide_direction(snake_pos, snake_dir, other_snake_pos, food_pos):
head_x, head_y = snake_pos[0]
food_x, food_y = food_pos
# Calculate distance to food in each direction
distances = {
(0, -1): float('inf'), # Up
(0, 1): float('inf'), # Down
(-1, 0): float('inf'), # Left
(1, 0): float('inf') # Right
}
# Check each possible direction
for direction, (dx, dy) in enumerate([(0, -1), (0, 1), (-1, 0), (1, 0)]):
# Skip the opposite direction of current direction (can't go backwards)
if (dx, dy) == (-snake_dir[0], -snake_dir[1]):
continue
new_x = (head_x + dx) % GRID_WIDTH
new_y = (head_y + dy) % GRID_HEIGHT
# Check if moving in this direction would hit itself
if (new_x, new_y) in snake_pos:
continue
# Check if moving in this direction would hit the other snake
if (new_x, new_y) in other_snake_pos:
continue
# Calculate Manhattan distance to food
distance = abs(new_x - food_x) + abs(new_y - food_y)
directions = [(0, -1), (0, 1), (-1, 0), (1, 0)]
distances[directions[direction]] = distance
# If all directions are blocked, try any valid move
if all(d == float('inf') for d in distances.values()):
for direction, (dx, dy) in enumerate([(0, -1), (0, 1), (-1, 0), (1, 0)]):
if (dx, dy) == (-snake_dir[0], -snake_dir[1]):
continue
new_x = (head_x + dx) % GRID_WIDTH
new_y = (head_y + dy) % GRID_HEIGHT
# Only avoid hitting itself, accept other risks
if (new_x, new_y) not in snake_pos:
return (dx, dy)
# Choose the direction with the minimum distance to food
if distances:
min_distance = min(distances.values())
best_directions = [d for d, dist in distances.items() if dist == min_distance]
return random.choice(best_directions)
# Default to current direction if nothing else works
return snake_dir
def move_snakes():
global snake1_pos, snake1_length, snake2_pos, snake2_length, food_pos, snake1_score, snake2_score
# Move snake 1
new_head1 = ((snake1_pos[0][0] + snake1_dir[0]) % GRID_WIDTH,
(snake1_pos[0][1] + snake1_dir[1]) % GRID_HEIGHT)
snake1_pos.insert(0, new_head1)
# Move snake 2
new_head2 = ((snake2_pos[0][0] + snake2_dir[0]) % GRID_WIDTH,
(snake2_pos[0][1] + snake2_dir[1]) % GRID_HEIGHT)
snake2_pos.insert(0, new_head2)
# Check if either snake got the food
if new_head1 == food_pos:
snake1_length += 1
snake1_score += 10
generate_new_food()
elif new_head2 == food_pos:
snake2_length += 1
snake2_score += 10
generate_new_food()
else:
# Trim tails if they didn't eat
snake1_pos = snake1_pos[:snake1_length]
snake2_pos = snake2_pos[:snake2_length]
def generate_new_food():
global food_pos
# Try to find a position that's not on either snake
while True:
food_pos = (random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1))
if food_pos not in snake1_pos and food_pos not in snake2_pos:
break
def check_collisions():
# Check if snake 1 has collided with itself
if len(snake1_pos) != len(set(snake1_pos)):
return 2 # Snake 2 wins
# Check if snake 2 has collided with itself
if len(snake2_pos) != len(set(snake2_pos)):
return 1 # Snake 1 wins
# Check if snake 1's head collided with snake 2's body
if snake1_pos[0] in snake2_pos[1:]:
return 2 # Snake 2 wins
# Check if snake 2's head collided with snake 1's body
if snake2_pos[0] in snake1_pos[1:]:
return 1 # Snake 1 wins
# Check if the snakes' heads collided with each other
if snake1_pos[0] == snake2_pos[0]:
return 3 # Draw
return 0 # No collision
def game_over(winner):
if winner == 1:
winner_text = "Green Snake Wins!"
elif winner == 2:
winner_text = "Purple Snake Wins!"
else:
winner_text = "It's a Draw!"
game_over_text = font.render("Game Over!", True, WHITE)
winner_text = font.render(winner_text, True, WHITE)
score_text = font.render(f"Green: {snake1_score} - Purple: {snake2_score}", True, WHITE)
restart_text = font.render("Press R to Restart or Q to Quit", True, WHITE)
SCREEN.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - 90))
SCREEN.blit(winner_text, (WIDTH // 2 - winner_text.get_width() // 2, HEIGHT // 2 - 30))
SCREEN.blit(score_text, (WIDTH // 2 - score_text.get_width() // 2, HEIGHT // 2 + 30))
SCREEN.blit(restart_text, (WIDTH // 2 - restart_text.get_width() // 2, HEIGHT // 2 + 90))
pygame.display.flip()
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
return True
if event.key == pygame.K_q:
pygame.quit()
sys.exit()
return False
def reset_game():
global snake1_pos, snake1_dir, snake1_length, snake2_pos, snake2_dir, snake2_length, food_pos, snake1_score, snake2_score
snake1_pos = [(GRID_WIDTH // 4, GRID_HEIGHT // 2)]
snake1_dir = (1, 0)
snake1_length = 1
snake2_pos = [(3 * GRID_WIDTH // 4, GRID_HEIGHT // 2)]
snake2_dir = (-1, 0)
snake2_length = 1
food_pos = (random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1))
snake1_score = 0
snake2_score = 0
def main():
global snake1_dir, snake2_dir
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Allow manual speed adjustment
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_PLUS or event.key == pygame.K_EQUALS:
global GAME_SPEED
GAME_SPEED = min(30, GAME_SPEED + 2)
if event.key == pygame.K_MINUS:
GAME_SPEED = max(1, GAME_SPEED - 2)
# AI decision making
snake1_dir = ai_decide_direction(snake1_pos, snake1_dir, snake2_pos, food_pos)
snake2_dir = ai_decide_direction(snake2_pos, snake2_dir, snake1_pos, food_pos)
# Move snakes
move_snakes()
# Check for collisions
collision_result = check_collisions()
if collision_result > 0:
if game_over(collision_result):
reset_game()
else:
continue
# Draw everything
SCREEN.fill(BLACK)
draw_grid()
draw_snake(snake1_pos, GREEN)
draw_snake(snake2_pos, PURPLE)
draw_food()
# Display scores
snake1_score_text = font.render(f"Green: {snake1_score}", True, GREEN)
snake2_score_text = font.render(f"Purple: {snake2_score}", True, PURPLE)
speed_text = font.render(f"Speed: {GAME_SPEED}", True, WHITE)
SCREEN.blit(snake1_score_text, (10, 10))
SCREEN.blit(snake2_score_text, (WIDTH - snake2_score_text.get_width() - 10, 10))
SCREEN.blit(speed_text, (WIDTH // 2 - speed_text.get_width() // 2, 10))
# Update display
pygame.display.flip()
# Control game speed
clock.tick(GAME_SPEED)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment