@johnmorrisdotca/kazu 1.2.0 → 1.4.0
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/CHANGELOG.md +34 -0
- package/README.md +69 -27
- package/dist/akari-entry.d.ts +1 -0
- package/dist/akari-entry.js +1 -0
- package/dist/akari.constants.d.ts +12 -0
- package/dist/akari.constants.js +12 -0
- package/dist/akari.types.d.ts +19 -0
- package/dist/akariGenerate.d.ts +10 -5
- package/dist/akariGenerate.js +101 -65
- package/dist/akariLogic.d.ts +10 -0
- package/dist/akariLogic.js +84 -0
- package/dist/akariRate.d.ts +6 -0
- package/dist/akariRate.js +34 -0
- package/dist/akariSolve.d.ts +1 -1
- package/dist/akariSolve.js +8 -96
- package/dist/akariTemplate.d.ts +7 -0
- package/dist/akariTemplate.js +78 -0
- package/dist/cells.d.ts +4 -4
- package/dist/cells.js +6 -6
- package/dist/csp.d.ts +63 -0
- package/dist/csp.js +162 -0
- package/dist/fillomino-entry.d.ts +1 -0
- package/dist/fillomino-entry.js +1 -0
- package/dist/fillomino.constants.d.ts +6 -2
- package/dist/fillomino.constants.js +6 -2
- package/dist/fillomino.types.d.ts +18 -1
- package/dist/fillominoBuild.d.ts +10 -0
- package/dist/fillominoBuild.js +78 -0
- package/dist/fillominoGenerate.d.ts +7 -1
- package/dist/fillominoGenerate.js +75 -107
- package/dist/fillominoLogic.d.ts +14 -0
- package/dist/fillominoLogic.js +177 -0
- package/dist/fillominoMount.js +4 -1
- package/dist/fillominoRate.d.ts +3 -0
- package/dist/fillominoRate.js +57 -0
- package/dist/fillominoSolve.js +15 -88
- package/dist/groupSolve.d.ts +9 -0
- package/dist/groupSolve.js +66 -0
- package/dist/hitori-entry.d.ts +1 -0
- package/dist/hitori-entry.js +1 -0
- package/dist/hitori.constants.d.ts +7 -1
- package/dist/hitori.constants.js +7 -1
- package/dist/hitori.types.d.ts +18 -1
- package/dist/hitoriBoard.js +2 -1
- package/dist/hitoriBuild.d.ts +12 -0
- package/dist/hitoriBuild.js +117 -0
- package/dist/hitoriGenerate.d.ts +10 -3
- package/dist/hitoriGenerate.js +59 -157
- package/dist/hitoriLogic.d.ts +12 -0
- package/dist/hitoriLogic.js +189 -0
- package/dist/hitoriRate.d.ts +3 -0
- package/dist/hitoriRate.js +32 -0
- package/dist/hitoriSolve.d.ts +4 -1
- package/dist/hitoriSolve.js +11 -62
- package/dist/kakuro-entry.d.ts +1 -0
- package/dist/kakuro-entry.js +1 -0
- package/dist/kakuro.constants.d.ts +6 -1
- package/dist/kakuro.constants.js +6 -1
- package/dist/kakuro.types.d.ts +19 -0
- package/dist/kakuroBuild.d.ts +30 -0
- package/dist/kakuroBuild.js +337 -0
- package/dist/kakuroGenerate.d.ts +10 -3
- package/dist/kakuroGenerate.js +54 -122
- package/dist/kakuroLogic.d.ts +13 -0
- package/dist/kakuroLogic.js +153 -0
- package/dist/kakuroRate.d.ts +3 -0
- package/dist/kakuroRate.js +40 -0
- package/dist/kakuroSolve.js +17 -69
- package/dist/kakuroTemplate.d.ts +3 -0
- package/dist/kakuroTemplate.js +130 -0
- package/dist/kinds.js +1 -1
- package/dist/layout.js +1 -0
- package/dist/mount.d.ts +0 -18
- package/dist/mount.js +24 -3
- package/dist/names.js +3 -3
- package/dist/numberPlace.d.ts +1 -1
- package/dist/numberPlace.js +21 -8
- package/dist/shikaku-entry.d.ts +1 -0
- package/dist/shikaku-entry.js +1 -0
- package/dist/shikaku.constants.d.ts +5 -2
- package/dist/shikaku.constants.js +5 -2
- package/dist/shikaku.types.d.ts +18 -1
- package/dist/shikakuBuild.d.ts +21 -0
- package/dist/shikakuBuild.js +135 -0
- package/dist/shikakuGenerate.d.ts +6 -1
- package/dist/shikakuGenerate.js +50 -35
- package/dist/shikakuLogic.d.ts +13 -0
- package/dist/shikakuLogic.js +84 -0
- package/dist/shikakuRate.d.ts +3 -0
- package/dist/shikakuRate.js +27 -0
- package/dist/shikakuSolve.d.ts +1 -1
- package/dist/shikakuSolve.js +23 -44
- package/dist/shikakuTemplate.d.ts +3 -0
- package/dist/shikakuTemplate.js +43 -0
- package/dist/slitherlink-entry.d.ts +1 -0
- package/dist/slitherlink-entry.js +1 -0
- package/dist/slitherlink.constants.d.ts +5 -0
- package/dist/slitherlink.constants.js +5 -0
- package/dist/slitherlink.types.d.ts +18 -0
- package/dist/slitherlinkGenerate.d.ts +10 -3
- package/dist/slitherlinkGenerate.js +161 -80
- package/dist/slitherlinkLogic.d.ts +10 -0
- package/dist/slitherlinkLogic.js +267 -0
- package/dist/slitherlinkRate.d.ts +3 -0
- package/dist/slitherlinkRate.js +26 -0
- package/dist/slitherlinkSolve.d.ts +1 -1
- package/dist/slitherlinkSolve.js +9 -117
- package/dist/slitherlinkTemplate.d.ts +3 -0
- package/dist/slitherlinkTemplate.js +101 -0
- package/dist/strings.js +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,40 @@ All notable changes to this project are written here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.4.0] - 2026-10-05
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **A 25×25 Sudoku, the Colossus.** `number-place` takes a side of 25 (`KAZU_SPECS["number-place"].sizes` is now 4, 6, 9, 16, 25; boxes of 5×5; `mostCells` 625) at `easy`, `medium` and `hard`, every puzzle with exactly one answer. Its symbols go on from the 16×16's: 1 to 9, then A to P for 10 to 25 (`symbolOf`, `valueOfSymbol`, `decodeCells` and the run and note codes read A to P). Easy yields to singles alone (366 numbers printed), medium to one guess (305), hard to two nested guesses (283). The player's pad has 25 keys and the eraser in three rows, and the keyboard types the letters up to P; because N is the number 23 there, Pencil moves to the slash key on this size (`keysColossus` says so in English and Japanese). Made in a browser's time, over seeds 1 to 50 on a Mac: easy a median 13 ms (slowest 30), medium 81 ms (161), hard 308 ms (590). Hard is the one a phone may take a few seconds over, so a page that deals one for a person should do it in a worker, as the demo's slower generators do.
|
|
14
|
+
- A 25×25 is made and checked by proof, not by counting every answer: `provedByGuessing(grid, layout, guesses)` is what a person's reasoning proves (singles, then guesses at the most constrained cell, each shown wrong or finished by singles), and `countKazuSolutions`, `solveKazu` and `kazuGuessDepth` use singles inside the search from the 25×25 up, which takes a medium or hard 25×25 from minutes to a few milliseconds. Every smaller size is searched exactly as before.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Akari never returns the old fixed lattice.** When a level found no random board within its 60 attempts, 1.3.0 returned the fixed rooms of 1.2.0 without saying so: 74% black squares and five full black rows at 14×14, which was about 45% of 14×14 easy seeds (and 17% of 12×12, 35% of 13×13, 65% of 15×15 and 83% of 16×16 easy seeds, and some medium ones at 13×13 and larger). The generator now tries again with more black squares (1.5, 1.8, 2.1 and 2.4 times the usual share, thirty attempts each), which a large board needs to be solvable by its numbers. Every seed that made a random board in 1.3.0 makes the identical one (`src/levels.pins.test.js`, and a comparison over 12×12 to 16×16 at every level); only the seeds that used to return the lattice make a different board, and they make a random one. `src/akariLattice.test.ts` asserts, over every size from 3 to 16 and every level, that no seed returns the lattice, and that the black share and the boards vary. Generation time is unchanged where it was already random (14×14 extra-hard: median 212 ms, slowest 362 ms) and the old lattice cases take a median of 9 ms at 14×14 easy.
|
|
19
|
+
|
|
20
|
+
## [1.3.0] - 2026-10-05
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **Four levels on six grid puzzles.** Shikaku, Akari, Slitherlink, Hitori, Fillomino and Kakuro each make `easy`, `medium`, `hard` and `extra-hard` boards (the level types and `SHIKAKU_LEVELS`, `AKARI_LEVELS`, `SLITHERLINK_LEVELS`, `HITORI_LEVELS`, `FILLOMINO_LEVELS` and `KAKURO_LEVELS`), every one with exactly one answer, and every puzzle now carries its `level`. Easy and medium are solved by plain rules (easy keeps more of its numbers, medium has as few as the rules allow); hard needs supposing something and seeing it break; extra-hard needs the most of that.
|
|
25
|
+
- **More sizes.** `AKARI_SIZES` (5, 7, 10, 14), `SLITHERLINK_SIZES` (5, 7, 10), `HITORI_SIZES` (5, 6, 7, 8, 9, 10, 12), `FILLOMINO_SIZES` (6, 8, 10, 12), `KAKURO_SIZES` (6, 8, 10, 12) and `SHIKAKU_SIZES` (5, 7, 10, 14) list what the demo offers. Hitori takes any side from 4 to 12 (was 5 and 7), Kakuro any side from 5 to 12 (was one size, 10), Fillomino any side from 4 to 12 (was 4 to 8 and 36 squares).
|
|
26
|
+
- **A measured difficulty.** `rateShikaku`, `rateAkari`, `rateSlitherlink`, `rateHitori`, `rateFillomino` and `rateKakuro` solve a board with one answer the way a person does (the rules alone, then supposing one thing, then more) and report how deep that went (`depth`, `probes`) and what the board is made of (numbers, runs, regions, loop length). `docs/LEVELS.md` defines each level per kind and tables the measures and the generation times by size and level; `node scripts/measure-levels.mjs` makes the tables again, and draws a puzzle as text with `--show`.
|
|
27
|
+
- A shared engine (`src/csp.ts`) under the six kinds: counting answers, reasoning with and without supposing, and proving one answer by reasoning when that is enough.
|
|
28
|
+
- `generateAkari`, `generateSlitherlink`, `generateHitori` and `generateKakuro` take the level as a last argument (`generateAkari(width, height, seed, level?)`, `generateSlitherlink(width, height, seed, level?)`, `generateHitori(size, seed, level?)`, `generateKakuro(seed, level?, size?)`), `"medium"` if left out; Shikaku and Fillomino keep `(width, height, level, seed)` and add `extra-hard`.
|
|
29
|
+
- The demo has a Level choice, in English and Japanese, on all six pages, and the sizes above.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- **The six generators make new boards.** Akari, Slitherlink, Hitori and Kakuro used fixed layouts (the Akari rooms, a loop of one or two blocks with nearly every square numbered and most of them 0, three or four shaded squares in Hitori, a 10×10 of 2×2 to 3×3 blocks) and made the same few motifs again and again; they now build random boards: Akari scatters black squares and takes numbers away, Slitherlink grows a winding loop and takes numbers away (few say 0), Hitori shades about a quarter of the squares and repairs the numbers until the answer is single, Kakuro lays runs out row by row and repairs digits, and Shikaku packs interlocking rectangles instead of cutting straight lines. Fillomino's generator is new and no longer slow (a 6×6 took up to 1.7 s). **A seed makes a different puzzle from the one 1.2.0 made** for Shikaku, Akari, Slitherlink, Hitori, Kakuro and Fillomino; a progress code carries its board, so a saved game is unaffected, but a site that keeps a game as kind, size, level and seed will find that seed is another puzzle. The six number puzzles are untouched, and `src/site.fixture.json` still makes all 3,600 of them again, byte for byte.
|
|
34
|
+
- The six solvers count answers with a shared engine that reasons first: a board that the rules (or one supposition) settle is proved with no search at all, and others are searched from what reasoning left. Counts are the same as before on every board the tests compare (exhaustive enumeration of small boards, and the old solvers); only the node counts, and so what a given `nodes` budget reaches, are different, and answers are found in far fewer nodes. `hint*` calls are quicker as a result.
|
|
35
|
+
- If a generator cannot make a board of the level within its attempts it makes one of the next level down, and finally a plain one, rather than throw; the rating of the board shows what it is. On the 20,800 boards of `docs/LEVELS.md` (seeds 1 to 200 at every size and level) this happened on none.
|
|
36
|
+
- Fillomino's number choice in the player offers only numbers a board can hold (up to the biggest given, or the biggest stretch of squares with none), not up to the square count.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- `generateKakuro(97)` threw "No uniquely solvable Kakuro board was proved within the generation budget"; no seed throws now, at any level or size.
|
|
41
|
+
- `solveHitori` could count one shade pattern twice (so a board with two answers could be reported as having three, and a limit could be reached early); it counts each distinct pattern once.
|
|
42
|
+
|
|
9
43
|
## [1.2.0] - 2026-10-05
|
|
10
44
|
|
|
11
45
|
### Added
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<h1 align="center">Kazu <sub>数</sub></h1>
|
|
2
2
|
|
|
3
3
|
<p align="center"><strong>Grid number and logic puzzles for JavaScript and TypeScript.</strong><br>
|
|
4
|
-
Sudoku (4×4 to a
|
|
4
|
+
Sudoku (4×4 to a 25×25 Colossus), Jigsaw, Diagonal and Killer Sudoku, Futoshiki, Skyscrapers, Shikaku, Hitori, Nurikabe, Akari, Juosan, Slitherlink, Masyu, Yajilin, Ripple Effect, Kakuro, Fillomino and Heyawake. Dedicated typed engines, independently checked puzzles, SVG drawing, saved progress, and English and Japanese players for touch, mouse and keyboard. No runtime dependencies.</p>
|
|
5
5
|
|
|
6
6
|
<p align="center">
|
|
7
7
|
<a href="https://github.com/johnmorrisdotca/kazu/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/johnmorrisdotca/kazu/actions/workflows/ci.yml/badge.svg"></a>
|
|
@@ -60,8 +60,9 @@ And in a page, a puzzle to play, by touch, mouse and keyboard, with nothing else
|
|
|
60
60
|
|
|
61
61
|
## Features
|
|
62
62
|
|
|
63
|
-
- **Seven puzzles, three levels.** Sudoku (4×4, 6×6, 9×9
|
|
64
|
-
- **Six number puzzles, three levels.** Sudoku (4×4, 6×6, 9×9
|
|
63
|
+
- **Seven puzzles, three levels.** Sudoku (4×4, 6×6, 9×9, a 16×16 Giant and a 25×25 Colossus), Jigsaw, Diagonal and Killer Sudoku, Futoshiki and Skyscrapers, each at `easy`, `medium` and `hard`, named by kebab-case keys.
|
|
64
|
+
- **Six number puzzles, three levels.** Sudoku (4×4, 6×6, 9×9, a 16×16 Giant and a 25×25 Colossus), Jigsaw, Diagonal and Killer Sudoku, Futoshiki and Skyscrapers, each at `easy`, `medium` and `hard`, named by kebab-case keys. Shikaku and Juosan use dedicated rectangle and territory models.
|
|
65
|
+
- **Four levels on the grid puzzles.** Shikaku, Akari, Slitherlink, Hitori, Fillomino and Kakuro each make `easy`, `medium`, `hard` and `extra-hard` boards, at three or more sizes each, every one with exactly one answer and rated by what a person must do to solve it: [Levels of the grid puzzles](#levels-of-the-grid-puzzles).
|
|
65
66
|
- **Exactly one answer.** A generator makes puzzles from a seed, and a solver that counts answers confirms there is one. The same kind, size, level and seed make the same puzzle in every browser and every Node, for ever.
|
|
66
67
|
- **A check a server can trust.** `checkKazu` reads a finished grid in O(cells), with no search, and says the first thing wrong in words.
|
|
67
68
|
- **A hint that is a reason.** Which cell to fill next, with the rule that says so (a cell with one number left, a number with one place left), never built on a wrong entry.
|
|
@@ -176,7 +177,7 @@ exactly one answer. Every kind is named by a kebab-case key.
|
|
|
176
177
|
|
|
177
178
|
| Key | Called | In Japanese | Sizes | What it prints | What is new |
|
|
178
179
|
| --- | --- | --- | --- | --- | --- |
|
|
179
|
-
| `number-place` | Sudoku | ナンプレ | 4×4, 6×6, 9×9, 16×16 | numbers | every row, column and box holds each number once; the 16×16 Giant uses 1 to 9 and then A to G |
|
|
180
|
+
| `number-place` | Sudoku | ナンプレ | 4×4, 6×6, 9×9, 16×16, 25×25 | numbers | every row, column and box holds each number once; the 16×16 Giant uses 1 to 9 and then A to G, and the 25×25 Colossus goes on to P |
|
|
180
181
|
| `jigsaw` | Jigsaw Sudoku | 変形ナンプレ | 5×5, 6×6, 7×7, 9×9 | numbers, and the regions | the boxes are irregular regions, each joined and none a row or a column |
|
|
181
182
|
| `diagonal` | Diagonal Sudoku | 対角ナンプレ | 6×6, 9×9 | numbers | the two long diagonals hold each number once too |
|
|
182
183
|
| `sum-cages` | Killer Sudoku | サムナンプレ | 6×6, 9×9 | cages and their sums | next to no numbers: dashed cages each add to a sum, and repeat nothing |
|
|
@@ -217,7 +218,7 @@ has always used. It restates the rules rather than reading the solver's mind, so
|
|
|
217
218
|
A solver and a generator never run on a server unless you ask them to.
|
|
218
219
|
|
|
219
220
|
A run is kept as short strings the site's own stored runs decode as they are: `encodeRun(entries)` and
|
|
220
|
-
`decodeRun(code, size)` (one character a cell, `.` for empty, A to
|
|
221
|
+
`decodeRun(code, size)` (one character a cell, `.` for empty, A to P past nine), `encodeSteps` and
|
|
221
222
|
`decodeSteps` for a step log, `kazuHash(givens)` for a fingerprint of a puzzle, and `encodeNotes` and
|
|
222
223
|
`decodeNotes` for Kazu's own pencil marks.
|
|
223
224
|
|
|
@@ -292,8 +293,8 @@ out of the notes of the cells it shares a group with. **Undo** takes the last ch
|
|
|
292
293
|
cell to fill next and why, **Check** says how many cells are wrong, never which. A clock starts on the first entry
|
|
293
294
|
and stops when the last cell is right, and waits while the page is hidden.
|
|
294
295
|
|
|
295
|
-
The keys: the arrows move, a number (1 to 9,
|
|
296
|
-
number writes it as a pencil mark, Backspace empties the cell, N turns Pencil on or off, Ctrl or Cmd with Z
|
|
296
|
+
The keys: the arrows move, a number (1 to 9, then A to G on the 16×16 and on to P on the 25×25) fills the chosen cell, Shift with a
|
|
297
|
+
number writes it as a pencil mark, Backspace empties the cell, N turns Pencil on or off (the slash key on the 25×25, where N is the number 23), Ctrl or Cmd with Z
|
|
297
298
|
undoes, Escape lets the cell go. The board's box keeps one steady square, and the lines of words under it keep
|
|
298
299
|
the room their longest wording takes, so nothing moves as numbers are written or messages come and go. Nothing
|
|
299
300
|
the player touches can be selected. Its words are English and Japanese and follow the page's `lang`.
|
|
@@ -416,10 +417,10 @@ All of these are held by tests, and the ones with a name are exported.
|
|
|
416
417
|
| --- | --- | --- |
|
|
417
418
|
| Puzzles | the six keys of `KAZU_KINDS` | the table under [The puzzles](#the-puzzles) |
|
|
418
419
|
| Levels | `easy`, `medium`, `hard` | `KAZU_LEVELS` |
|
|
419
|
-
| Sizes | each puzzle's own, 4×4 to
|
|
420
|
+
| Sizes | each puzzle's own, 4×4 to 25×25 | `KAZU_SPECS[kind].sizes` |
|
|
420
421
|
| A seed | a whole number from 1 to 2,147,483,647 | `KAZU_SEED_MOST`, `isKazuSeed` |
|
|
421
|
-
| Symbols in a grid | `1` to `9`, then `A` to `G` for the 16×16 | `symbolOf`, `valueOfSymbol` |
|
|
422
|
-
| The longest givens code | Sudoku
|
|
422
|
+
| Symbols in a grid | `1` to `9`, then `A` to `G` for the 16×16 and on to `P` for the 25×25 | `symbolOf`, `valueOfSymbol` |
|
|
423
|
+
| The longest givens code | Sudoku 625 characters, Jigsaw 162, Diagonal 81, Killer Sudoku 286, Futoshiki 133, Skyscrapers 77 | `KAZU_SPECS[kind].mostCells`, for a route that must refuse anything larger |
|
|
423
424
|
| Answers counted | two, so that "many" costs no more than "two" | the `limit` argument of `countKazuSolutions` |
|
|
424
425
|
| The solver's work | 2,000,000 steps, then it says it cannot say (`null`) | the `budget` argument of `solveKazu` |
|
|
425
426
|
| A step log | the newest 400 steps | `KAZU_STEPS_KEPT` |
|
|
@@ -481,7 +482,7 @@ Import the engine, player, and drawing from `@johnmorrisdotca/kazu/yajilin`, `@j
|
|
|
481
482
|
|
|
482
483
|
## Shikaku — rectangles in Kazu
|
|
483
484
|
|
|
484
|
-
The demo includes square, wide (10 × 6), tall (6 × 10) and custom rectangular boards, with width and height from 2 to 16. The named Courtyard (square), Long Table (wide) and Narrow Garden (tall) packs each hold three uniquely proved challenges with useful titles. Shares and saved settings preserve both dimensions. It uses Kazu’s shared materials and pieces palette.
|
|
485
|
+
The demo includes square boards (5, 7, 10 and 14 on a side), wide (10 × 6), tall (6 × 10) and custom rectangular boards, with width and height from 2 to 16, at four levels. The named Courtyard (square), Long Table (wide) and Narrow Garden (tall) packs each hold three uniquely proved challenges with useful titles. Shares and saved settings preserve both dimensions. It uses Kazu’s shared materials and pieces palette.
|
|
485
486
|
|
|
486
487
|
Shikaku belongs to the number-and-grid family. Its moves are rectangles rather than number entries, so it has a dedicated model and optional entry points; the existing six `KazuKind` values and saved Sudoku codes remain compatible.
|
|
487
488
|
|
|
@@ -522,7 +523,8 @@ Juosan has a dedicated immutable engine and package paths: `@johnmorrisdotca/kaz
|
|
|
522
523
|
|
|
523
524
|
- `ShikakuBoard`: `width`, `height`, and row-major `clues` (zero for an empty cell). Dimensions are 2–16; clue areas sum to the grid area.
|
|
524
525
|
- `ShikakuRectangle`: zero-based `x`, `y`, `width`, `height`.
|
|
525
|
-
- `generateShikaku(width, height, level, seed)`: deterministic puzzle and solution. The answer is counted independently
|
|
526
|
+
- `generateShikaku(width, height, level, seed)`: deterministic puzzle and solution; `level` is `easy`, `medium`, `hard` or `extra-hard` (`SHIKAKU_LEVELS`), and `SHIKAKU_SIZES` lists the square sides the demo offers (5, 7, 10, 14). The board is cut into interlocking rectangles by packing, not by straight cuts, each carries one number, and the answer is counted independently; every level is checked by solving the board (see [Levels](#levels-of-the-grid-puzzles)). It never returns an unproved board. If no board of a level is found within its attempts the next level down is made instead, which the rating shows.
|
|
527
|
+
- `rateShikaku(board)`: how hard a board is, measured by solving it: `depth` (0 rules alone, 1 supposing one rectangle, 2 more), `rules` (how many of the three rules a depth-0 solve needed), `probes`, and the number, area and ambiguity of the rectangles.
|
|
526
528
|
- `solveShikaku(board, placements?, {limit?, nodes?})`: exact-cover count, first answer, nodes visited and `complete`. The default limit is two answers and 100,000 nodes. Only `complete && count === 1` proves uniqueness; a stopped search is explicitly incomplete.
|
|
527
529
|
- `checkShikaku(board, rectangles)`: coverage and rectangle rule errors, independent of a stored answer. It accepts any valid completion.
|
|
528
530
|
- `newShikaku`, `placeShikaku`, `removeShikaku`, `undoShikaku`: immutable game operations. A placement replaces intersecting rectangles, and rule errors are allowed until checked. The game strips generated solutions.
|
|
@@ -537,13 +539,13 @@ The demo is `site/shikaku.html` after `pnpm site`; its generator runs in a modul
|
|
|
537
539
|
|
|
538
540
|
## Akari — light the grid
|
|
539
541
|
|
|
540
|
-
Akari (美術館) places bulbs in white squares. Each bulb lights in straight lines until a black square or the edge. Every white square must be lit, bulbs cannot see each other, and a numbered black square must touch exactly that many bulbs. Boards may be square, wide, tall or custom, with each side from 2 to 16. The seeded generator
|
|
542
|
+
Akari (美術館) places bulbs in white squares. Each bulb lights in straight lines until a black square or the edge. Every white square must be lit, bulbs cannot see each other, and a numbered black square must touch exactly that many bulbs. Boards may be square, wide, tall or custom, with each side from 2 to 16. The seeded generator scatters black squares at random (half the time in rotating pairs), lights them with random bulbs, numbers every black square that touches a white one, and then takes numbers away for as long as the board can still be solved the way the level asks, so the layouts are not a fixed motif. It returns a board only when its answer is proved single.
|
|
541
543
|
|
|
542
544
|
```js
|
|
543
545
|
import { generateAkari, checkAkari } from "@johnmorrisdotca/kazu/akari";
|
|
544
546
|
import { mountAkari } from "@johnmorrisdotca/kazu/akari/play";
|
|
545
547
|
|
|
546
|
-
const puzzle = generateAkari(7, 7, 42);
|
|
548
|
+
const puzzle = generateAkari(7, 7, 42, "hard"); // width, height, seed, level ("medium" if left out)
|
|
547
549
|
const player = mountAkari(document.querySelector("#board"), {
|
|
548
550
|
board: puzzle, material: "ivory", pieces: "ink", language: "en",
|
|
549
551
|
});
|
|
@@ -553,7 +555,8 @@ const player = mountAkari(document.querySelector("#board"), {
|
|
|
553
555
|
Use `@johnmorrisdotca/kazu/akari`, `@johnmorrisdotca/kazu/akari/play`, or `@johnmorrisdotca/kazu/akari/draw`. The root package also re-exports the engine; the dedicated drawing and player entries keep those features optional. There are no runtime dependencies.
|
|
554
556
|
|
|
555
557
|
- `AkariBoard`: width, height and row-major `cells`: `null` is white, `false` is an unnumbered black square, and `0`–`4` are numbered black squares.
|
|
556
|
-
- `generateAkari(width, height, seed)`: deterministic puzzle and its solution,
|
|
558
|
+
- `generateAkari(width, height, seed, level?)`: deterministic puzzle and its solution at `easy`, `medium`, `hard` or `extra-hard` (`AKARI_LEVELS`; `AKARI_SIZES` lists the square sides on offer: 5, 7, 10, 14). Easy keeps most of its numbers, medium is solved by the rules alone with as few as it can, hard needs supposing a bulb or an empty square, extra-hard needs the most of that. It returns only when an independent count proves exactly one answer; if no board of the level is found within its attempts the next level down is made, and the first generator, which cannot fail, is the last resort.
|
|
559
|
+
- `rateAkari(board)`: how hard a board is, measured by solving it: `depth` (0 rules alone, 1 supposing one square, 2 more), `probes`, and the numbers, bulbs and white squares.
|
|
557
560
|
- `solveAkari(board, {limit?, nodes?})`: counts placements, returns the first answer, visited nodes and `complete`; only `complete && count === 1` proves uniqueness. The default answer limit is two and the node budget is 250,000.
|
|
558
561
|
- `checkAkari(board, bulbs)`: checks a complete placement from the rules, independently of the generated answer. `progressAkari` reports dark squares and immediate conflicts while permitting unfinished numbered clues.
|
|
559
562
|
- `newAkari`, `toggleAkari`, `undoAkari`, `akariFinished`, `hintAkari`: immutable play operations. Hints require a proved unique answer and mark the game as helped.
|
|
@@ -571,14 +574,14 @@ The demo is `site/akari.html` after `pnpm site`. It shares Kazu's family header,
|
|
|
571
574
|
import { generateSlitherlink } from "@johnmorrisdotca/kazu/slitherlink";
|
|
572
575
|
import { mountSlitherlink } from "@johnmorrisdotca/kazu/slitherlink/play";
|
|
573
576
|
|
|
574
|
-
const puzzle = generateSlitherlink(7, 7, 42);
|
|
577
|
+
const puzzle = generateSlitherlink(7, 7, 42, "hard"); // width, height, seed, level ("medium" if left out)
|
|
575
578
|
const player = mountSlitherlink(document.querySelector("#board"), {
|
|
576
579
|
board: puzzle, material: "ivory", language: "en",
|
|
577
580
|
});
|
|
578
581
|
// player.progress() saves the public clues and selected edges.
|
|
579
582
|
```
|
|
580
583
|
|
|
581
|
-
The Slitherlink engine has its own edge model, checker, progress checker, bounded solution counter, seeded generator and immutable play state. `solveSlitherlink` distinguishes an exhausted search from a proved count; the generator returns only boards proved to have one loop. Boards may be 2–10 cells wide and high. The
|
|
584
|
+
The Slitherlink engine has its own edge model, checker, progress checker, bounded solution counter, seeded generator and immutable play state. `solveSlitherlink` distinguishes an exhausted search from a proved count; the generator returns only boards proved to have one loop. `generateSlitherlink(width, height, seed, level?)` makes `easy`, `medium`, `hard` or `extra-hard` (`SLITHERLINK_LEVELS`) boards, and `SLITHERLINK_SIZES` lists the square sides on offer (5, 7, 10). `rateSlitherlink(board)` measures a board by solving it: `depth` (0 rules alone, 1 supposing one edge, 2 more), `probes`, the numbers, how many of them say 0, and the loop's length. Boards may be 2–10 cells wide and high. The generator grows a random winding loop (a connected region without holes whose outline never touches itself), numbers every square with how many of its edges the loop uses, and takes numbers away, squares numbered 0 first, for as long as the board can still be solved the way the level asks, so boards are not a few shapes and few squares say 0. The player supports touch and mouse edge toggles, arrow-key focus, Enter/Space, undo, restart, checking, proved hints, save/restore, and ivory, wood and slate materials in English and Japanese.
|
|
582
585
|
|
|
583
586
|
Use `@johnmorrisdotca/kazu/slitherlink`, `@johnmorrisdotca/kazu/slitherlink/play`, or `@johnmorrisdotca/kazu/slitherlink/draw`. The demo is `site/slitherlink.html` after `pnpm site`. The rules are described by [Nikoli](https://www.nikoli.co.jp/en/puzzles/slitherlink/). This implementation uses original generated layouts and does not copy Nikoli puzzle grids, wording or artwork.
|
|
584
587
|
|
|
@@ -608,13 +611,13 @@ Kakuro fills white cells with digits 1–9. Each across and down run must match
|
|
|
608
611
|
import { generateKakuro, solveKakuro, checkKakuro } from "@johnmorrisdotca/kazu/kakuro";
|
|
609
612
|
import { mountKakuro } from "@johnmorrisdotca/kazu/kakuro/play";
|
|
610
613
|
|
|
611
|
-
const puzzle = generateKakuro(42);
|
|
614
|
+
const puzzle = generateKakuro(42, "hard", 8); // seed, level ("medium"), size including the totals' row and column (10)
|
|
612
615
|
const proof = solveKakuro(puzzle); // uniqueness only when complete && count === 1
|
|
613
616
|
const player = mountKakuro(document.querySelector("#board"), { board: puzzle, language: "en" });
|
|
614
617
|
player.progress(); // public clues, entries and pencil marks; no answer
|
|
615
618
|
```
|
|
616
619
|
|
|
617
|
-
`@johnmorrisdotca/kazu/kakuro/draw` provides standalone SVG drawing.
|
|
620
|
+
`@johnmorrisdotca/kazu/kakuro/draw` provides standalone SVG drawing. `generateKakuro(seed, level?, size?)` makes a board of any side from 5 to 12 (`KAKURO_SIZES` lists those on offer: 6, 8, 10, 12) at `easy`, `medium`, `hard` or `extra-hard` (`KAKURO_LEVELS`). It lays out the black squares row by row so that no run is a single square or longer than the level allows, fills random digits, and changes digits or darkens squares until the answer is single; easy and medium also ease the board until the rules they promise are enough, and hard and extra-hard ask for supposing. A board is accepted only after a bounded exact count proves one answer, and a seed never throws: if a level is not found within its attempts the next level down is made, and the first generator is the last resort on a 10×10. `rateKakuro(board)` measures a board by solving it: `depth`, `plain` (the single-run rules were enough), `probes`, the runs, the longest run and the share of totals that can be made one way only. `solveKakuro` reports `complete: false` when its node budget or answer limit stops counting. `checkKakuro` validates completed runs independently; `progressKakuro` permits blanks while marking impossible totals and repeats. The bilingual player supports touch, arrows, digits, pencil mode, Undo, Hint, Check, Restart and versioned saved progress.
|
|
618
621
|
|
|
619
622
|
The Kakuro entries are `@johnmorrisdotca/kazu/kakuro`, `@johnmorrisdotca/kazu/kakuro/play` and `@johnmorrisdotca/kazu/kakuro/draw`.
|
|
620
623
|
|
|
@@ -628,21 +631,36 @@ Each cell holds a number. All orthogonally connected cells with the same number
|
|
|
628
631
|
import { generateFillomino, checkFillomino, solveFillomino } from "@johnmorrisdotca/kazu/fillomino";
|
|
629
632
|
import { mountFillomino } from "@johnmorrisdotca/kazu/fillomino/play";
|
|
630
633
|
|
|
631
|
-
const puzzle = generateFillomino(
|
|
634
|
+
const puzzle = generateFillomino(6, 6, "hard", 17);
|
|
632
635
|
const result = solveFillomino(puzzle);
|
|
633
636
|
if (!result.complete || result.count !== 1) throw new Error("The answer was not proved unique");
|
|
634
637
|
checkFillomino(puzzle, result.solution);
|
|
635
638
|
mountFillomino(document.querySelector("#board"), { board: puzzle });
|
|
636
639
|
```
|
|
637
640
|
|
|
638
|
-
`FillominoBoard` contains `width`, `height`, and row-major `givens`, with zero for an empty cell. Engine validation
|
|
641
|
+
`FillominoBoard` contains `width`, `height`, and row-major `givens`, with zero for an empty cell. Engine validation and the seeded generator both support rectangular boards from 4 to 12 cells per side (`FILLOMINO_SIZES` lists the square sides on offer: 6, 8, 10, 12). A seed reproduces its puzzle. The levels are `easy`, `medium`, `hard` and `extra-hard` (`FILLOMINO_LEVELS`), and `rateFillomino(board)` measures a board by solving it: `depth` (0 rules alone, 1 supposing one number, 2 more), `probes`, the givens and their share, the regions, how many have no given and how big they are. The generator cuts the board into connected regions with no two of one size touching, gives every square, and takes givens away while the board can still be solved the way the level asks. Search bounds report when counting stopped rather than treating a partial search as a uniqueness proof.
|
|
639
642
|
|
|
640
643
|
`checkFillomino(board, entries)` checks givens, oversized connected groups and completion independently of the generated answer. An unfinished group smaller than its number can still grow. `solveFillomino(board, entries?, { limit?, nodes? })` counts filled solutions by growing connected regions, including regions with no given. Only `complete && count === 1` proves uniqueness. `newFillomino`, `setFillominoCell`, `undoFillomino`, `restartFillomino`, `hintFillomino`, and `fillominoFinished` are immutable game helpers. Progress codes contain public clues, entries, and the persistent assisted flag; they contain no stored answer.
|
|
641
644
|
|
|
642
|
-
The player accepts touch, mouse, and keyboard input, with undo, check, a proved hint, restart, and local progress codes. Hints persistently mark a run as assisted. The English and Japanese player uses the same board materials and number styles as Shikaku. The demo offers 4×4 through
|
|
645
|
+
The player accepts touch, mouse, and keyboard input, with undo, check, a proved hint, restart, and local progress codes. Hints persistently mark a run as assisted. The English and Japanese player uses the same board materials and number styles as Shikaku. The demo offers 4×4 through 12×12 settings at four levels. It is at [fillomino.html](https://johnmorrisdotca.github.io/kazu/fillomino.html).
|
|
643
646
|
|
|
644
647
|
[Nikoli's Fillomino rules](https://www.nikoli.co.jp/en/puzzles/fillomino/) describe numbered connected regions, exact area, and separation between equal-area regions. This implementation generates original puzzles and does not reuse published grids or artwork.
|
|
645
648
|
|
|
649
|
+
## Levels of the grid puzzles
|
|
650
|
+
|
|
651
|
+
Shikaku, Akari, Slitherlink, Hitori, Fillomino and Kakuro make boards at `easy`, `medium`, `hard` and `extra-hard`. Every board has exactly one answer, and a level says what a person has to do to solve it, measured by solving the board with the package's own rules: easy and medium need only the rules (easy keeps more numbers, medium as few as the rules allow), hard needs supposing something and watching it break, and extra-hard needs the most of that. `rateShikaku`, `rateAkari`, `rateSlitherlink`, `rateHitori`, `rateFillomino` and `rateKakuro` return the measure of a board (`depth`, `probes` and what it is made of), so a site can show it or pick boards by it.
|
|
652
|
+
|
|
653
|
+
| Kind | Call | Sizes on offer | Largest size, extra-hard: median / slowest to make |
|
|
654
|
+
| --- | --- | --- | --- |
|
|
655
|
+
| Shikaku | `generateShikaku(width, height, level, seed)` | 5, 7, 10, 14 (any side 2–16) | 14 × 14: 89 ms / 302 ms |
|
|
656
|
+
| Akari | `generateAkari(width, height, seed, level?)` | 5, 7, 10, 14 (any side 2–16) | 14 × 14: 212 ms / 366 ms |
|
|
657
|
+
| Slitherlink | `generateSlitherlink(width, height, seed, level?)` | 5, 7, 10 (any side 2–10) | 10 × 10: 231 ms / 286 ms |
|
|
658
|
+
| Hitori | `generateHitori(size, seed, level?)` | 5, 6, 7, 8, 9, 10, 12 (any side 4–12) | 12 × 12: 157 ms / 511 ms |
|
|
659
|
+
| Fillomino | `generateFillomino(width, height, level, seed)` | 6, 8, 10, 12 (any side 4–12) | 12 × 12: 187 ms / 422 ms |
|
|
660
|
+
| Kakuro | `generateKakuro(seed, level?, size?)` | 6, 8, 10, 12 (any side 5–12) | 12 × 12: 273 ms / 1,254 ms |
|
|
661
|
+
|
|
662
|
+
[docs/LEVELS.md](docs/LEVELS.md) defines each level for each kind, defines the measure, and tables it by size and level over 200 seeds, with the median, 95th percentile and slowest time to make a board; `node scripts/measure-levels.mjs` makes the tables again. These are the same boards in every browser and every Node for a given kind, size, level and seed, but they are **not** the boards 1.2.0 made for that seed.
|
|
663
|
+
|
|
646
664
|
## Heyawake — rooms and white paths
|
|
647
665
|
|
|
648
666
|
The Heyawake demo supports rectangular room boards, black/white/blank marking, keyboard and touch play, undo, a contradiction check, unique-solution hints, restart, local progress, and English/Japanese labels. Use `@johnmorrisdotca/kazu/heyawake`, `@johnmorrisdotca/kazu/heyawake/play`, or `@johnmorrisdotca/kazu/heyawake/draw`; generated answers are never included in progress data.
|
|
@@ -675,6 +693,9 @@ the page's part (the mount and the element) is another.
|
|
|
675
693
|
├── hitoriDraw.ts
|
|
676
694
|
├── hitoriGame.ts
|
|
677
695
|
├── hitoriGenerate.ts
|
|
696
|
+
├── hitoriBuild.ts
|
|
697
|
+
├── hitoriLogic.ts
|
|
698
|
+
├── hitoriRate.ts
|
|
678
699
|
├── hitoriMount.ts
|
|
679
700
|
├── hitoriPlay.types.ts
|
|
680
701
|
├── hitoriSolve.ts
|
|
@@ -745,6 +766,9 @@ the page's part (the mount and the element) is another.
|
|
|
745
766
|
├── fillominoDraw.ts
|
|
746
767
|
├── fillominoGame.ts
|
|
747
768
|
├── fillominoGenerate.ts
|
|
769
|
+
├── fillominoBuild.ts
|
|
770
|
+
├── fillominoLogic.ts
|
|
771
|
+
├── fillominoRate.ts
|
|
748
772
|
├── fillominoMount.ts
|
|
749
773
|
├── fillominoPlay.types.ts
|
|
750
774
|
├── fillominoSolve.ts
|
|
@@ -763,6 +787,10 @@ the page's part (the mount and the element) is another.
|
|
|
763
787
|
├── kakuroDraw.ts
|
|
764
788
|
├── kakuroGame.ts
|
|
765
789
|
├── kakuroGenerate.ts
|
|
790
|
+
├── kakuroBuild.ts
|
|
791
|
+
├── kakuroLogic.ts
|
|
792
|
+
├── kakuroRate.ts
|
|
793
|
+
├── kakuroTemplate.ts
|
|
766
794
|
├── kakuroMount.ts
|
|
767
795
|
├── kakuroPlay.types.ts
|
|
768
796
|
├── kakuroSolve.ts
|
|
@@ -774,6 +802,10 @@ the page's part (the mount and the element) is another.
|
|
|
774
802
|
├── shikakuDraw.ts
|
|
775
803
|
├── shikakuGame.ts
|
|
776
804
|
├── shikakuGenerate.ts
|
|
805
|
+
├── shikakuBuild.ts
|
|
806
|
+
├── shikakuLogic.ts
|
|
807
|
+
├── shikakuRate.ts
|
|
808
|
+
├── shikakuTemplate.ts
|
|
777
809
|
├── shikakuMount.ts
|
|
778
810
|
├── shikakuPacks.ts
|
|
779
811
|
├── shikakuPlay.types.ts
|
|
@@ -790,6 +822,9 @@ the page's part (the mount and the element) is another.
|
|
|
790
822
|
├── akariDraw.ts
|
|
791
823
|
├── akariGame.ts
|
|
792
824
|
├── akariGenerate.ts
|
|
825
|
+
├── akariLogic.ts
|
|
826
|
+
├── akariRate.ts
|
|
827
|
+
├── akariTemplate.ts
|
|
793
828
|
├── akariMount.ts
|
|
794
829
|
├── akariPlay.types.ts
|
|
795
830
|
├── akariSolve.ts
|
|
@@ -804,6 +839,9 @@ the page's part (the mount and the element) is another.
|
|
|
804
839
|
├── slitherlinkDraw.ts
|
|
805
840
|
├── slitherlinkGame.ts
|
|
806
841
|
├── slitherlinkGenerate.ts
|
|
842
|
+
├── slitherlinkLogic.ts
|
|
843
|
+
├── slitherlinkRate.ts
|
|
844
|
+
├── slitherlinkTemplate.ts
|
|
807
845
|
├── slitherlinkMount.ts
|
|
808
846
|
├── slitherlinkPlay.types.ts
|
|
809
847
|
├── slitherlinkSolve.ts
|
|
@@ -842,7 +880,8 @@ src/
|
|
|
842
880
|
├── index.ts the main entry: everything but the drawing and the page
|
|
843
881
|
├── kinds.ts the six puzzles' keys, sizes and levels, and the shape of a puzzle
|
|
844
882
|
├── random.ts the seeded random numbers every puzzle is made from
|
|
845
|
-
├──
|
|
883
|
+
├── csp.ts the one small engine under the six grid kinds: counting answers, and reasoning with and without supposing
|
|
884
|
+
├── cells.ts a grid of numbers as a string, 1 to 9 and A to P
|
|
846
885
|
├── layout.ts the groups that must each hold every number once: rows, columns, boxes, regions, diagonals, cages
|
|
847
886
|
├── groupSolve.ts the solver for puzzles made of groups: counting, singles, depth
|
|
848
887
|
├── numberPlace.ts Sudoku and Diagonal Sudoku: the generator and how givens are carved
|
|
@@ -905,7 +944,7 @@ Using Kazu in something? Open an *Add my project* issue and we will add you.
|
|
|
905
944
|
### The family
|
|
906
945
|
|
|
907
946
|
<!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
|
|
908
|
-
Kazu is one of
|
|
947
|
+
Kazu is one of twenty-two packages, each made for the same site, each at
|
|
909
948
|
[github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
|
|
910
949
|
|
|
911
950
|
- [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
|
|
@@ -927,8 +966,11 @@ Kazu is one of nineteen packages, each made for the same site, each at
|
|
|
927
966
|
- [Hikidashi](https://github.com/johnmorrisdotca/hikidashi) (引き出し): a drawer of small Japanese text tools: era dates, kanji numerals, readings and sentence difficulty. [Demo](https://johnmorrisdotca.github.io/hikidashi/).
|
|
928
967
|
- [Chizu](https://github.com/johnmorrisdotca/chizu) (地図): maps of the world and of countries' regions, in English and Japanese, with a quiz and callouts. [Demo](https://johnmorrisdotca.github.io/chizu/).
|
|
929
968
|
- [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
|
|
969
|
+
- [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
|
|
970
|
+
- [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
|
|
971
|
+
- [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
|
|
930
972
|
|
|
931
|
-
**This package is Kazu.** The demos of all
|
|
973
|
+
**This package is Kazu.** The demos of all twenty-two share one header and footer, so each links the rest.
|
|
932
974
|
<!-- family:end -->
|
|
933
975
|
|
|
934
976
|
## Development
|
|
@@ -958,14 +1000,14 @@ MIT, © John Morris. The puzzles are made in code and the drawing is SVG; there
|
|
|
958
1000
|
|
|
959
1001
|
## Hitori
|
|
960
1002
|
|
|
961
|
-
Hitori is included as a small standalone rules engine, drawing and player. Its public board has a `size`
|
|
1003
|
+
Hitori is included as a small standalone rules engine, drawing and player. Its public board has a `size` from 4 to 12 (`HITORI_SIZES` lists those on offer: 5, 6, 7, 8, 9, 10, 12) and a flat row-major `numbers` array. A solution is a Boolean shade mask: `true` means black. The solver counts minimal shade patterns, excluding redundant extra black cells; `complete: true` means the search finished, while a node-budget stop never claims uniqueness. `generateHitori(size, seed, level?)` makes `easy`, `medium`, `hard` or `extra-hard` (`HITORI_LEVELS`) puzzles: a random set of shaded squares that never touch and leave the rest in one piece, white squares numbered from a random Latin square so nothing repeats among them, and every shaded square numbered like a white one in its row or column, repaired until the answer is single. Easy is solved by the duplicates, pairs and sandwiches alone, medium once the whites must stay connected, hard by supposing, extra-hard needs the most supposing of several boards. The generator returns only puzzles proved to have one minimal answer, and `rateHitori(board)` measures a board by solving it: `depth`, `reach`, `probes` and how much is shaded and repeated.
|
|
962
1004
|
|
|
963
1005
|
```ts
|
|
964
1006
|
import { generateHitori, checkHitori, solveHitori } from "@johnmorrisdotca/kazu/hitori";
|
|
965
1007
|
import { drawHitori } from "@johnmorrisdotca/kazu/hitori/draw";
|
|
966
1008
|
import { mountHitori } from "@johnmorrisdotca/kazu/hitori/play";
|
|
967
1009
|
|
|
968
|
-
const puzzle = generateHitori(
|
|
1010
|
+
const puzzle = generateHitori(9, 42, "hard"); // size, seed, level ("medium" if left out)
|
|
969
1011
|
checkHitori(puzzle, puzzle.solution); // { ok: true, errors: [] }
|
|
970
1012
|
solveHitori(puzzle); // count: 1, complete: true
|
|
971
1013
|
```
|
package/dist/akari-entry.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./akari.constants.ts";
|
|
|
3
3
|
export * from "./akariBoard.ts";
|
|
4
4
|
export * from "./akariSolve.ts";
|
|
5
5
|
export * from "./akariGenerate.ts";
|
|
6
|
+
export * from "./akariRate.ts";
|
|
6
7
|
export * from "./akariGame.ts";
|
|
7
8
|
export { drawAkari } from "./akariDraw.ts";
|
|
8
9
|
export type { AkariDrawOptions } from "./akariPlay.types.ts";
|
package/dist/akari-entry.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./akari.constants.js";
|
|
|
3
3
|
export * from "./akariBoard.js";
|
|
4
4
|
export * from "./akariSolve.js";
|
|
5
5
|
export * from "./akariGenerate.js";
|
|
6
|
+
export * from "./akariRate.js";
|
|
6
7
|
export * from "./akariGame.js";
|
|
7
8
|
export { drawAkari } from "./akariDraw.js";
|
|
8
9
|
export { mountAkari } from "./akariMount.js";
|
|
@@ -1,2 +1,14 @@
|
|
|
1
1
|
export declare const AKARI_MOST_SIDE = 16;
|
|
2
2
|
export declare const AKARI_MOST_NODES = 250000;
|
|
3
|
+
/** How hard a puzzle is made, by what a person must do to solve it; see `rateAkari`. */
|
|
4
|
+
export declare const AKARI_LEVELS: readonly ["easy", "medium", "hard", "extra-hard"];
|
|
5
|
+
/** The sides the demo and the site offer for a square board; any side from 2 to `AKARI_MOST_SIDE` can be made. */
|
|
6
|
+
export declare const AKARI_SIZES: readonly [5, 7, 10, 14];
|
|
7
|
+
export declare const AKARI_MOST_ATTEMPTS = 60;
|
|
8
|
+
/**
|
|
9
|
+
* When a level finds no board in `AKARI_MOST_ATTEMPTS` plain attempts (a large board of an easy level, mostly: one attempt in a
|
|
10
|
+
* hundred succeeds on a 14×14), the generator tries again with more black squares, each of these multiples of the usual share in
|
|
11
|
+
* turn, `AKARI_DENSER_ATTEMPTS` attempts at each. Black squares are what make a large board solvable by its numbers.
|
|
12
|
+
*/
|
|
13
|
+
export declare const AKARI_DENSER_CROWDS: readonly [1.5, 1.8, 2.1, 2.4];
|
|
14
|
+
export declare const AKARI_DENSER_ATTEMPTS = 30;
|
package/dist/akari.constants.js
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
1
|
export const AKARI_MOST_SIDE = 16;
|
|
2
2
|
export const AKARI_MOST_NODES = 250000;
|
|
3
|
+
/** How hard a puzzle is made, by what a person must do to solve it; see `rateAkari`. */
|
|
4
|
+
export const AKARI_LEVELS = ["easy", "medium", "hard", "extra-hard"];
|
|
5
|
+
/** The sides the demo and the site offer for a square board; any side from 2 to `AKARI_MOST_SIDE` can be made. */
|
|
6
|
+
export const AKARI_SIZES = [5, 7, 10, 14];
|
|
7
|
+
export const AKARI_MOST_ATTEMPTS = 60;
|
|
8
|
+
/**
|
|
9
|
+
* When a level finds no board in `AKARI_MOST_ATTEMPTS` plain attempts (a large board of an easy level, mostly: one attempt in a
|
|
10
|
+
* hundred succeeds on a 14×14), the generator tries again with more black squares, each of these multiples of the usual share in
|
|
11
|
+
* turn, `AKARI_DENSER_ATTEMPTS` attempts at each. Black squares are what make a large board solvable by its numbers.
|
|
12
|
+
*/
|
|
13
|
+
export const AKARI_DENSER_CROWDS = [1.5, 1.8, 2.1, 2.4];
|
|
14
|
+
export const AKARI_DENSER_ATTEMPTS = 30;
|
package/dist/akari.types.d.ts
CHANGED
|
@@ -4,10 +4,29 @@ export type AkariBoard = {
|
|
|
4
4
|
height: number;
|
|
5
5
|
cells: readonly (number | null | false)[];
|
|
6
6
|
};
|
|
7
|
+
export type AkariLevel = "easy" | "medium" | "hard" | "extra-hard";
|
|
7
8
|
export type AkariPuzzle = AkariBoard & {
|
|
8
9
|
seed: number;
|
|
10
|
+
level: AkariLevel;
|
|
9
11
|
solution: readonly number[];
|
|
10
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* How hard a board is, measured by solving it. `depth` 0 means the plain rules solve it, 1 means somebody has to
|
|
15
|
+
* suppose a bulb or an empty square and see it break, 2 means more than that. `probes` is how many suppositions
|
|
16
|
+
* the depth-1 reasoning needed. The rest describes the board.
|
|
17
|
+
*/
|
|
18
|
+
export type AkariRating = {
|
|
19
|
+
depth: 0 | 1 | 2;
|
|
20
|
+
probes: number;
|
|
21
|
+
whites: number;
|
|
22
|
+
blacks: number;
|
|
23
|
+
clues: number;
|
|
24
|
+
bulbs: number;
|
|
25
|
+
/** Numbered squares as a share of the black squares that touch a white one. */
|
|
26
|
+
clueShare: number;
|
|
27
|
+
/** White squares as a share of the board. */
|
|
28
|
+
openShare: number;
|
|
29
|
+
};
|
|
11
30
|
export type AkariCheck = {
|
|
12
31
|
ok: boolean;
|
|
13
32
|
illuminated: number;
|
package/dist/akariGenerate.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import type { AkariPuzzle } from "./akari.types.ts";
|
|
1
|
+
import type { AkariLevel, AkariPuzzle } from "./akari.types.ts";
|
|
2
2
|
/**
|
|
3
|
-
* Makes a seeded board
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Makes a seeded board and proves it has one answer. A random wall of black squares is lit by randomly placed
|
|
4
|
+
* bulbs, every black square that touches a white one is numbered, and then numbers are taken away for as long as
|
|
5
|
+
* the puzzle can still be solved the way the level asks: easy and medium by the rules alone (easy keeps most
|
|
6
|
+
* of its numbers), hard by supposing one square at a time, extra-hard for as long as the answer stays single,
|
|
7
|
+
* which leaves a board that needs more supposing than that. If no board of the level is found within the
|
|
8
|
+
* attempts, the next one down is tried; if none of the levels finds one, the same is tried again with more
|
|
9
|
+
* black squares, which a large board needs to be solvable at all (see `AKARI_DENSER_CROWDS`). The fixed lattice
|
|
10
|
+
* of the first generator is only what is left if even that fails, which no seed does at any size offered.
|
|
6
11
|
*/
|
|
7
|
-
export declare function generateAkari(width?: number, height?: number, seed?: number): AkariPuzzle;
|
|
12
|
+
export declare function generateAkari(width?: number, height?: number, seed?: number, level?: AkariLevel): AkariPuzzle;
|