263 lines
6.3 KiB
JavaScript
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);
|