Add main menu system with game mode selection
This commit is contained in:
parent
3cbf7b8981
commit
33cca6cd3c
30
ROADMAP.md
30
ROADMAP.md
@ -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
|
||||||
|
37
src/game.py
37
src/game.py
@ -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
101
src/menu.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user