sudoku-pro 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -6
- package/package.json +6 -1
- package/sudoku-pro.js +187 -141
package/README.md
CHANGED
|
@@ -22,7 +22,13 @@ npm run hard
|
|
|
22
22
|
|
|
23
23
|
```javascript
|
|
24
24
|
// Import the module
|
|
25
|
-
const {
|
|
25
|
+
const {
|
|
26
|
+
generateSudoku,
|
|
27
|
+
printSudoku,
|
|
28
|
+
solveSudoku,
|
|
29
|
+
getHint,
|
|
30
|
+
waitForHint,
|
|
31
|
+
} = require("sudoku-pro");
|
|
26
32
|
|
|
27
33
|
// Get the difficulty level
|
|
28
34
|
const args = process.argv.slice(2);
|
|
@@ -30,16 +36,17 @@ const args = process.argv.slice(2);
|
|
|
30
36
|
const difficulty = args[0]; // Get the difficulty level from command line arguments
|
|
31
37
|
|
|
32
38
|
// Generate a Sudoku puzzle
|
|
33
|
-
const sudoku = generateSudoku(difficulty);
|
|
39
|
+
const { sudoku, solvedSudoku } = generateSudoku(difficulty);
|
|
34
40
|
|
|
35
41
|
// Print the puzzle
|
|
36
42
|
printSudoku(sudoku);
|
|
37
43
|
|
|
38
|
-
//
|
|
39
|
-
|
|
44
|
+
// Function to wait for user input for hint or complete solution
|
|
45
|
+
waitForHint(sudoku, solvedSudoku);
|
|
40
46
|
|
|
41
|
-
//
|
|
42
|
-
printSudoku(sudoku);
|
|
47
|
+
// This script will continue running until the user chooses to exit
|
|
43
48
|
|
|
44
49
|
process.exit();
|
|
45
50
|
```
|
|
51
|
+
|
|
52
|
+
Now, in addition to generating and solving Sudoku puzzles, users can also request hints or print the complete solution while the script is running.
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sudoku-pro",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "sudoku-pro humbly presents itself as your go-to toolkit for generating, solving, and printing Sudoku puzzles spanning different difficulty levels. Powered by the reliable backtracking algorithm, it aspires to offer enthusiasts and learners alike a simple yet effective platform to engage with Sudoku puzzles. With Sudoku-Pro, embark on a journey of brain-teasing challenges or delve into educational endeavors with ease. This humble interface aims to make Sudoku puzzle creation and enjoyment accessible to all, ensuring a delightful experience for users of varying skill levels. Join us in exploring the possibilities of Sudoku puzzles while we humbly accompany you on your puzzle-solving adventures.",
|
|
5
|
+
"changelog": {
|
|
6
|
+
"1.0.6": "generateSudoku, printSudoku, solveSudoku",
|
|
7
|
+
"1.0.7": "getHint function added",
|
|
8
|
+
"1.0.7": "fixed the bug which cause issue while generating hard sudoku"
|
|
9
|
+
},
|
|
5
10
|
"main": "sudoku-pro.js",
|
|
6
11
|
"repository": {
|
|
7
12
|
"type": "git",
|
package/sudoku-pro.js
CHANGED
|
@@ -1,185 +1,231 @@
|
|
|
1
|
+
const readline = require("readline");
|
|
2
|
+
|
|
1
3
|
// Get the difficulty level
|
|
2
4
|
const args = process.argv.slice(2);
|
|
3
5
|
|
|
4
6
|
function shuffle(array) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
// Fisher-Yates shuffle algorithm
|
|
8
|
+
for (let i = array.length - 1; i > 0; i--) {
|
|
9
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
10
|
+
[array[i], array[j]] = [array[j], array[i]];
|
|
11
|
+
}
|
|
12
|
+
return array;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
function generateSudoku(difficulty) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
16
|
+
const sudoku = Array.from({ length: 9 }, () => Array(9).fill(0));
|
|
17
|
+
|
|
18
|
+
// Fill the diagonal blocks (3x3 sub-grids) with valid numbers
|
|
19
|
+
for (let i = 0; i < 9; i += 3) {
|
|
20
|
+
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
21
|
+
shuffle(nums);
|
|
22
|
+
for (let j = 0; j < 3; j++) {
|
|
23
|
+
for (let k = 0; k < 3; k++) {
|
|
24
|
+
sudoku[i + j][i + k] = nums.pop();
|
|
25
|
+
}
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Solve the puzzle
|
|
30
|
+
const solvedSudoku = JSON.parse(JSON.stringify(sudoku)); // Create a deep copy for later use
|
|
31
|
+
solveSudoku(solvedSudoku);
|
|
32
|
+
|
|
33
|
+
// Determine the number of cells to remove based on difficulty level
|
|
34
|
+
let numToRemove;
|
|
35
|
+
if (difficulty === "e") {
|
|
36
|
+
numToRemove = Math.floor(Math.random() * 20) + 20; // Easy difficulty
|
|
37
|
+
} else if (difficulty === "m") {
|
|
38
|
+
numToRemove = Math.floor(Math.random() * 25) + 40; // Medium difficulty
|
|
39
|
+
} else if (difficulty === "h") {
|
|
40
|
+
do {
|
|
41
|
+
numToRemove = Math.floor(Math.random() * 30) + 70; // Hard difficulty
|
|
42
|
+
} while (numToRemove > 81); // Keep recalculating until numToRemove <= 81
|
|
43
|
+
} else {
|
|
44
|
+
throw new Error(
|
|
45
|
+
'Invalid difficulty level. Please use "e" for easy, "m" for medium, or "h" for hard.'
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create a copy of the solved Sudoku to remove cells from
|
|
50
|
+
const sudokuCopy = JSON.parse(JSON.stringify(solvedSudoku));
|
|
51
|
+
|
|
52
|
+
// Remove some numbers to create the puzzle
|
|
53
|
+
for (let i = 0; i < numToRemove; i++) {
|
|
54
|
+
const row = Math.floor(Math.random() * 9);
|
|
55
|
+
const col = Math.floor(Math.random() * 9);
|
|
56
|
+
if (sudokuCopy[row][col] !== 0) {
|
|
57
|
+
sudokuCopy[row][col] = 0;
|
|
38
58
|
} else {
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Remove some numbers to create the puzzle
|
|
43
|
-
for (let i = 0; i < numToRemove; i++) {
|
|
44
|
-
const row = Math.floor(Math.random() * 9);
|
|
45
|
-
const col = Math.floor(Math.random() * 9);
|
|
46
|
-
if (sudoku[row][col] !== 0) {
|
|
47
|
-
sudoku[row][col] = 0;
|
|
48
|
-
} else {
|
|
49
|
-
i--; // Try again if the cell is already empty
|
|
50
|
-
}
|
|
59
|
+
i--; // Try again if the cell is already empty
|
|
51
60
|
}
|
|
61
|
+
}
|
|
52
62
|
|
|
53
|
-
|
|
63
|
+
return { sudoku: sudokuCopy, solvedSudoku };
|
|
54
64
|
}
|
|
55
65
|
|
|
56
66
|
function solveSudoku(sudoku) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
const emptyCell = findEmptyCell(sudoku);
|
|
68
|
+
if (!emptyCell) {
|
|
69
|
+
return true; // Puzzle solved successfully
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const [row, col] = emptyCell;
|
|
73
|
+
for (let num = 1; num <= 9; num++) {
|
|
74
|
+
if (isValidMove(sudoku, row, col, num)) {
|
|
75
|
+
sudoku[row][col] = num;
|
|
76
|
+
if (solveSudoku(sudoku)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
sudoku[row][col] = 0; // Undo the assignment
|
|
60
80
|
}
|
|
81
|
+
}
|
|
61
82
|
|
|
62
|
-
|
|
63
|
-
for (let num = 1; num <= 9; num++) {
|
|
64
|
-
if (isValidMove(sudoku, row, col, num)) {
|
|
65
|
-
sudoku[row][col] = num;
|
|
66
|
-
if (solveSudoku(sudoku)) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
sudoku[row][col] = 0; // Undo the assignment
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return false; // No solution found, backtrack
|
|
83
|
+
return false; // No solution found, backtrack
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
function findEmptyCell(sudoku) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
87
|
+
// Function to find an empty cell in the Sudoku grid
|
|
88
|
+
for (let row = 0; row < 9; row++) {
|
|
89
|
+
for (let col = 0; col < 9; col++) {
|
|
90
|
+
if (sudoku[row][col] === 0) {
|
|
91
|
+
return [row, col];
|
|
92
|
+
}
|
|
84
93
|
}
|
|
85
|
-
|
|
94
|
+
}
|
|
95
|
+
return null; // No empty cell found
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
function isValidMove(sudoku, row, col, num) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
99
|
+
// Check if assigning num to the cell at (row, col) is a valid move
|
|
100
|
+
// Check row
|
|
101
|
+
for (let i = 0; i < 9; i++) {
|
|
102
|
+
if (sudoku[row][i] === num) {
|
|
103
|
+
return false; // Number already exists in the row
|
|
95
104
|
}
|
|
105
|
+
}
|
|
96
106
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
107
|
+
// Check column
|
|
108
|
+
for (let i = 0; i < 9; i++) {
|
|
109
|
+
if (sudoku[i][col] === num) {
|
|
110
|
+
return false; // Number already exists in the column
|
|
102
111
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check 3x3 subgrid
|
|
115
|
+
const startRow = Math.floor(row / 3) * 3;
|
|
116
|
+
const startCol = Math.floor(col / 3) * 3;
|
|
117
|
+
for (let i = 0; i < 3; i++) {
|
|
118
|
+
for (let j = 0; j < 3; j++) {
|
|
119
|
+
if (sudoku[startRow + i][startCol + j] === num) {
|
|
120
|
+
return false; // Number already exists in the subgrid
|
|
121
|
+
}
|
|
113
122
|
}
|
|
123
|
+
}
|
|
114
124
|
|
|
115
|
-
|
|
125
|
+
return true; // Valid move
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
function printSudoku(sudoku) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
});
|
|
129
|
+
console.log("Sudoku Puzzle:");
|
|
130
|
+
console.log("-------------------------");
|
|
131
|
+
sudoku.forEach((row, rowIndex) => {
|
|
132
|
+
if (rowIndex % 3 === 0 && rowIndex !== 0) {
|
|
133
|
+
console.log("------|-------|-------");
|
|
134
|
+
}
|
|
135
|
+
row.forEach((cell, cellIndex) => {
|
|
136
|
+
if (cellIndex % 3 === 0 && cellIndex !== 0) {
|
|
137
|
+
process.stdout.write("| ");
|
|
138
|
+
}
|
|
139
|
+
process.stdout.write(String(cell || ".") + " ");
|
|
140
|
+
if (cellIndex === 8) {
|
|
141
|
+
console.log();
|
|
142
|
+
}
|
|
134
143
|
});
|
|
135
|
-
|
|
144
|
+
});
|
|
145
|
+
console.log("-------------------------");
|
|
136
146
|
}
|
|
137
147
|
|
|
138
148
|
function printSudokuWithAnswers(sudoku) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
149
|
+
console.log("Sudoku Puzzle with Answers:");
|
|
150
|
+
console.log("-------------------------");
|
|
151
|
+
sudoku.forEach((row, rowIndex) => {
|
|
152
|
+
if (rowIndex % 3 === 0 && rowIndex !== 0) {
|
|
153
|
+
console.log("------|-------|-------");
|
|
154
|
+
}
|
|
155
|
+
row.forEach((cell, cellIndex) => {
|
|
156
|
+
if (cellIndex % 3 === 0 && cellIndex !== 0) {
|
|
157
|
+
process.stdout.write("| ");
|
|
158
|
+
}
|
|
159
|
+
process.stdout.write(String(cell) + " ");
|
|
160
|
+
if (cellIndex === 8) {
|
|
161
|
+
console.log();
|
|
162
|
+
}
|
|
154
163
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
sudokuPrinted = true;
|
|
158
|
-
clearInterval(intervalId);
|
|
164
|
+
});
|
|
165
|
+
console.log("-------------------------");
|
|
159
166
|
}
|
|
160
167
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
let
|
|
168
|
+
function getHint(sudoku, solvedSudoku) {
|
|
169
|
+
const emptyCells = [];
|
|
170
|
+
for (let i = 0; i < 9; i++) {
|
|
171
|
+
for (let j = 0; j < 9; j++) {
|
|
172
|
+
if (sudoku[i][j] === 0) {
|
|
173
|
+
emptyCells.push([i, j]);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (emptyCells.length === 0) {
|
|
178
|
+
console.log("No empty cells left to provide a hint.");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const randomIndex = Math.floor(Math.random() * emptyCells.length);
|
|
182
|
+
const [row, col] = emptyCells[randomIndex];
|
|
183
|
+
sudoku[row][col] = solvedSudoku[row][col];
|
|
184
|
+
console.log(
|
|
185
|
+
`Hint: The number at row ${row + 1}, column ${col + 1} is ${
|
|
186
|
+
solvedSudoku[row][col]
|
|
187
|
+
}`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
164
190
|
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
191
|
+
function waitForHint(sudoku, solvedSudoku) {
|
|
192
|
+
const rl = readline.createInterface({
|
|
193
|
+
input: process.stdin,
|
|
194
|
+
output: process.stdout,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
rl.question(
|
|
198
|
+
"Press 'h' for a hint or 'c' for the complete solution: ",
|
|
199
|
+
(answer) => {
|
|
200
|
+
if (answer.trim().toLowerCase() === "h") {
|
|
201
|
+
getHint(sudoku, solvedSudoku);
|
|
174
202
|
printSudoku(sudoku);
|
|
175
|
-
|
|
176
|
-
printSudokuWithAnswers(
|
|
203
|
+
} else if (answer.trim().toLowerCase() === "c") {
|
|
204
|
+
printSudokuWithAnswers(solvedSudoku);
|
|
205
|
+
process.exit();
|
|
206
|
+
} else {
|
|
207
|
+
console.log(
|
|
208
|
+
"Invalid input. Please press 'h' for a hint or 'c' for the complete solution."
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
rl.close();
|
|
212
|
+
waitForHint(sudoku, solvedSudoku); // Ask again for hint or complete solution
|
|
177
213
|
}
|
|
178
|
-
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const difficulty = args[0]; // Get the difficulty level from command line arguments
|
|
218
|
+
|
|
219
|
+
const { sudoku, solvedSudoku } = generateSudoku(difficulty);
|
|
220
|
+
printSudoku(sudoku);
|
|
221
|
+
|
|
222
|
+
waitForHint(sudoku, solvedSudoku);
|
|
179
223
|
|
|
180
224
|
module.exports = {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
225
|
+
generateSudoku,
|
|
226
|
+
printSudoku,
|
|
227
|
+
solveSudoku,
|
|
228
|
+
printSudokuWithAnswers,
|
|
229
|
+
getHint,
|
|
230
|
+
waitForHint,
|
|
185
231
|
};
|