Files
suji/index.js
2025-09-02 15:35:01 +02:00

263 lines
6.3 KiB
JavaScript

const grid8 = [
'', '', '', 5, 1, 2, '', 6, '',
2, 5, '', '', '', '', 9, 1, '',
1, 7, '', '', '', 6, '', 5, 8,
'', '', '', '', 8, '', 1, '', '',
5, 1, '', 3, '', 4, '', 9, 6,
'', '', 2, '', 6, '', '', '', '',
9, 2, '', 4, '', '', '', 8, 7,
'', 4, 3, '', '', '', '', 2, 1,
'', 6, '', 2, 7, 1, '', '', '',
];
const grid1 = [
2, '', 5, '', '', 7, '', '', 6,
4, '', '', 9, 6, '', '', 2, '',
'', '', '', '', 8, '', '', 4, 5,
9, 8, '', '', 7, 4, '', '', '',
5, 7, '', 8, '', 2, '', 6, 9,
'', '', '', 6, 3, '', '', 5, 7,
7, 5, '', '', 2, '', '', '', '',
'', 6, '', '', 5, 1, '', '', 2,
3, '', '', 4, '', '', 5, '', 8,
];
const grid6 = [
2, '', 3, '', 6, '', '', '', 8,
'', 7, '', '', 5, 1, 3, '', '',
'', 5, 9, '', '', '', '', '', '',
4, '', 2, 6, 3, '', '', 5, 9,
'', '', '', '', 9, '', '', '', '',
3, 9, '', '', 1, 4, 2, '', 6,
'', '', '', '', '', '', 4, 2, '',
'', '', 1, 8, 4, '', '', 3, '',
7, '', '', '', 2, '', 9, '', 5,
];
function makeGrid() {
const sujiDiv = document.getElementById("suji");
for (let x = 0; x < 3; x++) {
const bigRow = document.createElement("div");
for (let y = 0; y < 3; y++) {
const bigCol = document.createElement("span");
bigCol.classList.add("big-cell");
for (let i = 0; i < 3; i++) {
const row = document.createElement("div");
for (let j = 0; j < 3; j++) {
const col = document.createElement("span");
const id = 9*(x*3 + i) + y*3 + j;
col.classList.add("cell");
col.id = 'cell' + id;
col.innerHTML = 'A'
row.appendChild(col);
}
bigCol.appendChild(row);
}
bigRow.appendChild(bigCol);
}
sujiDiv.appendChild(bigRow);
}
}
function setCell(id, value, highlight=false) {
const cell = document.getElementById('cell' + id);
cell.innerHTML = value;
if (highlight) {
cell.classList.add('cell-highlight');
} else {
cell.classList.remove('cell-highlight');
}
}
function setGrid(grid) {
for (let i = 0; i < grid.length; i++) {
setCell(i, grid[i]);
}
}
function boxIndices(i) {
const indices = [];
const boxRow = 3 * Math.floor(i / 27);
const boxCol = 3 * Math.floor((i % 9) / 3);
let j = boxRow*9 + boxCol;
indices.push(j, j+1, j+2);
j += 9;
indices.push(j, j+1, j+2);
j += 9;
indices.push(j, j+1, j+2);
return indices;
}
function trimPossibles(grid, i, possibles) {
function trim(i, j) {
if (grid[j] !== '') return;
const k = possibles[j].indexOf(grid[i]);
if (possibles[j].includes(grid[i])) {
possibles[j].splice(possibles[j].indexOf(grid[i]), 1);
}
}
// 1. trim row
const rowStart = 9 * Math.floor(i / 9);
for (let j = rowStart; j < rowStart + 9; j++) {
trim(i, j);
}
// 2. trim column
const colStart = i % 9;
for (let j = colStart; j < grid.length; j+=9) {
trim(i, j);
}
// 3. trim box
for (let j of boxIndices(i)) {
trim(i, j);
}
}
function checkOnlyOptionInBox(grid, i, possibles) {
if (possibles[i].length < 2) {
return;
}
for (let x = 1; x < 10; x++) {
let other = false;
for (let j of boxIndices(i)) {
if (i === j) continue;
if (possibles[j].includes(x) || grid[j] === x) {
other = true;
break;
}
}
if (!other) {
possibles[i] = [x];
return;
}
}
}
function updateGrid(grid, possibles) {
// 4. update grid
for (let i = 0; i < grid.length; i++) {
if (possibles[i].length === 1) {
grid[i] = possibles[i][0];
possibles[i] = [];
setCell(i, grid[i], true);
}
}
}
function solveGrid(grid) {
const possibles = [];
// populate possibles
for (let i = 0; i < grid.length; i++) {
if (grid[i] === '') {
possibles[i] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
} else {
possibles[i] = [];
}
}
let count = 0;
while (grid.indexOf('') >= 0 && count < 10000) {
for (let i = 0; i < grid.length; i++) {
if (grid[i] === '') {
checkOnlyOptionInBox(grid, i, possibles);
continue;
}
trimPossibles(grid, i, possibles);
updateGrid(grid, possibles);
}
count += 1;
}
return grid.indexOf('') === -1;
}
let grid = grid6.slice();
function rand(n) {
return Math.floor(Math.random() * n);
}
function pick(arr) {
return arr[rand(arr.length)];
}
function fillGridRecursive(grid, possibles, i) {
if (i >= 81) {
return true;
}
let ok;
do {
const x = pick(possibles[i]);
if (x === undefined) {
return false;
}
grid[i] = x;
const newPossibles = possibles.map(x => x.slice());
newPossibles[i] = [];
trimPossibles(grid, i, newPossibles);
ok = fillGridRecursive(grid, newPossibles, i+1);
if (ok) {
break;
}
grid[i+1] = '';
possibles[i].splice(possibles[i].indexOf(x), 1);
} while (!ok);
return true;
}
function makeFullGrid() {
const possibles = [];
const grid = new Array(81);
grid.fill('');
// populate possibles
for (let i = 0; i < 81; i++) {
possibles[i] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
}
fillGridRecursive(grid, possibles, 0);
return grid;
}
function generate() {
grid = makeFullGrid();
setGrid(grid);
}
function knock() {
const start = Date.now();
while (Date.now() - start < 2000) {
const index = rand(81);
const preSolve = grid.slice();
grid[index] = '';
const ok = solveGrid(grid);
grid = preSolve;
if (ok) {
grid[index] = '';
}
setGrid(grid);
}
}
function main() {
makeGrid();
setGrid(grid);
document.getElementById("solve").onclick = e => { solveGrid(grid); };
document.getElementById("generate").onclick = e => { generate(); };
document.getElementById("knock").onclick = e => { knock(); };
}
window.addEventListener('load', main);