@johnmorrisdotca/kazu 1.1.0 → 1.2.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.
Files changed (323) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +379 -7
  3. package/dist/akari-draw-entry.d.ts +2 -0
  4. package/dist/akari-draw-entry.js +1 -0
  5. package/dist/akari-entry.d.ts +12 -0
  6. package/dist/akari-entry.js +10 -0
  7. package/dist/akari-play-entry.d.ts +2 -0
  8. package/dist/akari-play-entry.js +1 -0
  9. package/dist/akari.constants.d.ts +2 -0
  10. package/dist/akari.constants.js +2 -0
  11. package/dist/akari.types.d.ts +33 -0
  12. package/dist/akari.types.js +1 -0
  13. package/dist/akariBoard.d.ts +11 -0
  14. package/dist/akariBoard.js +96 -0
  15. package/dist/akariDraw.d.ts +4 -0
  16. package/dist/akariDraw.js +38 -0
  17. package/dist/akariGame.d.ts +15 -0
  18. package/dist/akariGame.js +71 -0
  19. package/dist/akariGenerate.d.ts +7 -0
  20. package/dist/akariGenerate.js +78 -0
  21. package/dist/akariMount.d.ts +3 -0
  22. package/dist/akariMount.js +214 -0
  23. package/dist/akariPlay.types.d.ts +25 -0
  24. package/dist/akariPlay.types.js +1 -0
  25. package/dist/akariSolve.d.ts +6 -0
  26. package/dist/akariSolve.js +106 -0
  27. package/dist/akariStrings.d.ts +98 -0
  28. package/dist/akariStrings.js +53 -0
  29. package/dist/akariStyle.d.ts +1 -0
  30. package/dist/akariStyle.js +9 -0
  31. package/dist/fillomino-draw-entry.d.ts +2 -0
  32. package/dist/fillomino-draw-entry.js +1 -0
  33. package/dist/fillomino-entry.d.ts +8 -0
  34. package/dist/fillomino-entry.js +7 -0
  35. package/dist/fillomino-play-entry.d.ts +4 -0
  36. package/dist/fillomino-play-entry.js +3 -0
  37. package/dist/fillomino.constants.d.ts +5 -0
  38. package/dist/fillomino.constants.js +5 -0
  39. package/dist/fillomino.types.d.ts +30 -0
  40. package/dist/fillomino.types.js +1 -0
  41. package/dist/fillominoBoard.d.ts +6 -0
  42. package/dist/fillominoBoard.js +84 -0
  43. package/dist/fillominoDraw.d.ts +3 -0
  44. package/dist/fillominoDraw.js +31 -0
  45. package/dist/fillominoGame.d.ts +13 -0
  46. package/dist/fillominoGame.js +65 -0
  47. package/dist/fillominoGenerate.d.ts +3 -0
  48. package/dist/fillominoGenerate.js +125 -0
  49. package/dist/fillominoMount.d.ts +2 -0
  50. package/dist/fillominoMount.js +211 -0
  51. package/dist/fillominoPlay.types.d.ts +25 -0
  52. package/dist/fillominoPlay.types.js +1 -0
  53. package/dist/fillominoSolve.d.ts +6 -0
  54. package/dist/fillominoSolve.js +111 -0
  55. package/dist/fillominoStrings.d.ts +102 -0
  56. package/dist/fillominoStrings.js +13 -0
  57. package/dist/fillominoStyle.d.ts +1 -0
  58. package/dist/fillominoStyle.js +13 -0
  59. package/dist/fillominoWorker.d.ts +1 -0
  60. package/dist/fillominoWorker.js +10 -0
  61. package/dist/heyawake-draw-entry.d.ts +2 -0
  62. package/dist/heyawake-draw-entry.js +1 -0
  63. package/dist/heyawake-entry.d.ts +13 -0
  64. package/dist/heyawake-entry.js +11 -0
  65. package/dist/heyawake-play-entry.d.ts +2 -0
  66. package/dist/heyawake-play-entry.js +1 -0
  67. package/dist/heyawake.constants.d.ts +5 -0
  68. package/dist/heyawake.constants.js +5 -0
  69. package/dist/heyawake.types.d.ts +35 -0
  70. package/dist/heyawake.types.js +1 -0
  71. package/dist/heyawakeBoard.d.ts +7 -0
  72. package/dist/heyawakeBoard.js +116 -0
  73. package/dist/heyawakeDraw.d.ts +3 -0
  74. package/dist/heyawakeDraw.js +27 -0
  75. package/dist/heyawakeGame.d.ts +12 -0
  76. package/dist/heyawakeGame.js +51 -0
  77. package/dist/heyawakeGenerate.d.ts +2 -0
  78. package/dist/heyawakeGenerate.js +114 -0
  79. package/dist/heyawakeMount.d.ts +2 -0
  80. package/dist/heyawakeMount.js +106 -0
  81. package/dist/heyawakePlay.types.d.ts +24 -0
  82. package/dist/heyawakePlay.types.js +1 -0
  83. package/dist/heyawakeSolve.d.ts +5 -0
  84. package/dist/heyawakeSolve.js +52 -0
  85. package/dist/heyawakeStrings.d.ts +19 -0
  86. package/dist/heyawakeStrings.js +7 -0
  87. package/dist/heyawakeStyle.d.ts +1 -0
  88. package/dist/heyawakeStyle.js +2 -0
  89. package/dist/heyawakeWorker.d.ts +1 -0
  90. package/dist/heyawakeWorker.js +10 -0
  91. package/dist/hitori-draw-entry.d.ts +2 -0
  92. package/dist/hitori-draw-entry.js +1 -0
  93. package/dist/hitori-entry.d.ts +6 -0
  94. package/dist/hitori-entry.js +6 -0
  95. package/dist/hitori-play-entry.d.ts +4 -0
  96. package/dist/hitori-play-entry.js +3 -0
  97. package/dist/hitori.constants.d.ts +2 -0
  98. package/dist/hitori.constants.js +2 -0
  99. package/dist/hitori.types.d.ts +20 -0
  100. package/dist/hitori.types.js +1 -0
  101. package/dist/hitoriBoard.d.ts +8 -0
  102. package/dist/hitoriBoard.js +72 -0
  103. package/dist/hitoriDraw.d.ts +4 -0
  104. package/dist/hitoriDraw.js +25 -0
  105. package/dist/hitoriGame.d.ts +10 -0
  106. package/dist/hitoriGame.js +48 -0
  107. package/dist/hitoriGenerate.d.ts +3 -0
  108. package/dist/hitoriGenerate.js +163 -0
  109. package/dist/hitoriMount.d.ts +3 -0
  110. package/dist/hitoriMount.js +111 -0
  111. package/dist/hitoriPlay.types.d.ts +25 -0
  112. package/dist/hitoriPlay.types.js +1 -0
  113. package/dist/hitoriSolve.d.ts +6 -0
  114. package/dist/hitoriSolve.js +69 -0
  115. package/dist/hitoriStrings.d.ts +70 -0
  116. package/dist/hitoriStrings.js +17 -0
  117. package/dist/hitoriStyle.d.ts +1 -0
  118. package/dist/hitoriStyle.js +9 -0
  119. package/dist/index.d.ts +11 -0
  120. package/dist/index.js +11 -0
  121. package/dist/juosan-draw-entry.d.ts +2 -0
  122. package/dist/juosan-draw-entry.js +1 -0
  123. package/dist/juosan-entry.d.ts +11 -0
  124. package/dist/juosan-entry.js +10 -0
  125. package/dist/juosan-play-entry.d.ts +4 -0
  126. package/dist/juosan-play-entry.js +3 -0
  127. package/dist/juosan.constants.d.ts +4 -0
  128. package/dist/juosan.constants.js +4 -0
  129. package/dist/juosan.types.d.ts +33 -0
  130. package/dist/juosan.types.js +1 -0
  131. package/dist/juosanBoard.d.ts +7 -0
  132. package/dist/juosanBoard.js +102 -0
  133. package/dist/juosanDraw.d.ts +4 -0
  134. package/dist/juosanDraw.js +49 -0
  135. package/dist/juosanGame.d.ts +20 -0
  136. package/dist/juosanGame.js +86 -0
  137. package/dist/juosanGenerate.d.ts +3 -0
  138. package/dist/juosanGenerate.js +31 -0
  139. package/dist/juosanMount.d.ts +3 -0
  140. package/dist/juosanMount.js +206 -0
  141. package/dist/juosanPlay.types.d.ts +24 -0
  142. package/dist/juosanPlay.types.js +1 -0
  143. package/dist/juosanSolve.d.ts +3 -0
  144. package/dist/juosanSolve.js +82 -0
  145. package/dist/juosanStrings.d.ts +77 -0
  146. package/dist/juosanStrings.js +43 -0
  147. package/dist/juosanStyle.d.ts +1 -0
  148. package/dist/juosanStyle.js +1 -0
  149. package/dist/kakuro-draw-entry.d.ts +2 -0
  150. package/dist/kakuro-draw-entry.js +1 -0
  151. package/dist/kakuro-entry.d.ts +12 -0
  152. package/dist/kakuro-entry.js +10 -0
  153. package/dist/kakuro-play-entry.d.ts +4 -0
  154. package/dist/kakuro-play-entry.js +3 -0
  155. package/dist/kakuro.constants.d.ts +6 -0
  156. package/dist/kakuro.constants.js +6 -0
  157. package/dist/kakuro.types.d.ts +46 -0
  158. package/dist/kakuro.types.js +1 -0
  159. package/dist/kakuroBoard.d.ts +8 -0
  160. package/dist/kakuroBoard.js +117 -0
  161. package/dist/kakuroDraw.d.ts +10 -0
  162. package/dist/kakuroDraw.js +21 -0
  163. package/dist/kakuroGame.d.ts +12 -0
  164. package/dist/kakuroGame.js +74 -0
  165. package/dist/kakuroGenerate.d.ts +3 -0
  166. package/dist/kakuroGenerate.js +130 -0
  167. package/dist/kakuroMount.d.ts +2 -0
  168. package/dist/kakuroMount.js +109 -0
  169. package/dist/kakuroPlay.types.d.ts +20 -0
  170. package/dist/kakuroPlay.types.js +1 -0
  171. package/dist/kakuroSolve.d.ts +6 -0
  172. package/dist/kakuroSolve.js +85 -0
  173. package/dist/kakuroStrings.d.ts +82 -0
  174. package/dist/kakuroStrings.js +5 -0
  175. package/dist/kakuroStyle.d.ts +1 -0
  176. package/dist/kakuroStyle.js +1 -0
  177. package/dist/masyu-draw-entry.d.ts +2 -0
  178. package/dist/masyu-draw-entry.js +1 -0
  179. package/dist/masyu-entry.d.ts +6 -0
  180. package/dist/masyu-entry.js +6 -0
  181. package/dist/masyu-play-entry.d.ts +4 -0
  182. package/dist/masyu-play-entry.js +3 -0
  183. package/dist/masyu.constants.d.ts +2 -0
  184. package/dist/masyu.constants.js +2 -0
  185. package/dist/masyu.types.d.ts +46 -0
  186. package/dist/masyu.types.js +1 -0
  187. package/dist/masyuBoard.d.ts +10 -0
  188. package/dist/masyuBoard.js +88 -0
  189. package/dist/masyuDraw.d.ts +2 -0
  190. package/dist/masyuDraw.js +33 -0
  191. package/dist/masyuGame.d.ts +9 -0
  192. package/dist/masyuGame.js +46 -0
  193. package/dist/masyuGenerate.d.ts +3 -0
  194. package/dist/masyuGenerate.js +56 -0
  195. package/dist/masyuMount.d.ts +2 -0
  196. package/dist/masyuMount.js +118 -0
  197. package/dist/masyuPlay.types.d.ts +1 -0
  198. package/dist/masyuPlay.types.js +1 -0
  199. package/dist/masyuSolve.d.ts +6 -0
  200. package/dist/masyuSolve.js +88 -0
  201. package/dist/masyuStrings.d.ts +28 -0
  202. package/dist/masyuStrings.js +4 -0
  203. package/dist/masyuStyle.d.ts +1 -0
  204. package/dist/masyuStyle.js +11 -0
  205. package/dist/nurikabe-draw-entry.d.ts +2 -0
  206. package/dist/nurikabe-draw-entry.js +1 -0
  207. package/dist/nurikabe-entry.d.ts +6 -0
  208. package/dist/nurikabe-entry.js +6 -0
  209. package/dist/nurikabe-play-entry.d.ts +4 -0
  210. package/dist/nurikabe-play-entry.js +3 -0
  211. package/dist/nurikabe.constants.d.ts +2 -0
  212. package/dist/nurikabe.constants.js +2 -0
  213. package/dist/nurikabe.types.d.ts +20 -0
  214. package/dist/nurikabe.types.js +1 -0
  215. package/dist/nurikabeBoard.d.ts +8 -0
  216. package/dist/nurikabeBoard.js +72 -0
  217. package/dist/nurikabeDraw.d.ts +4 -0
  218. package/dist/nurikabeDraw.js +15 -0
  219. package/dist/nurikabeGame.d.ts +8 -0
  220. package/dist/nurikabeGame.js +40 -0
  221. package/dist/nurikabeGenerate.d.ts +3 -0
  222. package/dist/nurikabeGenerate.js +31 -0
  223. package/dist/nurikabeMount.d.ts +3 -0
  224. package/dist/nurikabeMount.js +99 -0
  225. package/dist/nurikabePlay.types.d.ts +25 -0
  226. package/dist/nurikabePlay.types.js +1 -0
  227. package/dist/nurikabeSolve.d.ts +6 -0
  228. package/dist/nurikabeSolve.js +75 -0
  229. package/dist/nurikabeStrings.d.ts +70 -0
  230. package/dist/nurikabeStrings.js +5 -0
  231. package/dist/nurikabeStyle.d.ts +1 -0
  232. package/dist/nurikabeStyle.js +9 -0
  233. package/dist/ripple-draw-entry.d.ts +2 -0
  234. package/dist/ripple-draw-entry.js +1 -0
  235. package/dist/ripple-entry.d.ts +12 -0
  236. package/dist/ripple-entry.js +11 -0
  237. package/dist/ripple-play-entry.d.ts +4 -0
  238. package/dist/ripple-play-entry.js +3 -0
  239. package/dist/ripple.constants.d.ts +4 -0
  240. package/dist/ripple.constants.js +4 -0
  241. package/dist/ripple.types.d.ts +34 -0
  242. package/dist/ripple.types.js +1 -0
  243. package/dist/rippleBoard.d.ts +7 -0
  244. package/dist/rippleBoard.js +99 -0
  245. package/dist/rippleDraw.d.ts +3 -0
  246. package/dist/rippleDraw.js +32 -0
  247. package/dist/rippleGame.d.ts +13 -0
  248. package/dist/rippleGame.js +85 -0
  249. package/dist/rippleGenerate.d.ts +3 -0
  250. package/dist/rippleGenerate.js +57 -0
  251. package/dist/rippleMount.d.ts +2 -0
  252. package/dist/rippleMount.js +179 -0
  253. package/dist/ripplePlay.types.d.ts +23 -0
  254. package/dist/ripplePlay.types.js +1 -0
  255. package/dist/rippleSolve.d.ts +6 -0
  256. package/dist/rippleSolve.js +83 -0
  257. package/dist/rippleStrings.d.ts +86 -0
  258. package/dist/rippleStrings.js +5 -0
  259. package/dist/rippleStyle.d.ts +1 -0
  260. package/dist/rippleStyle.js +1 -0
  261. package/dist/shikaku-entry.d.ts +1 -0
  262. package/dist/shikaku-entry.js +1 -0
  263. package/dist/shikakuPacks.d.ts +27 -0
  264. package/dist/shikakuPacks.js +26 -0
  265. package/dist/slitherlink-draw-entry.d.ts +2 -0
  266. package/dist/slitherlink-draw-entry.js +1 -0
  267. package/dist/slitherlink-entry.d.ts +11 -0
  268. package/dist/slitherlink-entry.js +9 -0
  269. package/dist/slitherlink-play-entry.d.ts +4 -0
  270. package/dist/slitherlink-play-entry.js +3 -0
  271. package/dist/slitherlink.constants.d.ts +2 -0
  272. package/dist/slitherlink.constants.js +2 -0
  273. package/dist/slitherlink.types.d.ts +34 -0
  274. package/dist/slitherlink.types.js +1 -0
  275. package/dist/slitherlinkBoard.d.ts +12 -0
  276. package/dist/slitherlinkBoard.js +132 -0
  277. package/dist/slitherlinkDraw.d.ts +4 -0
  278. package/dist/slitherlinkDraw.js +51 -0
  279. package/dist/slitherlinkGame.d.ts +8 -0
  280. package/dist/slitherlinkGame.js +63 -0
  281. package/dist/slitherlinkGenerate.d.ts +3 -0
  282. package/dist/slitherlinkGenerate.js +101 -0
  283. package/dist/slitherlinkMount.d.ts +3 -0
  284. package/dist/slitherlinkMount.js +267 -0
  285. package/dist/slitherlinkPlay.types.d.ts +23 -0
  286. package/dist/slitherlinkPlay.types.js +1 -0
  287. package/dist/slitherlinkSolve.d.ts +6 -0
  288. package/dist/slitherlinkSolve.js +127 -0
  289. package/dist/slitherlinkStrings.d.ts +86 -0
  290. package/dist/slitherlinkStrings.js +47 -0
  291. package/dist/slitherlinkStyle.d.ts +1 -0
  292. package/dist/slitherlinkStyle.js +18 -0
  293. package/dist/version.d.ts +1 -1
  294. package/dist/version.js +1 -1
  295. package/dist/yajilin-draw-entry.d.ts +2 -0
  296. package/dist/yajilin-draw-entry.js +1 -0
  297. package/dist/yajilin-entry.d.ts +6 -0
  298. package/dist/yajilin-entry.js +6 -0
  299. package/dist/yajilin-play-entry.d.ts +4 -0
  300. package/dist/yajilin-play-entry.js +3 -0
  301. package/dist/yajilin.constants.d.ts +2 -0
  302. package/dist/yajilin.constants.js +2 -0
  303. package/dist/yajilin.types.d.ts +63 -0
  304. package/dist/yajilin.types.js +1 -0
  305. package/dist/yajilinBoard.d.ts +9 -0
  306. package/dist/yajilinBoard.js +91 -0
  307. package/dist/yajilinDraw.d.ts +2 -0
  308. package/dist/yajilinDraw.js +33 -0
  309. package/dist/yajilinGame.d.ts +15 -0
  310. package/dist/yajilinGame.js +58 -0
  311. package/dist/yajilinGenerate.d.ts +3 -0
  312. package/dist/yajilinGenerate.js +224 -0
  313. package/dist/yajilinMount.d.ts +2 -0
  314. package/dist/yajilinMount.js +162 -0
  315. package/dist/yajilinPlay.types.d.ts +1 -0
  316. package/dist/yajilinPlay.types.js +1 -0
  317. package/dist/yajilinSolve.d.ts +6 -0
  318. package/dist/yajilinSolve.js +82 -0
  319. package/dist/yajilinStrings.d.ts +32 -0
  320. package/dist/yajilinStrings.js +4 -0
  321. package/dist/yajilinStyle.d.ts +1 -0
  322. package/dist/yajilinStyle.js +12 -0
  323. package/package.json +180 -3
