Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Snake Game Using Turtle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# My Interactive Snake Game

Hey there! I’m [Prashant Gohel](https://github.com/prashantgohel321)

I took the classic Snake game and gave it a modern, interactive twist — with a sleek UI, smooth gameplay, and fun new controls. This project was all about making a nostalgic game feel fresh again!

![alt text](<demo (1).gif>)

## What I Added

**Fresh UI:** Clean, responsive, and almost full-screen — with a neat header for score and controls.

**Interactive Controls**: Play, Pause, Resume, Restart — all on-screen (plus spacebar support!).

**High Score System**: Tracks and saves your best score in highscore.txt — challenge yourself!

**Smooth Game Flow**: Smart state system for seamless transitions between screens.

----

<br>

<center>
<i>💡 Built with Python</i><br>
Feel free to fork, star ⭐, or suggest improvements — I’d love to hear your thoughts!
</center>
28 changes: 28 additions & 0 deletions Snake Game Using Turtle/colors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
This file contains the color palette for the game, now including
colors for the new interactive buttons.
"""
# A fresh and vibrant color theme
# --> food.py
FOOD_COLOR = "#C70039" # A bright, contrasting red

# --> main.py
BG_COLOR = '#F0F8FF' # AliceBlue, a very light and clean background

# --> scoreboard.py
GAME_OVER_COLOR = '#D21312' # Strong red for game over message
SCORE_COLOR = '#27374D' # Dark blue for high-contrast text
MESSAGE_COLOR = '#27374D' # Consistent dark blue for other messages

# --> snake.py
FIRST_SEGMENT_COLOR = '#006400' # DarkGreen for the snake's head
BODY_COLOR = '#2E8B57' # SeaGreen for the snake's body

# --> wall.py
WALL_COLOR = '#27374D' # Dark blue for a solid, visible border

# --> UI Controls (Buttons)
BUTTON_BG_COLOR = "#526D82"
BUTTON_TEXT_COLOR = "#F0F8FF"
BUTTON_BORDER_COLOR = "#27374D"

Binary file added Snake Game Using Turtle/demo (1).gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions Snake Game Using Turtle/food.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
This file handles the creation of food. Its placement is now controlled
by the main game logic to ensure it spawns within the correct boundaries.
"""

from turtle import Turtle
import random
import colors

class Food(Turtle):
""" This class generates food for the snake to eat. """
def __init__(self):
super().__init__()
self.shape("circle")
self.penup()
self.shapesize(stretch_len=0.7, stretch_wid=0.7)
self.color(colors.FOOD_COLOR)
self.speed("fastest")

def refresh(self, left_wall, right_wall, bottom_wall, top_wall):
"""Moves the food to a new random position within the provided game boundaries."""
# Add a margin so food doesn't spawn exactly on the edge
margin = 20
random_x = random.randint(int(left_wall) + margin, int(right_wall) - margin)
random_y = random.randint(int(bottom_wall) + margin, int(top_wall) - margin)
self.goto(random_x, random_y)

1 change: 1 addition & 0 deletions Snake Game Using Turtle/highscore.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6
195 changes: 195 additions & 0 deletions Snake Game Using Turtle/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
"""
This is the main file that runs the Snake game.
It handles screen setup, dynamic boundaries, UI controls (buttons),
game state management, and the main game loop.
"""
from turtle import Screen, Turtle
from snake import Snake
from food import Food
from scoreboard import Scoreboard
from wall import Wall
import colors

# --- CONSTANTS ---
MOVE_DELAY_MS = 100 # Game speed in milliseconds

# --- GAME STATE ---
game_state = "start" # Possible states: "start", "playing", "paused", "game_over"

# --- SCREEN SETUP ---
screen = Screen()
screen.setup(width=0.9, height=0.9) # Set up a nearly fullscreen window
screen.bgcolor(colors.BG_COLOR)
screen.title("Interactive Snake Game")
screen.tracer(0)

# --- DYNAMIC GAME BOUNDARIES ---
WIDTH = screen.window_width()
HEIGHT = screen.window_height()
# These boundaries are calculated to be inside the visible wall with a safe margin
LEFT_WALL = -WIDTH / 2 + 25
RIGHT_WALL = WIDTH / 2 - 25
TOP_WALL = HEIGHT / 2 - 85
BOTTOM_WALL = -HEIGHT / 2 + 25

# --- GAME OBJECTS ---
wall = Wall()
snake = Snake()
food = Food()
# Initial food placement is now handled after boundaries are calculated
food.refresh(LEFT_WALL, RIGHT_WALL, BOTTOM_WALL, TOP_WALL)
scoreboard = Scoreboard()

# --- UI CONTROLS (BUTTONS) ---
buttons = {} # Dictionary to hold button turtles and their properties

def create_button(name, x, y, width=120, height=40):
"""Creates a turtle-based button with a label."""
if name in buttons and buttons[name]['turtle'] is not None:
buttons[name]['turtle'].clear()

button_turtle = Turtle()
button_turtle.hideturtle()
button_turtle.penup()
button_turtle.speed("fastest")

button_turtle.goto(x - width/2, y - height/2)
button_turtle.color(colors.BUTTON_BORDER_COLOR, colors.BUTTON_BG_COLOR)
button_turtle.begin_fill()
for _ in range(2):
button_turtle.forward(width)
button_turtle.left(90)
button_turtle.forward(height)
button_turtle.left(90)
button_turtle.end_fill()

button_turtle.goto(x, y - 12)
button_turtle.color(colors.BUTTON_TEXT_COLOR)
button_turtle.write(name, align="center", font=("Lucida Sans", 14, "bold"))

buttons[name] = {'turtle': button_turtle, 'x': x, 'y': y, 'w': width, 'h': height, 'visible': True}

def hide_button(name):
"""Hides a button by clearing its turtle."""
if name in buttons and buttons[name]['visible']:
buttons[name]['turtle'].clear()
buttons[name]['visible'] = False

def manage_buttons():
"""Shows or hides buttons based on the current game state."""
all_buttons = ["Play", "Pause", "Resume", "Restart"]
for btn_name in all_buttons:
hide_button(btn_name)

btn_x = WIDTH / 2 - 100
btn_y = HEIGHT / 2 - 45

if game_state == "start":
create_button("Play", 0, -100)
elif game_state == "playing":
create_button("Pause", btn_x, btn_y)
elif game_state == "paused":
create_button("Resume", btn_x, btn_y)
elif game_state == "game_over":
create_button("Restart", btn_x, btn_y)

# --- GAME LOGIC & STATE TRANSITIONS ---
def start_game():
global game_state
if game_state == "start":
game_state = "playing"
scoreboard.update_scoreboard()

def toggle_pause_resume():
global game_state
if game_state == "playing":
game_state = "paused"
scoreboard.display_pause()
elif game_state == "paused":
game_state = "playing"
scoreboard.update_scoreboard()

def restart_game():
global game_state
if game_state == "game_over":
game_state = "playing"
snake.reset()
food.refresh(LEFT_WALL, RIGHT_WALL, BOTTOM_WALL, TOP_WALL)
scoreboard.reset()

def is_click_on_button(name, x, y):
"""Checks if a click (x, y) is within the bounds of a visible button."""
if name in buttons and buttons[name]['visible']:
btn = buttons[name]
return (btn['x'] - btn['w']/2 < x < btn['x'] + btn['w']/2 and
btn['y'] - btn['h']/2 < y < btn['y'] + btn['h']/2)
return False

def handle_click(x, y):
"""Main click handler to delegate actions based on button clicks."""
if game_state == "start" and is_click_on_button("Play", x, y):
start_game()
elif game_state == "playing" and is_click_on_button("Pause", x, y):
toggle_pause_resume()
elif game_state == "paused" and is_click_on_button("Resume", x, y):
toggle_pause_resume()
elif game_state == "game_over" and is_click_on_button("Restart", x, y):
restart_game()

# --- KEYBOARD HANDLERS ---
def handle_snake_up():
if game_state in ["start", "playing"]:
start_game()
snake.up()
def handle_snake_down():
if game_state in ["start", "playing"]:
start_game()
snake.down()
def handle_snake_left():
if game_state in ["start", "playing"]:
start_game()
snake.left()
def handle_snake_right():
if game_state in ["start", "playing"]:
start_game()
snake.right()

# --- KEY & MOUSE BINDINGS ---
screen.listen()
screen.onkey(handle_snake_up, "Up")
screen.onkey(handle_snake_down, "Down")
screen.onkey(handle_snake_left, "Left")
screen.onkey(handle_snake_right, "Right")
screen.onkey(toggle_pause_resume, "space")
screen.onkey(restart_game, "r")
screen.onkey(restart_game, "R")
screen.onclick(handle_click)

# --- MAIN GAME LOOP ---
def game_loop():
global game_state
if game_state == "playing":
snake.move()
# Collision with food
if snake.head.distance(food) < 20:
food.refresh(LEFT_WALL, RIGHT_WALL, BOTTOM_WALL, TOP_WALL)
snake.extend()
scoreboard.increase_score()
# Collision with wall
if not (LEFT_WALL < snake.head.xcor() < RIGHT_WALL and BOTTOM_WALL < snake.head.ycor() < TOP_WALL):
game_state = "game_over"
scoreboard.game_over()
# Collision with tail
for segment in snake.segments[1:]:
if snake.head.distance(segment) < 10:
game_state = "game_over"
scoreboard.game_over()
manage_buttons()
screen.update()
screen.ontimer(game_loop, MOVE_DELAY_MS)

# --- INITIALIZE GAME ---
scoreboard.display_start_message()
game_loop()
screen.exitonclick()

80 changes: 80 additions & 0 deletions Snake Game Using Turtle/scoreboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""
This file manages the display of the score, high score, and game messages.
It now positions the score dynamically in the top-left corner.
"""
from turtle import Turtle, Screen
import colors

# Constants for styling and alignment
ALIGNMENT = "left"
SCORE_FONT = ("Lucida Sans", 20, "bold")
MESSAGE_FONT = ("Courier", 40, "bold")
INSTRUCTION_FONT = ("Lucida Sans", 16, "normal")

class Scoreboard(Turtle):
""" This class maintains the scoreboard, high score, and game messages. """
def __init__(self):
super().__init__()
self.screen = Screen() # Get access to the screen object
self.score = 0
self.high_score = self.load_high_score()
self.penup()
self.hideturtle()
self.update_scoreboard()

def load_high_score(self):
"""Loads high score from highscore.txt. Returns 0 if not found."""
try:
with open("highscore.txt", mode="r") as file:
return int(file.read())
except (FileNotFoundError, ValueError):
return 0

def update_scoreboard(self):
"""Clears and rewrites the score and high score in the top-left corner."""
self.clear()
self.color(colors.SCORE_COLOR)
# Dynamically calculate position to be well-placed in the header
x_pos = -self.screen.window_width() / 2 + 30
y_pos = self.screen.window_height() / 2 - 60
self.goto(x_pos, y_pos)
self.write(f"Score: {self.score} | High Score: {self.high_score}", align=ALIGNMENT, font=SCORE_FONT)

def increase_score(self):
"""Increases score and updates the display."""
self.score += 1
self.update_scoreboard()

def reset(self):
"""Checks for new high score, saves it, and resets the score."""
if self.score > self.high_score:
self.high_score = self.score
with open("highscore.txt", mode="w") as file:
file.write(str(self.high_score))
self.score = 0
self.update_scoreboard()

def game_over(self):
"""Displays the Game Over message and instructions."""
self.goto(0, 40)
self.color(colors.GAME_OVER_COLOR)
self.write("GAME OVER", align="center", font=MESSAGE_FONT)
self.goto(0, -40)
self.write("Click 'Restart' or Press 'R'", align="center", font=INSTRUCTION_FONT)

def display_pause(self):
"""Displays the PAUSED message."""
self.goto(0, 40)
self.color(colors.MESSAGE_COLOR)
self.write("PAUSED", align="center", font=MESSAGE_FONT)
self.goto(0, -40)
self.write("Click 'Resume' or Press 'Space'", align="center", font=INSTRUCTION_FONT)

def display_start_message(self):
"""Displays the welcome message and starting instructions."""
self.goto(0, 40)
self.color(colors.MESSAGE_COLOR)
self.write("SNAKE GAME", align="center", font=MESSAGE_FONT)
self.goto(0, -40)
self.write("Click 'Play' or an Arrow Key to Start", align="center", font=INSTRUCTION_FONT)

1 change: 1 addition & 0 deletions Snake Game Using Turtle/screenshots
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading