Add main menu system with game mode selection

This commit is contained in:
Rbanh 2025-02-23 12:16:14 -05:00
parent 3cbf7b8981
commit 33cca6cd3c
3 changed files with 150 additions and 18 deletions

View File

@ -3,11 +3,15 @@
This is a living document that tracks the development progress of the AI Snake Game project. This is a living document that tracks the development progress of the AI Snake Game project.
## Current Status ## Current Status
Last Updated: [Restart Functionality Implementation] Last Updated: [Menu System Implementation]
- Added game restart functionality - Added main menu interface with game mode selection
- Added "Press any key to restart" message - Implemented menu navigation and selection
- Implemented game state reset - Added game mode display during gameplay
- Integrated menu system with game states
- Previous updates: - Previous updates:
- Added game restart functionality
- Added "Press any key to restart" message
- Implemented game state reset
- Added Food class with spawning mechanics - Added Food class with spawning mechanics
- Integrated food system with main game loop - Integrated food system with main game loop
- Implemented basic scoring system - Implemented basic scoring system
@ -36,9 +40,9 @@ Last Updated: [Restart Functionality Implementation]
- [x] Add restart functionality - [x] Add restart functionality
## Phase 3: Game Polish and UI 🔄 ## Phase 3: Game Polish and UI 🔄
- [ ] Add main menu interface - [x] Add main menu interface
- [ ] Create menu layout - [x] Create menu layout
- [ ] Add game mode selection - [x] Add game mode selection
- [ ] Add settings options - [ ] Add settings options
- [x] Implement pause functionality - [x] Implement pause functionality
- [ ] Add visual effects and animations - [ ] Add visual effects and animations
@ -51,9 +55,9 @@ Last Updated: [Restart Functionality Implementation]
- [ ] Add background music - [ ] Add background music
- [ ] Add menu sounds - [ ] Add menu sounds
- [ ] Display score and high score - [ ] Display score and high score
- [ ] Add game over screen with restart option - [x] Add game over screen with restart option
## Phase 4: AI Implementation ## Phase 4: AI Implementation 🔄
- [ ] Design AI algorithm (pathfinding) - [ ] Design AI algorithm (pathfinding)
- [ ] Research and select appropriate algorithm - [ ] Research and select appropriate algorithm
- [ ] Implement basic pathfinding - [ ] Implement basic pathfinding
@ -64,7 +68,7 @@ Last Updated: [Restart Functionality Implementation]
- [ ] Easy mode (basic pathfinding) - [ ] Easy mode (basic pathfinding)
- [ ] Medium mode (improved decision making) - [ ] Medium mode (improved decision making)
- [ ] Hard mode (optimal pathfinding) - [ ] Hard mode (optimal pathfinding)
- [ ] Create mode selection (Player vs AI) - [x] Create mode selection (Player vs AI)
- [ ] Optimize AI performance - [ ] Optimize AI performance
## Phase 5: Final Polish ## Phase 5: Final Polish
@ -81,9 +85,9 @@ Last Updated: [Restart Functionality Implementation]
- [ ] Final testing and refinements - [ ] Final testing and refinements
## Next Steps ## Next Steps
1. Create main menu interface 1. Implement AI snake movement
- Design menu layout - Design pathfinding algorithm
- Implement menu navigation - Create AI controller class
2. Add high score system 2. Add high score system
- Implement score persistence - Implement score persistence
- Add high score display - Add high score display

View File

