@luckyfoxdesign/sudoku-generator 1.0.3 β 1.1.2
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 +89 -58
- package/dist/index.cjs +9 -2
- package/dist/index.global.js +9 -2
- package/dist/index.js +8 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
# Sudoku Generator
|
|
2
2
|
|
|
3
|
-
A lightweight Sudoku puzzle generator that creates complete solutions and playable puzzles
|
|
3
|
+
A lightweight Sudoku puzzle generator that creates complete solutions and playable puzzles with controlled difficulty. Works in browser and Node.js environments.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@luckyfoxdesign/sudoku-generator) [](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- π² Generates randomized, valid Sudoku puzzles and solutions
|
|
10
|
-
-
|
|
11
|
-
-
|
|
10
|
+
- π― Difficulty-based generation: easy, medium, hard, expert
|
|
11
|
+
- π§© Creates playable puzzles with controlled cell removal
|
|
12
|
+
- β‘ Fast generation using backtracking + constraint solving
|
|
12
13
|
- π Works in browser and Node.js
|
|
13
14
|
- π¦ Zero dependencies
|
|
14
15
|
- π§ Multiple export formats (ESM, CommonJS, IIFE)
|
|
@@ -23,21 +24,18 @@ npm install @luckyfoxdesign/sudoku-generator
|
|
|
23
24
|
## Quick Start
|
|
24
25
|
|
|
25
26
|
```javascript
|
|
26
|
-
import { generateSudokuGrid, generateCompleteSudokuGrid } from '@luckyfoxdesign/sudoku-generator';
|
|
27
|
-
|
|
28
|
-
// Generate a puzzle
|
|
29
|
-
const puzzle =
|
|
30
|
-
console.log(
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
// [[5, 3, 4, 6, 7, 8, 9, 1, 2],
|
|
39
|
-
// [6, 7, 2, 1, 9, 5, 3, 4, 8],
|
|
40
|
-
// ...]
|
|
27
|
+
import { generateSudoku, generateSudokuGrid, generateCompleteSudokuGrid } from '@luckyfoxdesign/sudoku-generator';
|
|
28
|
+
|
|
29
|
+
// Generate a puzzle with controlled difficulty
|
|
30
|
+
const { puzzle, solution, difficulty, score } = generateSudoku('hard');
|
|
31
|
+
console.log(difficulty); // 'hard'
|
|
32
|
+
console.log(score); // 41-80
|
|
33
|
+
// puzzle: [[5, 0, 0, 6, 0, 0, 9, 0, 2], ...] β zeros are empty cells
|
|
34
|
+
// solution: [[5, 3, 4, 6, 7, 8, 9, 1, 2], ...] β complete grid
|
|
35
|
+
|
|
36
|
+
// Or generate a simple puzzle (legacy API)
|
|
37
|
+
const simplePuzzle = generateSudokuGrid();
|
|
38
|
+
// [[5, 0, 4, 6, 0, 8, 9, 0, 2], ...]
|
|
41
39
|
```
|
|
42
40
|
|
|
43
41
|
## Usage
|
|
@@ -53,13 +51,12 @@ console.log(solution);
|
|
|
53
51
|
<body>
|
|
54
52
|
<script src="https://unpkg.com/@luckyfoxdesign/sudoku-generator/dist/index.global.js"></script>
|
|
55
53
|
<script>
|
|
56
|
-
// Generate a puzzle
|
|
57
|
-
const puzzle = Sudoku.
|
|
58
|
-
console.log(
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
console.log(solution);
|
|
54
|
+
// Generate a puzzle with difficulty
|
|
55
|
+
const { puzzle, solution, difficulty } = Sudoku.generateSudoku('medium');
|
|
56
|
+
console.log(difficulty); // 'medium'
|
|
57
|
+
|
|
58
|
+
// Legacy API still works
|
|
59
|
+
const simplePuzzle = Sudoku.generateSudokuGrid();
|
|
63
60
|
</script>
|
|
64
61
|
</body>
|
|
65
62
|
</html>
|
|
@@ -68,14 +65,14 @@ console.log(solution);
|
|
|
68
65
|
### React / Vue / Svelte
|
|
69
66
|
|
|
70
67
|
```javascript
|
|
71
|
-
import {
|
|
68
|
+
import { generateSudoku } from '@luckyfoxdesign/sudoku-generator';
|
|
72
69
|
|
|
73
70
|
function SudokuGame() {
|
|
74
|
-
const [
|
|
75
|
-
|
|
71
|
+
const [{ puzzle, solution }] = useState(() => generateSudoku('medium'));
|
|
72
|
+
|
|
76
73
|
return (
|
|
77
74
|
<div>
|
|
78
|
-
{
|
|
75
|
+
{puzzle.map((row, i) => (
|
|
79
76
|
<div key={i}>
|
|
80
77
|
{row.map((cell, j) => (
|
|
81
78
|
<span key={j}>
|
|
@@ -92,23 +89,48 @@ function SudokuGame() {
|
|
|
92
89
|
### Node.js (ESM)
|
|
93
90
|
|
|
94
91
|
```javascript
|
|
95
|
-
import { generateSudokuGrid, generateCompleteSudokuGrid } from '@luckyfoxdesign/sudoku-generator';
|
|
92
|
+
import { generateSudoku, generateSudokuGrid, generateCompleteSudokuGrid } from '@luckyfoxdesign/sudoku-generator';
|
|
96
93
|
|
|
97
|
-
const puzzle =
|
|
98
|
-
const solution = generateCompleteSudokuGrid();
|
|
94
|
+
const { puzzle, solution, difficulty, score } = generateSudoku('expert');
|
|
99
95
|
```
|
|
100
96
|
|
|
101
97
|
### Node.js (CommonJS)
|
|
102
98
|
|
|
103
99
|
```javascript
|
|
104
|
-
const { generateSudokuGrid, generateCompleteSudokuGrid } = require('@luckyfoxdesign/sudoku-generator');
|
|
100
|
+
const { generateSudoku, generateSudokuGrid, generateCompleteSudokuGrid } = require('@luckyfoxdesign/sudoku-generator');
|
|
105
101
|
|
|
106
|
-
const puzzle =
|
|
107
|
-
const solution = generateCompleteSudokuGrid();
|
|
102
|
+
const { puzzle, solution, difficulty, score } = generateSudoku('hard');
|
|
108
103
|
```
|
|
109
104
|
|
|
110
105
|
## API
|
|
111
106
|
|
|
107
|
+
### `generateSudoku(difficulty?)`
|
|
108
|
+
|
|
109
|
+
Generates a Sudoku puzzle with controlled difficulty. Returns both the puzzle and solution together.
|
|
110
|
+
|
|
111
|
+
**Parameters:**
|
|
112
|
+
- `difficulty` β `'easy' | 'medium' | 'hard' | 'expert'` (default: `'easy'`)
|
|
113
|
+
|
|
114
|
+
**Returns:** `{ puzzle: number[][], solution: number[][], difficulty: string, score: number }`
|
|
115
|
+
|
|
116
|
+
| Difficulty | Score range | Description |
|
|
117
|
+
|------------|-------------|-------------|
|
|
118
|
+
| `easy` | 0β15 | Solved by naked singles only |
|
|
119
|
+
| `medium` | 16β40 | Requires hidden singles |
|
|
120
|
+
| `hard` | 41β80 | Requires more advanced techniques |
|
|
121
|
+
| `expert` | 81+ | Requires complex constraint solving |
|
|
122
|
+
|
|
123
|
+
**Example:**
|
|
124
|
+
```javascript
|
|
125
|
+
const { puzzle, solution, difficulty, score } = generateSudoku('hard');
|
|
126
|
+
console.log(difficulty); // 'hard'
|
|
127
|
+
console.log(score); // e.g. 57
|
|
128
|
+
console.log(puzzle[0]); // [5, 0, 0, 6, 0, 0, 9, 0, 2]
|
|
129
|
+
console.log(solution[0]); // [5, 3, 4, 6, 7, 8, 9, 1, 2]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
112
134
|
### `generateSudokuGrid()`
|
|
113
135
|
|
|
114
136
|
Generates a playable Sudoku puzzle with some cells removed (marked as 0).
|
|
@@ -159,12 +181,21 @@ console.log(grid[0][0].removedValues); // [2, 7]
|
|
|
159
181
|
|
|
160
182
|
## Puzzle Generation Details
|
|
161
183
|
|
|
162
|
-
###
|
|
184
|
+
### Difficulty-based generation (`generateSudoku`)
|
|
163
185
|
|
|
164
|
-
|
|
186
|
+
Puzzles are generated by iteratively removing cells and scoring the result using a constraint-solving engine. The score is determined by the techniques required to solve the puzzle:
|
|
187
|
+
|
|
188
|
+
- **Naked singles** β only one candidate in a cell (low weight)
|
|
189
|
+
- **Hidden singles** β only one cell in a unit can hold a value (medium weight)
|
|
190
|
+
- **More advanced techniques** β pointing pairs, naked/hidden subsets, etc. (higher weights)
|
|
191
|
+
|
|
192
|
+
Cells are removed until the puzzle's score falls within the target difficulty range. If the target cannot be reached within a time limit, the closest available result is returned.
|
|
193
|
+
|
|
194
|
+
### Legacy cell removal strategy (`generateSudokuGrid`)
|
|
195
|
+
|
|
196
|
+
When generating puzzles with the legacy API:
|
|
165
197
|
- **~50% of cells** are randomly removed
|
|
166
198
|
- **Columns 0, 3, and 6** (first column of each 3Γ3 block) are **never removed**
|
|
167
|
-
- This ensures structural integrity and solvability
|
|
168
199
|
|
|
169
200
|
### Example Grid Structure
|
|
170
201
|
|
|
@@ -211,6 +242,7 @@ npm test
|
|
|
211
242
|
|
|
212
243
|
Tests cover:
|
|
213
244
|
- β
Puzzle generation (with empty cells)
|
|
245
|
+
- β
Difficulty-based generation (all 4 levels)
|
|
214
246
|
- β
Complete solution generation (no empty cells)
|
|
215
247
|
- β
Grid structure validation
|
|
216
248
|
- β
Sudoku rules (rows, columns, 3Γ3 blocks)
|
|
@@ -275,23 +307,21 @@ Check the package:
|
|
|
275
307
|
|
|
276
308
|
### Generation Algorithm
|
|
277
309
|
|
|
278
|
-
The generator uses a **backtracking algorithm** to fill the grid:
|
|
279
|
-
|
|
280
310
|
1. **Generate Complete Solution:**
|
|
281
|
-
-
|
|
282
|
-
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
-
|
|
311
|
+
- Uses a backtracking algorithm on an empty 9Γ9 grid
|
|
312
|
+
- Tries random numbers 1β9 per cell; backtracks on conflicts
|
|
313
|
+
- Produces a guaranteed valid, fully filled solution
|
|
314
|
+
|
|
315
|
+
2. **Create Puzzle:**
|
|
316
|
+
- **`generateSudoku(difficulty)`** β removes cells one by one, scores the result using a constraint solver, and stops when the score hits the target range
|
|
317
|
+
- **`generateSudokuGrid()`** β removes ~50% of cells randomly, preserving columns 0, 3, and 6
|
|
288
318
|
|
|
289
|
-
|
|
290
|
-
-
|
|
291
|
-
-
|
|
292
|
-
-
|
|
319
|
+
3. **Difficulty Scoring:**
|
|
320
|
+
- The solver applies human-like techniques (naked singles, hidden singles, pointing pairs, etc.)
|
|
321
|
+
- Each technique has a weight; score = sum of (weight Γ applications)
|
|
322
|
+
- Score determines difficulty: easy (0β15), medium (16β40), hard (41β80), expert (81+)
|
|
293
323
|
|
|
294
|
-
|
|
324
|
+
Every generated puzzle has a unique solution.
|
|
295
325
|
|
|
296
326
|
## Performance
|
|
297
327
|
|
|
@@ -311,19 +341,20 @@ This ensures every generated grid is a complete, valid Sudoku solution, and ever
|
|
|
311
341
|
|
|
312
342
|
### Game Development
|
|
313
343
|
```javascript
|
|
314
|
-
import {
|
|
344
|
+
import { generateSudoku } from '@luckyfoxdesign/sudoku-generator';
|
|
315
345
|
|
|
316
|
-
|
|
317
|
-
const solution =
|
|
346
|
+
// Puzzle and solution in one call, matched by difficulty
|
|
347
|
+
const { puzzle, solution, difficulty } = generateSudoku('medium');
|
|
348
|
+
// Use puzzle for the board, solution for validation
|
|
318
349
|
```
|
|
319
350
|
|
|
320
351
|
### Puzzle Books / Print
|
|
321
352
|
```javascript
|
|
322
|
-
import {
|
|
353
|
+
import { generateSudoku } from '@luckyfoxdesign/sudoku-generator';
|
|
323
354
|
|
|
324
|
-
// Generate 100
|
|
355
|
+
// Generate 100 hard puzzles
|
|
325
356
|
for (let i = 0; i < 100; i++) {
|
|
326
|
-
const puzzle =
|
|
357
|
+
const { puzzle } = generateSudoku('hard');
|
|
327
358
|
printPuzzle(puzzle);
|
|
328
359
|
}
|
|
329
360
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
var
|
|
1
|
+
var z=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var C=(f,t)=>{for(var e in t)z(f,e,{get:t[e],enumerable:!0})},R=(f,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of F(t))!T.call(f,c)&&c!==e&&z(f,c,{get:()=>t[c],enumerable:!(i=B(t,c))||i.enumerable});return f};var U=f=>R(z({},"__esModule",{value:!0}),f);var dt={};C(dt,{generateCompleteSudokuGrid:()=>N,generateSudoku:()=>st,generateSudokuGrid:()=>ot,generateSudokuGridWithMetadata:()=>E,getBlockRange:()=>w});module.exports=U(dt);/**
|
|
2
|
+
* Solver module for Sudoku Generator
|
|
3
|
+
*
|
|
4
|
+
* Sudoku solving techniques β from simple to advanced.
|
|
5
|
+
* Used for puzzle difficulty assessment and controlled generation.
|
|
6
|
+
*
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/function P(f,t,e){if(f[t][e]!==0)return[];let i=new Set;for(let n=0;n<9;n++)f[t][n]!==0&&i.add(f[t][n]);for(let n=0;n<9;n++)f[n][e]!==0&&i.add(f[n][e]);let c=Math.floor(t/3)*3,r=Math.floor(e/3)*3;for(let n=c;n<c+3;n++)for(let s=r;s<r+3;s++)f[n][s]!==0&&i.add(f[n][s]);let o=[];for(let n=1;n<=9;n++)i.has(n)||o.push(n);return o}function H(f){let t=new Map;for(let e=0;e<9;e++)for(let i=0;i<9;i++)f[e][i]===0&&t.set(`${e},${i}`,new Set(P(f,e,i)));return t}function K(f,t,e,i){for(let o=0;o<9;o++){let n=`${t},${o}`,s=f.get(n);s&&s.delete(i)}for(let o=0;o<9;o++){let n=`${o},${e}`,s=f.get(n);s&&s.delete(i)}let c=Math.floor(t/3)*3,r=Math.floor(e/3)*3;for(let o=c;o<c+3;o++)for(let n=r;n<r+3;n++){let s=`${o},${n}`,l=f.get(s);l&&l.delete(i)}}function j(f,t,e,i,c){f[e][i]=c,t.delete(`${e},${i}`),K(t,e,i,c)}function L(f,t){let e=0;for(let[i,c]of t)if(c.size===1){let[r,o]=i.split(",").map(Number),n=[...c][0];j(f,t,r,o,n),e++}return e}function Q(f,t){let e=0,i=y();for(let c of i)for(let r=1;r<=9;r++){let o=null,n=0;for(let[s,l]of c){let u=`${s},${l}`,d=t.get(u);if(d&&d.has(r)&&(o={r:s,c:l,key:u},n++,n>1))break}n===1&&(j(f,t,o.r,o.c,r),e++)}return e}function y(){let f=[];for(let t=0;t<9;t++){let e=[];for(let i=0;i<9;i++)e.push([t,i]);f.push(e)}for(let t=0;t<9;t++){let e=[];for(let i=0;i<9;i++)e.push([i,t]);f.push(e)}for(let t=0;t<9;t+=3)for(let e=0;e<9;e+=3){let i=[];for(let c=t;c<t+3;c++)for(let r=e;r<e+3;r++)i.push([c,r]);f.push(i)}return f}function Y(f,t){let e=0,i=y();for(let c of i){let r=[];for(let[o,n]of c){let s=t.get(`${o},${n}`);s&&s.size===2&&r.push({r:o,c:n,candidates:s})}for(let o=0;o<r.length;o++)for(let n=o+1;n<r.length;n++){let s=r[o],l=r[n];if(s.candidates.size!==l.candidates.size)continue;let u=!0;for(let a of s.candidates)if(!l.candidates.has(a)){u=!1;break}if(!u)continue;let d=[...s.candidates];for(let[a,h]of c){if(a===s.r&&h===s.c||a===l.r&&h===l.c)continue;let g=t.get(`${a},${h}`);if(g)for(let p of d)g.has(p)&&(g.delete(p),e++)}}}return e}function _(f,t){let e=0,i=y();for(let c of i){let r=new Map;for(let n=1;n<=9;n++){let s=[];for(let[l,u]of c){let d=t.get(`${l},${u}`);d&&d.has(n)&&s.push(`${l},${u}`)}s.length===2&&r.set(n,s)}let o=[...r.keys()];for(let n=0;n<o.length;n++)for(let s=n+1;s<o.length;s++){let l=r.get(o[n]),u=r.get(o[s]);if(l[0]!==u[0]||l[1]!==u[1])continue;let d=new Set([o[n],o[s]]);for(let a of l){let h=t.get(a);if(h)for(let g of[...h])d.has(g)||(h.delete(g),e++)}}}return e}function J(f,t){let e=0,i=y();for(let c of i){let r=[];for(let[o,n]of c){let s=t.get(`${o},${n}`);s&&s.size>=2&&s.size<=3&&r.push({r:o,c:n,candidates:s})}for(let o=0;o<r.length;o++)for(let n=o+1;n<r.length;n++)for(let s=n+1;s<r.length;s++){let l=new Set([...r[o].candidates,...r[n].candidates,...r[s].candidates]);if(l.size!==3)continue;let u=new Set([`${r[o].r},${r[o].c}`,`${r[n].r},${r[n].c}`,`${r[s].r},${r[s].c}`]);for(let[d,a]of c){let h=`${d},${a}`;if(u.has(h))continue;let g=t.get(h);if(g)for(let p of l)g.has(p)&&(g.delete(p),e++)}}}return e}function O(f,t){let e=0;for(let i=1;i<=9;i++){let c=[];for(let o=0;o<9;o++){let n=[];for(let s=0;s<9;s++){let l=t.get(`${o},${s}`);l&&l.has(i)&&n.push(s)}n.length===2&&c.push({line:o,positions:n})}for(let o=0;o<c.length;o++)for(let n=o+1;n<c.length;n++){let s=c[o],l=c[n];if(!(s.positions[0]!==l.positions[0]||s.positions[1]!==l.positions[1]))for(let u of s.positions)for(let d=0;d<9;d++){if(d===s.line||d===l.line)continue;let a=t.get(`${d},${u}`);a&&a.has(i)&&(a.delete(i),e++)}}let r=[];for(let o=0;o<9;o++){let n=[];for(let s=0;s<9;s++){let l=t.get(`${s},${o}`);l&&l.has(i)&&n.push(s)}n.length===2&&r.push({line:o,positions:n})}for(let o=0;o<r.length;o++)for(let n=o+1;n<r.length;n++){let s=r[o],l=r[n];if(!(s.positions[0]!==l.positions[0]||s.positions[1]!==l.positions[1]))for(let u of s.positions)for(let d=0;d<9;d++){if(d===s.line||d===l.line)continue;let a=t.get(`${u},${d}`);a&&a.has(i)&&(a.delete(i),e++)}}}return e}function X(f,t){let e=0;for(let i=0;i<9;i+=3)for(let c=0;c<9;c+=3)for(let r=1;r<=9;r++){let o=new Set,n=new Set;for(let s=i;s<i+3;s++)for(let l=c;l<c+3;l++){let u=t.get(`${s},${l}`);u&&u.has(r)&&(o.add(s),n.add(l))}if(o.size===1){let s=[...o][0];for(let l=0;l<9;l++){if(l>=c&&l<c+3)continue;let u=t.get(`${s},${l}`);u&&u.has(r)&&(u.delete(r),e++)}}if(n.size===1){let s=[...n][0];for(let l=0;l<9;l++){if(l>=i&&l<i+3)continue;let u=t.get(`${l},${s}`);u&&u.has(r)&&(u.delete(r),e++)}}}for(let i=0;i<9;i++)for(let c=1;c<=9;c++){let r=new Set;for(let o=0;o<9;o++){let n=t.get(`${i},${o}`);n&&n.has(c)&&r.add(Math.floor(o/3))}if(r.size===1){let o=[...r][0]*3,n=Math.floor(i/3)*3;for(let s=n;s<n+3;s++)if(s!==i)for(let l=o;l<o+3;l++){let u=t.get(`${s},${l}`);u&&u.has(c)&&(u.delete(c),e++)}}}for(let i=0;i<9;i++)for(let c=1;c<=9;c++){let r=new Set;for(let o=0;o<9;o++){let n=t.get(`${o},${i}`);n&&n.has(c)&&r.add(Math.floor(o/3))}if(r.size===1){let o=[...r][0]*3,n=Math.floor(i/3)*3;for(let s=n;s<n+3;s++)if(s!==i)for(let l=o;l<o+3;l++){let u=t.get(`${l},${s}`);u&&u.has(c)&&(u.delete(c),e++)}}}return e}function Z(f,t){let e=0;for(let i=1;i<=9;i++){let c=[];for(let o=0;o<9;o++){let n=[];for(let s=0;s<9;s++){let l=t.get(`${o},${s}`);l&&l.has(i)&&n.push(s)}n.length>=2&&n.length<=3&&c.push({line:o,positions:n})}for(let o=0;o<c.length;o++)for(let n=o+1;n<c.length;n++)for(let s=n+1;s<c.length;s++){let l=new Set([...c[o].positions,...c[n].positions,...c[s].positions]);if(l.size!==3)continue;let u=new Set([c[o].line,c[n].line,c[s].line]);for(let d of l)for(let a=0;a<9;a++){if(u.has(a))continue;let h=t.get(`${a},${d}`);h&&h.has(i)&&(h.delete(i),e++)}}let r=[];for(let o=0;o<9;o++){let n=[];for(let s=0;s<9;s++){let l=t.get(`${s},${o}`);l&&l.has(i)&&n.push(s)}n.length>=2&&n.length<=3&&r.push({line:o,positions:n})}for(let o=0;o<r.length;o++)for(let n=o+1;n<r.length;n++)for(let s=n+1;s<r.length;s++){let l=new Set([...r[o].positions,...r[n].positions,...r[s].positions]);if(l.size!==3)continue;let u=new Set([r[o].line,r[n].line,r[s].line]);for(let d of l)for(let a=0;a<9;a++){if(u.has(a))continue;let h=t.get(`${d},${a}`);h&&h.has(i)&&(h.delete(i),e++)}}}return e}function G(f,t){let e=0,i=[];for(let[c,r]of t)if(r.size===2){let[o,n]=c.split(",").map(Number);i.push({r:o,c:n,candidates:[...r]})}for(let c of i){let[r,o]=c.candidates,n=i.filter(s=>s!==c&&S(c.r,c.c,s.r,s.c));for(let s of n){if(!s.candidates.includes(r)||s.candidates.includes(o))continue;let l=s.candidates[0]===r?s.candidates[1]:s.candidates[0];for(let u of n)if(u!==s&&!(!u.candidates.includes(o)||!u.candidates.includes(l)))for(let[d,a]of t){if(!a.has(l))continue;let[h,g]=d.split(",").map(Number);h===s.r&&g===s.c||h===u.r&&g===u.c||h===c.r&&g===c.c||S(h,g,s.r,s.c)&&S(h,g,u.r,u.c)&&(a.delete(l),e++)}}}return e}function S(f,t,e,i){return f===e&&t===i?!1:f===e||t===i||Math.floor(f/3)===Math.floor(e/3)&&Math.floor(t/3)===Math.floor(i/3)}function tt(f,t){let e=0,i=[];for(let[c,r]of t)if(r.size===2){let[o,n]=c.split(",").map(Number);i.push({r:o,c:n,candidates:[...r].sort((s,l)=>s-l)})}for(let c=0;c<i.length;c++)for(let r=c+1;r<i.length;r++){let o=i[c],n=i[r];if(o.candidates[0]!==n.candidates[0]||o.candidates[1]!==n.candidates[1]||o.r!==n.r||Math.floor(o.c/3)===Math.floor(n.c/3))continue;let[s,l]=o.candidates;for(let u=0;u<9;u++){if(u===o.r||Math.floor(u/3)===Math.floor(o.r/3))continue;let d=`${u},${o.c}`,a=`${u},${n.c}`,h=t.get(d),g=t.get(a);!h||!g||!h.has(s)||!h.has(l)||!g.has(s)||!g.has(l)||(h.size===2&&g.size>2?(g.delete(s),g.delete(l),e+=2):g.size===2&&h.size>2&&(h.delete(s),h.delete(l),e+=2))}}return e}var I=[{name:"nakedSingle",fn:L,weight:1,type:"place"},{name:"hiddenSingle",fn:Q,weight:2,type:"place"},{name:"nakedPair",fn:Y,weight:5,type:"eliminate"},{name:"hiddenPair",fn:_,weight:7,type:"eliminate"},{name:"nakedTriple",fn:J,weight:8,type:"eliminate"},{name:"pointingPairs",fn:X,weight:6,type:"eliminate"},{name:"xWing",fn:O,weight:15,type:"eliminate"},{name:"swordfish",fn:Z,weight:20,type:"eliminate"},{name:"xyWing",fn:G,weight:18,type:"eliminate"},{name:"uniqueRectangle",fn:tt,weight:20,type:"eliminate"}];function x(f){let t=H(f),e=[];for(;t.size>0;){let i=!1;for(let c of I){let r=c.fn(f,t);if(r>0){e.push({technique:c.name,count:r}),i=!0;break}}if(!i)break}return{solved:t.size===0,steps:e,grid:f}}function et(f){let t=f.map(c=>[...c]),e=0;function i(c){if(e>=2)return;for(;c<81&&t[Math.floor(c/9)][c%9]!==0;)c++;if(c===81){e++;return}let r=Math.floor(c/9),o=c%9,n=P(t,r,o);for(let s of n)if(t[r][o]=s,i(c+1),e>=2)return;t[r][o]=0}return i(0),e}var k={easy:{min:0,max:15},medium:{min:16,max:40},hard:{min:41,max:80},expert:{min:81,max:1/0}};function V(f){let t=0,e={};for(let i of I)e[i.name]=i.weight;for(let i of f)t+=(e[i.technique]||0)*i.count;return t}function v(f){return f<=k.easy.max?"easy":f<=k.medium.max?"medium":f<=k.hard.max?"hard":"expert"}function M(f,t,e={}){let{maxAttempts:i=200}=e,c=k[t],r=f.map(l=>[...l]),o=[];for(let l=0;l<9;l++)for(let u=0;u<9;u++)o.push([l,u]);nt(o);let n=new Set,s=null;for(let l=0;l<i;l++){let u=!1;for(let d=0;d<o.length;d++){let[a,h]=o[d],g=`${a},${h}`;if(r[a][h]===0||n.has(g))continue;let p=r[a][h];if(r[a][h]=0,et(r)!==1){r[a][h]=p,n.add(g);continue}let D=r.map($=>[...$]),b=x(D);if(!b.solved){r[a][h]=p,n.add(g);continue}let m=V(b.steps),W=v(m);if(m>=c.min&&m<=c.max&&(s={puzzle:r.map($=>[...$]),solution:f.map($=>[...$]),difficulty:W,score:m,steps:b.steps}),m>c.max){r[a][h]=p,n.add(g);continue}u=!0;break}if(!u)break}if(!s){let l=r.map(a=>[...a]),u=x(l),d=V(u.steps);s={puzzle:r.map(a=>[...a]),solution:f.map(a=>[...a]),difficulty:v(d),score:d,steps:u.steps}}return s}function nt(f){for(let t=f.length-1;t>0;t--){let e=Math.floor(Math.random()*(t+1));[f[t],f[e]]=[f[e],f[t]]}}/**
|
|
2
9
|
* @luckyfoxdesign/sudoku-generator
|
|
3
10
|
* Generates complete, valid 9x9 Sudoku grids using backtracking algorithm
|
|
4
11
|
*
|
|
5
12
|
* @license MIT
|
|
6
13
|
* @author Lucky Fox Design <luckyfoxinthebox@gmail.com>
|
|
7
14
|
* @see https://github.com/luckyfoxdesign/sudoku-generator
|
|
8
|
-
*/function
|
|
15
|
+
*/function ot(){return E().map(t=>t.map(e=>e.chosenValue))}function st(f="easy"){let t=N(),e=M(t,f);return{puzzle:e.puzzle,solution:e.solution,difficulty:e.difficulty,score:e.score}}function N(){let f=q();for(let t=0;t<81;t++){let e=Math.floor(t/9),i=t%9;A(e,i,f)===0&&(t-=2,t<-1&&(t=-1))}return f.map(t=>t.map(e=>e.chosenValue))}function E(){let f=q();for(let t=0;t<81;t++){let e=Math.floor(t/9),i=t%9;A(e,i,f)===0&&(t-=2,t<-1&&(t=-1))}return ft(f),f}function q(){let f=[];for(let t=0;t<9;t++){f[t]=[];for(let e=0;e<9;e++)f[t][e]={chosenValue:0,removedValues:[],gameSet:new Set([1,2,3,4,5,6,7,8,9])}}return f}function it(){return Math.random()<.5}function ft(f){for(let t=0;t<f.length;t++)for(let e=0;e<f[t].length;e++)it()&&e%3!==0&&(f[t][e].chosenValue=0)}function A(f,t,e){for(;;){if(ht(f,t,e))return at(f,t,e),0;let i=ut(f,t,e);if(!ct(i,f,t,e)&&!rt(i,f,t,e)&&!lt(i,f,t,e))return 1}}function ct(f,t,e,i){for(let c=0;c<e;c++)if(i[t][c].chosenValue===f)return!0;return!1}function rt(f,t,e,i){for(let c=0;c<t;c++)if(i[c][e].chosenValue===f)return!0;return!1}function lt(f,t,e,i){let c=w(t),r=w(e);for(let o of c)for(let n of r)if(!(o>t||o===t&&n>=e)&&i[o][n].chosenValue===f)return!0;return!1}function w(f){return f>=0&&f<=2?[0,1,2]:f>=3&&f<=5?[3,4,5]:[6,7,8]}function ut(f,t,e){let i=[...e[f][t].gameSet],c=Math.floor(Math.random()*i.length),r=i[c];return e[f][t].gameSet.delete(r),e[f][t].removedValues.push(r),e[f][t].chosenValue=r,r}function at(f,t,e){e[f][t].removedValues.forEach(i=>{e[f][t].gameSet.add(i)}),e[f][t].removedValues.length=0,e[f][t].chosenValue=0}function ht(f,t,e){return e[f][t].gameSet.size===0&&e[f][t].removedValues.length===9}0&&(module.exports={generateCompleteSudokuGrid,generateSudoku,generateSudokuGrid,generateSudokuGridWithMetadata,getBlockRange});
|
package/dist/index.global.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
var Sudoku=(()=>{var
|
|
1
|
+
var Sudoku=(()=>{var z=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var C=(f,t)=>{for(var e in t)z(f,e,{get:t[e],enumerable:!0})},R=(f,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of F(t))!T.call(f,c)&&c!==e&&z(f,c,{get:()=>t[c],enumerable:!(i=B(t,c))||i.enumerable});return f};var U=f=>R(z({},"__esModule",{value:!0}),f);var dt={};C(dt,{generateCompleteSudokuGrid:()=>N,generateSudoku:()=>st,generateSudokuGrid:()=>ot,generateSudokuGridWithMetadata:()=>E,getBlockRange:()=>w});/**
|
|
2
|
+
* Solver module for Sudoku Generator
|
|
3
|
+
*
|
|
4
|
+
* Sudoku solving techniques β from simple to advanced.
|
|
5
|
+
* Used for puzzle difficulty assessment and controlled generation.
|
|
6
|
+
*
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/function P(f,t,e){if(f[t][e]!==0)return[];let i=new Set;for(let n=0;n<9;n++)f[t][n]!==0&&i.add(f[t][n]);for(let n=0;n<9;n++)f[n][e]!==0&&i.add(f[n][e]);let c=Math.floor(t/3)*3,r=Math.floor(e/3)*3;for(let n=c;n<c+3;n++)for(let s=r;s<r+3;s++)f[n][s]!==0&&i.add(f[n][s]);let o=[];for(let n=1;n<=9;n++)i.has(n)||o.push(n);return o}function H(f){let t=new Map;for(let e=0;e<9;e++)for(let i=0;i<9;i++)f[e][i]===0&&t.set(`${e},${i}`,new Set(P(f,e,i)));return t}function K(f,t,e,i){for(let o=0;o<9;o++){let n=`${t},${o}`,s=f.get(n);s&&s.delete(i)}for(let o=0;o<9;o++){let n=`${o},${e}`,s=f.get(n);s&&s.delete(i)}let c=Math.floor(t/3)*3,r=Math.floor(e/3)*3;for(let o=c;o<c+3;o++)for(let n=r;n<r+3;n++){let s=`${o},${n}`,l=f.get(s);l&&l.delete(i)}}function j(f,t,e,i,c){f[e][i]=c,t.delete(`${e},${i}`),K(t,e,i,c)}function L(f,t){let e=0;for(let[i,c]of t)if(c.size===1){let[r,o]=i.split(",").map(Number),n=[...c][0];j(f,t,r,o,n),e++}return e}function Q(f,t){let e=0,i=y();for(let c of i)for(let r=1;r<=9;r++){let o=null,n=0;for(let[s,l]of c){let u=`${s},${l}`,d=t.get(u);if(d&&d.has(r)&&(o={r:s,c:l,key:u},n++,n>1))break}n===1&&(j(f,t,o.r,o.c,r),e++)}return e}function y(){let f=[];for(let t=0;t<9;t++){let e=[];for(let i=0;i<9;i++)e.push([t,i]);f.push(e)}for(let t=0;t<9;t++){let e=[];for(let i=0;i<9;i++)e.push([i,t]);f.push(e)}for(let t=0;t<9;t+=3)for(let e=0;e<9;e+=3){let i=[];for(let c=t;c<t+3;c++)for(let r=e;r<e+3;r++)i.push([c,r]);f.push(i)}return f}function Y(f,t){let e=0,i=y();for(let c of i){let r=[];for(let[o,n]of c){let s=t.get(`${o},${n}`);s&&s.size===2&&r.push({r:o,c:n,candidates:s})}for(let o=0;o<r.length;o++)for(let n=o+1;n<r.length;n++){let s=r[o],l=r[n];if(s.candidates.size!==l.candidates.size)continue;let u=!0;for(let a of s.candidates)if(!l.candidates.has(a)){u=!1;break}if(!u)continue;let d=[...s.candidates];for(let[a,h]of c){if(a===s.r&&h===s.c||a===l.r&&h===l.c)continue;let g=t.get(`${a},${h}`);if(g)for(let p of d)g.has(p)&&(g.delete(p),e++)}}}return e}function _(f,t){let e=0,i=y();for(let c of i){let r=new Map;for(let n=1;n<=9;n++){let s=[];for(let[l,u]of c){let d=t.get(`${l},${u}`);d&&d.has(n)&&s.push(`${l},${u}`)}s.length===2&&r.set(n,s)}let o=[...r.keys()];for(let n=0;n<o.length;n++)for(let s=n+1;s<o.length;s++){let l=r.get(o[n]),u=r.get(o[s]);if(l[0]!==u[0]||l[1]!==u[1])continue;let d=new Set([o[n],o[s]]);for(let a of l){let h=t.get(a);if(h)for(let g of[...h])d.has(g)||(h.delete(g),e++)}}}return e}function J(f,t){let e=0,i=y();for(let c of i){let r=[];for(let[o,n]of c){let s=t.get(`${o},${n}`);s&&s.size>=2&&s.size<=3&&r.push({r:o,c:n,candidates:s})}for(let o=0;o<r.length;o++)for(let n=o+1;n<r.length;n++)for(let s=n+1;s<r.length;s++){let l=new Set([...r[o].candidates,...r[n].candidates,...r[s].candidates]);if(l.size!==3)continue;let u=new Set([`${r[o].r},${r[o].c}`,`${r[n].r},${r[n].c}`,`${r[s].r},${r[s].c}`]);for(let[d,a]of c){let h=`${d},${a}`;if(u.has(h))continue;let g=t.get(h);if(g)for(let p of l)g.has(p)&&(g.delete(p),e++)}}}return e}function O(f,t){let e=0;for(let i=1;i<=9;i++){let c=[];for(let o=0;o<9;o++){let n=[];for(let s=0;s<9;s++){let l=t.get(`${o},${s}`);l&&l.has(i)&&n.push(s)}n.length===2&&c.push({line:o,positions:n})}for(let o=0;o<c.length;o++)for(let n=o+1;n<c.length;n++){let s=c[o],l=c[n];if(!(s.positions[0]!==l.positions[0]||s.positions[1]!==l.positions[1]))for(let u of s.positions)for(let d=0;d<9;d++){if(d===s.line||d===l.line)continue;let a=t.get(`${d},${u}`);a&&a.has(i)&&(a.delete(i),e++)}}let r=[];for(let o=0;o<9;o++){let n=[];for(let s=0;s<9;s++){let l=t.get(`${s},${o}`);l&&l.has(i)&&n.push(s)}n.length===2&&r.push({line:o,positions:n})}for(let o=0;o<r.length;o++)for(let n=o+1;n<r.length;n++){let s=r[o],l=r[n];if(!(s.positions[0]!==l.positions[0]||s.positions[1]!==l.positions[1]))for(let u of s.positions)for(let d=0;d<9;d++){if(d===s.line||d===l.line)continue;let a=t.get(`${u},${d}`);a&&a.has(i)&&(a.delete(i),e++)}}}return e}function X(f,t){let e=0;for(let i=0;i<9;i+=3)for(let c=0;c<9;c+=3)for(let r=1;r<=9;r++){let o=new Set,n=new Set;for(let s=i;s<i+3;s++)for(let l=c;l<c+3;l++){let u=t.get(`${s},${l}`);u&&u.has(r)&&(o.add(s),n.add(l))}if(o.size===1){let s=[...o][0];for(let l=0;l<9;l++){if(l>=c&&l<c+3)continue;let u=t.get(`${s},${l}`);u&&u.has(r)&&(u.delete(r),e++)}}if(n.size===1){let s=[...n][0];for(let l=0;l<9;l++){if(l>=i&&l<i+3)continue;let u=t.get(`${l},${s}`);u&&u.has(r)&&(u.delete(r),e++)}}}for(let i=0;i<9;i++)for(let c=1;c<=9;c++){let r=new Set;for(let o=0;o<9;o++){let n=t.get(`${i},${o}`);n&&n.has(c)&&r.add(Math.floor(o/3))}if(r.size===1){let o=[...r][0]*3,n=Math.floor(i/3)*3;for(let s=n;s<n+3;s++)if(s!==i)for(let l=o;l<o+3;l++){let u=t.get(`${s},${l}`);u&&u.has(c)&&(u.delete(c),e++)}}}for(let i=0;i<9;i++)for(let c=1;c<=9;c++){let r=new Set;for(let o=0;o<9;o++){let n=t.get(`${o},${i}`);n&&n.has(c)&&r.add(Math.floor(o/3))}if(r.size===1){let o=[...r][0]*3,n=Math.floor(i/3)*3;for(let s=n;s<n+3;s++)if(s!==i)for(let l=o;l<o+3;l++){let u=t.get(`${l},${s}`);u&&u.has(c)&&(u.delete(c),e++)}}}return e}function Z(f,t){let e=0;for(let i=1;i<=9;i++){let c=[];for(let o=0;o<9;o++){let n=[];for(let s=0;s<9;s++){let l=t.get(`${o},${s}`);l&&l.has(i)&&n.push(s)}n.length>=2&&n.length<=3&&c.push({line:o,positions:n})}for(let o=0;o<c.length;o++)for(let n=o+1;n<c.length;n++)for(let s=n+1;s<c.length;s++){let l=new Set([...c[o].positions,...c[n].positions,...c[s].positions]);if(l.size!==3)continue;let u=new Set([c[o].line,c[n].line,c[s].line]);for(let d of l)for(let a=0;a<9;a++){if(u.has(a))continue;let h=t.get(`${a},${d}`);h&&h.has(i)&&(h.delete(i),e++)}}let r=[];for(let o=0;o<9;o++){let n=[];for(let s=0;s<9;s++){let l=t.get(`${s},${o}`);l&&l.has(i)&&n.push(s)}n.length>=2&&n.length<=3&&r.push({line:o,positions:n})}for(let o=0;o<r.length;o++)for(let n=o+1;n<r.length;n++)for(let s=n+1;s<r.length;s++){let l=new Set([...r[o].positions,...r[n].positions,...r[s].positions]);if(l.size!==3)continue;let u=new Set([r[o].line,r[n].line,r[s].line]);for(let d of l)for(let a=0;a<9;a++){if(u.has(a))continue;let h=t.get(`${d},${a}`);h&&h.has(i)&&(h.delete(i),e++)}}}return e}function G(f,t){let e=0,i=[];for(let[c,r]of t)if(r.size===2){let[o,n]=c.split(",").map(Number);i.push({r:o,c:n,candidates:[...r]})}for(let c of i){let[r,o]=c.candidates,n=i.filter(s=>s!==c&&S(c.r,c.c,s.r,s.c));for(let s of n){if(!s.candidates.includes(r)||s.candidates.includes(o))continue;let l=s.candidates[0]===r?s.candidates[1]:s.candidates[0];for(let u of n)if(u!==s&&!(!u.candidates.includes(o)||!u.candidates.includes(l)))for(let[d,a]of t){if(!a.has(l))continue;let[h,g]=d.split(",").map(Number);h===s.r&&g===s.c||h===u.r&&g===u.c||h===c.r&&g===c.c||S(h,g,s.r,s.c)&&S(h,g,u.r,u.c)&&(a.delete(l),e++)}}}return e}function S(f,t,e,i){return f===e&&t===i?!1:f===e||t===i||Math.floor(f/3)===Math.floor(e/3)&&Math.floor(t/3)===Math.floor(i/3)}function tt(f,t){let e=0,i=[];for(let[c,r]of t)if(r.size===2){let[o,n]=c.split(",").map(Number);i.push({r:o,c:n,candidates:[...r].sort((s,l)=>s-l)})}for(let c=0;c<i.length;c++)for(let r=c+1;r<i.length;r++){let o=i[c],n=i[r];if(o.candidates[0]!==n.candidates[0]||o.candidates[1]!==n.candidates[1]||o.r!==n.r||Math.floor(o.c/3)===Math.floor(n.c/3))continue;let[s,l]=o.candidates;for(let u=0;u<9;u++){if(u===o.r||Math.floor(u/3)===Math.floor(o.r/3))continue;let d=`${u},${o.c}`,a=`${u},${n.c}`,h=t.get(d),g=t.get(a);!h||!g||!h.has(s)||!h.has(l)||!g.has(s)||!g.has(l)||(h.size===2&&g.size>2?(g.delete(s),g.delete(l),e+=2):g.size===2&&h.size>2&&(h.delete(s),h.delete(l),e+=2))}}return e}var I=[{name:"nakedSingle",fn:L,weight:1,type:"place"},{name:"hiddenSingle",fn:Q,weight:2,type:"place"},{name:"nakedPair",fn:Y,weight:5,type:"eliminate"},{name:"hiddenPair",fn:_,weight:7,type:"eliminate"},{name:"nakedTriple",fn:J,weight:8,type:"eliminate"},{name:"pointingPairs",fn:X,weight:6,type:"eliminate"},{name:"xWing",fn:O,weight:15,type:"eliminate"},{name:"swordfish",fn:Z,weight:20,type:"eliminate"},{name:"xyWing",fn:G,weight:18,type:"eliminate"},{name:"uniqueRectangle",fn:tt,weight:20,type:"eliminate"}];function x(f){let t=H(f),e=[];for(;t.size>0;){let i=!1;for(let c of I){let r=c.fn(f,t);if(r>0){e.push({technique:c.name,count:r}),i=!0;break}}if(!i)break}return{solved:t.size===0,steps:e,grid:f}}function et(f){let t=f.map(c=>[...c]),e=0;function i(c){if(e>=2)return;for(;c<81&&t[Math.floor(c/9)][c%9]!==0;)c++;if(c===81){e++;return}let r=Math.floor(c/9),o=c%9,n=P(t,r,o);for(let s of n)if(t[r][o]=s,i(c+1),e>=2)return;t[r][o]=0}return i(0),e}var k={easy:{min:0,max:15},medium:{min:16,max:40},hard:{min:41,max:80},expert:{min:81,max:1/0}};function V(f){let t=0,e={};for(let i of I)e[i.name]=i.weight;for(let i of f)t+=(e[i.technique]||0)*i.count;return t}function v(f){return f<=k.easy.max?"easy":f<=k.medium.max?"medium":f<=k.hard.max?"hard":"expert"}function M(f,t,e={}){let{maxAttempts:i=200}=e,c=k[t],r=f.map(l=>[...l]),o=[];for(let l=0;l<9;l++)for(let u=0;u<9;u++)o.push([l,u]);nt(o);let n=new Set,s=null;for(let l=0;l<i;l++){let u=!1;for(let d=0;d<o.length;d++){let[a,h]=o[d],g=`${a},${h}`;if(r[a][h]===0||n.has(g))continue;let p=r[a][h];if(r[a][h]=0,et(r)!==1){r[a][h]=p,n.add(g);continue}let D=r.map($=>[...$]),b=x(D);if(!b.solved){r[a][h]=p,n.add(g);continue}let m=V(b.steps),W=v(m);if(m>=c.min&&m<=c.max&&(s={puzzle:r.map($=>[...$]),solution:f.map($=>[...$]),difficulty:W,score:m,steps:b.steps}),m>c.max){r[a][h]=p,n.add(g);continue}u=!0;break}if(!u)break}if(!s){let l=r.map(a=>[...a]),u=x(l),d=V(u.steps);s={puzzle:r.map(a=>[...a]),solution:f.map(a=>[...a]),difficulty:v(d),score:d,steps:u.steps}}return s}function nt(f){for(let t=f.length-1;t>0;t--){let e=Math.floor(Math.random()*(t+1));[f[t],f[e]]=[f[e],f[t]]}}/**
|
|
2
9
|
* @luckyfoxdesign/sudoku-generator
|
|
3
10
|
* Generates complete, valid 9x9 Sudoku grids using backtracking algorithm
|
|
4
11
|
*
|
|
5
12
|
* @license MIT
|
|
6
13
|
* @author Lucky Fox Design <luckyfoxinthebox@gmail.com>
|
|
7
14
|
* @see https://github.com/luckyfoxdesign/sudoku-generator
|
|
8
|
-
*/function
|
|
15
|
+
*/function ot(){return E().map(t=>t.map(e=>e.chosenValue))}function st(f="easy"){let t=N(),e=M(t,f);return{puzzle:e.puzzle,solution:e.solution,difficulty:e.difficulty,score:e.score}}function N(){let f=q();for(let t=0;t<81;t++){let e=Math.floor(t/9),i=t%9;A(e,i,f)===0&&(t-=2,t<-1&&(t=-1))}return f.map(t=>t.map(e=>e.chosenValue))}function E(){let f=q();for(let t=0;t<81;t++){let e=Math.floor(t/9),i=t%9;A(e,i,f)===0&&(t-=2,t<-1&&(t=-1))}return ft(f),f}function q(){let f=[];for(let t=0;t<9;t++){f[t]=[];for(let e=0;e<9;e++)f[t][e]={chosenValue:0,removedValues:[],gameSet:new Set([1,2,3,4,5,6,7,8,9])}}return f}function it(){return Math.random()<.5}function ft(f){for(let t=0;t<f.length;t++)for(let e=0;e<f[t].length;e++)it()&&e%3!==0&&(f[t][e].chosenValue=0)}function A(f,t,e){for(;;){if(ht(f,t,e))return at(f,t,e),0;let i=ut(f,t,e);if(!ct(i,f,t,e)&&!rt(i,f,t,e)&&!lt(i,f,t,e))return 1}}function ct(f,t,e,i){for(let c=0;c<e;c++)if(i[t][c].chosenValue===f)return!0;return!1}function rt(f,t,e,i){for(let c=0;c<t;c++)if(i[c][e].chosenValue===f)return!0;return!1}function lt(f,t,e,i){let c=w(t),r=w(e);for(let o of c)for(let n of r)if(!(o>t||o===t&&n>=e)&&i[o][n].chosenValue===f)return!0;return!1}function w(f){return f>=0&&f<=2?[0,1,2]:f>=3&&f<=5?[3,4,5]:[6,7,8]}function ut(f,t,e){let i=[...e[f][t].gameSet],c=Math.floor(Math.random()*i.length),r=i[c];return e[f][t].gameSet.delete(r),e[f][t].removedValues.push(r),e[f][t].chosenValue=r,r}function at(f,t,e){e[f][t].removedValues.forEach(i=>{e[f][t].gameSet.add(i)}),e[f][t].removedValues.length=0,e[f][t].chosenValue=0}function ht(f,t,e){return e[f][t].gameSet.size===0&&e[f][t].removedValues.length===9}return U(dt);})();
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Solver module for Sudoku Generator
|
|
3
|
+
*
|
|
4
|
+
* Sudoku solving techniques β from simple to advanced.
|
|
5
|
+
* Used for puzzle difficulty assessment and controlled generation.
|
|
6
|
+
*
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/function V(r,t,o){if(r[t][o]!==0)return[];let i=new Set;for(let e=0;e<9;e++)r[t][e]!==0&&i.add(r[t][e]);for(let e=0;e<9;e++)r[e][o]!==0&&i.add(r[e][o]);let c=Math.floor(t/3)*3,f=Math.floor(o/3)*3;for(let e=c;e<c+3;e++)for(let s=f;s<f+3;s++)r[e][s]!==0&&i.add(r[e][s]);let n=[];for(let e=1;e<=9;e++)i.has(e)||n.push(e);return n}function A(r){let t=new Map;for(let o=0;o<9;o++)for(let i=0;i<9;i++)r[o][i]===0&&t.set(`${o},${i}`,new Set(V(r,o,i)));return t}function D(r,t,o,i){for(let n=0;n<9;n++){let e=`${t},${n}`,s=r.get(e);s&&s.delete(i)}for(let n=0;n<9;n++){let e=`${n},${o}`,s=r.get(e);s&&s.delete(i)}let c=Math.floor(t/3)*3,f=Math.floor(o/3)*3;for(let n=c;n<c+3;n++)for(let e=f;e<f+3;e++){let s=`${n},${e}`,l=r.get(s);l&&l.delete(i)}}function v(r,t,o,i,c){r[o][i]=c,t.delete(`${o},${i}`),D(t,o,i,c)}function W(r,t){let o=0;for(let[i,c]of t)if(c.size===1){let[f,n]=i.split(",").map(Number),e=[...c][0];v(r,t,f,n,e),o++}return o}function B(r,t){let o=0,i=y();for(let c of i)for(let f=1;f<=9;f++){let n=null,e=0;for(let[s,l]of c){let u=`${s},${l}`,d=t.get(u);if(d&&d.has(f)&&(n={r:s,c:l,key:u},e++,e>1))break}e===1&&(v(r,t,n.r,n.c,f),o++)}return o}function y(){let r=[];for(let t=0;t<9;t++){let o=[];for(let i=0;i<9;i++)o.push([t,i]);r.push(o)}for(let t=0;t<9;t++){let o=[];for(let i=0;i<9;i++)o.push([i,t]);r.push(o)}for(let t=0;t<9;t+=3)for(let o=0;o<9;o+=3){let i=[];for(let c=t;c<t+3;c++)for(let f=o;f<o+3;f++)i.push([c,f]);r.push(i)}return r}function F(r,t){let o=0,i=y();for(let c of i){let f=[];for(let[n,e]of c){let s=t.get(`${n},${e}`);s&&s.size===2&&f.push({r:n,c:e,candidates:s})}for(let n=0;n<f.length;n++)for(let e=n+1;e<f.length;e++){let s=f[n],l=f[e];if(s.candidates.size!==l.candidates.size)continue;let u=!0;for(let a of s.candidates)if(!l.candidates.has(a)){u=!1;break}if(!u)continue;let d=[...s.candidates];for(let[a,h]of c){if(a===s.r&&h===s.c||a===l.r&&h===l.c)continue;let g=t.get(`${a},${h}`);if(g)for(let p of d)g.has(p)&&(g.delete(p),o++)}}}return o}function T(r,t){let o=0,i=y();for(let c of i){let f=new Map;for(let e=1;e<=9;e++){let s=[];for(let[l,u]of c){let d=t.get(`${l},${u}`);d&&d.has(e)&&s.push(`${l},${u}`)}s.length===2&&f.set(e,s)}let n=[...f.keys()];for(let e=0;e<n.length;e++)for(let s=e+1;s<n.length;s++){let l=f.get(n[e]),u=f.get(n[s]);if(l[0]!==u[0]||l[1]!==u[1])continue;let d=new Set([n[e],n[s]]);for(let a of l){let h=t.get(a);if(h)for(let g of[...h])d.has(g)||(h.delete(g),o++)}}}return o}function C(r,t){let o=0,i=y();for(let c of i){let f=[];for(let[n,e]of c){let s=t.get(`${n},${e}`);s&&s.size>=2&&s.size<=3&&f.push({r:n,c:e,candidates:s})}for(let n=0;n<f.length;n++)for(let e=n+1;e<f.length;e++)for(let s=e+1;s<f.length;s++){let l=new Set([...f[n].candidates,...f[e].candidates,...f[s].candidates]);if(l.size!==3)continue;let u=new Set([`${f[n].r},${f[n].c}`,`${f[e].r},${f[e].c}`,`${f[s].r},${f[s].c}`]);for(let[d,a]of c){let h=`${d},${a}`;if(u.has(h))continue;let g=t.get(h);if(g)for(let p of l)g.has(p)&&(g.delete(p),o++)}}}return o}function R(r,t){let o=0;for(let i=1;i<=9;i++){let c=[];for(let n=0;n<9;n++){let e=[];for(let s=0;s<9;s++){let l=t.get(`${n},${s}`);l&&l.has(i)&&e.push(s)}e.length===2&&c.push({line:n,positions:e})}for(let n=0;n<c.length;n++)for(let e=n+1;e<c.length;e++){let s=c[n],l=c[e];if(!(s.positions[0]!==l.positions[0]||s.positions[1]!==l.positions[1]))for(let u of s.positions)for(let d=0;d<9;d++){if(d===s.line||d===l.line)continue;let a=t.get(`${d},${u}`);a&&a.has(i)&&(a.delete(i),o++)}}let f=[];for(let n=0;n<9;n++){let e=[];for(let s=0;s<9;s++){let l=t.get(`${s},${n}`);l&&l.has(i)&&e.push(s)}e.length===2&&f.push({line:n,positions:e})}for(let n=0;n<f.length;n++)for(let e=n+1;e<f.length;e++){let s=f[n],l=f[e];if(!(s.positions[0]!==l.positions[0]||s.positions[1]!==l.positions[1]))for(let u of s.positions)for(let d=0;d<9;d++){if(d===s.line||d===l.line)continue;let a=t.get(`${u},${d}`);a&&a.has(i)&&(a.delete(i),o++)}}}return o}function U(r,t){let o=0;for(let i=0;i<9;i+=3)for(let c=0;c<9;c+=3)for(let f=1;f<=9;f++){let n=new Set,e=new Set;for(let s=i;s<i+3;s++)for(let l=c;l<c+3;l++){let u=t.get(`${s},${l}`);u&&u.has(f)&&(n.add(s),e.add(l))}if(n.size===1){let s=[...n][0];for(let l=0;l<9;l++){if(l>=c&&l<c+3)continue;let u=t.get(`${s},${l}`);u&&u.has(f)&&(u.delete(f),o++)}}if(e.size===1){let s=[...e][0];for(let l=0;l<9;l++){if(l>=i&&l<i+3)continue;let u=t.get(`${l},${s}`);u&&u.has(f)&&(u.delete(f),o++)}}}for(let i=0;i<9;i++)for(let c=1;c<=9;c++){let f=new Set;for(let n=0;n<9;n++){let e=t.get(`${i},${n}`);e&&e.has(c)&&f.add(Math.floor(n/3))}if(f.size===1){let n=[...f][0]*3,e=Math.floor(i/3)*3;for(let s=e;s<e+3;s++)if(s!==i)for(let l=n;l<n+3;l++){let u=t.get(`${s},${l}`);u&&u.has(c)&&(u.delete(c),o++)}}}for(let i=0;i<9;i++)for(let c=1;c<=9;c++){let f=new Set;for(let n=0;n<9;n++){let e=t.get(`${n},${i}`);e&&e.has(c)&&f.add(Math.floor(n/3))}if(f.size===1){let n=[...f][0]*3,e=Math.floor(i/3)*3;for(let s=e;s<e+3;s++)if(s!==i)for(let l=n;l<n+3;l++){let u=t.get(`${l},${s}`);u&&u.has(c)&&(u.delete(c),o++)}}}return o}function H(r,t){let o=0;for(let i=1;i<=9;i++){let c=[];for(let n=0;n<9;n++){let e=[];for(let s=0;s<9;s++){let l=t.get(`${n},${s}`);l&&l.has(i)&&e.push(s)}e.length>=2&&e.length<=3&&c.push({line:n,positions:e})}for(let n=0;n<c.length;n++)for(let e=n+1;e<c.length;e++)for(let s=e+1;s<c.length;s++){let l=new Set([...c[n].positions,...c[e].positions,...c[s].positions]);if(l.size!==3)continue;let u=new Set([c[n].line,c[e].line,c[s].line]);for(let d of l)for(let a=0;a<9;a++){if(u.has(a))continue;let h=t.get(`${a},${d}`);h&&h.has(i)&&(h.delete(i),o++)}}let f=[];for(let n=0;n<9;n++){let e=[];for(let s=0;s<9;s++){let l=t.get(`${s},${n}`);l&&l.has(i)&&e.push(s)}e.length>=2&&e.length<=3&&f.push({line:n,positions:e})}for(let n=0;n<f.length;n++)for(let e=n+1;e<f.length;e++)for(let s=e+1;s<f.length;s++){let l=new Set([...f[n].positions,...f[e].positions,...f[s].positions]);if(l.size!==3)continue;let u=new Set([f[n].line,f[e].line,f[s].line]);for(let d of l)for(let a=0;a<9;a++){if(u.has(a))continue;let h=t.get(`${d},${a}`);h&&h.has(i)&&(h.delete(i),o++)}}}return o}function K(r,t){let o=0,i=[];for(let[c,f]of t)if(f.size===2){let[n,e]=c.split(",").map(Number);i.push({r:n,c:e,candidates:[...f]})}for(let c of i){let[f,n]=c.candidates,e=i.filter(s=>s!==c&&z(c.r,c.c,s.r,s.c));for(let s of e){if(!s.candidates.includes(f)||s.candidates.includes(n))continue;let l=s.candidates[0]===f?s.candidates[1]:s.candidates[0];for(let u of e)if(u!==s&&!(!u.candidates.includes(n)||!u.candidates.includes(l)))for(let[d,a]of t){if(!a.has(l))continue;let[h,g]=d.split(",").map(Number);h===s.r&&g===s.c||h===u.r&&g===u.c||h===c.r&&g===c.c||z(h,g,s.r,s.c)&&z(h,g,u.r,u.c)&&(a.delete(l),o++)}}}return o}function z(r,t,o,i){return r===o&&t===i?!1:r===o||t===i||Math.floor(r/3)===Math.floor(o/3)&&Math.floor(t/3)===Math.floor(i/3)}function L(r,t){let o=0,i=[];for(let[c,f]of t)if(f.size===2){let[n,e]=c.split(",").map(Number);i.push({r:n,c:e,candidates:[...f].sort((s,l)=>s-l)})}for(let c=0;c<i.length;c++)for(let f=c+1;f<i.length;f++){let n=i[c],e=i[f];if(n.candidates[0]!==e.candidates[0]||n.candidates[1]!==e.candidates[1]||n.r!==e.r||Math.floor(n.c/3)===Math.floor(e.c/3))continue;let[s,l]=n.candidates;for(let u=0;u<9;u++){if(u===n.r||Math.floor(u/3)===Math.floor(n.r/3))continue;let d=`${u},${n.c}`,a=`${u},${e.c}`,h=t.get(d),g=t.get(a);!h||!g||!h.has(s)||!h.has(l)||!g.has(s)||!g.has(l)||(h.size===2&&g.size>2?(g.delete(s),g.delete(l),o+=2):g.size===2&&h.size>2&&(h.delete(s),h.delete(l),o+=2))}}return o}var P=[{name:"nakedSingle",fn:W,weight:1,type:"place"},{name:"hiddenSingle",fn:B,weight:2,type:"place"},{name:"nakedPair",fn:F,weight:5,type:"eliminate"},{name:"hiddenPair",fn:T,weight:7,type:"eliminate"},{name:"nakedTriple",fn:C,weight:8,type:"eliminate"},{name:"pointingPairs",fn:U,weight:6,type:"eliminate"},{name:"xWing",fn:R,weight:15,type:"eliminate"},{name:"swordfish",fn:H,weight:20,type:"eliminate"},{name:"xyWing",fn:K,weight:18,type:"eliminate"},{name:"uniqueRectangle",fn:L,weight:20,type:"eliminate"}];function S(r){let t=A(r),o=[];for(;t.size>0;){let i=!1;for(let c of P){let f=c.fn(r,t);if(f>0){o.push({technique:c.name,count:f}),i=!0;break}}if(!i)break}return{solved:t.size===0,steps:o,grid:r}}function Q(r){let t=r.map(c=>[...c]),o=0;function i(c){if(o>=2)return;for(;c<81&&t[Math.floor(c/9)][c%9]!==0;)c++;if(c===81){o++;return}let f=Math.floor(c/9),n=c%9,e=V(t,f,n);for(let s of e)if(t[f][n]=s,i(c+1),o>=2)return;t[f][n]=0}return i(0),o}var k={easy:{min:0,max:15},medium:{min:16,max:40},hard:{min:41,max:80},expert:{min:81,max:1/0}};function w(r){let t=0,o={};for(let i of P)o[i.name]=i.weight;for(let i of r)t+=(o[i.technique]||0)*i.count;return t}function x(r){return r<=k.easy.max?"easy":r<=k.medium.max?"medium":r<=k.hard.max?"hard":"expert"}function j(r,t,o={}){let{maxAttempts:i=200}=o,c=k[t],f=r.map(l=>[...l]),n=[];for(let l=0;l<9;l++)for(let u=0;u<9;u++)n.push([l,u]);Y(n);let e=new Set,s=null;for(let l=0;l<i;l++){let u=!1;for(let d=0;d<n.length;d++){let[a,h]=n[d],g=`${a},${h}`;if(f[a][h]===0||e.has(g))continue;let p=f[a][h];if(f[a][h]=0,Q(f)!==1){f[a][h]=p,e.add(g);continue}let E=f.map($=>[...$]),b=S(E);if(!b.solved){f[a][h]=p,e.add(g);continue}let m=w(b.steps),q=x(m);if(m>=c.min&&m<=c.max&&(s={puzzle:f.map($=>[...$]),solution:r.map($=>[...$]),difficulty:q,score:m,steps:b.steps}),m>c.max){f[a][h]=p,e.add(g);continue}u=!0;break}if(!u)break}if(!s){let l=f.map(a=>[...a]),u=S(l),d=w(u.steps);s={puzzle:f.map(a=>[...a]),solution:r.map(a=>[...a]),difficulty:x(d),score:d,steps:u.steps}}return s}function Y(r){for(let t=r.length-1;t>0;t--){let o=Math.floor(Math.random()*(t+1));[r[t],r[o]]=[r[o],r[t]]}}/**
|
|
2
9
|
* @luckyfoxdesign/sudoku-generator
|
|
3
10
|
* Generates complete, valid 9x9 Sudoku grids using backtracking algorithm
|
|
4
11
|
*
|
|
5
12
|
* @license MIT
|
|
6
13
|
* @author Lucky Fox Design <luckyfoxinthebox@gmail.com>
|
|
7
14
|
* @see https://github.com/luckyfoxdesign/sudoku-generator
|
|
8
|
-
*/function
|
|
15
|
+
*/function lt(){return J().map(t=>t.map(o=>o.chosenValue))}function ut(r="easy"){let t=_(),o=j(t,r);return{puzzle:o.puzzle,solution:o.solution,difficulty:o.difficulty,score:o.score}}function _(){let r=M();for(let t=0;t<81;t++){let o=Math.floor(t/9),i=t%9;N(o,i,r)===0&&(t-=2,t<-1&&(t=-1))}return r.map(t=>t.map(o=>o.chosenValue))}function J(){let r=M();for(let t=0;t<81;t++){let o=Math.floor(t/9),i=t%9;N(o,i,r)===0&&(t-=2,t<-1&&(t=-1))}return X(r),r}function M(){let r=[];for(let t=0;t<9;t++){r[t]=[];for(let o=0;o<9;o++)r[t][o]={chosenValue:0,removedValues:[],gameSet:new Set([1,2,3,4,5,6,7,8,9])}}return r}function O(){return Math.random()<.5}function X(r){for(let t=0;t<r.length;t++)for(let o=0;o<r[t].length;o++)O()&&o%3!==0&&(r[t][o].chosenValue=0)}function N(r,t,o){for(;;){if(ot(r,t,o))return nt(r,t,o),0;let i=et(r,t,o);if(!Z(i,r,t,o)&&!G(i,r,t,o)&&!tt(i,r,t,o))return 1}}function Z(r,t,o,i){for(let c=0;c<o;c++)if(i[t][c].chosenValue===r)return!0;return!1}function G(r,t,o,i){for(let c=0;c<t;c++)if(i[c][o].chosenValue===r)return!0;return!1}function tt(r,t,o,i){let c=I(t),f=I(o);for(let n of c)for(let e of f)if(!(n>t||n===t&&e>=o)&&i[n][e].chosenValue===r)return!0;return!1}function I(r){return r>=0&&r<=2?[0,1,2]:r>=3&&r<=5?[3,4,5]:[6,7,8]}function et(r,t,o){let i=[...o[r][t].gameSet],c=Math.floor(Math.random()*i.length),f=i[c];return o[r][t].gameSet.delete(f),o[r][t].removedValues.push(f),o[r][t].chosenValue=f,f}function nt(r,t,o){o[r][t].removedValues.forEach(i=>{o[r][t].gameSet.add(i)}),o[r][t].removedValues.length=0,o[r][t].chosenValue=0}function ot(r,t,o){return o[r][t].gameSet.size===0&&o[r][t].removedValues.length===9}export{_ as generateCompleteSudokuGrid,ut as generateSudoku,lt as generateSudokuGrid,J as generateSudokuGridWithMetadata,I as getBlockRange};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luckyfoxdesign/sudoku-generator",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "A lightweight Sudoku puzzle generator
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "A lightweight Sudoku puzzle generator with difficulty control (easyβexpert). Generates complete solutions and playable puzzles using backtracking and constraint-solving techniques.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sudoku",
|
|
7
7
|
"puzzle",
|