feat: implement scoring system and win condition (Phase 50)
- Add useTimer hook for elapsed time tracking - Add useScore hook with comprehensive metrics: - Total keystrokes (all keys) - Edit keystrokes (only editing keys) - Optimal keystrokes (empty cells × 3) - Efficiency percentage (optimal/edit × 100) - Final score (70% efficiency + 30% time-based) - Win condition detects board completion only in normal mode - Track all keystrokes with timestamps in useGame - Update example app with full scoreboard display - Expose boardRef in BoardManager for state checking - Adjust easy mode to 5 removed cells for testing
This commit is contained in:
+142
-4
@@ -982,7 +982,7 @@ Back in normal mode (blue cursor)
|
||||
- ✅ All validation rules working correctly
|
||||
|
||||
### Next Steps
|
||||
- Validation and conflict detection
|
||||
|
||||
- Win condition checking
|
||||
|
||||
---
|
||||
@@ -1086,11 +1086,149 @@ Cell loses .user-entered, can use 'i' again
|
||||
- ✅ All erase commands working correctly
|
||||
|
||||
### Next Steps
|
||||
- Validation and conflict detection
|
||||
- Win condition checking
|
||||
|
||||
---
|
||||
|
||||
## Iteration 12: [Pending]
|
||||
## Iteration 12: Phase 50 - Scoring System and Win Condition
|
||||
|
||||
**Date**: 2025-01-22
|
||||
|
||||
### Goals
|
||||
- Implement complete scoring system based on vim-maze patterns
|
||||
- Track time, keystrokes, and efficiency
|
||||
- Detect win condition when puzzle is complete
|
||||
- Display all metrics in example app
|
||||
|
||||
### Tasks Completed
|
||||
|
||||
#### 1. Created useTimer Hook
|
||||
**File: `src/useScore/useTimer.ts`**
|
||||
- Tracks elapsed time using `requestAnimationFrame`
|
||||
- Methods: `startTimer()`, `stopTimer()`, `resetTimer()`
|
||||
- Returns `timeValue` in milliseconds
|
||||
- Direct copy from vim-maze (works perfectly as-is)
|
||||
|
||||
#### 2. Created useScore Hook
|
||||
**File: `src/useScore/useScore.ts`**
|
||||
|
||||
**Metrics Tracked:**
|
||||
- **Total keystrokes**: All keys pressed during gameplay
|
||||
- **Edit keystrokes**: Only editing keys (i, r, c, x, d, Delete, Escape, Backspace, 1-9)
|
||||
- **Optimal keystrokes**: Empty cells × 3 (i + digit + Escape minimum)
|
||||
- **Efficiency**: `(optimal / edit) × 100` - 100% = perfect, no mistakes
|
||||
- **Final score**: Weighted combination of efficiency (70%) and time (30%)
|
||||
|
||||
**Key Logic:**
|
||||
```typescript
|
||||
const EDIT_KEYS = ['i', 'r', 'c', 'x', 'd', 'Delete', 'Escape', 'Backspace', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||||
|
||||
const totalKeystrokes = keyLog.length;
|
||||
const editKeystrokes = keyLog.filter(entry => EDIT_KEYS.includes(entry.key)).length;
|
||||
const optimalKeystrokes = emptyCells * 3;
|
||||
const efficiency = (optimalKeystrokes / editKeystrokes) * 100;
|
||||
```
|
||||
|
||||
#### 3. Win Condition Detection
|
||||
**Trigger:** Board has no empty cells (no '.' characters) AND cursor is in normal mode
|
||||
|
||||
**Why check normal mode?**
|
||||
- Prevents premature win detection while still typing
|
||||
- User must exit edit mode (Escape or auto-exit from r/c)
|
||||
- Gives clean transition to game-won state
|
||||
|
||||
**Implementation:**
|
||||
```typescript
|
||||
useEffect(() => {
|
||||
if (gameStatus !== 'started') return;
|
||||
if (cursorMode !== 'normal') return; // Wait until edit mode exits
|
||||
|
||||
const boardStr = board.boardRef.current;
|
||||
const hasEmptyCells = boardStr.includes('.');
|
||||
|
||||
if (!hasEmptyCells) {
|
||||
setGameStatus('game-won');
|
||||
}
|
||||
}, [board.boardRendered, gameStatus, cursorMode]);
|
||||
```
|
||||
|
||||
#### 4. Keystroke Tracking in useGame
|
||||
**Implementation:**
|
||||
- Add keyboard listener during 'started' state
|
||||
- Log all keystrokes with timestamp
|
||||
- Clear log when game starts
|
||||
- Pass to useScore for metrics calculation
|
||||
|
||||
#### 5. Updated Example App Scoreboard
|
||||
**Displays:**
|
||||
- Time (formatted as mm:ss)
|
||||
- Total Keys (all keystrokes)
|
||||
- Edit Keys (shown as "current / optimal")
|
||||
- Efficiency (percentage)
|
||||
- Final Score (shown only when game-won)
|
||||
|
||||
**Layout:** Clean, monospace display with dividers between sections
|
||||
|
||||
#### 6. Adjusted Difficulty for Testing
|
||||
- Easy mode: 5 removed cells (was 40, then 10, now 5)
|
||||
- Default difficulty: Easy (for faster testing)
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Score Calculation:**
|
||||
```typescript
|
||||
const efficiencyScore = Math.max(0, Math.min(100, efficiency));
|
||||
const timeScore = Math.max(0, 100 - (timeMs / 1000 / 60) * 10);
|
||||
const finalScore = Math.round((efficiencyScore * 0.7 + timeScore * 0.3) * 1000);
|
||||
```
|
||||
|
||||
**Efficiency Interpretation:**
|
||||
- 100% = Perfect play (only optimal keystrokes used)
|
||||
- 90% = Excellent (few corrections)
|
||||
- 70% = Good (some mistakes)
|
||||
- <50% = Many corrections/mistakes
|
||||
|
||||
**Timer Lifecycle:**
|
||||
```
|
||||
waiting → [start game] → started (timer starts)
|
||||
↓
|
||||
started → [board complete + normal mode] → game-won (timer stops)
|
||||
↓
|
||||
game-won → [quit] → waiting (timer resets)
|
||||
```
|
||||
|
||||
### Types Added
|
||||
|
||||
**ScoreManager Type:**
|
||||
```typescript
|
||||
export type ScoreManager = {
|
||||
timeValue: number;
|
||||
startTimer: () => void;
|
||||
stopTimer: () => void;
|
||||
resetTimer: () => void;
|
||||
totalKeystrokes: number;
|
||||
editKeystrokes: number;
|
||||
optimalKeystrokes: number;
|
||||
efficiency: number;
|
||||
finalScore: number | null;
|
||||
};
|
||||
```
|
||||
|
||||
**BoardManager Updates:**
|
||||
- Added `boardRef: RefObject<string>` to expose board state
|
||||
- Cleaned up return object (removed internal refs)
|
||||
|
||||
### Build Status
|
||||
- ✅ TypeScript compiles cleanly
|
||||
- ✅ All metrics calculating correctly
|
||||
- ✅ Win condition triggers only when exiting edit mode
|
||||
- ✅ Scoreboard displays all values properly
|
||||
|
||||
### Next Steps
|
||||
- Polish and final testing
|
||||
- Documentation updates
|
||||
|
||||
---
|
||||
|
||||
## Iteration 13: [Pending]
|
||||
|
||||
*To be filled as implementation progresses...*
|
||||
|
||||
Reference in New Issue
Block a user