@ -3,6 +3,7 @@ import sys
from enum import Enum, auto from enum import Enum, auto
from snake import Snake, Direction from snake import Snake, Direction
from food import Food from food import Food
from menu import Menu, GameMode
class GameState(Enum): class GameState(Enum):
MENU = auto() MENU = auto()
@ -24,11 +25,13 @@ class Game:
# Game objects # Game objects
self.block_size = 20 self.block_size = 20
self.menu = Menu(self.width, self.height)
self.reset_game() self.reset_game()
# Game state # Game state
self.state = GameState.PLAYING # Changed to start in playing state for now self.state = GameState.MENU # Start in menu state
self.running = True self.running = True
self.game_mode = None
def reset_game(self): def reset_game(self):
"""Reset the game state for a new game""" """Reset the game state for a new game"""
@ -47,6 +50,22 @@ class Game:
self.state = GameState.PAUSED self.state = GameState.PAUSED
elif self.state == GameState.PAUSED: elif self.state == GameState.PAUSED:
self.state = GameState.PLAYING self.state = GameState.PLAYING
elif self.state == GameState.MENU:
self.running = False
if self.state == GameState.MENU:
# Handle menu input
selected_mode = self.menu.handle_input(event)
if selected_mode is not None:
if selected_mode == GameMode.PLAYER:
self.game_mode = GameMode.PLAYER
self.state = GameState.PLAYING
elif selected_mode in (GameMode.AI_EASY, GameMode.AI_MEDIUM, GameMode.AI_HARD):
self.game_mode = selected_mode
self.state = GameState.PLAYING
else: # Quit selected
self.running = False
elif self.state == GameState.PLAYING: elif self.state == GameState.PLAYING:
# Handle snake direction # Handle snake direction
if event.key == pygame.K_UP: if event.key == pygame.K_UP:
@ -57,10 +76,11 @@ class Game:
self.snake.change_direction(Direction.LEFT) self.snake.change_direction(Direction.LEFT)
elif event.key == pygame.K_RIGHT: elif event.key == pygame.K_RIGHT:
self.snake.change_direction(Direction.RIGHT) self.snake.change_direction(Direction.RIGHT)
elif self.state == GameState.GAME_OVER: elif self.state == GameState.GAME_OVER:
# Any key to restart in game over state # Any key to return to menu in game over state
self.state = GameState.MENU
self.reset_game() self.reset_game()
self.state = GameState.PLAYING
def update(self): def update(self):
if self.state == GameState.PLAYING: if self.state == GameState.PLAYING:
@ -81,7 +101,10 @@ class Game:
# Clear screen # Clear screen
self.screen.fill((0, 0, 0)) # Black background self.screen.fill((0, 0, 0)) # Black background
if self.state == GameState.PLAYING or self.state == GameState.PAUSED: if self.state == GameState.MENU:
self.menu.draw(self.screen)
elif self.state == GameState.PLAYING or self.state == GameState.PAUSED:
# Draw game objects # Draw game objects
self.snake.draw(self.screen) self.snake.draw(self.screen)
self.food.draw(self.screen) self.food.draw(self.screen)
@ -91,6 +114,10 @@ class Game:
score_text = font.render(f'Score: {self.score}', True, (255, 255, 255)) score_text = font.render(f'Score: {self.score}', True, (255, 255, 255))
self.screen.blit(score_text, (10, 10)) self.screen.blit(score_text, (10, 10))
# Draw game mode
mode_text = font.render(f'Mode: {self.game_mode.name}', True, (255, 255, 255))
self.screen.blit(mode_text, (10, 50))
# Draw pause indicator # Draw pause indicator
if self.state == GameState.PAUSED: if self.state == GameState.PAUSED:
pause_text = font.render('PAUSED', True, (255, 255, 255)) pause_text = font.render('PAUSED', True, (255, 255, 255))
@ -101,7 +128,7 @@ class Game:
font = pygame.font.Font(None, 74) font = pygame.font.Font(None, 74)
text = font.render('Game Over!', True, (255, 0, 0)) text = font.render('Game Over!', True, (255, 0, 0))
score_text = font.render(f'Score: {self.score}', True, (255, 255, 255)) score_text = font.render(f'Score: {self.score}', True, (255, 255, 255))
restart_text = font.render('Press any key to restart', True, (255, 255, 255)) restart_text = font.render('Press any key for menu', True, (255, 255, 255))
text_rect = text.get_rect(center=(self.width//2, self.height//2 - 50)) text_rect = text.get_rect(center=(self.width//2, self.height//2 - 50))
score_rect = score_text.get_rect(center=(self.width//2, self.height//2 + 50)) score_rect = score_text.get_rect(center=(self.width//2, self.height//2 + 50))

101
src/menu.py Normal file
View File

@ -0,0 +1,101 @@
import pygame
from enum import Enum, auto
from typing import List, Tuple, Callable
class MenuItem:
def __init__(self, text: str, action: Callable, position: Tuple[int, int] = (0, 0)):
self.text = text
self.action = action
self.position = position
self.is_selected = False
self.color = (255, 255, 255) # Default white
self.selected_color = (0, 255, 0) # Green when selected
self.font = pygame.font.Font(None, 64)
def draw(self, screen: pygame.Surface):
"""Draw the menu item"""
color = self.selected_color if self.is_selected else self.color
text_surface = self.font.render(self.text, True, color)
text_rect = text_surface.get_rect(center=self.position)
screen.blit(text_surface, text_rect)
class GameMode(Enum):
PLAYER = auto()
AI_EASY = auto()
AI_MEDIUM = auto()
AI_HARD = auto()
class Menu:
def __init__(self, screen_width: int, screen_height: int):
self.width = screen_width
self.height = screen_height
self.selected_index = 0
self.items: List[MenuItem] = []
self.setup_menu_items()
def setup_menu_items(self):
"""Initialize menu items with their positions"""
# Calculate vertical spacing
start_y = self.height // 3
spacing = 80
center_x = self.width // 2
# Create menu items
items_data = [
("Player Game", lambda: GameMode.PLAYER),
("AI Easy", lambda: GameMode.AI_EASY),
("AI Medium", lambda: GameMode.AI_MEDIUM),
("AI Hard", lambda: GameMode.AI_HARD),
("Quit", lambda: None)
]
for i, (text, action) in enumerate(items_data):
position = (center_x, start_y + i * spacing)
self.items.append(MenuItem(text, action, position))
# Select first item
self.items[0].is_selected = True
def handle_input(self, event: pygame.event.Event) -> GameMode:
"""Handle menu navigation and selection"""
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
# Move selection up
self.items[self.selected_index].is_selected = False
self.selected_index = (self.selected_index - 1) % len(self.items)
self.items[self.selected_index].is_selected = True
elif event.key == pygame.K_DOWN:
# Move selection down
self.items[self.selected_index].is_selected = False
self.selected_index = (self.selected_index + 1) % len(self.items)
self.items[self.selected_index].is_selected = True
elif event.key == pygame.K_RETURN:
# Execute selected item's action
return self.items[self.selected_index].action()
return None
def draw(self, screen: pygame.Surface):
"""Draw the menu"""
# Draw title
title_font = pygame.font.Font(None, 100)
title_text = title_font.render("AI Snake Game", True, (0, 255, 0))
title_rect = title_text.get_rect(center=(self.width // 2, 100))
screen.blit(title_text, title_rect)
# Draw menu items
for item in self.items:
item.draw(screen)
# Draw instructions
instruction_font = pygame.font.Font(None, 36)
instructions = [
"Use UP/DOWN arrows to navigate",
"Press ENTER to select",
"Press ESC to quit"
]
start_y = self.height - 150
for i, instruction in enumerate(instructions):
text = instruction_font.render(instruction, True, (128, 128, 128))
rect = text.get_rect(center=(self.width // 2, start_y + i * 30))
screen.blit(text, rect)