Files
vim-maze/example/src/App.tsx
T
pastilhas 0749aba648 refactor: separate score calculation from score management
BREAKING CHANGES:
- Remove timeLimit from GameOptions (use gameOver function instead)
- Remove finalScore from ScoreManager (pure data collection only)
- Remove rows/cols parameters from useScore
- Remove finalScore calculation logic from useScore hook

ADDITIONS:
- Create calculateMazeScore() utility function
- ScoreManager now only returns raw metrics: timeValue, keystrokes, optimalSteps, efficiency, distToKey, distToExit
- Score calculation moved to consumer (calculateMazeScore takes scoreManager + mazeSize)
- Example app now uses calculateMazeScore to compute final score on game-won

BENEFITS:
- ScoreManager is now a pure data collector with no side effects
- Scoring algorithm is reusable and testable in isolation
- Game-over conditions fully controlled via gameOver option
- Clear separation of concerns: collection vs calculation
2025-11-02 17:04:21 +00:00

83 lines
3.8 KiB
TypeScript

import { useGame, version, calculateMazeScore } from "../../src";
import { useKeyBindings } from "./useKeyBindings";
import { useIsMobile } from "./useIsMobile";
import { useMemo } from "react";
import '../../src/game.css';
function App() {
const isMobile = useIsMobile();
const cols = isMobile ? 12 : 32;
const rows = isMobile ? 8 : 24;
const gameManager = useGame({ cols, rows }, useKeyBindings);
const { containerRef, gameStatus, scoreManager } = gameManager;
const { timeValue, keystrokes, optimalSteps, efficiency } = scoreManager;
const mazeSize = rows * cols;
const finalScore = useMemo(
() => gameStatus === "game-won" ? calculateMazeScore(scoreManager, mazeSize) : null,
[gameStatus, scoreManager, mazeSize]
);
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 (
<div className="flex flex-col items-center justify-around min-h-[100vh]">
<h1 className="text-6xl">VIMazing vim-maze <small>v{version}</small> example</h1>
<div className="mx-auto my-4 w-fit bg-muted rounded-lg p-4">
{(gameStatus === "started" || gameStatus === "has-key" || gameStatus === "game-won") && (
<div className="flex gap-8 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>
<div className="text-muted-foreground">Keystrokes</div>
<div className="text-lg font-bold">{keystrokes}</div>
</div>
{optimalSteps > 0 && (
<>
<div className="w-px h-12 bg-muted-foreground/20" />
<div>
<div className="text-muted-foreground">Optimal Steps</div>
<div className="text-lg font-bold">{optimalSteps}</div>
</div>
<div>
<div className="text-muted-foreground">Efficiency</div>
<div className="text-lg font-bold">{efficiency}%</div>
</div>
</>
)}
{gameStatus === "game-won" && finalScore && (
<>
<div className="w-px h-12 bg-muted-foreground/20" />
<div className="text-center">
<div className="text-muted-foreground">Final Score</div>
<div className="text-3xl font-bold bg-gradient-to-r from-green-400 to-emerald-500 bg-clip-text text-transparent">{finalScore}</div>
</div>
</>
)}
</div>
)}
</div>
<div className="relative mx-auto my-4 w-fit" id="maze_container">
<div ref={containerRef} className="relative" />
</div>
<div className="text-center text-2xl text-muted-foreground">
{gameStatus === "waiting" && <p>Press <kbd className="px-2 py-1 bg-muted rounded">space</kbd> to start</p>}
{gameStatus === "started" && <p>Use <kbd className="px-2 py-1 bg-muted rounded">hjkl</kbd> to change direction • Press <kbd className="px-2 py-1 bg-muted rounded">q</kbd> to quit</p>}
{gameStatus === "has-key" && <p>Use <kbd className="px-2 py-1 bg-muted rounded">hjkl</kbd> to change direction • Press <kbd className="px-2 py-1 bg-muted rounded">q</kbd> to quit</p>}
{gameStatus === "game-over" && <p>Game Over! Press <kbd className="px-2 py-1 bg-muted rounded">space</kbd> to restart</p>}
{gameStatus === "game-won" && <p>You Won! Press <kbd className="px-2 py-1 bg-muted rounded">space</kbd> to play again</p>}
</div>
</div>
);
};
export default App;