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

View File

@ -3,6 +3,7 @@ import sys
from enum import Enum, auto
from snake import Snake, Direction
from food import Food
from menu import Menu, GameMode
class GameState(Enum):
MENU = auto()
@ -24,11 +25,13 @@ class Game:
# Game objects
self.block_size = 20
self.menu = Menu(self.width, self.height)
self.reset_game()
# 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.game_mode = None
def reset_game(self):
"""Reset the game state for a new game"""
@ -47,6 +50,22 @@ class Game:
self.state = GameState.PAUSED
elif self.state == GameState.PAUSED:
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:
# Handle snake direction
if event.key == pygame.K_UP:
@ -57,10 +76,11 @@ class Game:
self.snake.change_direction(Direction.LEFT)
elif event.key == pygame.K_RIGHT:
self.snake.change_direction(Direction.RIGHT)
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.state = GameState.PLAYING
def update(self):
if self.state == GameState.PLAYING:
@ -81,7 +101,10 @@ class Game:
# Clear screen
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
self.snake.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))
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
if self.state == GameState.PAUSED:
pause_text = font.render('PAUSED', True, (255, 255, 255))
@ -101,7 +128,7 @@ class Game:
font = pygame.font.Font(None, 74)
text = font.render('Game Over!', True, (255, 0, 0))
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))
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)