From de566223b982421165656eb0491fe903d9709257 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Thu, 29 Jan 2026 15:38:20 -0500 Subject: [PATCH] Add reactive game template refactoring plan --- REFACTORING_PLAN.md | 438 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 REFACTORING_PLAN.md diff --git a/REFACTORING_PLAN.md b/REFACTORING_PLAN.md new file mode 100644 index 0000000..dd4e462 --- /dev/null +++ b/REFACTORING_PLAN.md @@ -0,0 +1,438 @@ +# Reactive Game Template Refactoring Plan + +**Goal**: Transform `basic1.cpp` into a clean, event-driven reactive game template where: +- Display only updates when input is received (ideal for e-ink displays) +- All drawing happens inside dedicated functions +- Game state is separate from input handling +- Clear structure for developers to customize game logic + +--- + +## Phase 1: Define Core Structures and Types + +### ☐ Step 1.1: Create InputEvent structure +**Location**: After includes, before interrupt handlers +**Action**: Define a unified `InputEvent` structure to normalize all input types (touch, button, gesture) +```cpp +// Input event types +enum InputType { + INPUT_NONE = 0, + INPUT_TOUCH_DOWN, + INPUT_TOUCH_MOVE, + INPUT_TOUCH_UP, + INPUT_BUTTON_0, + INPUT_BUTTON_1, + INPUT_GESTURE +}; + +// Unified input event structure +struct InputEvent { + InputType type; + int16_t x; + int16_t y; + uint8_t gesture_code; // For gesture events + uint8_t button_id; // For button events + uint8_t pressure; // Touch pressure/weight + bool valid; // Set to true if event is valid +}; +``` + +### ☐ Step 1.2: Create GameState structure +**Location**: After InputEvent definition +**Action**: Define a `GameState` structure to hold all game-specific data (example for drawing game) +```cpp +// Game state - customize this for your game +struct GameState { + // Drawing game state + int16_t last_x; + int16_t last_y; + bool is_drawing; + + // General game state + uint32_t score; + bool game_over; + uint32_t frame_count; + + // Statistics + uint32_t touch_success_count; + uint32_t touch_fail_count; +}; +``` + +### ☐ Step 1.3: Create GameConfig structure +**Location**: After GameState definition +**Action**: Define configurable parameters for the game +```cpp +// Game configuration - adjust these for your game +struct GameConfig { + uint32_t touch_debounce_ms; // Touch polling rate + uint32_t button_debounce_ms; // Button debounce delay + bool enable_gestures; // Enable gesture recognition + bool enable_continuous_draw; // Allow continuous drawing while touched + bool debug_verbose; // Print debug messages +}; +``` + +--- + +## Phase 2: Refactor Input System + +### ☐ Step 2.1: Simplify touch interrupt handler +**Location**: `touch_interrupt_handler()` function (lines ~52-83) +**Action**: Remove all processing logic, keep only flag setting +- Remove `touch->read_touch()` call from ISR +- Remove printf statements from ISR +- Keep only `touch_interrupt_flag` and `touch_event_down` flag setting + +### ☐ Step 2.2: Simplify button interrupt handler +**Location**: `button_interrupt_handler()` function (lines ~98-113) +**Action**: Remove printf from ISR, keep only flag setting +- Remove `printf()` calls from interrupt handler +- Keep only flag setting logic + +### ☐ Step 2.3: Create process_touch_input() function +**Location**: After interrupt handlers, before refresh_screen() +**Action**: Create dedicated function to process touch events +```cpp +/** + * @brief Process touch input and convert to InputEvent + * + * Reads touch data from controller and creates appropriate InputEvent. + * Handles debouncing and filtering internally. + * + * @param config Game configuration + * @param last_time Pointer to last touch time for debouncing + * @return InputEvent structure (valid=false if no valid input) + */ +InputEvent process_touch_input(const GameConfig& config, uint32_t* last_time); +``` + +### ☐ Step 2.4: Create process_button_input() function +**Location**: After process_touch_input() +**Action**: Create dedicated function to process button events +```cpp +/** + * @brief Process button input and convert to InputEvent + * + * Checks button flags and verifies button state with debouncing. + * Clears flags after processing. + * + * @param config Game configuration + * @return InputEvent structure (valid=false if no valid input) + */ +InputEvent process_button_input(const GameConfig& config); +``` + +### ☐ Step 2.5: Create get_gesture_name() helper function +**Location**: After process_button_input() +**Action**: Extract gesture name lookup into helper function +```cpp +/** + * @brief Get human-readable gesture name + * + * @param gesture_code Gesture code from touch controller + * @return Constant string with gesture name + */ +const char* get_gesture_name(uint8_t gesture_code); +``` + +--- + +## Phase 3: Create Game Logic Functions + +### ☐ Step 3.1: Create game_init() function +**Location**: After input processing functions +**Action**: Initialize game state +```cpp +/** + * @brief Initialize game state + * + * Called once at startup to set initial game values. + * Customize this for your game. + * + * @param state Pointer to GameState to initialize + */ +void game_init(GameState* state); +``` + +### ☐ Step 3.2: Create game_update() function +**Location**: After game_init() +**Action**: Update game state based on input +```cpp +/** + * @brief Update game state based on input event + * + * This is where your game logic goes. + * Called whenever an input event occurs. + * + * @param state Pointer to GameState to update + * @param input Input event to process + * @param config Game configuration + * @return true if screen needs refresh (drawing occurred) + */ +bool game_update(GameState* state, const InputEvent& input, const GameConfig& config); +``` + +### ☐ Step 3.3: Create game_draw() function +**Location**: After game_update() +**Action**: Draw game visuals based on state +```cpp +/** + * @brief Draw game graphics to screen buffer + * + * All drawing operations go here. + * Called only when game_update() returns true. + * + * @param state Pointer to current GameState + * @param renderer Renderer for drawing primitives + * @param gui GUI system for widgets (optional) + */ +void game_draw(const GameState* state, LowLevelRenderer* renderer, LowLevelGUI* gui); +``` + +--- + +## Phase 4: Refactor Main Function + +### ☐ Step 4.1: Move global variables into main() +**Location**: main() function, beginning +**Action**: Move state variables into main as local variables +- Convert `last_x`, `last_y`, `was_touched` into GameState +- Convert `touch_fail_count`, `touch_success_count` into GameState +- Keep only interrupt flags as globals + +### ☐ Step 4.2: Create and initialize GameConfig +**Location**: main() function, after display init +**Action**: Create GameConfig instance with sensible defaults +```cpp +GameConfig config = { + .touch_debounce_ms = 10, + .button_debounce_ms = 50, + .enable_gestures = true, + .enable_continuous_draw = true, + .debug_verbose = false +}; +``` + +### ☐ Step 4.3: Create and initialize GameState +**Location**: main() function, after GameConfig +**Action**: Create GameState and call game_init() +```cpp +GameState game_state; +game_init(&game_state); +``` + +### ☐ Step 4.4: Call game_draw() for initial screen +**Location**: main() function, replace GUI setup code +**Action**: Replace hardcoded GUI drawing with game_draw() call +- Remove lines creating windows, status bars, gauges +- Call `game_draw(&game_state, &renderer, &gui);` +- Keep initial refresh_screen() call + +### ☐ Step 4.5: Refactor main loop with reactive pattern +**Location**: main() while loop (lines ~321-423) +**Action**: Replace polling logic with clean reactive pattern +```cpp +// Reactive game loop pattern: +while (1) { + __wfi(); // Sleep until interrupt + + InputEvent input = {0}; + bool needs_refresh = false; + + // 1. Process button input first (higher priority) + input = process_button_input(config); + if (input.valid) { + needs_refresh = game_update(&game_state, input, config); + } + + // 2. Process touch input + if (!input.valid) { + input = process_touch_input(config, &last_touch_time); + if (input.valid) { + needs_refresh = game_update(&game_state, input, config); + } + } + + // 3. Draw and refresh only if needed + if (needs_refresh) { + game_draw(&game_state, &renderer, &gui); + refresh_screen(bit_buffer, display); + } +} +``` + +--- + +## Phase 5: Implement Function Bodies + +### ☐ Step 5.1: Implement process_touch_input() +**Action**: Move touch reading logic from main loop into this function +- Check `touch_interrupt_flag`, return invalid event if not set +- Implement debouncing using `last_time` parameter +- Read `TouchData` from touch controller +- Convert to `InputEvent` structure +- Handle `touch_event_down` flag for TOUCH_DOWN vs TOUCH_MOVE vs TOUCH_UP +- Return populated InputEvent + +### ☐ Step 5.2: Implement process_button_input() +**Action**: Move button handling logic from main loop into this function +- Check button flags (`button_key0_pressed`, `button_key1_pressed`) +- Implement debouncing with `sleep_ms()` and `gpio_get()` verification +- Clear flags after processing +- Return InputEvent with button_id set + +### ☐ Step 5.3: Implement get_gesture_name() +**Action**: Move gesture switch statement into this function +- Simple switch on gesture_code +- Return const char* string + +### ☐ Step 5.4: Implement game_init() +**Action**: Initialize GameState fields +```cpp +void game_init(GameState* state) { + state->last_x = -1; + state->last_y = -1; + state->is_drawing = false; + state->score = 0; + state->game_over = false; + state->frame_count = 0; + state->touch_success_count = 0; + state->touch_fail_count = 0; +} +``` + +### ☐ Step 5.5: Implement game_update() +**Action**: Move game logic (drawing lines) into this function +- Handle different InputEvent types +- Update GameState based on input +- Return true if drawing occurred (needs refresh) +- Example: For drawing game, draw line from last_x/y to current x/y + +### ☐ Step 5.6: Implement game_draw() +**Action**: Create initial UI or game graphics +- Can draw GUI elements (windows, gauges, etc.) +- Or draw game-specific graphics +- For drawing game, no additional drawing needed (done in game_update) +- Can add score display, game over screen, etc. + +--- + +## Phase 6: Documentation and Comments + +### ☐ Step 6.1: Update file header comment +**Action**: Change description to reflect template nature +- Update title to "Reactive Game Template" +- Add description of architecture +- Add usage instructions + +### ☐ Step 6.2: Add section separator comments +**Action**: Add clear comment blocks separating each section +```cpp +// ============================================================================ +// INPUT EVENT STRUCTURES +// ============================================================================ + +// ============================================================================ +// GAME STATE AND CONFIGURATION +// ============================================================================ + +// ============================================================================ +// INTERRUPT HANDLERS (Keep these minimal!) +// ============================================================================ + +// ============================================================================ +// INPUT PROCESSING +// ============================================================================ + +// ============================================================================ +// GAME LOGIC (Customize this section for your game!) +// ============================================================================ + +// ============================================================================ +// MAIN PROGRAM +// ============================================================================ +``` + +### ☐ Step 6.3: Add customization guide comments +**Action**: Add comments explaining what to modify for new games +```cpp +// ============================================================================ +// HOW TO CREATE YOUR OWN GAME: +// ============================================================================ +// 1. Modify GameState structure with your game variables +// 2. Implement game_init() to set initial values +// 3. Implement game_update() to handle input and update state +// 4. Implement game_draw() to render your game graphics +// 5. Adjust GameConfig for your game's needs +// 6. The reactive loop and input system work automatically! +// ============================================================================ +``` + +--- + +## Phase 7: Testing and Validation + +### ☐ Step 7.1: Verify compilation +**Action**: Build the project and fix any compilation errors +- Run `./build_and_flash.sh` +- Fix any syntax errors or missing definitions + +### ☐ Step 7.2: Test on hardware (if available) +**Action**: Flash to device and test input/drawing +- Verify touch input works +- Verify buttons work (on e-ink board) +- Verify display only updates on input + +### ☐ Step 7.3: Create example game variations +**Action**: Create commented-out example implementations in the file +- Add example game_update() for different game types +- Add example game_draw() for different visualizations +- Keep default drawing game as primary example + +--- + +## Phase 8: Create Template Documentation + +### ☐ Step 8.1: Create TEMPLATE_USAGE.md +**Action**: Write comprehensive guide for using the template +- Architecture overview +- How to customize for different games +- Example game ideas (snake, pong, tic-tac-toe, etc.) +- Input handling best practices +- E-ink optimization tips + +### ☐ Step 8.2: Update README.md +**Action**: Add section about the reactive game template +- Link to TEMPLATE_USAGE.md +- Highlight key features +- Show before/after code structure + +--- + +## Completion Checklist + +- [ ] All Phase 1 steps completed (Core Structures) +- [ ] All Phase 2 steps completed (Input System) +- [ ] All Phase 3 steps completed (Game Logic) +- [ ] All Phase 4 steps completed (Main Function) +- [ ] All Phase 5 steps completed (Function Bodies) +- [ ] All Phase 6 steps completed (Documentation) +- [ ] All Phase 7 steps completed (Testing) +- [ ] All Phase 8 steps completed (Template Documentation) + +--- + +## Notes for Implementation + +**Work one step at a time**: Each checkbox should be completed and verified before moving to the next. + +**Test incrementally**: After each phase, verify the code still compiles. + +**Keep it simple**: The template should be easy to understand and modify. + +**Optimize for e-ink**: Minimize refreshes, only redraw on input. + +**Power efficiency**: Use `__wfi()` to sleep between inputs. + +**Flexibility**: Template should work on all board types (TFT with touch, e-ink with buttons, etc.)