fertig-classic-games/server/words/sudokuEngine.js

98 lines
2.4 KiB
JavaScript

function isValid(grid, row, col, num) {
for (let c = 0; c < 9; c++) if (grid[row][c] === num) return false;
for (let r = 0; r < 9; r++) if (grid[r][col] === num) return false;
const br = Math.floor(row / 3) * 3;
const bc = Math.floor(col / 3) * 3;
for (let r = br; r < br + 3; r++)
for (let c = bc; c < bc + 3; c++)
if (grid[r][c] === num) return false;
return true;
}
function shuffle(arr) {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
function fillBox(grid, startRow, startCol) {
const nums = shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9]);
let idx = 0;
for (let r = startRow; r < startRow + 3; r++)
for (let c = startCol; c < startCol + 3; c++)
grid[r][c] = nums[idx++];
}
function solve(grid) {
for (let r = 0; r < 9; r++) {
for (let c = 0; c < 9; c++) {
if (grid[r][c] === 0) {
for (const num of shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9])) {
if (isValid(grid, r, c, num)) {
grid[r][c] = num;
if (solve(grid)) return true;
grid[r][c] = 0;
}
}
return false;
}
}
}
return true;
}
function generateSolution() {
const grid = Array.from({ length: 9 }, () => Array(9).fill(0));
// Seed diagonal boxes first (no inter-box constraints) for faster solve
fillBox(grid, 0, 0);
fillBox(grid, 3, 3);
fillBox(grid, 6, 6);
solve(grid);
return grid;
}
function cloneGrid(g) {
return g.map(row => [...row]);
}
function countGivens(grid) {
return grid.flat().filter(v => v !== 0).length;
}
const GIVENS = {
'very-easy': 50,
'easy': 36,
'regular': 28,
'hard': 24,
'brutal': 18,
};
function digHoles(solution, targetGivens) {
const grid = cloneGrid(solution);
const positions = [];
for (let r = 0; r < 9; r++)
for (let c = 0; c < 9; c++)
positions.push([r, c]);
shuffle(positions);
for (const [r, c] of positions) {
if (countGivens(grid) <= targetGivens) break;
const mr = 8 - r, mc = 8 - c;
if (grid[r][c] === 0 && grid[mr][mc] === 0) continue;
grid[r][c] = 0;
if (r !== mr || c !== mc) grid[mr][mc] = 0;
}
return grid;
}
export function generatePuzzle(difficulty) {
const target = GIVENS[difficulty] ?? 28;
const solution = generateSolution();
const grid = digHoles(solution, target);
return { grid, solution, difficulty };
}