Files
vim-sudoku/IMPLEMENTATION_PLAN.md
T
pastilhas 4e14d54122 feat: implement Phase 30 - useGameStatus hook
Extract game status logic into dedicated hook following vim-maze pattern:

New Module Structure:
- src/useGameStatus/types.ts: GameStatus and GameStatusManager types
- src/useGameStatus/useGameStatus.ts: Hook implementation
- src/useGameStatus/index.ts: Public exports

Hook Implementation:
- State machine with 5 states (waiting/started/paused/game-over/game-won)
- Methods: startGame, togglePause, quitGame, setGameStatus
- Clean state transitions with useState
- Simpler than vim-maze (no intermediate 'has-key' state)

Updated Core Files:
- src/useGame.ts: Use useGameStatus() instead of inline useState
  - Compose gameStatusManager into GameManager
  - Cleaner separation of concerns

- src/types.ts: Import and re-export GameStatus types
  - GameManager extends GameStatusManager (intersection type)
  - Single source of truth for status types

- src/index.ts: Export useGameStatus hook publicly

Benefits:
- Reusable state machine
- Easier to test in isolation
- Follows unified API architecture
- Matches vim-maze patterns

State Transitions:
waiting → startGame() → started ↔ togglePause() ↔ paused
started → (win/lose) → game-won/game-over
* → quitGame() → waiting

Phase 30 complete. Next: Phase 40 (useCursor composition).
2025-10-22 18:45:56 +01:00

9.1 KiB

vim-sudoku Unified API Implementation Plan

Overview

Port vim-sudoku to the unified API architecture established by vim-maze, following the composable hook pattern with useGame() as the single entry point.

Goals

  1. Follow unified API architecture from UNIFIED_API.md
  2. Maintain vim-maze patterns and conventions
  3. Preserve all working functionality from src_old
  4. Support platform integration via optional platformHook parameter
  5. Enable code reuse across games (vim-maze, vim-sudoku, vim-snake)

Architecture

Core Composition

// Inside useGame:
const board = useBoard();
const gameStatusManager = useGameStatus();
const { cursor, keyManager } = useCursor(board, gameStatusManager);
const scoreManager = useScore({ board, gameStatusManager, keyManager });

// Returns unified GameManager interface

File Structure

src/
├── index.ts                    # Main package exports
├── types.ts                    # Re-exports all types from modules
├── useGame.ts                  # Main orchestrator hook
├── useGameStatus/
│   ├── index.ts                # Public exports
│   ├── types.ts                # GameStatus, GameStatusManager
│   └── useGameStatus.ts        # Game state management
├── useBoard/
│   ├── index.ts                # Public exports
│   ├── types.ts                # SudokuBoard, SudokuCell, Difficulty, BoardManager
│   ├── useBoard.ts             # Board management hook
│   ├── SudokuGenerator.ts      # Puzzle generation (from src_old/lib/Sudoku.ts)
│   └── SudokuRenderer.ts       # DOM rendering for sudoku grid
├── useCursor/
│   ├── index.ts                # Public exports
│   ├── types.ts                # CursorManager, CursorMode, VimMotionSystem, CellEditor
│   ├── useCursor.ts            # Main cursor/input hook
│   ├── useCellEditor.ts        # Cell editing logic (analog to useHero)
│   ├── useCursorRender.ts      # Cursor rendering on DOM (analog to useHeroRender)
│   ├── useVimMotions.ts        # Vim keybinding system
│   └── useGameKeys.ts          # Game-specific key handling
└── useScore/
    ├── index.ts                # Public exports
    ├── types.ts                # TimerManager, ScoreManager
    ├── useScore.ts             # Main scoring hook
    └── useTimer.ts             # Timer state management

Implementation Steps

Phase 5: Prepare Example App

  • Update example/src/App.tsx to match vim-maze structure
  • Update App to display score metrics (time, keystrokes, final score)
  • Update App to show game status messages
  • Update App instructions for sudoku-specific keys (i for edit)
  • Keep it simple - no mobile support for now

Phase 10: Setup & Core Types

  • Create clean src/ directory structure
  • Define all types in module-specific types.ts files
  • Create index.ts exports for each module
  • Set up main types.ts re-export file

