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:
2025-10-22 20:42:50 +01:00
parent 710ec4b83b
commit 8db4455dc5
11 changed files with 339 additions and 25 deletions
+21 -5
View File
@@ -4,6 +4,7 @@ import { useBoard } from './useBoard/useBoard';
import { useGameStatus } from './useGameStatus';
import { useCursor } from './useCursor';
import { useCursorRender } from './useCursor/useCursorRender';
import { useScore } from './useScore';
export function useGame(options: GameOptions = {}, platformHook?: unknown): GameManager {
const { difficulty = 'easy' } = options;
@@ -19,8 +20,27 @@ export function useGame(options: GameOptions = {}, platformHook?: unknown): Game
const cursorPosition = cursor.position();
const cursorMode = cursor.mode();
const scoreManager = useScore({ board, gameStatusManager, keyLog, cursorMode });
useCursorRender(containerRef, cursorPosition, cursorMode, gameStatus, boardRendered);
useEffect(() => {
if (gameStatus !== 'started') return;
function handleKeyDown(e: KeyboardEvent) {
setKeyLog(prev => [...prev, { key: e.key, timestamp: Date.now() }]);
}
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [gameStatus]);
useEffect(() => {
if (gameStatus === 'started') {
setKeyLog([]);
}
}, [gameStatus]);
useEffect(() => {
renderBoard();
}, []);
@@ -39,11 +59,7 @@ export function useGame(options: GameOptions = {}, platformHook?: unknown): Game
renderBoard,
...gameStatusManager,
cursor,
scoreManager: {
timeValue: 0,
keystrokes: 0,
finalScore: null,
},
scoreManager,
keyLog,
clearKeyLog: () => setKeyLog([]),
getKeyLog: () => [...keyLog],