feat: implement Phase 30 - useGameStatus hook

Extract game status logic into dedicated hook following vim-maze pattern:

New Module Structure:
- src/useGameStatus/types.ts: GameStatus and GameStatusManager types
- src/useGameStatus/useGameStatus.ts: Hook implementation
- src/useGameStatus/index.ts: Public exports

Hook Implementation:
- State machine with 5 states (waiting/started/paused/game-over/game-won)
- Methods: startGame, togglePause, quitGame, setGameStatus
- Clean state transitions with useState
- Simpler than vim-maze (no intermediate 'has-key' state)

Updated Core Files:
- src/useGame.ts: Use useGameStatus() instead of inline useState
  - Compose gameStatusManager into GameManager
  - Cleaner separation of concerns

- src/types.ts: Import and re-export GameStatus types
  - GameManager extends GameStatusManager (intersection type)
  - Single source of truth for status types

- src/index.ts: Export useGameStatus hook publicly

Benefits:
- Reusable state machine
- Easier to test in isolation
- Follows unified API architecture
- Matches vim-maze patterns

State Transitions:
waiting → startGame() → started ↔ togglePause() ↔ paused
started → (win/lose) → game-won/game-over
* → quitGame() → waiting

Phase 30 complete. Next: Phase 40 (useCursor composition).
This commit is contained in:
2025-10-22 18:45:56 +01:00
parent 6f4a1accc4
commit 4e14d54122
8 changed files with 163 additions and 28 deletions
+6 -17
View File
@@ -1,13 +1,16 @@
import { useState, useEffect } from 'react';
import type { GameManager, GameOptions, GameStatus } from './types';
import type { GameManager, GameOptions } from './types';
import { useBoard } from './useBoard/useBoard';
import { useGameStatus } from './useGameStatus';
export function useGame(options: GameOptions = {}, platformHook?: unknown): GameManager {
const { difficulty = 'easy' } = options;
const board = useBoard();
const { containerRef, renderBoard, generateBoard } = board;
const [gameStatus, setGameStatus] = useState<GameStatus>('waiting');
const gameStatusManager = useGameStatus();
const { gameStatus } = gameStatusManager;
const [keyLog, setKeyLog] = useState<{ key: string; timestamp: number }[]>([]);
useEffect(() => {
@@ -26,21 +29,7 @@ export function useGame(options: GameOptions = {}, platformHook?: unknown): Game
const gameManager: GameManager = {
containerRef,
renderBoard,
gameStatus,
setGameStatus,
startGame: () => {
setGameStatus('started');
},
togglePause: (pause?: boolean) => {
if (typeof pause === 'boolean') {
setGameStatus(pause ? 'paused' : 'started');
} else {
setGameStatus(prev => prev === 'paused' ? 'started' : 'paused');
}
},
quitGame: () => {
setGameStatus('waiting');
},
...gameStatusManager,
cursor: {},
scoreManager: {
timeValue: 0,