import { createRng, shuffle } from "../rng"; export interface SudokuPuzzle { size: 6; // 0 = empty, 1-6 = given given: number[][]; solution: number[][]; } function isValid(grid: number[][], r: number, c: number, val: number): boolean { for (let i = 0; i < 6; i++) { if (grid[r][i] === val || grid[i][c] === val) return false; } // 2x3 box const br = Math.floor(r / 2) * 2, bc = Math.floor(c / 3) * 3; for (let dr = 0; dr < 2; dr++) for (let dc = 0; dc < 3; dc++) if (grid[br + dr][bc + dc] === val) return false; return true; } function solve(grid: number[][], rng: () => number): boolean { for (let r = 0; r < 6; r++) { for (let c = 0; c < 6; c++) { if (grid[r][c] !== 0) continue; const nums = shuffle([1, 2, 3, 4, 5, 6], rng); for (const n of nums) { if (!isValid(grid, r, c, n)) continue; grid[r][c] = n; if (solve(grid, rng)) return true; grid[r][c] = 0; } return false; } } return true; } function countSolutions(grid: number[][], limit = 2): number { let count = 0; function bt(): boolean { for (let r = 0; r < 6; r++) { for (let c = 0; c < 6; c++) { if (grid[r][c] !== 0) continue; for (let n = 1; n <= 6; n++) { if (!isValid(grid, r, c, n)) continue; grid[r][c] = n; bt(); grid[r][c] = 0; if (count >= limit) return true; } return false; } } count++; return count >= limit; } bt(); return count; } export function generateSudoku(date: string): SudokuPuzzle { const seed = date.split("-").reduce((a, n) => a * 1000 + parseInt(n), 0) + 555; const rng = createRng(seed); const grid: number[][] = Array.from({ length: 6 }, () => Array(6).fill(0)); solve(grid, rng); const solution = grid.map(r => [...r]); // Remove cells while puzzle remains uniquely solvable const positions = shuffle( Array.from({ length: 36 }, (_, i) => [Math.floor(i / 6), i % 6] as [number, number]), rng ); const given = solution.map(r => [...r]); let removed = 0; for (const [r, c] of positions) { if (removed >= 22) break; // keep ~14 givens in a 6x6 const val = given[r][c]; given[r][c] = 0; // Quick check: still solvable const test = given.map(row => [...row]); if (countSolutions(test) !== 1) { given[r][c] = val; // restore } else { removed++; } } return { size: 6, given, solution }; }