数独

更新时间:2023-02-22 15:57:53标签:算法web前端游戏

暴力破解大法

64
251
967
759
24
236
195
913
87
1import { sleep } from '@shared/utils';
2
3class Sudoku {
4 static readonly nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
5 static readonly square = 9;
6 static generateEmptyBoards() {
7 return new Array(Sudoku.square).fill(null).map(() => new Array(Sudoku.square).fill(null)) as (null | number)[][];
8 }
9 private row = 0;
10 private column = 0;
11 private original = Sudoku.generateEmptyBoards();
12 private direction: 'forward' | 'back' = 'forward';
13 private abortController?: AbortController;
14
15 public boards = Sudoku.generateEmptyBoards();
16
17 constructor(boards: (null | number)[][] = []) {
18 this.init(boards);
19 }
20
21 public init(boards: (null | number)[][]) {
22 for (let i = 0; i < this.original.length; i += 1) {
23 for (let j = 0; j < this.original[i].length; j += 1) {
24 this.original[i][j] = boards?.[i]?.[j] ?? null;
25 }
26 }
27 this.reset();
28 }
29 public reset() {
30 this.row = 0;
31 this.column = 0;
32 this.boards = this.original.map(row => [...row]);
33 }
34 public fill() {
35 if (this.original[this.row][this.column] === null) {
36 const rows = this.boards[this.row];
37 const columns = this.boards.map(row => row[this.column]);
38 const startColumn = this.column - this.column % 3;
39 const startRow = this.row - this.row % 3;
40 const squares = this.boards.reduce((pre, current, index) => {
41 if (index >= startRow && index < startRow + 3) {
42 pre.push(...current.slice(startColumn, startColumn + 3));
43 }
44 return pre;
45 }, [] as (number | null)[]);
46 const availableNums = Sudoku.nums.filter(num => !rows.includes(num) && !columns.includes(num) && !squares.includes(num));
47 const available = availableNums.find(num => {
48 const current = this.boards[this.row][this.column];
49 return current === null || num > current;
50 });
51 if (available) {
52 this.boards[this.row][this.column] = available;
53 this.direction = 'forward';
54 } else {
55 this.boards[this.row][this.column] = null;
56 this.direction = 'back';
57 }
58 }
59 }
60 private round() {
61 if (this.column > Sudoku.square - 1) {
62 this.row += 1;
63 this.column = 0;
64 return true;
65 }
66 if (this.column < 0) {
67 this.row -= 1;
68 this.column = Sudoku.square - 1;
69 return true;
70 }
71 this.fill();
72 this.column = this.direction === 'forward' ? this.column + 1 : this.column - 1;
73 return false;
74 }
75 public destroy() {
76 this.abortController?.abort();
77 }
78 public async async(cb?: (boards: (number | null)[][]) => void, millisecond = 0) {
79 this.abortController = new AbortController();
80 this.reset();
81 while (this.row < Sudoku.square && this.row >= 0) {
82 if (this.round()) continue;
83 await sleep(millisecond, this.abortController.signal);
84 cb?.(this.boards);
85 }
86 if (this.row < 0) {
87 throw new Error('无解');
88 }
89 return this.boards;
90 }
91 public sync() {
92 this.reset();
93 while (this.row < Sudoku.square && this.row >= 0) {
94 this.round();
95 }
96 if (this.row < 0) {
97 throw new Error('无解');
98 }
99 return this.boards;
100 }
101}
102
103export { Sudoku };