155 lines
5.6 KiB
Markdown
155 lines
5.6 KiB
Markdown
Multigame Migration Plan
|
|
Overview
|
|
Refactor the existing monolithic tic-tac-toe implementation into a modular game architecture that allows multiple games to run on the RP2350 platform while preserving the interrupt-driven, dual-core reactive architecture.
|
|
|
|
Goals
|
|
Abstract game logic from hardware/display management
|
|
Create reusable input processing system
|
|
Enable multiple games to coexist in the codebase
|
|
Maintain the efficient reactive architecture (event-driven, dual-core)
|
|
Keep hardware-specific code isolated in basic1.cpp
|
|
Architecture Components
|
|
1. Input Event System
|
|
Files: lib/input_event.h
|
|
|
|
Defines shared input event structures used by both InputManager and Game classes:
|
|
|
|
InputType enum (NONE, TOUCH_DOWN, TOUCH_MOVE, TOUCH_UP, BUTTON_0, BUTTON_1, GESTURE)
|
|
InputEvent struct with coordinates, gesture codes, button IDs, pressure, validity flag
|
|
2. Input Manager
|
|
Files: lib/input_manager.h, lib/input_manager.cpp
|
|
|
|
Handles all input processing and debouncing:
|
|
|
|
Constructor: InputManager(LowLevelTouch* touch, GameConfig* config)
|
|
Methods: process_touch_input(uint32_t* last_time), process_button_input()
|
|
Returns InputEvent objects to be passed to games
|
|
Manages touch debouncing and gesture recognition
|
|
Does NOT handle button GPIO setup (stays in basic1.cpp)
|
|
3. Abstract Game Base Class
|
|
Files: lib/game.h
|
|
|
|
Defines the interface all games must implement:
|
|
|
|
Constructor: Game(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui)
|
|
Protected members: width, height, renderer, gui
|
|
Pure virtual methods:
|
|
virtual void init() = 0 - Initialize game state
|
|
virtual bool update(const InputEvent& event) = 0 - Process input, return true if redraw needed
|
|
virtual void draw() = 0 - Render game to buffer
|
|
Virtual destructor: virtual ~Game() {}
|
|
4. Tic-Tac-Toe Game Implementation
|
|
Files: games/tic_tac_toe.h, games/tic_tac_toe.cpp
|
|
|
|
Concrete implementation of the Game interface:
|
|
|
|
Private GameState struct with board, current player, winner, statistics
|
|
Private method: check_winner()
|
|
Private constants: BOARD_SIZE, CELL_SIZE, BOARD_Y, LINE_WIDTH
|
|
Overrides: init(), update(const InputEvent& event), draw()
|
|
Encapsulates all tic-tac-toe logic extracted from basic1.cpp lines 162-770
|
|
5. Main Loop Refactor
|
|
File: basic1.cpp
|
|
|
|
Simplified main program focusing on hardware management:
|
|
|
|
Keep: Display initialization, dual-core setup, interrupt handlers, GPIO configuration
|
|
Remove: Game-specific logic (moves to TicTacToeGame)
|
|
Add: InputManager instantiation, Game* pointer instantiation
|
|
Modify main loop:
|
|
Migration Steps
|
|
Step 1: Create Input Event Header
|
|
Extract input structures from basic1.cpp (lines 134-156) to new file lib/input_event.h.
|
|
|
|
Validation:
|
|
|
|
Compile successfully
|
|
No duplicate definitions
|
|
Both basic1.cpp and new files include this header
|
|
Step 2: Create Input Manager
|
|
Move input processing functions (lines 313-442) to new InputManager class.
|
|
|
|
Validation:
|
|
|
|
Constructor properly stores touch pointer and config
|
|
process_touch_input() returns valid InputEvent on touch
|
|
process_button_input() returns valid InputEvent on button press
|
|
Debouncing still works correctly
|
|
Compile and run - verify touch/button detection unchanged
|
|
Step 3: Create Abstract Game Class
|
|
Create lib/game.h with base class definition.
|
|
|
|
Validation:
|
|
|
|
Header compiles successfully
|
|
Pure virtual methods defined correctly
|
|
Protected members accessible to derived classes
|
|
Step 4: Extract Tic-Tac-Toe Game
|
|
Create TicTacToeGame class inheriting from Game.
|
|
|
|
Validation:
|
|
|
|
Move GameState struct (lines 162-176) - verify all members present
|
|
Move check_winner() (lines 447-495) - verify logic identical
|
|
Move game_init() to init() override (lines 505-522)
|
|
Move game_update() to update() override (lines 536-668)
|
|
Move game_draw() to draw() override (lines 671-770)
|
|
Move constants: BOARD_SIZE, CELL_SIZE, BOARD_Y
|
|
Compile successfully
|
|
Step 5: Refactor Main Loop
|
|
Update basic1.cpp to use new architecture.
|
|
|
|
Validation:
|
|
|
|
Remove old GameState state variable
|
|
Remove old game functions
|
|
Instantiate InputManager after hardware init
|
|
Instantiate TicTacToeGame as Game*
|
|
Update main loop to use polymorphic calls
|
|
Compile successfully
|
|
Run and verify: Touch input works, buttons work, game plays correctly
|
|
Verify dual-core refresh still works (Core 1 handles display)
|
|
Test full game: place pieces, win conditions, restart
|
|
Step 6: Update Build System
|
|
Modify CMakeLists.txt to include new source files.
|
|
|
|
Validation:
|
|
|
|
Add lib/input_manager.cpp
|
|
Add games/tic_tac_toe.cpp
|
|
Clean build succeeds
|
|
Binary size reasonable (no major bloat)
|
|
Step 7: Final Testing
|
|
Comprehensive system test.
|
|
|
|
Validation:
|
|
|
|
Touch detection works (tap cells)
|
|
Button navigation works (KEY0/KEY1)
|
|
Game logic correct (wins, ties, restart)
|
|
Display refreshes properly (no artifacts)
|
|
Statistics persist across games
|
|
Debug output shows correct events
|
|
System remains responsive during e-ink refresh
|
|
Future Extensions
|
|
Adding New Games
|
|
To add a new game (e.g., Snake, Pong):
|
|
|
|
Create games/snake.h and games/snake.cpp
|
|
Inherit from Game base class
|
|
Implement init(), update(), draw()
|
|
Define game-specific state as private members
|
|
Add to CMakeLists.txt
|
|
Change game selection in basic1.cpp: Game* current_game = new SnakeGame(...)
|
|
Game Launcher (Future)
|
|
Create GameLauncher class to display menu
|
|
Allow runtime game switching
|
|
Handle cleanup: delete current_game; current_game = new SnakeGame(...)
|
|
Store game selection preference in flash
|
|
Key Design Principles
|
|
Hardware Isolation: All GPIO, interrupts, hardware config stay in basic1.cpp
|
|
State Encapsulation: Each game owns its state completely
|
|
Polymorphism: Use Game* pointer for runtime flexibility
|
|
Event-Driven: Games respond to InputEvent objects only
|
|
Reactive Rendering: Only redraw when update() returns true
|
|
Dual-Core Efficiency: Core 0 handles logic, Core 1 handles display refresh |