sudoku-pro 1.0.9 → 1.0.11

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 CHANGED
@@ -16,6 +16,7 @@ npm i sudoku-pro
16
16
  npm run easy
17
17
  npm run medium
18
18
  npm run hard
19
+ npm run very
19
20
  ```
20
21
 
21
22
  ## Example: How to use the functions in your applications
package/cli.js ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { generateSudoku, printSudoku, waitForHint } = require("./sudoku-pro.js");
5
+
6
+ const difficulty = (process.argv[2] || "m").toLowerCase();
7
+ const { sudoku, solvedSudoku } = generateSudoku(difficulty);
8
+
9
+ printSudoku(sudoku);
10
+ waitForHint(sudoku, solvedSudoku);
package/esm.mjs ADDED
@@ -0,0 +1,12 @@
1
+ import cjs from "./sudoku-pro.js";
2
+
3
+ // Re-export as named exports (frontend-friendly)
4
+ export const generateSudoku = cjs.generateSudoku;
5
+ export const printSudoku = cjs.printSudoku;
6
+ export const solveSudoku = cjs.solveSudoku;
7
+ export const printSudokuWithAnswers = cjs.printSudokuWithAnswers;
8
+ export const getHint = cjs.getHint;
9
+ export const waitForHint = cjs.waitForHint;
10
+
11
+ // Optional default export too (nice for interop)
12
+ export default cjs;
package/package.json CHANGED
@@ -1,56 +1,66 @@
1
1
  {
2
2
  "name": "sudoku-pro",
3
- "version": "1.0.9",
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.8": "fixed the bug which cause issue while generating hard sudoku"
9
- },
10
- "main": "sudoku-pro.js",
11
- "repository": {
12
- "type": "git",
13
- "url": "git+ssh://git@github.com/concurdev/sudoku-pro.git"
3
+ "version": "1.0.11",
4
+ "description": "Sudoku generator, solver, and CLI tool. Works in terminal and frontend apps with a clean library-first design.",
5
+ "license": "MIT",
6
+ "author": "Ashish Vashisht",
7
+
8
+ "main": "./sudoku-pro.js",
9
+
10
+ "exports": {
11
+ ".": {
12
+ "import": "./esm.mjs",
13
+ "require": "./sudoku-pro.js"
14
+ }
14
15
  },
15
- "scripts": {
16
- "easy": "node sudoku-pro e",
17
- "medium": "node sudoku-pro m",
18
- "hard": "node sudoku-pro h"
16
+
17
+ "bin": {
18
+ "sudoku-pro": "./cli.js"
19
19
  },
20
+
20
21
  "files": [
21
- "sudoku-pro.js"
22
+ "sudoku-pro.js",
23
+ "esm.mjs",
24
+ "cli.js",
25
+ "README.md",
26
+ "LICENSE"
22
27
  ],
28
+
29
+ "scripts": {
30
+ "easy": "node cli.js e",
31
+ "medium": "node cli.js m",
32
+ "hard": "node cli.js h",
33
+ "very": "node cli.js v",
34
+ "test": "node -e \"const { generateSudoku } = require('./sudoku-pro'); const g = generateSudoku('m'); console.log(g.sudoku.length === 9 ? 'ok' : 'fail')\""
35
+ },
36
+
23
37
  "keywords": [
24
- "Sudoku",
25
- "Sudoku Algorithm",
26
- "Sudoku Generator",
27
- "Sudoku Solver",
28
- "Sudoku Teaser",
29
- "Sudoku Puzzle",
30
- "Sudoku Engine",
31
- "Sudoku Difficulty",
32
- "Sudoku Logic",
33
- "Easy Sudoku",
34
- "Medium Sudoku",
35
- "Hard Sudoku",
36
- "Brain Teaser",
37
- "Puzzle",
38
- "Puzzle Game",
39
- "Game",
40
- "Board Game",
41
- "Logic Game",
42
- "Numbers Game",
43
- "Recreational Mathematics",
44
- "Backtracking"
38
+ "sudoku",
39
+ "sudoku-generator",
40
+ "sudoku-solver",
41
+ "sudoku-cli",
42
+ "puzzle",
43
+ "game",
44
+ "logic-game",
45
+ "backtracking",
46
+ "board-game",
47
+ "brain-teaser"
45
48
  ],
46
- "author": "Ashish Vashisht",
47
- "license": "MIT",
48
- "engines": {
49
- "node": ">= 10"
49
+
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+ssh://git@github.com/concurdev/sudoku-pro.git"
50
53
  },
54
+
51
55
  "bugs": {
52
56
  "url": "https://github.com/concurdev/sudoku-pro/issues"
53
57
  },
58
+
54
59
  "homepage": "https://github.com/concurdev/sudoku-pro#readme",
55
- "devDependencies": {}
60
+
61
+ "engines": {
62
+ "node": ">=14"
63
+ },
64
+
65
+ "sideEffects": false
56
66
  }
package/sudoku-pro.js CHANGED
@@ -1,10 +1,9 @@
1
- const readline = require("readline");
1
+ "use strict";
2
2
 
3
- // Get the difficulty level
4
- const args = process.argv.slice(2);
3
+ // NOTE: No top-level require("readline").
4
+ // This file is safe to import in frontend bundlers.
5
5
 
6
6
  function shuffle(array) {
7
- // Fisher-Yates shuffle algorithm
8
7
  for (let i = array.length - 1; i > 0; i--) {
9
8
  const j = Math.floor(Math.random() * (i + 1));
10
9
  [array[i], array[j]] = [array[j], array[i]];
@@ -15,7 +14,7 @@ function shuffle(array) {
15
14
  function generateSudoku(difficulty) {
16
15
  const sudoku = Array.from({ length: 9 }, () => Array(9).fill(0));
17
16
 
18
- // Fill the diagonal blocks (3x3 sub-grids) with valid numbers
17
+ // Fill diagonal blocks
19
18
  for (let i = 0; i < 9; i += 3) {
20
19
  const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
21
20
  shuffle(nums);
@@ -27,119 +26,104 @@ function generateSudoku(difficulty) {
27
26
  }
28
27
 
29
28
  // Solve the puzzle
30
- const solvedSudoku = JSON.parse(JSON.stringify(sudoku)); // Create a deep copy for later use
29
+ const solvedSudoku = JSON.parse(JSON.stringify(sudoku));
31
30
  solveSudoku(solvedSudoku);
32
31
 
33
- // Determine the number of cells to remove based on difficulty level
32
+ // Determine the number of cells to remove
34
33
  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
- );
34
+ switch (difficulty) {
35
+ case "e":
36
+ numToRemove = ensureNumToRemoveInRange(20, 20);
37
+ break;
38
+ case "m":
39
+ numToRemove = ensureNumToRemoveInRange(25, 50);
40
+ break;
41
+ case "h":
42
+ numToRemove = ensureNumToRemoveInRange(30, 60);
43
+ break;
44
+ case "v":
45
+ numToRemove = ensureNumToRemoveInRange(35, 75);
46
+ break;
47
+ default:
48
+ throw new Error(
49
+ 'Invalid difficulty level. Please use "e" for easy, "m" for medium, "h" for hard, or "v" for very hard.'
50
+ );
47
51
  }
48
52
 
49
- // Create a copy of the solved Sudoku to remove cells from
50
53
  const sudokuCopy = JSON.parse(JSON.stringify(solvedSudoku));
51
54
 
52
- // Remove some numbers to create the puzzle
53
55
  for (let i = 0; i < numToRemove; i++) {
54
56
  const row = Math.floor(Math.random() * 9);
55
57
  const col = Math.floor(Math.random() * 9);
56
58
  if (sudokuCopy[row][col] !== 0) {
57
59
  sudokuCopy[row][col] = 0;
58
60
  } else {
59
- i--; // Try again if the cell is already empty
61
+ i--;
60
62
  }
61
63
  }
62
64
 
63
65
  return { sudoku: sudokuCopy, solvedSudoku };
64
66
  }
65
67
 
68
+ function ensureNumToRemoveInRange(lowerBound, upperBound) {
69
+ let numToRemove;
70
+ do {
71
+ numToRemove = Math.floor(Math.random() * lowerBound) + upperBound;
72
+ } while (numToRemove > 81);
73
+ return numToRemove;
74
+ }
75
+
66
76
  function solveSudoku(sudoku) {
67
77
  const emptyCell = findEmptyCell(sudoku);
68
- if (!emptyCell) {
69
- return true; // Puzzle solved successfully
70
- }
78
+ if (!emptyCell) return true;
71
79
 
72
80
  const [row, col] = emptyCell;
73
81
  for (let num = 1; num <= 9; num++) {
74
82
  if (isValidMove(sudoku, row, col, num)) {
75
83
  sudoku[row][col] = num;
76
- if (solveSudoku(sudoku)) {
77
- return true;
78
- }
79
- sudoku[row][col] = 0; // Undo the assignment
84
+ if (solveSudoku(sudoku)) return true;
85
+ sudoku[row][col] = 0;
80
86
  }
81
87
  }
82
-
83
- return false; // No solution found, backtrack
88
+ return false;
84
89
  }
85
90
 
86
91
  function findEmptyCell(sudoku) {
87
- // Function to find an empty cell in the Sudoku grid
88
92
  for (let row = 0; row < 9; row++) {
89
93
  for (let col = 0; col < 9; col++) {
90
- if (sudoku[row][col] === 0) {
91
- return [row, col];
92
- }
94
+ if (sudoku[row][col] === 0) return [row, col];
93
95
  }
94
96
  }
95
- return null; // No empty cell found
97
+ return null;
96
98
  }
97
99
 
98
100
  function isValidMove(sudoku, row, col, num) {
99
- // Check if assigning num to the cell at (row, col) is a valid move
100
- // Check row
101
101
  for (let i = 0; i < 9; i++) {
102
- if (sudoku[row][i] === num) {
103
- return false; // Number already exists in the row
104
- }
102
+ if (sudoku[row][i] === num) return false;
105
103
  }
106
-
107
- // Check column
108
104
  for (let i = 0; i < 9; i++) {
109
- if (sudoku[i][col] === num) {
110
- return false; // Number already exists in the column
111
- }
105
+ if (sudoku[i][col] === num) return false;
112
106
  }
113
107
 
114
- // Check 3x3 subgrid
115
108
  const startRow = Math.floor(row / 3) * 3;
116
109
  const startCol = Math.floor(col / 3) * 3;
117
110
  for (let i = 0; i < 3; i++) {
118
111
  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
- }
112
+ if (sudoku[startRow + i][startCol + j] === num) return false;
122
113
  }
123
114
  }
124
-
125
- return true; // Valid move
115
+ return true;
126
116
  }
127
117
 
128
118
  function printSudoku(sudoku) {
129
119
  console.log("Sudoku Puzzle:");
130
120
  console.log("-------------------------");
131
121
  sudoku.forEach((row, rowIndex) => {
132
- if (rowIndex % 3 === 0 && rowIndex !== 0) {
133
- console.log("------|-------|-------");
134
- }
122
+ if (rowIndex % 3 === 0 && rowIndex !== 0) console.log("------|-------|-------");
135
123
  row.forEach((cell, cellIndex) => {
136
- if (cellIndex % 3 === 0 && cellIndex !== 0) {
137
- process.stdout.write("| ");
138
- }
124
+ if (cellIndex % 3 === 0 && cellIndex !== 0) process.stdout.write("| ");
139
125
  process.stdout.write(String(cell || ".") + " ");
140
- if (cellIndex === 8) {
141
- console.log();
142
- }
126
+ if (cellIndex === 8) console.log();
143
127
  });
144
128
  });
145
129
  console.log("-------------------------");
@@ -149,78 +133,82 @@ function printSudokuWithAnswers(sudoku) {
149
133
  console.log("Sudoku Puzzle with Answers:");
150
134
  console.log("-------------------------");
151
135
  sudoku.forEach((row, rowIndex) => {
152
- if (rowIndex % 3 === 0 && rowIndex !== 0) {
153
- console.log("------|-------|-------");
154
- }
136
+ if (rowIndex % 3 === 0 && rowIndex !== 0) console.log("------|-------|-------");
155
137
  row.forEach((cell, cellIndex) => {
156
- if (cellIndex % 3 === 0 && cellIndex !== 0) {
157
- process.stdout.write("| ");
158
- }
138
+ if (cellIndex % 3 === 0 && cellIndex !== 0) process.stdout.write("| ");
159
139
  process.stdout.write(String(cell) + " ");
160
- if (cellIndex === 8) {
161
- console.log();
162
- }
140
+ if (cellIndex === 8) console.log();
163
141
  });
164
142
  });
165
143
  console.log("-------------------------");
166
144
  }
167
145
 
168
- function getHint(sudoku, solvedSudoku) {
146
+ /**
147
+ * Browser-friendly hint:
148
+ * - same logic (fill a random empty cell)
149
+ * - DOES NOT console.log by default
150
+ * - Returns {row,col,value} or null
151
+ *
152
+ * If you want old behavior (printing), pass { log: true }.
153
+ */
154
+ function getHint(sudoku, solvedSudoku, opts = {}) {
169
155
  const emptyCells = [];
170
156
  for (let i = 0; i < 9; i++) {
171
157
  for (let j = 0; j < 9; j++) {
172
- if (sudoku[i][j] === 0) {
173
- emptyCells.push([i, j]);
174
- }
158
+ if (sudoku[i][j] === 0) emptyCells.push([i, j]);
175
159
  }
176
160
  }
177
161
  if (emptyCells.length === 0) {
178
- console.log("No empty cells left to provide a hint.");
179
- return;
162
+ if (opts.log) console.log("No empty cells left to provide a hint.");
163
+ return null;
180
164
  }
165
+
181
166
  const randomIndex = Math.floor(Math.random() * emptyCells.length);
182
167
  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
- );
168
+ const value = solvedSudoku[row][col];
169
+ sudoku[row][col] = value;
170
+
171
+ if (opts.log) {
172
+ console.log(`Hint: The number at row ${row + 1}, column ${col + 1} is ${value}`);
173
+ }
174
+
175
+ return { row, col, value };
189
176
  }
190
177
 
178
+ /**
179
+ * Terminal-only function. Safe for bundlers because it loads readline dynamically.
180
+ * If you call it in the browser, it throws a clean error.
181
+ */
191
182
  function waitForHint(sudoku, solvedSudoku) {
183
+ if (typeof process === "undefined" || !process.stdin) {
184
+ throw new Error("waitForHint() is terminal-only (Node.js). Use getHint() in the browser.");
185
+ }
186
+
187
+ // Lazy require so browser builds don't crash
188
+ // eslint-disable-next-line global-require
189
+ const readline = require("readline");
190
+
192
191
  const rl = readline.createInterface({
193
192
  input: process.stdin,
194
193
  output: process.stdout,
195
194
  });
196
195
 
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);
202
- printSudoku(sudoku);
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
196
+ rl.question("Press 'h' for a hint or 'c' for the complete solution: ", (answer) => {
197
+ const a = answer.trim().toLowerCase();
198
+ if (a === "h") {
199
+ getHint(sudoku, solvedSudoku, { log: true });
200
+ printSudoku(sudoku);
201
+ } else if (a === "c") {
202
+ printSudokuWithAnswers(solvedSudoku);
203
+ process.exit(0);
204
+ } else {
205
+ console.log("Invalid input. Please press 'h' for a hint or 'c' for the complete solution.");
213
206
  }
214
- );
207
+ rl.close();
208
+ waitForHint(sudoku, solvedSudoku);
209
+ });
215
210
  }
216
211
 
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);
223
-
224
212
  module.exports = {
225
213
  generateSudoku,
226
214
  printSudoku,