37-solveSudoku.js
4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/**
* @param {character[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
var solveSudoku = function (board) {
let empty = {};
let rows = new Array(9).fill(void 0).map(() => new Set());
let cols = new Array(9).fill(void 0).map(() => new Set());
let blocks = new Array(9).fill(void 0).map(() => new Set());
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
let block = block_no(i, j);
let v = board[i][j];
if (v !== '.') {
rows[i].add(v);
cols[j].add(v);
blocks[block].add(v);
}
mark({ block, i, j }, v);
}
}
function block_no(i, j) {
return (~~(i / 3)) * 3 + ~~(j / 3);
}
function mark({ block, i, j }, v) {
for (let n = 0; n < 9; n++) {
// skip self if is not empty cell
if ((i === n || j === n) && v !== '.') {
if (empty[i + ',' + j]) {
delete empty[i + ',' + j];
}
continue;
}
// cols
if (board[i][n] === '.') {
let e = empty[i + ',' + n] = empty[i + ',' + n] || { block: block_no(i, n), i, j: n, maybe: new Set(['1', '2', '3', '4', '5', '6', '7', '8', '9']) };
e.maybe.has(v)&&e.maybe.delete(v);
if (!e.maybe.size) {
delete empty[i + ',' + n];
}
}
// rows
if (board[n][j] === '.') {
let e = empty[n + ',' + j] = empty[n + ',' + j] || { block: block_no(n, j), i: n, j, maybe: new Set(['1', '2', '3', '4', '5', '6', '7', '8', '9']) };
e.maybe.has(v)&&e.maybe.delete(v);
if (!e.maybe.size) {
delete empty[n + ',' + j];
}
}
// blocks
let bi = ~~(block / 3);
let bj = block % 3;
for (let bn = 0; bn < 9; bn++) {
let i = bi * 3 + ~~(bn / 3);
let j = bj * 3 + bn % 3;
if (board[i][j] === '.') {
let e = empty[i + ',' + j] = empty[i + ',' + j] || { block: block_no(i, j), i, j, maybe: new Set(['1', '2', '3', '4', '5', '6', '7', '8', '9']) };
e.maybe.has(v)&&e.maybe.delete(v);
if (!e.maybe.size) {
delete empty[i + ',' + j];
}
}
}
}
}
function fill(i, j, block, v) {
board[i][j] = v;
empty[i + ',' + j].value = v;
rows[i].add(v);
cols[j].add(v);
blocks[block].add(v);
}
function unfill(i, j, block, v) {
board[i][j] = '.';
empty[i + ',' + j].value = void 0;
rows[i].delete(v);
cols[j].delete(v);
blocks[block].delete(v);
}
function deal() {
// find empty cell order by maybe size
let blanks = Object.keys(empty).map(k => empty[k]).filter(e => !e.value).sort((a, b) => {
return a.maybe.size - b.maybe.size;
});
for (let e of blanks) {
let { block, i, j, value } = e;
if (value) { continue; } // maybe signed
let union = new Set([...rows[i], ...cols[j], ...blocks[block]]);
let maybe = new Set(['1', '2', '3', '4', '5', '6', '7', '8', '9'].filter(v => !union.has(v)));
// empty cell must has some maybe number
if (!maybe.size) {
return false;
}
for (let v of [...maybe]) {
// set for testing
if (rows[i].has(v) || cols[j].has(v) || blocks[block].has(v)) {
return false;
}
fill(i, j, block, v);
if (deal()) {
break; // found
} else {
// rollback
unfill(i, j, block, v);
continue; // test next
}
}
if (board[i][j] === '.') {
return false;
}
}
return true;
}
deal();
};
// var sudoku = [["5", "3", ".", ".", "7", ".", ".", ".", "."], ["6", ".", ".", "1", "9", "5", ".", ".", "."], [".", "9", "8", ".", ".", ".", ".", "6", "."], ["8", ".", ".", ".", "6", ".", ".", ".", "3"], ["4", ".", ".", "8", ".", "3", ".", ".", "1"], ["7", ".", ".", ".", "2", ".", ".", ".", "6"], [".", "6", ".", ".", ".", ".", "2", "8", "."], [".", ".", ".", "4", "1", "9", ".", ".", "5"], [".", ".", ".", ".", "8", ".", ".", "7", "9"]];
var sudoku = [[".","6",".","5","9","3",".",".","."],["9",".","1",".",".",".","5",".","."],[".","3",".","4",".",".",".","9","."],["1",".","8",".","2",".",".",".","4"],["4",".",".","3",".","9",".",".","1"],["2",".",".",".","1",".","6",".","9"],[".","8",".",".",".","6",".","2","."],[".",".","4",".",".",".","8",".","7"],[".",".",".","7","8","5",".","1","."]];
console.time();
solveSudoku(sudoku);
console.info(sudoku);
console.timeEnd();