sudoku-pro 1.0.5 → 1.0.7
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 +5 -2
- package/sudoku-pro.js +185 -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,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sudoku-pro",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
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
|
+
},
|
|
5
9
|
"main": "sudoku-pro.js",
|
|
6
10
|
"repository": {
|
|
7
11
|
"type": "git",
|
|
@@ -32,7 +36,6 @@
|
|
|
32
36
|
"Puzzle",
|
|
33
37
|
"Puzzle Game",
|
|
34
38
|
"Game",
|
|
35
|
-
"Numbers Game",
|
|
36
39
|
"Board Game",
|
|
37
40
|
"Logic Game",
|
|
38
41
|
"Numbers Game",
|
package/sudoku-pro.js
CHANGED
|
@@ -1,185 +1,229 @@
|
|
|
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
|
+
numToRemove = Math.floor(Math.random() * 30) + 55; // Hard difficulty
|
|
41
|
+
} else {
|
|
42
|
+
throw new Error(
|
|
43
|
+
'Invalid difficulty level. Please use "e" for easy, "m" for medium, or "h" for hard.'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Create a copy of the solved Sudoku to remove cells from
|
|
48
|
+
const sudokuCopy = JSON.parse(JSON.stringify(solvedSudoku));
|
|
49
|
+
|
|
50
|
+
// Remove some numbers to create the puzzle
|
|
51
|
+
for (let i = 0; i < numToRemove; i++) {
|
|
52
|
+
const row = Math.floor(Math.random() * 9);
|
|
53
|
+
const col = Math.floor(Math.random() * 9);
|
|
54
|
+
if (sudokuCopy[row][col] !== 0) {
|
|
55
|
+
sudokuCopy[row][col] = 0;
|
|
38
56
|
} 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
|
-
}
|
|
57
|
+
i--; // Try again if the cell is already empty
|
|
51
58
|
}
|
|
59
|
+
}
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
return { sudoku: sudokuCopy, solvedSudoku };
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
function solveSudoku(sudoku) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
const emptyCell = findEmptyCell(sudoku);
|
|
66
|
+
if (!emptyCell) {
|
|
67
|
+
return true; // Puzzle solved successfully
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const [row, col] = emptyCell;
|
|
71
|
+
for (let num = 1; num <= 9; num++) {
|
|
72
|
+
if (isValidMove(sudoku, row, col, num)) {
|
|
73
|
+
sudoku[row][col] = num;
|
|
74
|
+
if (solveSudoku(sudoku)) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
sudoku[row][col] = 0; // Undo the assignment
|
|
60
78
|
}
|
|
79
|
+
}
|
|
61
80
|
|
|
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
|
|
81
|
+
return false; // No solution found, backtrack
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
function findEmptyCell(sudoku) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
85
|
+
// Function to find an empty cell in the Sudoku grid
|
|
86
|
+
for (let row = 0; row < 9; row++) {
|
|
87
|
+
for (let col = 0; col < 9; col++) {
|
|
88
|
+
if (sudoku[row][col] === 0) {
|
|
89
|
+
return [row, col];
|
|
90
|
+
}
|
|
84
91
|
}
|
|
85
|
-
|
|
92
|
+
}
|
|
93
|
+
return null; // No empty cell found
|
|
86
94
|
}
|
|
87
95
|
|
|
88
96
|
function isValidMove(sudoku, row, col, num) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
97
|
+
// Check if assigning num to the cell at (row, col) is a valid move
|
|
98
|
+
// Check row
|
|
99
|
+
for (let i = 0; i < 9; i++) {
|
|
100
|
+
if (sudoku[row][i] === num) {
|
|
101
|
+
return false; // Number already exists in the row
|
|
95
102
|
}
|
|
103
|
+
}
|
|
96
104
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
105
|
+
// Check column
|
|
106
|
+
for (let i = 0; i < 9; i++) {
|
|
107
|
+
if (sudoku[i][col] === num) {
|
|
108
|
+
return false; // Number already exists in the column
|
|
102
109
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Check 3x3 subgrid
|
|
113
|
+
const startRow = Math.floor(row / 3) * 3;
|
|
114
|
+
const startCol = Math.floor(col / 3) * 3;
|
|
115
|
+
for (let i = 0; i < 3; i++) {
|
|
116
|
+
for (let j = 0; j < 3; j++) {
|
|
117
|
+
if (sudoku[startRow + i][startCol + j] === num) {
|
|
118
|
+
return false; // Number already exists in the subgrid
|
|
119
|
+
}
|
|
113
120
|
}
|
|
121
|
+
}
|
|
114
122
|
|
|
115
|
-
|
|
123
|
+
return true; // Valid move
|
|
116
124
|
}
|
|
117
125
|
|
|
118
126
|
function printSudoku(sudoku) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
});
|
|
127
|
+
console.log("Sudoku Puzzle:");
|
|
128
|
+
console.log("-------------------------");
|
|
129
|
+
sudoku.forEach((row, rowIndex) => {
|
|
130
|
+
if (rowIndex % 3 === 0 && rowIndex !== 0) {
|
|
131
|
+
console.log("------|-------|-------");
|
|
132
|
+
}
|
|
133
|
+
row.forEach((cell, cellIndex) => {
|
|
134
|
+
if (cellIndex % 3 === 0 && cellIndex !== 0) {
|
|
135
|
+
process.stdout.write("| ");
|
|
136
|
+
}
|
|
137
|
+
process.stdout.write(String(cell || ".") + " ");
|
|
138
|
+
if (cellIndex === 8) {
|
|
139
|
+
console.log();
|
|
140
|
+
}
|
|
134
141
|
});
|
|
135
|
-
|
|
142
|
+
});
|
|
143
|
+
console.log("-------------------------");
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
function printSudokuWithAnswers(sudoku) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
147
|
+
console.log("Sudoku Puzzle with Answers:");
|
|
148
|
+
console.log("-------------------------");
|
|
149
|
+
sudoku.forEach((row, rowIndex) => {
|
|
150
|
+
if (rowIndex % 3 === 0 && rowIndex !== 0) {
|
|
151
|
+
console.log("------|-------|-------");
|
|
152
|
+
}
|
|
153
|
+
row.forEach((cell, cellIndex) => {
|
|
154
|
+
if (cellIndex % 3 === 0 && cellIndex !== 0) {
|
|
155
|
+
process.stdout.write("| ");
|
|
156
|
+
}
|
|
157
|
+
process.stdout.write(String(cell) + " ");
|
|
158
|
+
if (cellIndex === 8) {
|
|
159
|
+
console.log();
|
|
160
|
+
}
|
|
154
161
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
sudokuPrinted = true;
|
|
158
|
-
clearInterval(intervalId);
|
|
162
|
+
});
|
|
163
|
+
console.log("-------------------------");
|
|
159
164
|
}
|
|
160
165
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
let
|
|
166
|
+
function getHint(sudoku, solvedSudoku) {
|
|
167
|
+
const emptyCells = [];
|
|
168
|
+
for (let i = 0; i < 9; i++) {
|
|
169
|
+
for (let j = 0; j < 9; j++) {
|
|
170
|
+
if (sudoku[i][j] === 0) {
|
|
171
|
+
emptyCells.push([i, j]);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (emptyCells.length === 0) {
|
|
176
|
+
console.log("No empty cells left to provide a hint.");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const randomIndex = Math.floor(Math.random() * emptyCells.length);
|
|
180
|
+
const [row, col] = emptyCells[randomIndex];
|
|
181
|
+
sudoku[row][col] = solvedSudoku[row][col];
|
|
182
|
+
console.log(
|
|
183
|
+
`Hint: The number at row ${row + 1}, column ${col + 1} is ${
|
|
184
|
+
solvedSudoku[row][col]
|
|
185
|
+
}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
164
188
|
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
189
|
+
function waitForHint(sudoku, solvedSudoku) {
|
|
190
|
+
const rl = readline.createInterface({
|
|
191
|
+
input: process.stdin,
|
|
192
|
+
output: process.stdout,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
rl.question(
|
|
196
|
+
"Press 'h' for a hint or 'c' for the complete solution: ",
|
|
197
|
+
(answer) => {
|
|
198
|
+
if (answer.trim().toLowerCase() === "h") {
|
|
199
|
+
getHint(sudoku, solvedSudoku);
|
|
174
200
|
printSudoku(sudoku);
|
|
175
|
-
|
|
176
|
-
printSudokuWithAnswers(
|
|
201
|
+
} else if (answer.trim().toLowerCase() === "c") {
|
|
202
|
+
printSudokuWithAnswers(solvedSudoku);
|
|
203
|
+
process.exit();
|
|
204
|
+
} else {
|
|
205
|
+
console.log(
|
|
206
|
+
"Invalid input. Please press 'h' for a hint or 'c' for the complete solution."
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
rl.close();
|
|
210
|
+
waitForHint(sudoku, solvedSudoku); // Ask again for hint or complete solution
|
|
177
211
|
}
|
|
178
|
-
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const difficulty = args[0]; // Get the difficulty level from command line arguments
|
|
216
|
+
|
|
217
|
+
const { sudoku, solvedSudoku } = generateSudoku(difficulty);
|
|
218
|
+
printSudoku(sudoku);
|
|
219
|
+
|
|
220
|
+
waitForHint(sudoku, solvedSudoku);
|
|
179
221
|
|
|
180
222
|
module.exports = {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
223
|
+
generateSudoku,
|
|
224
|
+
printSudoku,
|
|
225
|
+
solveSudoku,
|
|
226
|
+
printSudokuWithAnswers,
|
|
227
|
+
getHint,
|
|
228
|
+
waitForHint,
|
|
185
229
|
};
|