feat: prepare vim-sudoku for unified API implementation
- Archive previous implementation attempt (src -> src_failed) - Create comprehensive IMPLEMENTATION_PLAN.md with phased approach - Create IMPLEMENTATION.md to track implementation progress - Update example app to match vim-maze patterns: - Simplify UI to desktop-only (no mobile support) - Add score metrics display (time, keystrokes, final score) - Update instructions for sudoku-specific keys (hjkl, i for edit) - Remove unnecessary mobile hooks Phase 5 complete. Ready to begin Phase 10 (Setup & Core Types).
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import type { BoardManager } from '../useBoard/types';
|
||||
import type { GameStatusManager } from '../useGameStatus/types';
|
||||
import type { ScoreManager, UseScoreProps } from './types';
|
||||
import { useTimer } from './useTimer';
|
||||
|
||||
export function useScore({ board, gameStatusManager, keystrokeCount }: UseScoreProps): ScoreManager {
|
||||
const timer = useTimer();
|
||||
const [mistakes, setMistakes] = useState<number>(0);
|
||||
const [hints, setHints] = useState<number>(0);
|
||||
|
||||
const addMistake = useCallback(() => {
|
||||
setMistakes(prev => prev + 1);
|
||||
}, []);
|
||||
|
||||
const addHint = useCallback(() => {
|
||||
setHints(prev => prev + 1);
|
||||
}, []);
|
||||
|
||||
const calculateEfficiency = useCallback((): number => {
|
||||
const boardString = board.boardRef.current.map(row =>
|
||||
row.map(cell => cell.value || '.').join('')
|
||||
).join('');
|
||||
|
||||
const filledCells = boardString.replace(/\./g, '').length;
|
||||
const totalCells = 81;
|
||||
const givenCells = board.initialBoardRef.current.replace(/\./g, '').length;
|
||||
const userFilledCells = filledCells - givenCells;
|
||||
|
||||
if (userFilledCells === 0) return 100;
|
||||
|
||||
const optimalMoves = userFilledCells;
|
||||
const actualMoves = keystrokeCount;
|
||||
|
||||
const efficiency = Math.round((optimalMoves / actualMoves) * 100);
|
||||
return Math.max(0, Math.min(100, efficiency));
|
||||
}, [board, keystrokeCount]);
|
||||
|
||||
const calculateFinalScore = useCallback((): number | null => {
|
||||
const { gameStatus } = gameStatusManager;
|
||||
|
||||
if (gameStatus !== 'game-won') return null;
|
||||
|
||||
const efficiency = calculateEfficiency();
|
||||
const timeInSeconds = timer.timeValue / 1000;
|
||||
const timeBonus = Math.max(0, 3600 - timeInSeconds) * 10;
|
||||
const mistakePenalty = mistakes * 100;
|
||||
const hintPenalty = hints * 200;
|
||||
|
||||
const baseScore = 10000;
|
||||
const efficiencyBonus = efficiency * 50;
|
||||
|
||||
const finalScore = baseScore + efficiencyBonus + timeBonus - mistakePenalty - hintPenalty;
|
||||
return Math.max(0, Math.round(finalScore));
|
||||
}, [gameStatusManager, calculateEfficiency, timer.timeValue, mistakes, hints]);
|
||||
|
||||
useEffect(() => {
|
||||
const { gameStatus } = gameStatusManager;
|
||||
|
||||
if (gameStatus === 'started') {
|
||||
timer.startTimer();
|
||||
} else if (gameStatus === 'paused' || gameStatus === 'game-over' || gameStatus === 'game-won') {
|
||||
timer.stopTimer();
|
||||
}
|
||||
|
||||
if (gameStatus === 'waiting') {
|
||||
timer.resetTimer();
|
||||
setMistakes(0);
|
||||
setHints(0);
|
||||
}
|
||||
}, [gameStatusManager.gameStatus, timer]);
|
||||
|
||||
useEffect(() => {
|
||||
const { gameStatus, setGameStatus } = gameStatusManager;
|
||||
|
||||
if (gameStatus === 'started' && board.isComplete() && board.isFullyValid()) {
|
||||
setGameStatus('game-won');
|
||||
}
|
||||
}, [board, gameStatusManager]);
|
||||
|
||||
return {
|
||||
timeValue: timer.timeValue,
|
||||
keystrokes: keystrokeCount,
|
||||
mistakes,
|
||||
hints,
|
||||
efficiency: calculateEfficiency(),
|
||||
finalScore: calculateFinalScore(),
|
||||
startTimer: timer.startTimer,
|
||||
stopTimer: timer.stopTimer,
|
||||
resetTimer: timer.resetTimer,
|
||||
addMistake,
|
||||
addHint,
|
||||
calculateEfficiency,
|
||||
calculateFinalScore
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user