- Add getHint() method to BoardManager with two-tier priority: 1. Find incorrect user-entered cells → flash red 2. Find empty cells → show correct digit green temporarily - Add Shift+H key handler in useCursor (normal mode only) - Visual feedback: - Red flash (hint-flash-error): incorrect cells, 1s duration, red bg + border - Green flash (hint-flash-correct): correct digit shown, 1s duration, green bg + border - Distinct from invalid-move (no background, 500ms) - Score penalties based on difficulty: - Easy: 25 points per hint - Medium: 50 points per hint - Hard: 100 points per hint - Track hints used in scoreboard display - Correct digit appears temporarily (1s) then disappears (player still does the work) - Update example app to show hints counter and Shift+H instruction
78 lines
2.2 KiB
TypeScript
78 lines
2.2 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import type { GameManager, GameOptions } from './types';
|
|
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;
|
|
const board = useBoard();
|
|
const { containerRef, renderBoard, generateBoard, boardRendered } = board;
|
|
|
|
const gameStatusManager = useGameStatus();
|
|
const { gameStatus } = gameStatusManager;
|
|
|
|
const [keyLog, setKeyLog] = useState<{ key: string; timestamp: number }[]>([]);
|
|
|
|
const [hintsUsedRef] = useState({ current: 0 });
|
|
|
|
const cursor = useCursor(gameStatusManager, containerRef, board, () => {
|
|
hintsUsedRef.current++;
|
|
});
|
|
const cursorPosition = cursor.position();
|
|
const cursorMode = cursor.mode();
|
|
|
|
const scoreManager = useScore({ board, gameStatusManager, keyLog, cursorMode, difficulty, hintsUsed: hintsUsedRef.current });
|
|
|
|
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();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (gameStatus === 'started') {
|
|
generateBoard(difficulty);
|
|
renderBoard();
|
|
} else if (gameStatus === 'waiting') {
|
|
renderBoard();
|
|
}
|
|
}, [gameStatus, difficulty]);
|
|
|
|
const gameManager: GameManager = {
|
|
containerRef,
|
|
renderBoard,
|
|
...gameStatusManager,
|
|
cursor,
|
|
scoreManager,
|
|
keyLog,
|
|
clearKeyLog: () => setKeyLog([]),
|
|
getKeyLog: () => [...keyLog],
|
|
};
|
|
|
|
if (typeof platformHook === 'function') {
|
|
platformHook(gameManager);
|
|
}
|
|
|
|
return gameManager;
|
|
}
|