import { useRef, useState, useCallback } from 'react'; import type { RefObject } from 'react'; import type { BoardManager, SudokuBoard, SudokuCell, Difficulty } from './types'; import { SudokuGenerator } from './SudokuGenerator'; import { SudokuRenderer } from './SudokuRenderer'; export function useBoard(): BoardManager { const containerRef = useRef(null); const boardRef = useRef([]); const initialBoardRef = useRef(''); const solutionRef = useRef(null); const initializeBoard = useCallback((boardString: string): SudokuBoard => { const board: SudokuBoard = []; for (let row = 0; row < 9; row++) { const boardRow: SudokuCell[] = []; for (let col = 0; col < 9; col++) { const index = row * 9 + col; const value = boardString[index]; boardRow.push({ value: value === '.' ? '' : value, isGiven: value !== '.', hasConflict: false }); } board.push(boardRow); } return board; }, []); const getCell = useCallback((row: number, col: number): SudokuCell | null => { const board = boardRef.current; if (row < 0 || row >= 9 || col < 0 || col >= 9) return null; return board[row]?.[col] || null; }, []); const canEditCell = useCallback((row: number, col: number): boolean => { const cell = getCell(row, col); return cell ? !cell.isGiven : false; }, [getCell]); const isValidPlacement = useCallback((boardString: string, row: number, col: number, digit: string): boolean => { if (digit === '.') return true; for (let c = 0; c < 9; c++) { const checkIdx = row * 9 + c; if (c !== col && boardString[checkIdx] === digit) { return false; } } for (let r = 0; r < 9; r++) { const checkIdx = r * 9 + col; if (r !== row && boardString[checkIdx] === digit) { return false; } } const boxRow = Math.floor(row / 3) * 3; const boxCol = Math.floor(col / 3) * 3; for (let r = boxRow; r < boxRow + 3; r++) { for (let c = boxCol; c < boxCol + 3; c++) { const checkIdx = r * 9 + c; if ((r !== row || c !== col) && boardString[checkIdx] === digit) { return false; } } } return true; }, []); const getConflicts = useCallback((boardString: string, row: number, col: number): number[] => { const conflicts: number[] = []; const digit = boardString[row * 9 + col]; if (digit === '.') return conflicts; for (let c = 0; c < 9; c++) { if (c !== col && boardString[row * 9 + c] === digit) { conflicts.push(row * 9 + c); } } for (let r = 0; r < 9; r++) { if (r !== row && boardString[r * 9 + col] === digit) { conflicts.push(r * 9 + col); } } const boxRow = Math.floor(row / 3) * 3; const boxCol = Math.floor(col / 3) * 3; for (let r = boxRow; r < boxRow + 3; r++) { for (let c = boxCol; c < boxCol + 3; c++) { const idx = r * 9 + c; if ((r !== row || c !== col) && boardString[idx] === digit) { if (!conflicts.includes(idx)) { conflicts.push(idx); } } } } return conflicts; }, []); const isFullyValid = useCallback((boardString: string): boolean => { for (let r = 0; r < 9; r++) { let rowSum = 0; for (let c = 0; c < 9; c++) { const digit = boardString[r * 9 + c]; if (digit === '.') return false; rowSum += parseInt(digit, 10); } if (rowSum !== 45) return false; } for (let c = 0; c < 9; c++) { let colSum = 0; for (let r = 0; r < 9; r++) { const digit = boardString[r * 9 + c]; colSum += parseInt(digit, 10); } if (colSum !== 45) return false; } for (let boxRow = 0; boxRow < 3; boxRow++) { for (let boxCol = 0; boxCol < 3; boxCol++) { let boxSum = 0; for (let r = boxRow * 3; r < boxRow * 3 + 3; r++) { for (let c = boxCol * 3; c < boxCol * 3 + 3; c++) { const digit = boardString[r * 9 + c]; boxSum += parseInt(digit, 10); } } if (boxSum !== 45) return false; } } return true; }, []); const isComplete = useCallback((boardString: string): boolean => { return !boardString.includes('.'); }, []); const boardToString = useCallback((board: SudokuBoard): string => { return board.map(row => row.map(cell => cell.value || '.').join('') ).join(''); }, []); const setCell = useCallback((row: number, col: number, value: string): boolean => { if (!canEditCell(row, col)) return false; const board = boardRef.current; const boardString = boardToString(board); const newBoardString = boardString.substring(0, row * 9 + col) + value + boardString.substring(row * 9 + col + 1); if (!isValidPlacement(newBoardString, row, col, value)) { return false; } board[row][col] = { value: value === '.' ? '' : value, isGiven: board[row][col].isGiven, hasConflict: false }; return true; }, [canEditCell, isValidPlacement, boardToString]); const generateBoard = useCallback((difficulty: Difficulty): void => { const newBoardString = SudokuGenerator.generate(difficulty); const solution = SudokuGenerator.solve(newBoardString); initialBoardRef.current = newBoardString; solutionRef.current = solution; boardRef.current = initializeBoard(newBoardString); }, [initializeBoard]); const solveBoard = useCallback((): boolean => { const solution = solutionRef.current; if (!solution) return false; boardRef.current = initializeBoard(solution); return true; }, [initializeBoard]); const resetBoard = useCallback((): void => { const initialBoard = initialBoardRef.current; if (initialBoard) { boardRef.current = initializeBoard(initialBoard); } }, [initializeBoard]); const renderBoard = useCallback((): void => { const container = containerRef.current; const board = boardRef.current; if (!container || board.length === 0) { if (container) { SudokuRenderer.renderEmptyBoard(container); } return; } SudokuRenderer.renderBoard(container, board); }, []); return { containerRef, boardRef, initialBoardRef, solutionRef, renderBoard, generateBoard, getCell, setCell, canEditCell, isValidPlacement: (row, col, digit) => { const boardString = boardToString(boardRef.current); return isValidPlacement(boardString, row, col, digit); }, getConflicts: (row, col) => { const boardString = boardToString(boardRef.current); return getConflicts(boardString, row, col); }, isComplete: () => { const boardString = boardToString(boardRef.current); return isComplete(boardString); }, isFullyValid: () => { const boardString = boardToString(boardRef.current); return isFullyValid(boardString); }, solveBoard, resetBoard }; }