Skip to content

Commit b637c45

Browse files
Snake Game Using Turtle | prashantgohel321
1 parent b161cba commit b637c45

File tree

10 files changed

+477
-0
lines changed

10 files changed

+477
-0
lines changed

Snake Game Using Turtle/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# My Interactive Snake Game
2+
3+
Hey there! I’m [Prashant Gohel](https://github.com/prashantgohel321)
4+
5+
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!
6+
7+
![alt text](<demo (1).gif>)
8+
9+
## What I Added
10+
11+
**Fresh UI:** Clean, responsive, and almost full-screen — with a neat header for score and controls.
12+
13+
**Interactive Controls**: Play, Pause, Resume, Restart — all on-screen (plus spacebar support!).
14+
15+
**High Score System**: Tracks and saves your best score in highscore.txt — challenge yourself!
16+
17+
**Smooth Game Flow**: Smart state system for seamless transitions between screens.
18+
19+
----
20+
21+
<br>
22+
23+
<center>
24+
<i>💡 Built with Python</i><br>
25+
Feel free to fork, star ⭐, or suggest improvements — I’d love to hear your thoughts!
26+
</center>

Snake Game Using Turtle/colors.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
This file contains the color palette for the game, now including
3+
colors for the new interactive buttons.
4+
"""
5+
# A fresh and vibrant color theme
6+
# --> food.py
7+
FOOD_COLOR = "#C70039" # A bright, contrasting red
8+
9+
# --> main.py
10+
BG_COLOR = '#F0F8FF' # AliceBlue, a very light and clean background
11+
12+
# --> scoreboard.py
13+
GAME_OVER_COLOR = '#D21312' # Strong red for game over message
14+
SCORE_COLOR = '#27374D' # Dark blue for high-contrast text
15+
MESSAGE_COLOR = '#27374D' # Consistent dark blue for other messages
16+
17+
# --> snake.py
18+
FIRST_SEGMENT_COLOR = '#006400' # DarkGreen for the snake's head
19+
BODY_COLOR = '#2E8B57' # SeaGreen for the snake's body
20+
21+
# --> wall.py
22+
WALL_COLOR = '#27374D' # Dark blue for a solid, visible border
23+
24+
# --> UI Controls (Buttons)
25+
BUTTON_BG_COLOR = "#526D82"
26+
BUTTON_TEXT_COLOR = "#F0F8FF"
27+
BUTTON_BORDER_COLOR = "#27374D"
28+
2.78 MB
Loading

Snake Game Using Turtle/food.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""
2+
This file handles the creation of food. Its placement is now controlled
3+
by the main game logic to ensure it spawns within the correct boundaries.
4+
"""
5+
6+
from turtle import Turtle
7+
import random
8+
import colors
9+
10+
class Food(Turtle):
11+
""" This class generates food for the snake to eat. """
12+
def __init__(self):
13+
super().__init__()
14+
self.shape("circle")
15+
self.penup()
16+
self.shapesize(stretch_len=0.7, stretch_wid=0.7)
17+
self.color(colors.FOOD_COLOR)
18+
self.speed("fastest")
19+
20+
def refresh(self, left_wall, right_wall, bottom_wall, top_wall):
21+
"""Moves the food to a new random position within the provided game boundaries."""
22+
# Add a margin so food doesn't spawn exactly on the edge
23+
margin = 20
24+
random_x = random.randint(int(left_wall) + margin, int(right_wall) - margin)
25+
random_y = random.randint(int(bottom_wall) + margin, int(top_wall) - margin)
26+
self.goto(random_x, random_y)
27+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6

Snake Game Using Turtle/main.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
"""
2+
This is the main file that runs the Snake game.
3+
It handles screen setup, dynamic boundaries, UI controls (buttons),
4+
game state management, and the main game loop.
5+
"""
6+
from turtle import Screen, Turtle
7+
from snake import Snake
8+
from food import Food
9+
from scoreboard import Scoreboard
10+
from wall import Wall
11+
import colors
12+
13+
# --- CONSTANTS ---
14+
MOVE_DELAY_MS = 100 # Game speed in milliseconds
15+
16+
# --- GAME STATE ---
17+
game_state = "start" # Possible states: "start", "playing", "paused", "game_over"
18+
19+
# --- SCREEN SETUP ---
20+
screen = Screen()
21+
screen.setup(width=0.9, height=0.9) # Set up a nearly fullscreen window
22+
screen.bgcolor(colors.BG_COLOR)
23+
screen.title("Interactive Snake Game")
24+
screen.tracer(0)
25+
26+
# --- DYNAMIC GAME BOUNDARIES ---
27+
WIDTH = screen.window_width()
28+
HEIGHT = screen.window_height()
29+
# These boundaries are calculated to be inside the visible wall with a safe margin
30+
LEFT_WALL = -WIDTH / 2 + 25
31+
RIGHT_WALL = WIDTH / 2 - 25
32+
TOP_WALL = HEIGHT / 2 - 85
33+
BOTTOM_WALL = -HEIGHT / 2 + 25
34+
35+
# --- GAME OBJECTS ---
36+
wall = Wall()
37+
snake = Snake()
38+
food = Food()
39+
# Initial food placement is now handled after boundaries are calculated
40+
food.refresh(LEFT_WALL, RIGHT_WALL, BOTTOM_WALL, TOP_WALL)
41+
scoreboard = Scoreboard()
42+
43+
# --- UI CONTROLS (BUTTONS) ---
44+
buttons = {} # Dictionary to hold button turtles and their properties
45+
46+
def create_button(name, x, y, width=120, height=40):
47+
"""Creates a turtle-based button with a label."""
48+
if name in buttons and buttons[name]['turtle'] is not None:
49+
buttons[name]['turtle'].clear()
50+
51+
button_turtle = Turtle()
52+
button_turtle.hideturtle()
53+
button_turtle.penup()
54+
button_turtle.speed("fastest")
55+
56+
button_turtle.goto(x - width/2, y - height/2)
57+
button_turtle.color(colors.BUTTON_BORDER_COLOR, colors.BUTTON_BG_COLOR)
58+
button_turtle.begin_fill()
59+
for _ in range(2):
60+
button_turtle.forward(width)
61+
button_turtle.left(90)
62+
button_turtle.forward(height)
63+
button_turtle.left(90)
64+
button_turtle.end_fill()
65+
66+
button_turtle.goto(x, y - 12)
67+
button_turtle.color(colors.BUTTON_TEXT_COLOR)
68+
button_turtle.write(name, align="center", font=("Lucida Sans", 14, "bold"))
69+
70+
buttons[name] = {'turtle': button_turtle, 'x': x, 'y': y, 'w': width, 'h': height, 'visible': True}
71+
72+
def hide_button(name):
73+
"""Hides a button by clearing its turtle."""
74+
if name in buttons and buttons[name]['visible']:
75+
buttons[name]['turtle'].clear()
76+
buttons[name]['visible'] = False
77+
78+
def manage_buttons():
79+
"""Shows or hides buttons based on the current game state."""
80+
all_buttons = ["Play", "Pause", "Resume", "Restart"]
81+
for btn_name in all_buttons:
82+
hide_button(btn_name)
83+
84+
btn_x = WIDTH / 2 - 100
85+
btn_y = HEIGHT / 2 - 45
86+
87+
if game_state == "start":
88+
create_button("Play", 0, -100)
89+
elif game_state == "playing":
90+
create_button("Pause", btn_x, btn_y)
91+
elif game_state == "paused":
92+
create_button("Resume", btn_x, btn_y)
93+
elif game_state == "game_over":
94+
create_button("Restart", btn_x, btn_y)
95+
96+
# --- GAME LOGIC & STATE TRANSITIONS ---
97+
def start_game():
98+
global game_state
99+
if game_state == "start":
100+
game_state = "playing"
101+
scoreboard.update_scoreboard()
102+
103+
def toggle_pause_resume():
104+
global game_state
105+
if game_state == "playing":
106+
game_state = "paused"
107+
scoreboard.display_pause()
108+
elif game_state == "paused":
109+
game_state = "playing"
110+
scoreboard.update_scoreboard()
111+
112+
def restart_game():
113+
global game_state
114+
if game_state == "game_over":
115+
game_state = "playing"
116+
snake.reset()
117+
food.refresh(LEFT_WALL, RIGHT_WALL, BOTTOM_WALL, TOP_WALL)
118+
scoreboard.reset()
119+
120+
def is_click_on_button(name, x, y):
121+
"""Checks if a click (x, y) is within the bounds of a visible button."""
122+
if name in buttons and buttons[name]['visible']:
123+
btn = buttons[name]
124+
return (btn['x'] - btn['w']/2 < x < btn['x'] + btn['w']/2 and
125+
btn['y'] - btn['h']/2 < y < btn['y'] + btn['h']/2)
126+
return False
127+
128+
def handle_click(x, y):
129+
"""Main click handler to delegate actions based on button clicks."""
130+
if game_state == "start" and is_click_on_button("Play", x, y):
131+
start_game()
132+
elif game_state == "playing" and is_click_on_button("Pause", x, y):
133+
toggle_pause_resume()
134+
elif game_state == "paused" and is_click_on_button("Resume", x, y):
135+
toggle_pause_resume()
136+
elif game_state == "game_over" and is_click_on_button("Restart", x, y):
137+
restart_game()
138+
139+
# --- KEYBOARD HANDLERS ---
140+
def handle_snake_up():
141+
if game_state in ["start", "playing"]:
142+
start_game()
143+
snake.up()
144+
def handle_snake_down():
145+
if game_state in ["start", "playing"]:
146+
start_game()
147+
snake.down()
148+
def handle_snake_left():
149+
if game_state in ["start", "playing"]:
150+
start_game()
151+
snake.left()
152+
def handle_snake_right():
153+
if game_state in ["start", "playing"]:
154+
start_game()
155+
snake.right()
156+
157+
# --- KEY & MOUSE BINDINGS ---
158+
screen.listen()
159+
screen.onkey(handle_snake_up, "Up")
160+
screen.onkey(handle_snake_down, "Down")
161+
screen.onkey(handle_snake_left, "Left")
162+
screen.onkey(handle_snake_right, "Right")
163+
screen.onkey(toggle_pause_resume, "space")
164+
screen.onkey(restart_game, "r")
165+
screen.onkey(restart_game, "R")
166+
screen.onclick(handle_click)
167+
168+
# --- MAIN GAME LOOP ---
169+
def game_loop():
170+
global game_state
171+
if game_state == "playing":
172+
snake.move()
173+
# Collision with food
174+
if snake.head.distance(food) < 20:
175+
food.refresh(LEFT_WALL, RIGHT_WALL, BOTTOM_WALL, TOP_WALL)
176+
snake.extend()
177+
scoreboard.increase_score()
178+
# Collision with wall
179+
if not (LEFT_WALL < snake.head.xcor() < RIGHT_WALL and BOTTOM_WALL < snake.head.ycor() < TOP_WALL):
180+
game_state = "game_over"
181+
scoreboard.game_over()
182+
# Collision with tail
183+
for segment in snake.segments[1:]:
184+
if snake.head.distance(segment) < 10:
185+
game_state = "game_over"
186+
scoreboard.game_over()
187+
manage_buttons()
188+
screen.update()
189+
screen.ontimer(game_loop, MOVE_DELAY_MS)
190+
191+
# --- INITIALIZE GAME ---
192+
scoreboard.display_start_message()
193+
game_loop()
194+
screen.exitonclick()
195+
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
This file manages the display of the score, high score, and game messages.
3+
It now positions the score dynamically in the top-left corner.
4+
"""
5+
from turtle import Turtle, Screen
6+
import colors
7+
8+
# Constants for styling and alignment
9+
ALIGNMENT = "left"
10+
SCORE_FONT = ("Lucida Sans", 20, "bold")
11+
MESSAGE_FONT = ("Courier", 40, "bold")
12+
INSTRUCTION_FONT = ("Lucida Sans", 16, "normal")
13+
14+
class Scoreboard(Turtle):
15+
""" This class maintains the scoreboard, high score, and game messages. """
16+
def __init__(self):
17+
super().__init__()
18+
self.screen = Screen() # Get access to the screen object
19+
self.score = 0
20+
self.high_score = self.load_high_score()
21+
self.penup()
22+
self.hideturtle()
23+
self.update_scoreboard()
24+
25+
def load_high_score(self):
26+
"""Loads high score from highscore.txt. Returns 0 if not found."""
27+
try:
28+
with open("highscore.txt", mode="r") as file:
29+
return int(file.read())
30+
except (FileNotFoundError, ValueError):
31+
return 0
32+
33+
def update_scoreboard(self):
34+
"""Clears and rewrites the score and high score in the top-left corner."""
35+
self.clear()
36+
self.color(colors.SCORE_COLOR)
37+
# Dynamically calculate position to be well-placed in the header
38+
x_pos = -self.screen.window_width() / 2 + 30
39+
y_pos = self.screen.window_height() / 2 - 60
40+
self.goto(x_pos, y_pos)
41+
self.write(f"Score: {self.score} | High Score: {self.high_score}", align=ALIGNMENT, font=SCORE_FONT)
42+
43+
def increase_score(self):
44+
"""Increases score and updates the display."""
45+
self.score += 1
46+
self.update_scoreboard()
47+
48+
def reset(self):
49+
"""Checks for new high score, saves it, and resets the score."""
50+
if self.score > self.high_score:
51+
self.high_score = self.score
52+
with open("highscore.txt", mode="w") as file:
53+
file.write(str(self.high_score))
54+
self.score = 0
55+
self.update_scoreboard()
56+
57+
def game_over(self):
58+
"""Displays the Game Over message and instructions."""
59+
self.goto(0, 40)
60+
self.color(colors.GAME_OVER_COLOR)
61+
self.write("GAME OVER", align="center", font=MESSAGE_FONT)
62+
self.goto(0, -40)
63+
self.write("Click 'Restart' or Press 'R'", align="center", font=INSTRUCTION_FONT)
64+
65+
def display_pause(self):
66+
"""Displays the PAUSED message."""
67+
self.goto(0, 40)
68+
self.color(colors.MESSAGE_COLOR)
69+
self.write("PAUSED", align="center", font=MESSAGE_FONT)
70+
self.goto(0, -40)
71+
self.write("Click 'Resume' or Press 'Space'", align="center", font=INSTRUCTION_FONT)
72+
73+
def display_start_message(self):
74+
"""Displays the welcome message and starting instructions."""
75+
self.goto(0, 40)
76+
self.color(colors.MESSAGE_COLOR)
77+
self.write("SNAKE GAME", align="center", font=MESSAGE_FONT)
78+
self.goto(0, -40)
79+
self.write("Click 'Play' or an Arrow Key to Start", align="center", font=INSTRUCTION_FONT)
80+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)