<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Sudoku Solver (WIP)</title>
<style type="text/css">
* {
box-sizing: border-box;
margin: 0;
position: relative;
}
html, body, div { height: 100%; }
body { display: table; width: 100%; }
button,textarea { font-size: 5.5vh; }
div { display: table-row; width: 100%; }
div * { display: table-cell; width: 100%; height: 100%; }
#button { height: 11vh; }
</style>
<script type="text/javascript">
var textarea;
var button;
var marking;
var puzzle = [
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0]
];
// easy puzzle
puzzle = [
[0,9,0,0,0,0,0,6,0],
[0,0,0,0,1,0,0,0,2],
[2,8,0,0,0,4,0,0,0],
[0,5,4,0,0,0,0,0,0],
[0,6,0,0,5,0,9,3,0],
[3,0,1,0,0,0,0,0,0],
[0,4,0,6,8,0,1,9,0],
[6,2,0,0,0,0,0,0,4],
[0,0,0,0,0,0,0,5,0]
]; // */
/* normal puzzle
puzzle = [
[0,3,1,0,6,4,5,0,0],
[2,0,0,0,0,9,0,3,0],
[0,0,0,0,0,2,9,0,1],
[0,0,0,0,4,0,0,0,0],
[0,0,3,2,8,5,0,4,0],
[0,0,0,0,3,0,0,8,5],
[0,0,0,0,0,0,0,0,2],
[5,0,0,0,9,0,0,6,4],
[4,6,0,0,0,0,0,0,3]
]; // */
/* hard puzzle
puzzle = [
[2,0,1,9,3,0,0,0,6],
[7,0,0,4,0,0,0,0,9],
[0,0,0,0,2,0,7,3,0],
[0,0,4,0,7,2,0,0,0],
[0,0,7,0,0,0,0,8,0],
[0,0,0,0,0,0,0,0,0],
[1,0,8,0,0,9,0,0,4],
[0,0,0,0,1,0,0,6,8],
[0,0,3,0,4,0,9,2,0],
]; // */
function mark () {
var marked = false;
for (var y = 0; y < 9; y++) {
for (var x = 0; x < 9; x++) {
if (puzzle[y][x]) {
// row, column
for (var i = 0; i < 9; i++) {
if (i != x) {
if (marking[y][i].has(puzzle[y][x])) {
marked = true;
}
marking[y][i].delete(puzzle[y][x]);
}
if (i != y) {
if (marking[i][x].has(puzzle[y][x])) {
marked = true;
}
marking[i][x].delete(puzzle[y][x]);
}
}
var zy = Math.floor(y / 3) * 3;
var zx = Math.floor(x / 3) * 3;
// zone
for (var iy = 0; iy < 3; iy++) {
var izy = zy + iy;
for (var ix = 0; ix < 3; ix++) {
var izx = zx + ix;
if (izy == y && izx == x) continue;
if (marking[izy][izx].has(puzzle[y][x])) {
marked = true;
}
marking[izy][izx].delete(puzzle[y][x]);
}
}
marking[y][x] = new Set([puzzle[y][x]]);
}
}
}
return marked;
}
function singles () {
for (var n = 1; n < 10; n++) {
// scan for lone N in row Y
for (var y = 0; y < 9; y++) {
var fx = -1;
for (var x = 0; x < 9; x++) {
if (marking[y][x].has(n)) {
if (0 <= fx) {
fx = -1;
break;
}
fx = x;
}
}
if (0 <= fx) {
marking[y][fx] = new Set([n]);
}
}
// scan for lone N in column X
for (var x = 0; x < 9; x++) {
var fy = -1;
for (var y = 0; y < 9; y++) {
if (marking[y][x].has(n)) {
if (0 <= fy) {
fy = -1;
break;
}
fy = y;
}
}
if (0 <= fy) {
marking[fy][x] = new Set([n]);
}
}
// scan for lone N in zone Y, X
for (var zy = 0; zy < 3; zy++) {
for (var zx = 0; zx < 3; zx++) {
var fx = -1;
var fy = -1;
zonescan: for (var iy = 0; iy < 3; iy++) {
izy = zy * 3 + iy;
for (var ix = 0; ix < 3; ix++) {
izx = zx * 3 + ix;
if (marking[izy][izx].has(n)) {
if (0 <= fy) {
fy = -1;
fx = -1;
break zonescan;
}
fy = izy;
fx = izx;
}
}
}
if (0 <= fy) {
marking[fy][fx] = new Set([n]);
}
}
}
}
}
function copysolved () {
var copied = false;
for (var y = 0; y < 9; y++) {
for (var x = 0; x < 9; x++) {
if (!puzzle[y][x] && 1 == marking[y][x].size) {
puzzle[y][x] = [...marking[y][x].values()][0];
copied = true;
}
}
}
return copied;
}
function showpuzzle () {
textarea.value = JSON.stringify(puzzle)
.replaceAll("],", "],\n\t")
.replaceAll("[[", "[\n\t[")
.replaceAll("]]", "]\n]");
}
function logmarking () {
var log = "";
for (var y = 0; y < 9; y++) {
log += "[";
for (var x = 0; x < 9; x++) {
for (var n = 1; n < 10; n++) {
log += marking[y][x].has(n) ? n : '.';
}
log += "]" + (8 == x ? "\n" : "[");
}
}
console.log(log);
}
var solveInterval;
var passes = 0;
function solve () {
passes++;
puzzle = JSON.parse(textarea.value);
singles();
if (!mark() && !copysolved()) {
console.log(`solving completed or abandoned in ${passes} passes`);
clearInterval(solveInterval);
logmarking();
}
showpuzzle();
}
function resetmarking () {
marking = [];
for (var y = 0; y < 9; y++) {
var row = [];
for (var x = 0; x < 9; x++) {
row.push(new Set([1, 2, 3, 4, 5, 6, 7, 8, 9]));
}
marking.push(row);
}
}
function resetpuzzle () {
puzzle = [];
for (var y = 0; y < 9; y++) {
var row = [];
for (var x = 0; x < 9; x++) {
row.push(0);
}
puzzle.push(row);
}
}
onload = function () {
textarea = document.querySelector("textarea");
textarea.focus();
resetmarking();
//resetpuzzle();
showpuzzle();
button = document.querySelector("button");
button.onclick = function () {
resetmarking();
puzzle = JSON.parse(textarea.value);
passes = 0;
solveInterval = setInterval(solve, 15);
}
}
</script>
</head>
<body>
<div>
<textarea></textarea>
</div>
<div id="button">
<button>solve</button>
</div>
</body>
</html>