package/CHANGELOG.md CHANGED
@@ -6,6 +6,22 @@ All notable changes to this project are written here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.2.0] - 2026-10-05
10
+
11
+ ### Added
12
+
13
+ - Hitori and Nurikabe: independent shading rules, bounded solution counting, original proof-backed puzzle families, bilingual players and package entry points.
14
+ - Juosan: a dedicated territory model, verified small training layouts and a bilingual player for supplied boards.
15
+ - Nine named Shikaku challenges across square, wide and tall routes.
16
+ - Akari: a separate rule engine, bounded solution counter, seeded unique puzzle generator, immutable play, accessible bilingual player, drawing and demo.
17
+ - Slitherlink: a dedicated edge-loop engine, bounded unique-puzzle generation, accessible bilingual player, demo and package entry points.
18
+ - Ripple Effect: a dedicated room and spacing engine, bounded unique 9×9 generation, bilingual accessible player, demo and package entries.
19
+ - Kakuro: a seeded uniquely proved crossword-sum family, independent bounded solver, immutable progress, SVG and accessible English/Japanese player.
20
+
21
+ - Masyu and Yajilin: cell-centre loop engines, bounded proof-backed 5×5 families, drawing, bilingual players and saved progress.
22
+ - Fillomino: connected numbered regions, bounded original rectangular generation, independent checks, hints and a bilingual player.
23
+ - Heyawake: rectangular rooms, shading and white-path rules, bounded original small-board generation, hints and a bilingual player.
24
+
9
25
  ## [1.1.0] - 2026-10-04