Phase 20: useBoard Hook

  • Port SudokuGenerator from src_old/lib/Sudoku.ts (Robatron's implementation)
  • Basic puzzle generation and rendering
  • Game flow integration (startGame/quitGame)

Phase 21: Replace with Better Generation Algorithm

  • Replace Robatron's algorithm with pattern-based generation
  • Implement SudokuGenerator class (from src_old/sudoku/)
  • Implement SudokuRenderer class for clean separation
  • Update useBoard to use new classes
  • Store both puzzle and solution refs
  • Port unit tests for validation
  • Three difficulty levels with configurable cell removal

Phase 30: useGameStatus Hook

  • Implement GameStatus state machine
    • 'waiting' → 'started' → 'game-won' / 'game-over'
    • 'paused' state support
  • Methods: startGame, togglePause, quitGame, setGameStatus
  • Extract from inline useState in useGame
  • Create dedicated hook following vim-maze pattern

Phase 40: useCursor Hook Composition

40.1 useCellEditor (analog to useHero)

  • Manage cursor position (row, col)
  • Handle cell editing operations
  • Validate edits against isGiven cells
  • Trigger validation on cell changes
  • Handle win condition detection

40.2 useVimMotions

  • Port vim motion system from vim-maze
  • Support: h/j/k/l, ^/$, g/G, . (repeat)
  • Support: i (insert), r (replace), x (delete)
  • Count prefix handling (e.g., 5j)
  • Mode transitions (normal ↔ edit)

40.3 useGameKeys

  • Keyboard event listener
  • Route keys through vim motion processor
  • Execute cursor movements
  • Execute cell editing commands
  • Track keystroke history
  • Handle game-specific keys (q, p, space)

40.4 useCursorRender (analog to useHeroRender)

  • Render cursor highlight on current cell
  • Show edit mode indicator
  • Flash animations (valid/invalid/locked)
  • Highlight conflict cells

40.5 useCursor Main Hook

  • Compose cellEditor, vimMotions, gameKeys, cursorRender
  • Return unified CursorManager interface
  • Split keyManager for useGame composition

Phase 50: useScore Hook

  • Port timer from src_old/useScore/hooks/useTimer.ts
  • Track keystrokes from keyManager.keyLog
  • Calculate efficiency metrics
  • Detect win/loss conditions based on completion
  • Compute final score on game-won

Phase 60: useGame Orchestrator

  • Compose all hooks (board, cursor, gameStatus, score)
  • Handle initial board generation
  • Coordinate rendering updates
  • Expose unified GameManager interface
  • Support optional platformHook parameter

Phase 70: Testing & Refinement

  • Test with example app
  • Verify all vim motions work
  • Verify edit mode (i/r/x) works
  • Verify validation and conflicts work
  • Verify win condition triggers
  • Test platform hook integration
  • Update UNIFIED_API.md with sudoku specifics

Key Design Decisions

1. Board Representation

Choice: Use 2D array of SudokuCell objects

type SudokuCell = {
  value: string;        // '1'-'9' or ''
  isGiven: boolean;     // true for puzzle clues
  hasConflict: boolean; // validation state
};
type SudokuBoard = SudokuCell[][];

Rationale: Easier to work with than 81-char string, matches vim-maze pattern of using structured data.

2. Cell Editing (analog to Hero Movement)

Choice: useCellEditor hook manages cell editing state

  • Similar to useHero managing hero position in maze
  • Handles validation before setting values
  • Triggers game status changes on completion

Rationale: Keeps editing logic separate from cursor movement, following vim-maze separation of concerns.

3. Edit Mode vs Insert Mode

Choice: Two modes - 'normal' and 'edit'

  • Normal mode: vim navigation + commands (i/r/x/d)
  • Edit mode: digit entry (1-9) + erase (x/Backspace)
  • i = insert (enter edit mode on empty cell)
  • r = replace (enter edit mode on filled cell)
  • Escape/Enter = exit edit mode

Rationale: Matches vim philosophy and src_old behavior.

4. Rendering Strategy

Choice: Keep rendering separate from state

  • SudokuRenderer handles DOM generation
  • useCursorRender handles cursor highlighting
  • useBoard calls renderer with board data

Rationale: Matches vim-maze pattern (MazeRenderer + useHeroRender).

5. Validation Approach

Choice: Validate on setCell, show conflicts visually

  • Real-time validation when setting digits
  • Flash animations for feedback
  • Highlight conflicting cells temporarily
  • Don't prevent invalid moves (optional strictMode)

Rationale: Provides immediate feedback without blocking player exploration.

Type Mappings (vim-maze → vim-sudoku)

vim-maze vim-sudoku Notes
MazeCell[][] SudokuBoard Board structure
Coord Coord Same (row, col)
useHero useCellEditor Cell editing vs movement
useHeroRender useCursorRender Visual feedback
MazeGenerator SudokuGenerator Puzzle generation
MazeRenderer SudokuRenderer Grid rendering
hero.moveTo cellEditor.setValue Action semantics
'entrance'/'exit'/'key' 'isGiven'/'value' Cell metadata

Differences from vim-maze

  1. No pathfinding: Simple grid bounds checking
  2. Edit mode: Need mode toggle for digit entry
  3. Validation: Real-time conflict detection
  4. Win condition: Complete + valid vs reach exit
  5. No animation: Cursor moves instantly (no multi-step)
  6. Board generation: On-demand vs one-time initialization

Success Criteria

  • All functionality from src_old preserved
  • Follows unified API architecture
  • Types match vim-maze patterns
  • Hook composition mirrors vim-maze
  • Example app works identically to src_old version
  • Platform hook integration works
  • Code is clean, documented, and maintainable
  • UNIFIED_API.md updated with vim-sudoku section

Notes

  • Keep game.css from src_old for styling
  • Port sudoku generation algorithm directly (working code)
  • Maintain existing vim keybindings (h/j/k/l, ^/$, g/G, ., i/r/x)
  • Preserve flash animations for visual feedback
  • Follow AGENTS.md conventions throughout