Files
vim-sudoku/src_failed/useBoard/useBoard.ts
T
pastilhas 9292b83dd0 feat: prepare vim-sudoku for unified API implementation
- 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).
2025-10-22 18:08:33 +01:00

240 lines
7.0 KiB
TypeScript

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<HTMLDivElement | null>(null);
const boardRef = useRef<SudokuBoard>([]);
const initialBoardRef = useRef<string>('');
const solutionRef = useRef<string | null>(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
};
}