10
26
 
11
27
  ### Added
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <h1 align="center">Kazu <sub>数</sub></h1>
2
2
 
3
- <p align="center"><strong>Grid number puzzles for JavaScript and TypeScript.</strong><br>
4
- Sudoku (4×4 to a 16×16 Giant), Jigsaw, Diagonal and Killer Sudoku, Futoshiki, Skyscrapers and Shikaku. A seeded generator whose every puzzle has exactly one answer, at three levels; a solver that counts answers; a check that reads a finished grid in O(cells); a hint that says which cell to fill next and why; puzzles and runs as short codes; the grid drawn as SVG; and played by touch, mouse and keyboard in any page, with pencil marks, undo and a clock, as one call or one tag. No dependencies.</p>
3
+ <p align="center"><strong>Grid number and logic puzzles for JavaScript and TypeScript.</strong><br>
4
+ Sudoku (4×4 to a 16×16 Giant), 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>
@@ -17,7 +17,7 @@ Sudoku (4×4 to a 16×16 Giant), Jigsaw, Diagonal and Killer Sudoku, Futoshiki,
17
17
  <img src="docs/phone.jpg" alt="A 6×6 Skyscrapers puzzle part filled in, on a phone in dark mode and in Japanese: the clues round the edge, the number pad, the buttons and the first of the settings under it" width="200">
