- 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).
101 lines
2.9 KiB
TypeScript
101 lines
2.9 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { useBoard } from './useBoard';
|
|
import { useCursor } from './useCursor';
|
|
import { useGameStatus } from './useGameStatus';
|
|
import { useScore } from './useScore';
|
|
import type { GameManager, GameOptions } from './types';
|
|
|
|
export function useGame(options: GameOptions, platformHook?: unknown): GameManager {
|
|
const board = useBoard();
|
|
const gameStatusManager = useGameStatus();
|
|
const { cursor, keyManager } = useCursor(board);
|
|
const scoreManager = useScore({
|
|
board,
|
|
gameStatusManager,
|
|
keystrokeCount: keyManager.keyLog.length
|
|
});
|
|
|
|
useEffect(() => {
|
|
board.generateBoard('medium');
|
|
}, [board]);
|
|
|
|
useEffect(() => {
|
|
const { gameStatus } = gameStatusManager;
|
|
|
|
if (gameStatus === 'waiting') {
|
|
board.renderBoard();
|
|
} else {
|
|
board.renderBoard();
|
|
const container = board.containerRef.current;
|
|
if (container) {
|
|
const pos = cursor.position();
|
|
const cellEl = container.querySelector(`.sudoku-cell[data-row="${pos.row}"][data-col="${pos.col}"]`) as HTMLElement;
|
|
if (cellEl) {
|
|
cellEl.classList.add('cursor');
|
|
if (cursor.mode() === 'edit') {
|
|
cellEl.classList.add('edit-mode');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, [gameStatusManager.gameStatus, board, cursor]);
|
|
|
|
useEffect(() => {
|
|
const container = board.containerRef.current;
|
|
if (!container) return;
|
|
|
|
const handleCellClick = (e: MouseEvent) => {
|
|
const target = e.target as HTMLElement;
|
|
const cellEl = target.closest('.sudoku-cell') as HTMLElement;
|
|
if (!cellEl) return;
|
|
|
|
const row = parseInt(cellEl.dataset.row || '0');
|
|
const col = parseInt(cellEl.dataset.col || '0');
|
|
|
|
const currentPos = cursor.position();
|
|
if (currentPos.row !== row || currentPos.col !== col) {
|
|
cursor.move(row - currentPos.row, col - currentPos.col, 1);
|
|
}
|
|
};
|
|
|
|
container.addEventListener('click', handleCellClick);
|
|
return () => container.removeEventListener('click', handleCellClick);
|
|
}, [board, cursor]);
|
|
|
|
const startGame = () => {
|
|
board.generateBoard('medium');
|
|
gameStatusManager.startGame();
|
|
};
|
|
|
|
const togglePause = (pause?: boolean) => {
|
|
gameStatusManager.togglePause(pause);
|
|
};
|
|
|
|
const quitGame = () => {
|
|
gameStatusManager.quitGame();
|
|
};
|
|
|
|
const gameManager: GameManager = {
|
|
containerRef: board.containerRef,
|
|
renderBoard: board.renderBoard,
|
|
gameStatus: gameStatusManager.gameStatus,
|
|
setGameStatus: gameStatusManager.setGameStatus,
|
|
startGame,
|
|
togglePause,
|
|
quitGame,
|
|
cursor,
|
|
scoreManager,
|
|
keyLog: keyManager.keyLog,
|
|
clearKeyLog: keyManager.clearKeyLog,
|
|
getKeyLog: keyManager.getKeyLog,
|
|
board,
|
|
solveBoard: board.solveBoard,
|
|
resetBoard: board.resetBoard
|
|
};
|
|
|
|
if (typeof platformHook === 'function') {
|
|
platformHook(gameManager);
|
|
}
|
|
|
|
return gameManager;
|
|
} |