Skip to content
Open
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
222 changes: 194 additions & 28 deletions snake_game_human.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class Direction(Enum):

BLOCK_SIZE = 20
SPEED = 20
MAX_SPEED = 50
SPEED_INCREMENT = 0.5

class SnakeGame:

Expand All @@ -47,12 +49,35 @@ def __init__(self, w=640, h=480):
self.food = None
self._place_food()

# pause state
self.paused = False
self.game_over = False
self.game_started = False

# high score
self.high_score = 0

def _place_food(self):
x = random.randint(0, (self.w-BLOCK_SIZE )//BLOCK_SIZE )*BLOCK_SIZE
y = random.randint(0, (self.h-BLOCK_SIZE )//BLOCK_SIZE )*BLOCK_SIZE
self.food = Point(x, y)
if self.food in self.snake:
self._place_food()

def is_valid_direction(self, new_direction):
"""Check if the new direction is valid (not opposite to current direction)"""
opposite_directions = {
Direction.RIGHT: Direction.LEFT,
Direction.LEFT: Direction.RIGHT,
Direction.UP: Direction.DOWN,
Direction.DOWN: Direction.UP
}
return new_direction != opposite_directions[self.direction]

def get_current_speed(self):
"""Calculate current speed based on score"""
current_speed = SPEED + (self.score * SPEED_INCREMENT)
return min(current_speed, MAX_SPEED)

def play_step(self):
# 1. collect user input
Expand All @@ -61,37 +86,64 @@ def play_step(self):
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.direction = Direction.LEFT
if event.key == pygame.K_SPACE:
self.paused = not self.paused
elif event.key == pygame.K_LEFT:
if self.is_valid_direction(Direction.LEFT):
self.direction = Direction.LEFT
elif event.key == pygame.K_RIGHT:
self.direction = Direction.RIGHT
if self.is_valid_direction(Direction.RIGHT):
self.direction = Direction.RIGHT
elif event.key == pygame.K_UP:
self.direction = Direction.UP
if self.is_valid_direction(Direction.UP):
self.direction = Direction.UP
elif event.key == pygame.K_DOWN:
self.direction = Direction.DOWN

# 2. move
self._move(self.direction) # update the head
self.snake.insert(0, self.head)
if self.is_valid_direction(Direction.DOWN):
self.direction = Direction.DOWN

# 2. move (skip if paused)
if not self.paused:
self._move(self.direction) # update the head
self.snake.insert(0, self.head)

# 3. check if game over
game_over = False
if self._is_collision():
game_over = True
return game_over, self.score

# 4. place new food or just move
if self.head == self.food:
self.score += 1
self._place_food()
if self._is_collision():
self.game_over = True
# 4. place new food or just move
if self.head == self.food:
self.score += 1
self._place_food()
else:
self.snake.pop()
else:
self.snake.pop()
game_over = False

# 5. update ui and clock
self._update_ui()
self.clock.tick(SPEED)
self.clock.tick(self.get_current_speed())
# 6. return game over and score
return game_over, self.score
return self.game_over, self.score

def reset_game(self):
"""Reset the game to initial state"""
# Update high score if current score is better
if self.score > self.high_score:
self.high_score = self.score

self.direction = Direction.RIGHT

self.head = Point(self.w/2, self.h/2)
self.snake = [self.head,
Point(self.head.x-BLOCK_SIZE, self.head.y),
Point(self.head.x-(2*BLOCK_SIZE), self.head.y)]

self.score = 0
self.food = None
self._place_food()

self.paused = False
self.game_over = False

def _is_collision(self):
# hits boundary
Expand All @@ -112,9 +164,115 @@ def _update_ui(self):

pygame.draw.rect(self.display, RED, pygame.Rect(self.food.x, self.food.y, BLOCK_SIZE, BLOCK_SIZE))

text = font.render("Score: " + str(self.score), True, WHITE)
self.display.blit(text, [0, 0])
# Display score at the top
score_text = font.render(f"Score: {self.score}", True, WHITE)
self.display.blit(score_text, [10, 10])

# Display current speed
speed_text = font.render(f"Speed: {self.get_current_speed():.1f}", True, WHITE)
self.display.blit(speed_text, [self.w - 220, 10])

# Display pause message
if self.paused:
pause_text = font.render("PAUSED - Press SPACE to Resume", True, WHITE)
text_rect = pause_text.get_rect(center=(self.w//2, self.h//2))
pygame.draw.rect(self.display, (50, 50, 50), text_rect.inflate(20, 20))
self.display.blit(pause_text, text_rect)

# Display instructions at the bottom
instructions = [
"Arrow Keys: Move | SPACE: Pause | ESC: Quit"
]
instruction_font = pygame.font.Font('arial.ttf', 18)
for i, instruction in enumerate(instructions):
instruction_text = instruction_font.render(instruction, True, WHITE)
instruction_rect = instruction_text.get_rect(center=(self.w//2, self.h - 20))
self.display.blit(instruction_text, instruction_rect)

pygame.display.flip()

def display_start_screen(self):
"""Display start screen with controls and wait for player to start"""
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
return # Any key to start

# Draw start screen
self.display.fill(BLACK)

# Display title
title_text = pygame.font.Font('arial.ttf', 50).render("SNAKE GAME", True, WHITE)
title_rect = title_text.get_rect(center=(self.w//2, self.h//2 - 120))
self.display.blit(title_text, title_rect)

# Display controls
control_lines = [
"CONTROLS:",
"Arrow Keys - Move the snake",
"SPACE - Pause/Resume",
"ESC - Quit game",
]
control_font = pygame.font.Font('arial.ttf', 20)
for i, line in enumerate(control_lines):
control_text = control_font.render(line, True, WHITE)
control_rect = control_text.get_rect(center=(self.w//2, self.h//2 - 20 + (i * 30)))
self.display.blit(control_text, control_rect)

# Display start instruction
start_text = pygame.font.Font('arial.ttf', 22).render("Press any key to start", True, BLUE2)
start_rect = start_text.get_rect(center=(self.w//2, self.h//2 + 120))
self.display.blit(start_text, start_rect)

pygame.display.flip()
self.clock.tick(SPEED)

def display_game_over_screen(self):
"""Display game over screen and handle user input for restart/quit"""
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return False # Signal to quit the program
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE or event.key == pygame.K_r:
return True # Signal to restart
elif event.key == pygame.K_ESCAPE:
return False # Signal to quit

# Draw game over screen
self.display.fill(BLACK)

# Draw snake and food (frozen state)
for pt in self.snake:
pygame.draw.rect(self.display, BLUE1, pygame.Rect(pt.x, pt.y, BLOCK_SIZE, BLOCK_SIZE))
pygame.draw.rect(self.display, BLUE2, pygame.Rect(pt.x+4, pt.y+4, 12, 12))
pygame.draw.rect(self.display, RED, pygame.Rect(self.food.x, self.food.y, BLOCK_SIZE, BLOCK_SIZE))

# Display score
score_text = font.render("Score: " + str(self.score), True, WHITE)
self.display.blit(score_text, [0, 0])

# Draw game over message
game_over_text = font.render("GAME OVER", True, RED)
game_over_rect = game_over_text.get_rect(center=(self.w//2, self.h//2 - 100))
self.display.blit(game_over_text, game_over_rect)

# Display final score and high score
final_score_text = font.render(f"Final Score: {self.score}", True, WHITE)
final_score_rect = final_score_text.get_rect(center=(self.w//2, self.h//2 - 40))
self.display.blit(final_score_text, final_score_rect)

high_score_display = font.render(f"High Score: {self.high_score}", True, WHITE)
high_score_rect = high_score_display.get_rect(center=(self.w//2, self.h//2))
self.display.blit(high_score_display, high_score_rect)

pygame.display.flip()
self.clock.tick(self.get_current_speed())

def _move(self, direction):
x = self.head.x
Expand All @@ -134,14 +292,22 @@ def _move(self, direction):
if __name__ == '__main__':
game = SnakeGame()

# Show start screen before game begins
game.display_start_screen()
game.game_started = True

# game loop
while True:
game_over, score = game.play_step()

if game_over == True:
break

if game_over:
# Display game over screen and wait for user input
restart = game.display_game_over_screen()
if restart:
game.reset_game()
game.game_started = True
else:
break

print('Final Score', score)


pygame.quit()