18
18
  </p>
19
19
 
20
- Kazu is a family of grid puzzles built around numbers. Sudoku and its companions fill cells with numbers; Shikaku divides cells into numbered rectangles. It is
20
+ Kazu is a family of grid puzzles: number placement, regions, shading, lights and loops. Each puzzle has its own rules and engine. The original number puzzles are
21
21
  played at [itsutsu.com](https://itsutsu.com), which this package was taken out of, and in
22
22
  [the demo](https://johnmorrisdotca.github.io/kazu/), with nothing to install.
23
23
 
@@ -48,7 +48,7 @@ And in a page, a puzzle to play, by touch, mouse and keyboard, with nothing else
48
48
 
49
49
  ## Who it is for
50
50
 
51
- - **Puzzle sites and apps** that want these six puzzles with the rules already right: puzzles everybody
51
+ - **Puzzle sites and apps** that want these number puzzles with the rules already right: puzzles everybody
52
52
  plays alike from a seed, a check a server can trust in O(cells), a hint that is a reason and not just
53
53
  an answer, and runs kept as short strings.
54
54
  - **Anyone making number puzzles of their own**, who wants a solver that counts answers, generators whose
@@ -60,7 +60,8 @@ And in a page, a puzzle to play, by touch, mouse and keyboard, with nothing else
60
60
 
61
61
  ## Features
62
62
 
63
- - **Six puzzles, three levels.** Sudoku (4×4, 6×6, 9×9 and a 16×16 Giant), Jigsaw, Diagonal and Killer Sudoku, Futoshiki and Skyscrapers, each at `easy`, `medium` and `hard`, named by kebab-case keys.
63
+ - **Seven puzzles, three levels.** Sudoku (4×4, 6×6, 9×9 and a 16×16 Giant), 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 and a 16×16 Giant), 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.
64
65
  - **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.
65
66
  - **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.
66
67
  - **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.
@@ -442,17 +443,54 @@ Not here yet, and each welcome as an [issue](https://github.com/johnmorrisdotca/
442
443
 
443
444
  Left out on purpose: a puzzle with more than one answer, and any account, ranking or storage. A page keeps its own runs: `onChange` hands them over.
444
445
 
446
+ ## Masyu — pearls and a single loop
447
+
448
+ Masyu is a line puzzle with its own cell-centre loop model. A white pearl lies on a straight section and the loop turns in at least one of its adjacent cells. A black pearl lies at a turn, with a straight section in each adjacent cell. One closed loop must pass through every pearl.
449
+
450
+ ```ts
451
+ import { generateMasyu, checkMasyu, solveMasyu } from "@johnmorrisdotca/kazu/masyu";
452
+ import { mountMasyu } from "@johnmorrisdotca/kazu/masyu/play";
453
+
454
+ const puzzle = generateMasyu(5, 42);
455
+ checkMasyu(puzzle, puzzle.solution); // { ok: true }
456
+ solveMasyu(puzzle); // count: 1, complete: true
457
+ mountMasyu(document.querySelector("#board"), { board: puzzle });
458
+ ```
459
+
460
+ The dedicated `/masyu`, `/masyu/play`, and `/masyu/draw` entry points keep the loop engine separate from number-entry state. Import them from `@johnmorrisdotca/kazu/masyu`, `@johnmorrisdotca/kazu/masyu/play`, and `@johnmorrisdotca/kazu/masyu/draw`. The solver uses bounded cell-degree search; it reports `complete: false` when its node budget stops. The seeded generator currently supports original 5×5 layouts only: four distinct loop families, with board symmetries, produce varied pearl patterns and loop lengths. It returns a puzzle only after a completed uniqueness proof. Larger boards are not advertised until they can be proved within the search budget. Hints expose a next loop edge from a proved unique solution; saved play data contains the public pearls, drawn edges, and whether a hint was used.
461
+
462
+ [Nikoli describes the Masyu rules here](https://www.nikoli.co.jp/en/puzzles/masyu/). The generated layouts are original and do not use Nikoli's grids or artwork.
463
+
464
+ ## Yajilin — arrows, shaded cells and a loop
465
+
466
+ Yajilin places black cells by arrow counts and draws one loop through every remaining empty cell. Black cells do not touch by an edge; arrow cells are not shaded and are not part of the loop. The loop uses cell centres, not Slitherlink's grid edges.
467
+
468
+ ```ts
469
+ import { generateYajilin, checkYajilin, solveYajilin } from "@johnmorrisdotca/kazu/yajilin";
470
+ import { mountYajilin } from "@johnmorrisdotca/kazu/yajilin/play";
471
+
472
+ const puzzle = generateYajilin(5, 42);
473
+ checkYajilin(puzzle, puzzle.solution.shaded, puzzle.solution.edges); // { ok: true }
474
+ solveYajilin(puzzle); // count: 1, complete: true
475
+ mountYajilin(document.querySelector("#board"), { board: puzzle });
476
+ ```
477
+
478
+ Import the engine, player, and drawing from `@johnmorrisdotca/kazu/yajilin`, `@johnmorrisdotca/kazu/yajilin/play`, and `@johnmorrisdotca/kazu/yajilin/draw`. The solver enumerates public shade assignments, then checks the remaining cell-centre loop, with a finite node budget and an explicit incomplete result. The original seeded generator currently supports 5×5 only and keeps boards whose unique answer was proved. It varies both 3×3 and 3×4 loop families and their symmetries. Hints infer either a shade or a loop edge from the public clues and mark progress as helped. Save data contains only clues, current shades and drawn edges.
479
+
480
+ [Nikoli's Yajilin rules](https://www.nikoli.co.jp/en/puzzles/yajilin/) define the arrow counts, non-touching shaded cells and single loop. These original layouts use no Nikoli puzzle grids or artwork.
481
+
445
482
  ## Shikaku — rectangles in Kazu
446
483
 
447
- The demo includes square, wide (10 × 6), tall (6 × 10) and custom rectangular boards, with width and height from 2 to 16. Shares and saved settings preserve both dimensions. It uses Kazu’s unchanged shared family stylesheet and header controls. Irregular outlines are not part of this classic rectangle-partition game.
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.
448
485
 
449
486
  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.
450
487
 
451
488
  ```js
452
489
  import { generateShikaku, newShikaku, placeShikaku, checkShikaku } from "@johnmorrisdotca/kazu/shikaku";
453
490
  import { mountShikaku } from "@johnmorrisdotca/kazu/shikaku/play";
491
+ import { generateShikakuChallenge } from "@johnmorrisdotca/kazu/shikaku";
454
492
 
455
- const puzzle = generateShikaku(7, 7, "medium", 42);
493
+ const { puzzle } = generateShikakuChallenge("wide", 2); // Causeway, a proved 10 × 6 puzzle
456
494
  const player = mountShikaku(document.querySelector("#board"), {
457
495
  board: puzzle, material: "ivory", pieces: "ink", language: "en",
458
496
  onFinish: game => console.log(game.helped ? "Solved with help" : "Solved"),
@@ -460,10 +498,28 @@ const player = mountShikaku(document.querySelector("#board"), {
460
498
  // player.progress() saves public clues and rectangles; player.destroy() removes the player.
461
499
  ```
462
500
 
501
+ `generateShikakuChallenge("square" | "wide" | "tall", 1..3)` selects a named challenge and checks uniqueness with the existing solver.
502
+
463
503
  Use `@johnmorrisdotca/kazu/shikaku`, `@johnmorrisdotca/kazu/shikaku/play`, or `@johnmorrisdotca/kazu/shikaku/draw`.
464
504
 
465
505
  The root entry re-exports the engine, `/play` re-exports `mountShikaku`, and `/draw` re-exports `drawShikaku`. The dedicated entries let a consumer load only Shikaku. There are no runtime dependencies.
466
506
 
507
+ ## Juosan — horizontal and vertical marks
508
+
509
+ Juosan gives each cell either a horizontal mark (`1`) or a vertical mark (`2`). A territory clue is the absolute difference between its horizontal and vertical mark counts; an unnumbered territory uses `difference: null`. Horizontal marks may make longer runs horizontally but never three vertically; vertical marks may make longer runs vertically but never three horizontally. See [Nikoli's English rules](https://www.nikoli.co.jp/en/puzzles/juosan/) for the original rule wording.
510
+
511
+ ```ts
512
+ import { checkJuosan, generateJuosan, solveJuosan } from "@johnmorrisdotca/kazu/juosan";
513
+ import { mountJuosan } from "@johnmorrisdotca/kazu/juosan/play";
514
+
515
+ const puzzle = generateJuosan(3, 2, "easy", 42); // 3 × 2 training board; answer proved unique
516
+ solveJuosan(puzzle, 2); // { count: 1, complete: true, ... }
517
+ checkJuosan(puzzle, puzzle.solution); // { ok: true, errors: [] }
518
+ const game = mountJuosan(document.querySelector("#board")!, { board: puzzle });
519
+ ```
520
+
521
+ Juosan has a dedicated immutable engine and package paths: `@johnmorrisdotca/kazu/juosan`, `@johnmorrisdotca/kazu/juosan/play`, and `@johnmorrisdotca/kazu/juosan/draw`. The player accepts any valid supplied board from 2×2 through 16×16. The built-in seeded generator currently supports the two small training shapes, 3×2 and 2×3. Even seeds split the board into straight three-cell territories; odd seeds use one whole-board territory. In each case, the maximum difference clue plus the directional run rule proves the single uniform orientation. Its `level` setting is reserved and does not change rule difficulty yet. The demo saves progress locally, marks hint use as assisted, and offers keyboard and touch input in English and Japanese.
522
+
467
523
  - `ShikakuBoard`: `width`, `height`, and row-major `clues` (zero for an empty cell). Dimensions are 2–16; clue areas sum to the grid area.
468
524
  - `ShikakuRectangle`: zero-based `x`, `y`, `width`, `height`.
469
525
  - `generateShikaku(width, height, level, seed)`: deterministic puzzle and solution. The answer is counted independently. Generation rejects ambiguous boards and stops after 200 attempts with an error; it never silently returns an unproved board. The seed range matches Kazu. Levels `easy`, `medium`, `hard` favor small, mixed and larger rectangles; these are generation profiles, not calibrated human difficulty ratings.
@@ -479,6 +535,130 @@ The demo is `site/shikaku.html` after `pnpm site`; its generator runs in a modul
479
535
 
480
536
  [Shikaku's rules are described by Nikoli](https://www.nikoli.co.jp/en/puzzles/shikaku/). This implementation generates its own puzzles and does not copy Nikoli's puzzle grids, wording or artwork.
481
537
 
538
+ ## Akari — light the grid
539
+
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 uses original horizontal and vertical paired-room layouts, varies bulb directions, and reflects the rooms across either axis. It rejects every board whose uniqueness proof does not finish.
541
+
542
+ ```js
543
+ import { generateAkari, checkAkari } from "@johnmorrisdotca/kazu/akari";
544
+ import { mountAkari } from "@johnmorrisdotca/kazu/akari/play";
545
+
546
+ const puzzle = generateAkari(7, 7, 42);
547
+ const player = mountAkari(document.querySelector("#board"), {
548
+ board: puzzle, material: "ivory", pieces: "ink", language: "en",
549
+ });
550
+ // player.progress() saves public clues and bulbs; player.destroy() removes the player.
551
+ ```
552
+
553
+ 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
+
555
+ - `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, selected from original horizontal and vertical paired-room layouts with seed-chosen bulb directions and symmetry. It returns only when an independent bounded count proves exactly one answer.
557
+ - `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
+ - `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
+ - `newAkari`, `toggleAkari`, `undoAkari`, `akariFinished`, `hintAkari`: immutable play operations. Hints require a proved unique answer and mark the game as helped.
560
+ - `encodeAkari`, `decodeAkari`: versioned JSON containing only public board data and player bulbs.
561
+ - `drawAkari(board, options)`: SVG with `ivory`, `wood` and `slate` materials, `ink` or `tiles` bulb pieces, and `en` or `ja` labels.
562
+ - `mountAkari(host, options)`: toggle bulbs by tap, click, Enter or Space. Arrow keys move through the grid; Delete removes a bulb. Undo, Check, Hint, Restart and a modal board view are built in. The handle has `game`, `progress`, `set`, `restart` and `destroy`.
563
+
564
+ The demo is `site/akari.html` after `pnpm site`. It shares Kazu's family header, footer, palette and felt board. Progress stays in local storage; share links carry board settings, not player data.
565
+
566
+ [Nikoli's Akari rules](https://www.nikoli.co.jp/en/puzzles/akari/) describe the same line-of-sight and numbered-square constraints. These boards are generated here; the implementation does not copy Nikoli's grids or artwork.
567
+
568
+ ## Slitherlink
569
+
570
+ ```js
571
+ import { generateSlitherlink } from "@johnmorrisdotca/kazu/slitherlink";
572
+ import { mountSlitherlink } from "@johnmorrisdotca/kazu/slitherlink/play";
573
+
574
+ const puzzle = generateSlitherlink(7, 7, 42);
575
+ const player = mountSlitherlink(document.querySelector("#board"), {
576
+ board: puzzle, material: "ivory", language: "en",
577
+ });
578
+ // player.progress() saves the public clues and selected edges.
579
+ ```
580
+
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 original generator uses rectangular and L-shaped loop families with seed-selected positions and reflections; its profiles describe these families, not human difficulty. 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
+
583
+ 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
+
585
+ ## Ripple Effect
586
+
587
+ ```ts
588
+ import { generateRipple, newRipple, hintRipple } from "@johnmorrisdotca/kazu/ripple";
589
+ import { mountRipple } from "@johnmorrisdotca/kazu/ripple/play";
590
+
591
+ const puzzle = generateRipple(9, 9, 42);
592
+ const game = newRipple(puzzle);
593
+ const hint = hintRipple(game); // only returned after a unique completion is proved
594
+ const player = mountRipple(document.querySelector<HTMLElement>("#board")!, {
595
+ board: puzzle, material: "ivory", pieces: "ink", language: "en",
596
+ });
597
+ ```
598
+
599
+ Each room contains every number from 1 through its size exactly once. Repeated N values in one row or column have at least N cells between them, so their coordinate distance must exceed N. The DOM-free engine has independent completion and progress checks and a bounded solution counter that reports when a search stopped before proof. The original seeded 9×9 generator uses 3×3 rooms, seeded row-band, column-stack and digit permutations, and uniqueness-preserving clue removal. It returns only puzzles proved to have one completion; custom sizes are not advertised until their generator family passes the same proof checks. These are original layouts and do not reproduce Nikoli puzzle grids or artwork.
600
+
601
+ Use `@johnmorrisdotca/kazu/ripple`, `@johnmorrisdotca/kazu/ripple/play`, or `@johnmorrisdotca/kazu/ripple/draw`. The touch and keyboard player includes number entry, pencil notes, undo, restart, checking, unique-proof hints, accessible room boundaries, save/restore, and ivory, wood and slate materials with ink or tile pieces in English and Japanese. The demo is `site/ripple.html` after `pnpm site`. Rules: [Nikoli’s Ripple Effect page](https://www.nikoli.co.jp/en/puzzles/ripple_effect/).
602
+
603
+ ## Kakuro — crossword sums in Kazu
604
+
605
+ Kakuro fills white cells with digits 1–9. Each across and down run must match its clue sum without repeating a digit. Run lengths are at least two, and every white cell belongs to exactly one run in each direction. [Nikoli describes the rules](https://www.nikoli.co.jp/en/puzzles/kakuro/); generated layouts here are original.
606
+
607
+ ```js
608
+ import { generateKakuro, solveKakuro, checkKakuro } from "@johnmorrisdotca/kazu/kakuro";
609
+ import { mountKakuro } from "@johnmorrisdotca/kazu/kakuro/play";
610
+
611
+ const puzzle = generateKakuro(42);
612
+ const proof = solveKakuro(puzzle); // uniqueness only when complete && count === 1
613
+ const player = mountKakuro(document.querySelector("#board"), { board: puzzle, language: "en" });
614
+ player.progress(); // public clues, entries and pencil marks; no answer
615
+ ```
616
+
617
+ `@johnmorrisdotca/kazu/kakuro/draw` provides standalone SVG drawing. The 10×10 seeded family uses crossing 2×2, 2×3, 3×2 and 3×3 regions of white cells with separated runs; every returned layout is accepted only after a bounded exact count proves one answer. This is a defined family, not a general-purpose random-layout generator. `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
+
619
+ The Kakuro entries are `@johnmorrisdotca/kazu/kakuro`, `@johnmorrisdotca/kazu/kakuro/play` and `@johnmorrisdotca/kazu/kakuro/draw`.
620
+
621
+ ## Fillomino — connected regions with exact areas
622
+
623
+ The dedicated package entries are `@johnmorrisdotca/kazu/fillomino`, `@johnmorrisdotca/kazu/fillomino/play`, and `@johnmorrisdotca/kazu/fillomino/draw`.
624
+
625
+ Each cell holds a number. All orthogonally connected cells with the same number form a region, and the region's area must equal that number. Two regions of the same area cannot touch. A completed region does not need to contain a printed clue; the checker and solver do not require one clue per region.
626
+
627
+ ```ts
628
+ import { generateFillomino, checkFillomino, solveFillomino } from "@johnmorrisdotca/kazu/fillomino";
629
+ import { mountFillomino } from "@johnmorrisdotca/kazu/fillomino/play";
630
+
631
+ const puzzle = generateFillomino(5, 5, "easy", 17);
632
+ const result = solveFillomino(puzzle);
633
+ if (!result.complete || result.count !== 1) throw new Error("The answer was not proved unique");
634
+ checkFillomino(puzzle, result.solution);
635
+ mountFillomino(document.querySelector("#board"), { board: puzzle });
636
+ ```
637
+
638
+ `FillominoBoard` contains `width`, `height`, and row-major `givens`, with zero for an empty cell. Engine validation supports boards up to 12×12; the seeded generator supports rectangular boards from 4 to 8 cells per side, capped at 36 total cells. A seed reproduces its puzzle. Easy, medium and hard are clue-density profiles rather than measured human difficulty; the generator retains additional clues when needed to prove uniqueness. Search bounds report when counting stopped rather than treating a partial search as a uniqueness proof.
639
+
640
+ `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
+
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 6×6 settings. It is at [fillomino.html](https://johnmorrisdotca.github.io/kazu/fillomino.html).
643
+
644
+ [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
+
646
+ ## Heyawake — rooms and white paths
647
+
648
+ 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.
649
+
650
+ ```js
651
+ import { generateHeyawake, checkHeyawake, solveHeyawake } from "@johnmorrisdotca/kazu/heyawake";
652
+
653
+ const puzzle = generateHeyawake(5, 4, "easy", 17);
654
+ checkHeyawake(puzzle, puzzle.solution); // checks room counts and all three global rules
655
+ solveHeyawake(puzzle); // { count: 1, complete: true, ... }
656
+ ```
657
+
658
+ The engine accepts boards up to 12×12. The original seeded generator supports rectangles from 4 to 8 cells per side, capped at 25 total cells to keep uniqueness proofs bounded. Its easy, medium and hard profiles start with different room-clue densities, then retain more room clues and may split rooms more finely when needed for a uniqueness proof. These are clue profiles, not measured human difficulty. See [the Heyawake rules and API guide](docs/HEYAWAKE.md).
659
+
660
+ [Nikoli's Heyawake rules](https://www.nikoli.co.jp/en/puzzles/heyawake/) describe numbered room counts, non-touching black cells, connected whites, and a maximum of two rooms in a straight uninterrupted white run. The package generates original grids and uses no published puzzle boards or artwork.
661
+
482
662
  ## Architecture
483
663
 
484
664
  The generators, the solvers, the check, the hint and the game are plain functions over short codes, with no
@@ -486,9 +666,108 @@ DOM. The drawing is SVG text in an entry of its own, so a server that only check
486
666
  the page's part (the mount and the element) is another.
487
667
 
488
668
  ```text
669
+ ├── hitori-draw-entry.ts
670
+ ├── hitori-entry.ts
671
+ ├── hitori-play-entry.ts
672
+ ├── hitori.constants.ts
673
+ ├── hitori.types.ts
674
+ ├── hitoriBoard.ts
675
+ ├── hitoriDraw.ts
676
+ ├── hitoriGame.ts
677
+ ├── hitoriGenerate.ts
678
+ ├── hitoriMount.ts
679
+ ├── hitoriPlay.types.ts
680
+ ├── hitoriSolve.ts
681
+ ├── hitoriStrings.ts
682
+ ├── hitoriStyle.ts
683
+ ├── nurikabe-draw-entry.ts
684
+ ├── nurikabe-entry.ts
685
+ ├── nurikabe-play-entry.ts
686
+ ├── nurikabe.constants.ts
687
+ ├── nurikabe.types.ts
688
+ ├── nurikabeBoard.ts
689
+ ├── nurikabeDraw.ts
690
+ ├── nurikabeGame.ts
691
+ ├── nurikabeGenerate.ts
692
+ ├── nurikabeMount.ts
693
+ ├── nurikabePlay.types.ts
694
+ ├── nurikabeSolve.ts
695
+ ├── nurikabeStrings.ts
696
+ ├── nurikabeStyle.ts
697
+ ├── juosan-draw-entry.ts
698
+ ├── juosan-entry.ts
699
+ ├── juosan-play-entry.ts
700
+ ├── juosan.constants.ts
701
+ ├── juosan.types.ts
702
+ ├── juosanBoard.ts
703
+ ├── juosanDraw.ts
704
+ ├── juosanGame.ts
705
+ ├── juosanGenerate.ts
706
+ ├── juosanMount.ts
707
+ ├── juosanPlay.types.ts
708
+ ├── juosanSolve.ts
709
+ ├── juosanStrings.ts
710
+ ├── juosanStyle.ts
711
+ ├── masyu-draw-entry.ts
712
+ ├── masyu-entry.ts
713
+ ├── masyu-play-entry.ts
714
+ ├── masyu.constants.ts
715
+ ├── masyu.types.ts
716
+ ├── masyuBoard.ts
717
+ ├── masyuDraw.ts
718
+ ├── masyuGame.ts
719
+ ├── masyuGenerate.ts
720
+ ├── masyuMount.ts
721
+ ├── masyuPlay.types.ts
722
+ ├── masyuSolve.ts
723
+ ├── masyuStrings.ts
724
+ ├── masyuStyle.ts
725
+ ├── yajilin-draw-entry.ts
726
+ ├── yajilin-entry.ts
727
+ ├── yajilin-play-entry.ts
728
+ ├── yajilin.constants.ts
729
+ ├── yajilin.types.ts
730
+ ├── yajilinBoard.ts
731
+ ├── yajilinDraw.ts
732
+ ├── yajilinGame.ts
733
+ ├── yajilinGenerate.ts
734
+ ├── yajilinMount.ts
735
+ ├── yajilinPlay.types.ts
736
+ ├── yajilinSolve.ts
737
+ ├── yajilinStrings.ts
738
+ ├── yajilinStyle.ts
739
+ ├── fillomino-draw-entry.ts
740
+ ├── fillomino-entry.ts
741
+ ├── fillomino-play-entry.ts
742
+ ├── fillomino.constants.ts
743
+ ├── fillomino.types.ts
744
+ ├── fillominoBoard.ts
745
+ ├── fillominoDraw.ts
746
+ ├── fillominoGame.ts
747
+ ├── fillominoGenerate.ts
748
+ ├── fillominoMount.ts
749
+ ├── fillominoPlay.types.ts
750
+ ├── fillominoSolve.ts
751
+ ├── fillominoStrings.ts
752
+ ├── fillominoStyle.ts
753
+ ├── fillominoWorker.ts
489
754
  ├── shikaku-draw-entry.ts
490
755
  ├── shikaku-entry.ts
491
756
  ├── shikaku-play-entry.ts
757
+ ├── kakuro-draw-entry.ts
758
+ ├── kakuro-entry.ts
759
+ ├── kakuro-play-entry.ts
760
+ ├── kakuro.constants.ts
761
+ ├── kakuro.types.ts
762
+ ├── kakuroBoard.ts
763
+ ├── kakuroDraw.ts
764
+ ├── kakuroGame.ts
765
+ ├── kakuroGenerate.ts
766
+ ├── kakuroMount.ts
767
+ ├── kakuroPlay.types.ts
768
+ ├── kakuroSolve.ts
769
+ ├── kakuroStrings.ts
770
+ ├── kakuroStyle.ts
492
771
  ├── shikaku.constants.ts
493
772
  ├── shikaku.types.ts
494
773
  ├── shikakuBoard.ts
@@ -496,11 +775,69 @@ the page's part (the mount and the element) is another.
496
775
  ├── shikakuGame.ts
497
776
  ├── shikakuGenerate.ts
498
777
  ├── shikakuMount.ts
778
+ ├── shikakuPacks.ts
499
779
  ├── shikakuPlay.types.ts
500
780
  ├── shikakuSolve.ts
501
781
  ├── shikakuStrings.ts
502
782
  ├── shikakuStyle.ts
503
783
  ├── shikakuWorker.ts
784
+ ├── akari-draw-entry.ts
785
+ ├── akari-entry.ts
786
+ ├── akari-play-entry.ts
787
+ ├── akari.constants.ts
788
+ ├── akari.types.ts
789
+ ├── akariBoard.ts
790
+ ├── akariDraw.ts
791
+ ├── akariGame.ts
792
+ ├── akariGenerate.ts
793
+ ├── akariMount.ts
794
+ ├── akariPlay.types.ts
795
+ ├── akariSolve.ts
796
+ ├── akariStrings.ts
797
+ ├── akariStyle.ts
798
+ ├── slitherlink-draw-entry.ts
799
+ ├── slitherlink-entry.ts
800
+ ├── slitherlink-play-entry.ts
801
+ ├── slitherlink.constants.ts
802
+ ├── slitherlink.types.ts
803
+ ├── slitherlinkBoard.ts
804
+ ├── slitherlinkDraw.ts
805
+ ├── slitherlinkGame.ts
806
+ ├── slitherlinkGenerate.ts
807
+ ├── slitherlinkMount.ts
808
+ ├── slitherlinkPlay.types.ts
809
+ ├── slitherlinkSolve.ts
810
+ ├── slitherlinkStrings.ts
811
+ ├── slitherlinkStyle.ts
812
+ ├── ripple-draw-entry.ts
813
+ ├── ripple-entry.ts
814
+ ├── ripple-play-entry.ts
815
+ ├── ripple.constants.ts
816
+ ├── ripple.types.ts
817
+ ├── rippleBoard.ts
818
+ ├── rippleDraw.ts
819
+ ├── rippleGame.ts
820
+ ├── rippleGenerate.ts
821
+ ├── rippleMount.ts
822
+ ├── ripplePlay.types.ts
823
+ ├── rippleSolve.ts
824
+ ├── rippleStrings.ts
825
+ ├── rippleStyle.ts
826
+ ├── heyawake-draw-entry.ts
827
+ ├── heyawake-entry.ts
828
+ ├── heyawake-play-entry.ts
829
+ ├── heyawake.constants.ts
830
+ ├── heyawake.types.ts
831
+ ├── heyawakeBoard.ts
832
+ ├── heyawakeDraw.ts
833
+ ├── heyawakeGame.ts
834
+ ├── heyawakeGenerate.ts
835
+ ├── heyawakeMount.ts
836
+ ├── heyawakePlay.types.ts
837
+ ├── heyawakeSolve.ts
838
+ ├── heyawakeStrings.ts
839
+ ├── heyawakeStyle.ts
840
+ ├── heyawakeWorker.ts
504
841
  src/
505
842
  ├── index.ts the main entry: everything but the drawing and the page
506
843
  ├── kinds.ts the six puzzles' keys, sizes and levels, and the shape of a puzzle
@@ -618,3 +955,38 @@ See [CHANGELOG.md](./CHANGELOG.md).
618
955
  ## Licence
619
956
 
620
957
  MIT, © John Morris. The puzzles are made in code and the drawing is SVG; there is no sound and no data file but the record of what the site made.
958
+
959
+ ## Hitori
960
+
961
+ Hitori is included as a small standalone rules engine, drawing and player. Its public board has a `size` of 5 or 7 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. Four original layouts per size are varied by seeded number relabeling and all board symmetries; the generator returns only puzzles proved to have one minimal answer.
962
+
963
+ ```ts
964
+ import { generateHitori, checkHitori, solveHitori } from "@johnmorrisdotca/kazu/hitori";
965
+ import { drawHitori } from "@johnmorrisdotca/kazu/hitori/draw";
966
+ import { mountHitori } from "@johnmorrisdotca/kazu/hitori/play";
967
+
968
+ const puzzle = generateHitori(5, 42);
969
+ checkHitori(puzzle, puzzle.solution); // { ok: true, errors: [] }
970
+ solveHitori(puzzle); // count: 1, complete: true
971
+ ```
972
+
973
+ `newHitori`, `shadeHitori`, `undoHitori`, `hintHitori`, `encodeHitori` and `decodeHitori` keep play state immutable and progress codes free of the answer. Hints are assistance and set `helped`; generated answers are never put into the player state. The demo is `site/hitori.html` after `pnpm site`, and stores progress in this browser only. The implementation follows [Nikoli's Hitori rules](https://www.nikoli.co.jp/en/puzzles/hitori/) and makes its own boards.
974
+
975
+ Use `@johnmorrisdotca/kazu/hitori`, `@johnmorrisdotca/kazu/hitori/play`, or `@johnmorrisdotca/kazu/hitori/draw`.
976
+ ## Nurikabe
977
+
978
+ Nurikabe is available through its own rules, drawing and player entries. This compact edition makes original 5×5 puzzles from seeded symmetric layouts; it returns a puzzle only after the bounded solver proves exactly one solution. The supported board size is intentionally limited to 5×5 so generation remains quick and dependable.
979
+
980
+ ```ts
981
+ import { generateNurikabe, checkNurikabe, solveNurikabe } from "@johnmorrisdotca/kazu/nurikabe";
982
+ import { drawNurikabe } from "@johnmorrisdotca/kazu/nurikabe/draw";
983
+ import { mountNurikabe } from "@johnmorrisdotca/kazu/nurikabe/play";
984
+
985
+ const puzzle = generateNurikabe(42);
986
+ checkNurikabe(puzzle, puzzle.solution); // { ok: true, errors: [] }
987
+ solveNurikabe(puzzle); // count: 1, complete: true
988
+ ```
989
+
990
+ A clue gives the exact size of its white island; each island has one clue, the remaining black sea is connected, and no 2×2 square is entirely black. `newNurikabe`, `markNurikabeSea`, `undoNurikabe`, `hintNurikabe`, `encodeNurikabe` and `decodeNurikabe` keep the player's state separate from the answer. The demo stores progress locally. The implementation follows [Nikoli's Nurikabe rules](https://www.nikoli.co.jp/en/puzzles/nurikabe/) and uses its own puzzle layouts.
991
+
992
+ Use `@johnmorrisdotca/kazu/nurikabe`, `@johnmorrisdotca/kazu/nurikabe/play`, or `@johnmorrisdotca/kazu/nurikabe/draw`.
@@ -0,0 +1,2 @@
1
+ export { drawAkari } from "./akariDraw.ts";
2
+ export type { AkariDrawOptions } from "./akariPlay.types.ts";
@@ -0,0 +1 @@
1
+ export { drawAkari } from "./akariDraw.js";
@@ -0,0 +1,12 @@
1
+ export * from "./akari.types.ts";
2
+ export * from "./akari.constants.ts";
3
+ export * from "./akariBoard.ts";
4
+ export * from "./akariSolve.ts";
5
+ export * from "./akariGenerate.ts";
6
+ export * from "./akariGame.ts";
7
+ export { drawAkari } from "./akariDraw.ts";
8
+ export type { AkariDrawOptions } from "./akariPlay.types.ts";
9
+ export { mountAkari } from "./akariMount.ts";
10
+ export { AKARI_PLAY_STYLE } from "./akariStyle.ts";
11
+ export { AKARI_STRINGS } from "./akariStrings.ts";
12
+ export type * from "./akariPlay.types.ts";
@@ -0,0 +1,10 @@
1
+ export * from "./akari.types.js";
2
+ export * from "./akari.constants.js";
3
+ export * from "./akariBoard.js";
4
+ export * from "./akariSolve.js";
5
+ export * from "./akariGenerate.js";
6
+ export * from "./akariGame.js";
7
+ export { drawAkari } from "./akariDraw.js";
8
+ export { mountAkari } from "./akariMount.js";
9
+ export { AKARI_PLAY_STYLE } from "./akariStyle.js";
10
+ export { AKARI_STRINGS } from "./akariStrings.js";
@@ -0,0 +1,2 @@
1
+ export { mountAkari } from "./akariMount.ts";
2
+ export type { AkariMount, AkariMountOptions, AkariDrawOptions, AkariLanguage, AkariMaterial, AkariPieces } from "./akariPlay.types.ts";
@@ -0,0 +1 @@
1
+ export { mountAkari } from "./akariMount.js";
@@ -0,0 +1,2 @@
1
+ export declare const AKARI_MOST_SIDE = 16;
2
+ export declare const AKARI_MOST_NODES = 250000;
@@ -0,0 +1,2 @@
1
+ export const AKARI_MOST_SIDE = 16;
2
+ export const AKARI_MOST_NODES = 250000;
@@ -0,0 +1,33 @@
1
+ /** A board is row-major. null is white, false is a plain black square, and 0–4 are numbered black squares. */
2
+ export type AkariBoard = {
3
+ width: number;
4
+ height: number;
5
+ cells: readonly (number | null | false)[];
6
+ };
7
+ export type AkariPuzzle = AkariBoard & {
8
+ seed: number;
9
+ solution: readonly number[];
10
+ };
11
+ export type AkariCheck = {
12
+ ok: boolean;
13
+ illuminated: number;
14
+ errors: readonly number[];
15
+ dark: readonly number[];
16
+ numbered: readonly number[];
17
+ conflicts: readonly number[];
18
+ };
19
+ export type AkariProgress = Omit<AkariCheck, "ok"> & {
20
+ ok: boolean;
21
+ };
22
+ export type AkariSolve = {
23
+ count: number;
24
+ solution: readonly number[] | null;
25
+ complete: boolean;
26
+ nodes: number;
27
+ };
28
+ export type AkariGame = {
29
+ board: AkariBoard;
30
+ bulbs: readonly number[];
31
+ history: readonly (readonly number[])[];
32
+ helped: boolean;
33
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { AkariBoard, AkariCheck, AkariProgress } from "./akari.types.ts";
2
+ /** Validates dimensions and the three public square kinds. */
3
+ export declare function isAkariBoard(value: unknown): value is AkariBoard;
4
+ /** Returns the orthogonally adjacent cells to a board position. */
5
+ export declare function akariNeighbours(board: AkariBoard, cell: number): number[];
6
+ /** White squares visible from a cell, including itself, stopping at black squares and edges. */
7
+ export declare function akariVisible(board: AkariBoard, cell: number): number[];
8
+ /** Checks a complete placement from the public rules, without consulting a stored answer. */
9
+ export declare function checkAkari(board: AkariBoard, bulbs: readonly number[]): AkariCheck;
10
+ /** Reports immediate contradictions while allowing required clues to remain unsatisfied during play. */
11
+ export declare function progressAkari(board: AkariBoard, bulbs: readonly number[]): AkariProgress;