Initial commit
This commit is contained in:
24
index.css
Normal file
24
index.css
Normal file
@@ -0,0 +1,24 @@
|
||||
.app {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.big-cell {
|
||||
border: 1px black solid;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.cell {
|
||||
border: 1px gray solid;
|
||||
width: 20pt;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cell-highlight {
|
||||
color: red;
|
||||
}
|
||||
|
||||
span.cell:empty:before {
|
||||
content: "\200b"; /* unicode zero width space character */
|
||||
}
|
24
index.html
Normal file
24
index.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<link rel="stylesheet" href="pico.min.css">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
<script src="index.js"></script>
|
||||
<title>Suji 0</title>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container">
|
||||
<article class="app">
|
||||
<div id="suji" class="suji"></div>
|
||||
</article>
|
||||
<article class="app">
|
||||
<button id="solve">Solve</button>
|
||||
<button id="generate" class="secondary">Generate</button>
|
||||
<button id="knock" class="secondary">Clear a few cells</button>
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
262
index.js
Normal file
262
index.js
Normal file
@@ -0,0 +1,262 @@
|
||||
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);
|
4
pico.min.css
vendored
Normal file
4
pico.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user