diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md new file mode 100644 index 0000000..4cd9b07 --- /dev/null +++ b/IMPLEMENTATION.md @@ -0,0 +1,68 @@ +# vim-sudoku Unified API Implementation Log + +This document tracks the step-by-step implementation of the unified API architecture for vim-sudoku. + +## Overview + +We are porting vim-sudoku from the old architecture (src_old) to follow the unified API patterns established by vim-maze. This enables consistent architecture across all VIMazing games and better platform integration. + +--- + +## Iteration 1: Preparation & Planning + +**Date**: 2025-01-22 + +### Goals +- Review existing implementations (vim-maze, src_old, src_failed) +- Create implementation plan +- Prepare example app for new API + +### Tasks Completed + +#### 1. Reviewed Architecture +- Analyzed vim-maze unified API implementation +- Examined src_old working implementation +- Identified src_failed attempt issues +- Documented key differences between vim-maze and vim-sudoku + +#### 2. Created Implementation Plan +- Created `IMPLEMENTATION_PLAN.md` with 7 phases (numbered x10 for flexibility) +- Defined file structure matching vim-maze patterns +- Documented key design decisions +- Established type mappings between vim-maze and vim-sudoku +- Listed success criteria + +#### 3. Prepared Example App (Phase 5) +- Updated `example/src/App.tsx` to match vim-maze structure +- Simplified to desktop-only (no mobile support) +- Added score metrics display (time, keystrokes, final score) +- Updated game status messages for sudoku context +- Changed navigation instructions (hjkl + i for edit mode) +- Verified `useKeyBindings` already matches vim-maze + +### File Changes +``` +vim-sudoku/ +├── IMPLEMENTATION_PLAN.md [CREATED] - Detailed implementation roadmap +├── IMPLEMENTATION.md [CREATED] - This file, tracking progress +├── example/src/App.tsx [MODIFIED] - Simplified, sudoku-focused UI +└── src/ → src_failed/ [RENAMED] - Previous failed attempt archived +``` + +### Key Decisions + +1. **No Mobile Support**: Sudoku with vim bindings requires keyboard, mobile not practical +2. **Simple Metrics**: Start with time, keystrokes, and final score (no efficiency yet) +3. **Phase Numbering**: Use x10 numbering (10, 20, 30...) to allow insertions + +### Next Steps +- Phase 10: Setup & Core Types + - Create clean src/ directory structure + - Define all type definitions + - Set up module exports + +--- + +## Iteration 2: [Pending] + +*To be filled as implementation progresses...* diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..b546496 --- /dev/null +++ b/IMPLEMENTATION_PLAN.md @@ -0,0 +1,251 @@ +# 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 + +```tsx +// 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 + +- [x] Update example/src/App.tsx to match vim-maze structure +- [x] Update App to display score metrics (time, keystrokes, final score) +- [x] Update App to show game status messages +- [x] Update App instructions for sudoku-specific keys (i for edit) +- [x] 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 +- [ ] Port SudokuRenderer from src_old rendering logic +- [ ] Implement useBoard hook with refs pattern + - containerRef for DOM + - boardRef for SudokuBoard (2D array of cells) + - initialBoardRef for puzzle state + - solutionRef for solved state +- [ ] Methods: renderBoard, generateBoard, getCell, setCell, canEditCell, isComplete, isFullyValid + +### Phase 30: useGameStatus Hook + +- [ ] Implement GameStatus state machine + - 'waiting' → 'started' → 'game-won' / 'game-over' + - 'paused' state support +- [ ] Methods: startGame, togglePause, quitGame, setGameStatus + +### 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 +```typescript +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 diff --git a/NPM_PUBLISHING_CHECKLIST.md b/NPM_PUBLISHING_CHECKLIST.md deleted file mode 100644 index 3b0088a..0000000 --- a/NPM_PUBLISHING_CHECKLIST.md +++ /dev/null @@ -1,46 +0,0 @@ -# 📋 npm Publishing Checklist for @vimazing/vim-sudoku - -## 🔴 Critical (Must Fix Before Publishing) - -- [ ] **Fix TypeScript build errors** - Package won't compile - - [ ] `Sudoku.ts`: operator type issues (lines 77, 93, 95) - - [ ] `useBoardState.ts`: remove unused `useCallback`, `useMemo` imports - - [ ] `types.ts`: remove non-existent `BoardString` export - - [ ] Remove unused variables: `idx`, `max_square` - -- [ ] **Add LICENSE file** - Required for MIT license in package.json - -- [ ] **Update package.json author** - Currently `you@example.com` - -## 🟡 Important (Should Fix) - -- [ ] **Update README.md** - - [ ] Remove "coming soon" for win detection (✅ implemented!) - - [ ] Document new `validateMoves` option in `useGame` - - [ ] Update scoring formula to include move penalty - -- [ ] **Fix tsconfig.json** - Conflicting `declaration` and `emitDeclarationOnly` flags - -- [ ] **Verify exports** - Check `src/index.ts` exports everything needed - -- [ ] **Add CHANGELOG.md** - Document version history and changes - -- [ ] **Add package.json keywords** - Improve npm discoverability (sudoku, vim, react, game, puzzle) - -- [ ] **Update .npmignore** - Verify `example/` is excluded - -## 🟢 Nice to Have - -- [ ] **Test locally** - Run `npm pack` and inspect tarball contents - -- [ ] **Add npm badges** - Version and downloads badges in README - -- [ ] **Add engines field** - Specify Node.js version requirements - -- [ ] **GitHub topics** - Add repository tags for discoverability - ---- - -**Total: 18 tasks** (6 critical, 6 important, 6 nice-to-have) - -**Status:** Ready to start working on critical issues 🚀 diff --git a/example/src/App.tsx b/example/src/App.tsx index b02fc63..fc6b80a 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,17 +1,57 @@ import { useGame } from "../../src/useGame"; -import { useKeyBindings } from './useKeyBindings'; -import "../../src/game.css"; +import { useKeyBindings } from "./useKeyBindings"; +import '../../src/game.css'; -export default function App() { - const { containerRef } = useGame({}, useKeyBindings); +function App() { + const gameManager = useGame({ difficulty: 'medium' }, useKeyBindings); + const { containerRef, gameStatus, scoreManager } = gameManager; + const { timeValue, keystrokes, finalScore } = scoreManager; + + const formatTime = (ms: number) => { + const seconds = Math.floor(ms / 1000); + const minutes = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${minutes}:${secs.toString().padStart(2, '0')}`; + }; return ( -
Press space to start
} + {gameStatus === "started" &&Use hjkl to navigate • Press i to edit • Press q to quit
} + {gameStatus === "game-over" &&Game Over! Press space to restart
} + {gameStatus === "game-won" &&You Won! Press space to play again
}