feat: implement Phase 20 - puzzle generation and rendering

Port sudoku generation algorithm and integrate with game flow:

Core Changes:
- src/useBoard/SudokuGenerator.ts: Ported from src_old/lib/Sudoku.ts
  - Full puzzle generation algorithm (robatron/sudoku.js)
  - Supports all difficulty levels (easy through inhuman)
  - Returns 81-character string representation

- src/useBoard/useBoard.ts: Enhanced with puzzle state
  - Added boardRef and initialBoardRef for state management
  - Added generateBoard(difficulty) method
  - Updated renderBoard() to display givens from board string
  - Cells show digits for givens, empty for '.' characters

- src/useGame.ts: Integrated game flow
  - Reads difficulty from GameOptions (defaults to 'easy')
  - Generates puzzle on startGame (gameStatus → 'started')
  - Re-renders board on status changes
  - quitGame returns to empty grid

Game Flow:
- Waiting: Empty 9x9 grid
- Press Space → startGame() → puzzle generated (~62 givens on easy)
- Press Q → quitGame() → empty grid

Testing platformHook integration:
- startGame trigger working via useKeyBindings
- quitGame trigger working via useKeyBindings
- Board state properly cleared/populated on status changes

Phase 20 complete. Next: Phase 30 (useGameStatus hook).
This commit is contained in:
2025-10-22 18:24:08 +01:00
parent b7d740386e
commit a6c4c5302f
4 changed files with 484 additions and 4 deletions
+12 -2
View File
@@ -2,9 +2,10 @@ import { useState, useEffect } from 'react';
import type { GameManager, GameOptions, GameStatus } from './types';
import { useBoard } from './useBoard/useBoard';
export function useGame(_options: GameOptions = {}, platformHook?: unknown): GameManager {
export function useGame(options: GameOptions = {}, platformHook?: unknown): GameManager {
const { difficulty = 'easy' } = options;
const board = useBoard();
const { containerRef, renderBoard } = board;
const { containerRef, renderBoard, generateBoard } = board;
const [gameStatus, setGameStatus] = useState<GameStatus>('waiting');
const [keyLog, setKeyLog] = useState<{ key: string; timestamp: number }[]>([]);
@@ -13,6 +14,15 @@ export function useGame(_options: GameOptions = {}, platformHook?: unknown): Gam
renderBoard();
}, []);
useEffect(() => {
if (gameStatus === 'started') {
generateBoard(difficulty);
renderBoard();
} else if (gameStatus === 'waiting') {
renderBoard();
}
}, [gameStatus]);
const gameManager: GameManager = {
containerRef,
renderBoard,