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
+15 -6
View File
@@ -5,10 +5,10 @@ import type { Difficulty } from "../../src/types";
import '../../src/game.css';
function App() {
const [difficulty, setDifficulty] = useState<Difficulty>('medium');
const [difficulty, setDifficulty] = useState<Difficulty>('easy');
const gameManager = useGame({ difficulty }, useKeyBindings);
const { containerRef, gameStatus, scoreManager } = gameManager;
const { timeValue, keystrokes, finalScore } = scoreManager;
const { timeValue, totalKeystrokes, editKeystrokes, optimalKeystrokes, efficiency, finalScore } = scoreManager;
const formatTime = (ms: number) => {
const seconds = Math.floor(ms / 1000);
@@ -66,16 +66,25 @@ function App() {
<div className="mx-auto my-4 w-fit bg-muted rounded-lg p-4">
{(gameStatus === "started" || gameStatus === "game-won") && (
<div className="flex gap-8 text-sm font-mono items-center">
<div className="flex gap-6 text-sm font-mono items-center">
<div>
<div className="text-muted-foreground">Time</div>
<div className="text-lg font-bold">{formatTime(timeValue)}</div>
</div>
<div className="w-px h-12 bg-muted-foreground/20" />
<div>
<div className="text-muted-foreground">Keystrokes</div>
<div className="text-lg font-bold">{keystrokes}</div>
<div className="text-muted-foreground">Total Keys</div>
<div className="text-lg font-bold">{totalKeystrokes}</div>
</div>
{gameStatus === "game-won" && finalScore && (
<div>
<div className="text-muted-foreground">Edit Keys</div>
<div className="text-lg font-bold">{editKeystrokes} / {optimalKeystrokes}</div>
</div>
<div>
<div className="text-muted-foreground">Efficiency</div>
<div className="text-lg font-bold">{efficiency}%</div>
</div>
{gameStatus === "game-won" && finalScore !== null && (
<>
<div className="w-px h-12 bg-muted-foreground/20" />
<div className="text-center">