@johnmorrisdotca/kazu 1.1.0 → 1.3.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 (372) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +424 -10
  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 +13 -0
  6. package/dist/akari-entry.js +11 -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 +7 -0
  10. package/dist/akari.constants.js +7 -0
  11. package/dist/akari.types.d.ts +52 -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 +10 -0
  20. package/dist/akariGenerate.js +101 -0
  21. package/dist/akariLogic.d.ts +10 -0
  22. package/dist/akariLogic.js +84 -0
  23. package/dist/akariMount.d.ts +3 -0
  24. package/dist/akariMount.js +214 -0
  25. package/dist/akariPlay.types.d.ts +25 -0
  26. package/dist/akariPlay.types.js +1 -0
  27. package/dist/akariRate.d.ts +6 -0
  28. package/dist/akariRate.js +34 -0
  29. package/dist/akariSolve.d.ts +6 -0
  30. package/dist/akariSolve.js +18 -0
  31. package/dist/akariStrings.d.ts +98 -0
  32. package/dist/akariStrings.js +53 -0
  33. package/dist/akariStyle.d.ts +1 -0
  34. package/dist/akariStyle.js +9 -0
  35. package/dist/akariTemplate.d.ts +7 -0
  36. package/dist/akariTemplate.js +78 -0
  37. package/dist/csp.d.ts +63 -0
  38. package/dist/csp.js +162 -0
  39. package/dist/fillomino-draw-entry.d.ts +2 -0
  40. package/dist/fillomino-draw-entry.js +1 -0
  41. package/dist/fillomino-entry.d.ts +9 -0
  42. package/dist/fillomino-entry.js +8 -0
  43. package/dist/fillomino-play-entry.d.ts +4 -0
  44. package/dist/fillomino-play-entry.js +3 -0
  45. package/dist/fillomino.constants.d.ts +9 -0
  46. package/dist/fillomino.constants.js +9 -0
  47. package/dist/fillomino.types.d.ts +47 -0
  48. package/dist/fillomino.types.js +1 -0
  49. package/dist/fillominoBoard.d.ts +6 -0
  50. package/dist/fillominoBoard.js +84 -0
  51. package/dist/fillominoBuild.d.ts +10 -0
  52. package/dist/fillominoBuild.js +78 -0
  53. package/dist/fillominoDraw.d.ts +3 -0
  54. package/dist/fillominoDraw.js +31 -0
  55. package/dist/fillominoGame.d.ts +13 -0
  56. package/dist/fillominoGame.js +65 -0
  57. package/dist/fillominoGenerate.d.ts +9 -0
  58. package/dist/fillominoGenerate.js +93 -0
  59. package/dist/fillominoLogic.d.ts +14 -0
  60. package/dist/fillominoLogic.js +177 -0
  61. package/dist/fillominoMount.d.ts +2 -0
  62. package/dist/fillominoMount.js +214 -0
  63. package/dist/fillominoPlay.types.d.ts +25 -0
  64. package/dist/fillominoPlay.types.js +1 -0
  65. package/dist/fillominoRate.d.ts +3 -0
  66. package/dist/fillominoRate.js +57 -0
  67. package/dist/fillominoSolve.d.ts +6 -0
  68. package/dist/fillominoSolve.js +38 -0
  69. package/dist/fillominoStrings.d.ts +102 -0
  70. package/dist/fillominoStrings.js +13 -0
  71. package/dist/fillominoStyle.d.ts +1 -0
  72. package/dist/fillominoStyle.js +13 -0
  73. package/dist/fillominoWorker.d.ts +1 -0
  74. package/dist/fillominoWorker.js +10 -0
  75. package/dist/heyawake-draw-entry.d.ts +2 -0
  76. package/dist/heyawake-draw-entry.js +1 -0
  77. package/dist/heyawake-entry.d.ts +13 -0
  78. package/dist/heyawake-entry.js +11 -0
  79. package/dist/heyawake-play-entry.d.ts +2 -0
  80. package/dist/heyawake-play-entry.js +1 -0
  81. package/dist/heyawake.constants.d.ts +5 -0
  82. package/dist/heyawake.constants.js +5 -0
  83. package/dist/heyawake.types.d.ts +35 -0
  84. package/dist/heyawake.types.js +1 -0
  85. package/dist/heyawakeBoard.d.ts +7 -0
  86. package/dist/heyawakeBoard.js +116 -0
  87. package/dist/heyawakeDraw.d.ts +3 -0
  88. package/dist/heyawakeDraw.js +27 -0
  89. package/dist/heyawakeGame.d.ts +12 -0
  90. package/dist/heyawakeGame.js +51 -0
  91. package/dist/heyawakeGenerate.d.ts +2 -0
  92. package/dist/heyawakeGenerate.js +114 -0
  93. package/dist/heyawakeMount.d.ts +2 -0
  94. package/dist/heyawakeMount.js +106 -0
  95. package/dist/heyawakePlay.types.d.ts +24 -0
  96. package/dist/heyawakePlay.types.js +1 -0
  97. package/dist/heyawakeSolve.d.ts +5 -0
  98. package/dist/heyawakeSolve.js +52 -0
  99. package/dist/heyawakeStrings.d.ts +19 -0
  100. package/dist/heyawakeStrings.js +7 -0
  101. package/dist/heyawakeStyle.d.ts +1 -0
  102. package/dist/heyawakeStyle.js +2 -0
  103. package/dist/heyawakeWorker.d.ts +1 -0
  104. package/dist/heyawakeWorker.js +10 -0
  105. package/dist/hitori-draw-entry.d.ts +2 -0
  106. package/dist/hitori-draw-entry.js +1 -0
  107. package/dist/hitori-entry.d.ts +7 -0
  108. package/dist/hitori-entry.js +7 -0
  109. package/dist/hitori-play-entry.d.ts +4 -0
  110. package/dist/hitori-play-entry.js +3 -0
  111. package/dist/hitori.constants.d.ts +8 -0
  112. package/dist/hitori.constants.js +8 -0
  113. package/dist/hitori.types.d.ts +37 -0
  114. package/dist/hitori.types.js +1 -0
  115. package/dist/hitoriBoard.d.ts +8 -0
  116. package/dist/hitoriBoard.js +73 -0
  117. package/dist/hitoriBuild.d.ts +12 -0
  118. package/dist/hitoriBuild.js +117 -0
  119. package/dist/hitoriDraw.d.ts +4 -0
  120. package/dist/hitoriDraw.js +25 -0
  121. package/dist/hitoriGame.d.ts +10 -0
  122. package/dist/hitoriGame.js +48 -0
  123. package/dist/hitoriGenerate.d.ts +10 -0
  124. package/dist/hitoriGenerate.js +65 -0
  125. package/dist/hitoriLogic.d.ts +12 -0
  126. package/dist/hitoriLogic.js +189 -0
  127. package/dist/hitoriMount.d.ts +3 -0
  128. package/dist/hitoriMount.js +111 -0
  129. package/dist/hitoriPlay.types.d.ts +25 -0
  130. package/dist/hitoriPlay.types.js +1 -0
  131. package/dist/hitoriRate.d.ts +3 -0
  132. package/dist/hitoriRate.js +32 -0
  133. package/dist/hitoriSolve.d.ts +9 -0
  134. package/dist/hitoriSolve.js +18 -0
  135. package/dist/hitoriStrings.d.ts +70 -0
  136. package/dist/hitoriStrings.js +17 -0
  137. package/dist/hitoriStyle.d.ts +1 -0
  138. package/dist/hitoriStyle.js +9 -0
  139. package/dist/index.d.ts +11 -0
  140. package/dist/index.js +11 -0
  141. package/dist/juosan-draw-entry.d.ts +2 -0
  142. package/dist/juosan-draw-entry.js +1 -0
  143. package/dist/juosan-entry.d.ts +11 -0
  144. package/dist/juosan-entry.js +10 -0
  145. package/dist/juosan-play-entry.d.ts +4 -0
  146. package/dist/juosan-play-entry.js +3 -0
  147. package/dist/juosan.constants.d.ts +4 -0
  148. package/dist/juosan.constants.js +4 -0
  149. package/dist/juosan.types.d.ts +33 -0
  150. package/dist/juosan.types.js +1 -0
  151. package/dist/juosanBoard.d.ts +7 -0
  152. package/dist/juosanBoard.js +102 -0
  153. package/dist/juosanDraw.d.ts +4 -0
  154. package/dist/juosanDraw.js +49 -0
  155. package/dist/juosanGame.d.ts +20 -0
  156. package/dist/juosanGame.js +86 -0
  157. package/dist/juosanGenerate.d.ts +3 -0
  158. package/dist/juosanGenerate.js +31 -0
  159. package/dist/juosanMount.d.ts +3 -0
  160. package/dist/juosanMount.js +206 -0
  161. package/dist/juosanPlay.types.d.ts +24 -0
  162. package/dist/juosanPlay.types.js +1 -0
  163. package/dist/juosanSolve.d.ts +3 -0
  164. package/dist/juosanSolve.js +82 -0
  165. package/dist/juosanStrings.d.ts +77 -0
  166. package/dist/juosanStrings.js +43 -0
  167. package/dist/juosanStyle.d.ts +1 -0
  168. package/dist/juosanStyle.js +1 -0
  169. package/dist/kakuro-draw-entry.d.ts +2 -0
  170. package/dist/kakuro-draw-entry.js +1 -0
  171. package/dist/kakuro-entry.d.ts +13 -0
  172. package/dist/kakuro-entry.js +11 -0
  173. package/dist/kakuro-play-entry.d.ts +4 -0
  174. package/dist/kakuro-play-entry.js +3 -0
  175. package/dist/kakuro.constants.d.ts +11 -0
  176. package/dist/kakuro.constants.js +11 -0
  177. package/dist/kakuro.types.d.ts +65 -0
  178. package/dist/kakuro.types.js +1 -0
  179. package/dist/kakuroBoard.d.ts +8 -0
  180. package/dist/kakuroBoard.js +117 -0
  181. package/dist/kakuroBuild.d.ts +30 -0
  182. package/dist/kakuroBuild.js +337 -0
  183. package/dist/kakuroDraw.d.ts +10 -0
  184. package/dist/kakuroDraw.js +21 -0
  185. package/dist/kakuroGame.d.ts +12 -0
  186. package/dist/kakuroGame.js +74 -0
  187. package/dist/kakuroGenerate.d.ts +10 -0
  188. package/dist/kakuroGenerate.js +62 -0
  189. package/dist/kakuroLogic.d.ts +13 -0
  190. package/dist/kakuroLogic.js +153 -0
  191. package/dist/kakuroMount.d.ts +2 -0
  192. package/dist/kakuroMount.js +109 -0
  193. package/dist/kakuroPlay.types.d.ts +20 -0
  194. package/dist/kakuroPlay.types.js +1 -0
  195. package/dist/kakuroRate.d.ts +3 -0
  196. package/dist/kakuroRate.js +40 -0
  197. package/dist/kakuroSolve.d.ts +6 -0
  198. package/dist/kakuroSolve.js +33 -0
  199. package/dist/kakuroStrings.d.ts +82 -0
  200. package/dist/kakuroStrings.js +5 -0
  201. package/dist/kakuroStyle.d.ts +1 -0
  202. package/dist/kakuroStyle.js +1 -0
  203. package/dist/kakuroTemplate.d.ts +3 -0
  204. package/dist/kakuroTemplate.js +130 -0
  205. package/dist/masyu-draw-entry.d.ts +2 -0
  206. package/dist/masyu-draw-entry.js +1 -0
  207. package/dist/masyu-entry.d.ts +6 -0
  208. package/dist/masyu-entry.js +6 -0
  209. package/dist/masyu-play-entry.d.ts +4 -0
  210. package/dist/masyu-play-entry.js +3 -0
  211. package/dist/masyu.constants.d.ts +2 -0
  212. package/dist/masyu.constants.js +2 -0
  213. package/dist/masyu.types.d.ts +46 -0
  214. package/dist/masyu.types.js +1 -0
  215. package/dist/masyuBoard.d.ts +10 -0
  216. package/dist/masyuBoard.js +88 -0
  217. package/dist/masyuDraw.d.ts +2 -0
  218. package/dist/masyuDraw.js +33 -0
  219. package/dist/masyuGame.d.ts +9 -0
  220. package/dist/masyuGame.js +46 -0
  221. package/dist/masyuGenerate.d.ts +3 -0
  222. package/dist/masyuGenerate.js +56 -0
  223. package/dist/masyuMount.d.ts +2 -0
  224. package/dist/masyuMount.js +118 -0
  225. package/dist/masyuPlay.types.d.ts +1 -0
  226. package/dist/masyuPlay.types.js +1 -0
  227. package/dist/masyuSolve.d.ts +6 -0
  228. package/dist/masyuSolve.js +88 -0
  229. package/dist/masyuStrings.d.ts +28 -0
  230. package/dist/masyuStrings.js +4 -0
  231. package/dist/masyuStyle.d.ts +1 -0
  232. package/dist/masyuStyle.js +11 -0
  233. package/dist/nurikabe-draw-entry.d.ts +2 -0
  234. package/dist/nurikabe-draw-entry.js +1 -0
  235. package/dist/nurikabe-entry.d.ts +6 -0
  236. package/dist/nurikabe-entry.js +6 -0
  237. package/dist/nurikabe-play-entry.d.ts +4 -0
  238. package/dist/nurikabe-play-entry.js +3 -0
  239. package/dist/nurikabe.constants.d.ts +2 -0
  240. package/dist/nurikabe.constants.js +2 -0
  241. package/dist/nurikabe.types.d.ts +20 -0
  242. package/dist/nurikabe.types.js +1 -0
  243. package/dist/nurikabeBoard.d.ts +8 -0
  244. package/dist/nurikabeBoard.js +72 -0
  245. package/dist/nurikabeDraw.d.ts +4 -0
  246. package/dist/nurikabeDraw.js +15 -0
  247. package/dist/nurikabeGame.d.ts +8 -0
  248. package/dist/nurikabeGame.js +40 -0
  249. package/dist/nurikabeGenerate.d.ts +3 -0
  250. package/dist/nurikabeGenerate.js +31 -0
  251. package/dist/nurikabeMount.d.ts +3 -0
  252. package/dist/nurikabeMount.js +99 -0
  253. package/dist/nurikabePlay.types.d.ts +25 -0
  254. package/dist/nurikabePlay.types.js +1 -0
  255. package/dist/nurikabeSolve.d.ts +6 -0
  256. package/dist/nurikabeSolve.js +75 -0
  257. package/dist/nurikabeStrings.d.ts +70 -0
  258. package/dist/nurikabeStrings.js +5 -0
  259. package/dist/nurikabeStyle.d.ts +1 -0
  260. package/dist/nurikabeStyle.js +9 -0
  261. package/dist/ripple-draw-entry.d.ts +2 -0
  262. package/dist/ripple-draw-entry.js +1 -0
  263. package/dist/ripple-entry.d.ts +12 -0
  264. package/dist/ripple-entry.js +11 -0
  265. package/dist/ripple-play-entry.d.ts +4 -0
  266. package/dist/ripple-play-entry.js +3 -0
  267. package/dist/ripple.constants.d.ts +4 -0
  268. package/dist/ripple.constants.js +4 -0
  269. package/dist/ripple.types.d.ts +34 -0
  270. package/dist/ripple.types.js +1 -0
  271. package/dist/rippleBoard.d.ts +7 -0
  272. package/dist/rippleBoard.js +99 -0
  273. package/dist/rippleDraw.d.ts +3 -0
  274. package/dist/rippleDraw.js +32 -0
  275. package/dist/rippleGame.d.ts +13 -0
  276. package/dist/rippleGame.js +85 -0
  277. package/dist/rippleGenerate.d.ts +3 -0
  278. package/dist/rippleGenerate.js +57 -0
  279. package/dist/rippleMount.d.ts +2 -0
  280. package/dist/rippleMount.js +179 -0
  281. package/dist/ripplePlay.types.d.ts +23 -0
  282. package/dist/ripplePlay.types.js +1 -0
  283. package/dist/rippleSolve.d.ts +6 -0
  284. package/dist/rippleSolve.js +83 -0
  285. package/dist/rippleStrings.d.ts +86 -0
  286. package/dist/rippleStrings.js +5 -0
  287. package/dist/rippleStyle.d.ts +1 -0
  288. package/dist/rippleStyle.js +1 -0
  289. package/dist/shikaku-entry.d.ts +2 -0
  290. package/dist/shikaku-entry.js +2 -0
  291. package/dist/shikaku.constants.d.ts +5 -2
  292. package/dist/shikaku.constants.js +5 -2
  293. package/dist/shikaku.types.d.ts +18 -1
  294. package/dist/shikakuBuild.d.ts +21 -0
  295. package/dist/shikakuBuild.js +135 -0
  296. package/dist/shikakuGenerate.d.ts +6 -1
  297. package/dist/shikakuGenerate.js +50 -35
  298. package/dist/shikakuLogic.d.ts +13 -0
  299. package/dist/shikakuLogic.js +84 -0
  300. package/dist/shikakuPacks.d.ts +27 -0
  301. package/dist/shikakuPacks.js +26 -0
  302. package/dist/shikakuRate.d.ts +3 -0
  303. package/dist/shikakuRate.js +27 -0
  304. package/dist/shikakuSolve.d.ts +1 -1
  305. package/dist/shikakuSolve.js +23 -44
  306. package/dist/shikakuTemplate.d.ts +3 -0
  307. package/dist/shikakuTemplate.js +43 -0
  308. package/dist/slitherlink-draw-entry.d.ts +2 -0
  309. package/dist/slitherlink-draw-entry.js +1 -0
  310. package/dist/slitherlink-entry.d.ts +12 -0
  311. package/dist/slitherlink-entry.js +10 -0
  312. package/dist/slitherlink-play-entry.d.ts +4 -0
  313. package/dist/slitherlink-play-entry.js +3 -0
  314. package/dist/slitherlink.constants.d.ts +7 -0
  315. package/dist/slitherlink.constants.js +7 -0
  316. package/dist/slitherlink.types.d.ts +52 -0
  317. package/dist/slitherlink.types.js +1 -0
  318. package/dist/slitherlinkBoard.d.ts +12 -0
  319. package/dist/slitherlinkBoard.js +132 -0
  320. package/dist/slitherlinkDraw.d.ts +4 -0
  321. package/dist/slitherlinkDraw.js +51 -0
  322. package/dist/slitherlinkGame.d.ts +8 -0
  323. package/dist/slitherlinkGame.js +63 -0
  324. package/dist/slitherlinkGenerate.d.ts +10 -0
  325. package/dist/slitherlinkGenerate.js +182 -0
  326. package/dist/slitherlinkLogic.d.ts +10 -0
  327. package/dist/slitherlinkLogic.js +267 -0
  328. package/dist/slitherlinkMount.d.ts +3 -0
  329. package/dist/slitherlinkMount.js +267 -0
  330. package/dist/slitherlinkPlay.types.d.ts +23 -0
  331. package/dist/slitherlinkPlay.types.js +1 -0
  332. package/dist/slitherlinkRate.d.ts +3 -0
  333. package/dist/slitherlinkRate.js +26 -0
  334. package/dist/slitherlinkSolve.d.ts +6 -0
  335. package/dist/slitherlinkSolve.js +19 -0
  336. package/dist/slitherlinkStrings.d.ts +86 -0
  337. package/dist/slitherlinkStrings.js +47 -0
  338. package/dist/slitherlinkStyle.d.ts +1 -0
  339. package/dist/slitherlinkStyle.js +18 -0
  340. package/dist/slitherlinkTemplate.d.ts +3 -0
  341. package/dist/slitherlinkTemplate.js +101 -0
  342. package/dist/version.d.ts +1 -1
  343. package/dist/version.js +1 -1
  344. package/dist/yajilin-draw-entry.d.ts +2 -0
  345. package/dist/yajilin-draw-entry.js +1 -0
  346. package/dist/yajilin-entry.d.ts +6 -0
  347. package/dist/yajilin-entry.js +6 -0
  348. package/dist/yajilin-play-entry.d.ts +4 -0
  349. package/dist/yajilin-play-entry.js +3 -0
  350. package/dist/yajilin.constants.d.ts +2 -0
  351. package/dist/yajilin.constants.js +2 -0
  352. package/dist/yajilin.types.d.ts +63 -0
  353. package/dist/yajilin.types.js +1 -0
  354. package/dist/yajilinBoard.d.ts +9 -0
  355. package/dist/yajilinBoard.js +91 -0
  356. package/dist/yajilinDraw.d.ts +2 -0
  357. package/dist/yajilinDraw.js +33 -0
  358. package/dist/yajilinGame.d.ts +15 -0
  359. package/dist/yajilinGame.js +58 -0
  360. package/dist/yajilinGenerate.d.ts +3 -0
  361. package/dist/yajilinGenerate.js +224 -0
  362. package/dist/yajilinMount.d.ts +2 -0
  363. package/dist/yajilinMount.js +162 -0
  364. package/dist/yajilinPlay.types.d.ts +1 -0
  365. package/dist/yajilinPlay.types.js +1 -0
  366. package/dist/yajilinSolve.d.ts +6 -0
  367. package/dist/yajilinSolve.js +82 -0
  368. package/dist/yajilinStrings.d.ts +32 -0
  369. package/dist/yajilinStrings.js +4 -0
  370. package/dist/yajilinStyle.d.ts +1 -0
  371. package/dist/yajilinStyle.js +12 -0
  372. package/package.json +180 -3
package/CHANGELOG.md CHANGED
@@ -6,6 +6,45 @@ All notable changes to this project are written here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.3.0] - 2026-10-05
10
+
11
+ ### Added
12
+
13
+ - **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.
14
+ - **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).
15
+ - **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`.
16
+ - 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.
17
+ - `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`.
18
+ - The demo has a Level choice, in English and Japanese, on all six pages, and the sizes above.
19
+
20
+ ### Changed
21
+
22
+ - **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.
23
+ - 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.
24
+ - 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.
25
+ - 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.
26
+
27
+ ### Fixed
28
+
29
+ - `generateKakuro(97)` threw "No uniquely solvable Kakuro board was proved within the generation budget"; no seed throws now, at any level or size.
30
+ - `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.
31
+
32
+ ## [1.2.0] - 2026-10-05
33
+
34
+ ### Added
35
+
36
+ - Hitori and Nurikabe: independent shading rules, bounded solution counting, original proof-backed puzzle families, bilingual players and package entry points.
37
+ - Juosan: a dedicated territory model, verified small training layouts and a bilingual player for supplied boards.
38
+ - Nine named Shikaku challenges across square, wide and tall routes.
39
+ - Akari: a separate rule engine, bounded solution counter, seeded unique puzzle generator, immutable play, accessible bilingual player, drawing and demo.
40
+ - Slitherlink: a dedicated edge-loop engine, bounded unique-puzzle generation, accessible bilingual player, demo and package entry points.
41
+ - Ripple Effect: a dedicated room and spacing engine, bounded unique 9×9 generation, bilingual accessible player, demo and package entries.
42
+ - Kakuro: a seeded uniquely proved crossword-sum family, independent bounded solver, immutable progress, SVG and accessible English/Japanese player.
43
+
44
+ - Masyu and Yajilin: cell-centre loop engines, bounded proof-backed 5×5 families, drawing, bilingual players and saved progress.
45
+ - Fillomino: connected numbered regions, bounded original rectangular generation, independent checks, hints and a bilingual player.
46
+ - Heyawake: rectangular rooms, shading and white-path rules, bounded original small-board generation, hints and a bilingual player.
47
+
9
48
  ## [1.1.0] - 2026-10-04
10
49
 
11
50
  ### 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,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
- - **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.
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).
64
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.
65
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.
66
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.
@@ -442,17 +444,54 @@ Not here yet, and each welcome as an [issue](https://github.com/johnmorrisdotca/
442
444
 
443
445
  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
446
 
447
+ ## Masyu — pearls and a single loop
448
+
449
+ 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.
450
+
451
+ ```ts
452
+ import { generateMasyu, checkMasyu, solveMasyu } from "@johnmorrisdotca/kazu/masyu";
453
+ import { mountMasyu } from "@johnmorrisdotca/kazu/masyu/play";
454
+
455
+ const puzzle = generateMasyu(5, 42);
456
+ checkMasyu(puzzle, puzzle.solution); // { ok: true }
457
+ solveMasyu(puzzle); // count: 1, complete: true
458
+ mountMasyu(document.querySelector("#board"), { board: puzzle });
459
+ ```
460
+
461
+ 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.
462
+
463
+ [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.
464
+
465
+ ## Yajilin — arrows, shaded cells and a loop
466
+
467
+ 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.
468
+
469
+ ```ts
470
+ import { generateYajilin, checkYajilin, solveYajilin } from "@johnmorrisdotca/kazu/yajilin";
471
+ import { mountYajilin } from "@johnmorrisdotca/kazu/yajilin/play";
472
+
473
+ const puzzle = generateYajilin(5, 42);
474
+ checkYajilin(puzzle, puzzle.solution.shaded, puzzle.solution.edges); // { ok: true }
475
+ solveYajilin(puzzle); // count: 1, complete: true
476
+ mountYajilin(document.querySelector("#board"), { board: puzzle });
477
+ ```
478
+
479
+ 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.
480
+
481
+ [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.
482
+
445
483
  ## Shikaku — rectangles in Kazu
446
484
 
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.
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.
448
486
 
449
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.
450
488
 
451
489
  ```js
452
490
  import { generateShikaku, newShikaku, placeShikaku, checkShikaku } from "@johnmorrisdotca/kazu/shikaku";
453
491
  import { mountShikaku } from "@johnmorrisdotca/kazu/shikaku/play";
492
+ import { generateShikakuChallenge } from "@johnmorrisdotca/kazu/shikaku";
454
493
 
455
- const puzzle = generateShikaku(7, 7, "medium", 42);
494
+ const { puzzle } = generateShikakuChallenge("wide", 2); // Causeway, a proved 10 × 6 puzzle
456
495
  const player = mountShikaku(document.querySelector("#board"), {
457
496
  board: puzzle, material: "ivory", pieces: "ink", language: "en",
458
497
  onFinish: game => console.log(game.helped ? "Solved with help" : "Solved"),
@@ -460,13 +499,32 @@ const player = mountShikaku(document.querySelector("#board"), {
460
499
  // player.progress() saves public clues and rectangles; player.destroy() removes the player.
461
500
  ```
462
501
 
502
+ `generateShikakuChallenge("square" | "wide" | "tall", 1..3)` selects a named challenge and checks uniqueness with the existing solver.
503
+
463
504
  Use `@johnmorrisdotca/kazu/shikaku`, `@johnmorrisdotca/kazu/shikaku/play`, or `@johnmorrisdotca/kazu/shikaku/draw`.
464
505
 
465
506
  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
507
 
508
+ ## Juosan — horizontal and vertical marks
509
+
510
+ 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.
511
+
512
+ ```ts
513
+ import { checkJuosan, generateJuosan, solveJuosan } from "@johnmorrisdotca/kazu/juosan";
514
+ import { mountJuosan } from "@johnmorrisdotca/kazu/juosan/play";
515
+
516
+ const puzzle = generateJuosan(3, 2, "easy", 42); // 3 × 2 training board; answer proved unique
517
+ solveJuosan(puzzle, 2); // { count: 1, complete: true, ... }
518
+ checkJuosan(puzzle, puzzle.solution); // { ok: true, errors: [] }
519
+ const game = mountJuosan(document.querySelector("#board")!, { board: puzzle });
520
+ ```
521
+
522
+ 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.
523
+
467
524
  - `ShikakuBoard`: `width`, `height`, and row-major `clues` (zero for an empty cell). Dimensions are 2–16; clue areas sum to the grid area.
468
525
  - `ShikakuRectangle`: zero-based `x`, `y`, `width`, `height`.
469
- - `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.
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.
470
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.
471
529
  - `checkShikaku(board, rectangles)`: coverage and rectangle rule errors, independent of a stored answer. It accepts any valid completion.
472
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.
@@ -479,6 +537,146 @@ The demo is `site/shikaku.html` after `pnpm site`; its generator runs in a modul
479
537
 
480
538
  [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
539
 
540
+ ## Akari — light the grid
541
+
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.
543
+
544
+ ```js
545
+ import { generateAkari, checkAkari } from "@johnmorrisdotca/kazu/akari";
546
+ import { mountAkari } from "@johnmorrisdotca/kazu/akari/play";
547
+
548
+ const puzzle = generateAkari(7, 7, 42, "hard"); // width, height, seed, level ("medium" if left out)
549
+ const player = mountAkari(document.querySelector("#board"), {
550
+ board: puzzle, material: "ivory", pieces: "ink", language: "en",
551
+ });
552
+ // player.progress() saves public clues and bulbs; player.destroy() removes the player.
553
+ ```
554
+
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.
556
+
557
+ - `AkariBoard`: width, height and row-major `cells`: `null` is white, `false` is an unnumbered black square, and `0`–`4` are numbered black squares.
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.
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.
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.
562
+ - `newAkari`, `toggleAkari`, `undoAkari`, `akariFinished`, `hintAkari`: immutable play operations. Hints require a proved unique answer and mark the game as helped.
563
+ - `encodeAkari`, `decodeAkari`: versioned JSON containing only public board data and player bulbs.
564
+ - `drawAkari(board, options)`: SVG with `ivory`, `wood` and `slate` materials, `ink` or `tiles` bulb pieces, and `en` or `ja` labels.
565
+ - `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`.
566
+
567
+ 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.
568
+
569
+ [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.
570
+
571
+ ## Slitherlink
572
+
573
+ ```js
574
+ import { generateSlitherlink } from "@johnmorrisdotca/kazu/slitherlink";
575
+ import { mountSlitherlink } from "@johnmorrisdotca/kazu/slitherlink/play";
576
+
577
+ const puzzle = generateSlitherlink(7, 7, 42, "hard"); // width, height, seed, level ("medium" if left out)
578
+ const player = mountSlitherlink(document.querySelector("#board"), {
579
+ board: puzzle, material: "ivory", language: "en",
580
+ });
581
+ // player.progress() saves the public clues and selected edges.
582
+ ```
583
+
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.
585
+
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.
587
+
588
+ ## Ripple Effect
589
+
590
+ ```ts
591
+ import { generateRipple, newRipple, hintRipple } from "@johnmorrisdotca/kazu/ripple";
592
+ import { mountRipple } from "@johnmorrisdotca/kazu/ripple/play";
593
+
594
+ const puzzle = generateRipple(9, 9, 42);
595
+ const game = newRipple(puzzle);
596
+ const hint = hintRipple(game); // only returned after a unique completion is proved
597
+ const player = mountRipple(document.querySelector<HTMLElement>("#board")!, {
598
+ board: puzzle, material: "ivory", pieces: "ink", language: "en",
599
+ });
600
+ ```
601
+
602
+ 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.
603
+
604
+ 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/).
605
+
606
+ ## Kakuro — crossword sums in Kazu
607
+
608
+ 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.
609
+
610
+ ```js
611
+ import { generateKakuro, solveKakuro, checkKakuro } from "@johnmorrisdotca/kazu/kakuro";
612
+ import { mountKakuro } from "@johnmorrisdotca/kazu/kakuro/play";
613
+
614
+ const puzzle = generateKakuro(42, "hard", 8); // seed, level ("medium"), size including the totals' row and column (10)
615
+ const proof = solveKakuro(puzzle); // uniqueness only when complete && count === 1
616
+ const player = mountKakuro(document.querySelector("#board"), { board: puzzle, language: "en" });
617
+ player.progress(); // public clues, entries and pencil marks; no answer
618
+ ```
619
+
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.
621
+
622
+ The Kakuro entries are `@johnmorrisdotca/kazu/kakuro`, `@johnmorrisdotca/kazu/kakuro/play` and `@johnmorrisdotca/kazu/kakuro/draw`.
623
+
624
+ ## Fillomino — connected regions with exact areas
625
+
626
+ The dedicated package entries are `@johnmorrisdotca/kazu/fillomino`, `@johnmorrisdotca/kazu/fillomino/play`, and `@johnmorrisdotca/kazu/fillomino/draw`.
627
+
628
+ 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.
629
+
630
+ ```ts
631
+ import { generateFillomino, checkFillomino, solveFillomino } from "@johnmorrisdotca/kazu/fillomino";
632
+ import { mountFillomino } from "@johnmorrisdotca/kazu/fillomino/play";
633
+
634
+ const puzzle = generateFillomino(6, 6, "hard", 17);
635
+ const result = solveFillomino(puzzle);
636
+ if (!result.complete || result.count !== 1) throw new Error("The answer was not proved unique");
637
+ checkFillomino(puzzle, result.solution);
638
+ mountFillomino(document.querySelector("#board"), { board: puzzle });
639
+ ```
640
+
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.
642
+
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.
644
+
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).
646
+
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.
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
+
664
+ ## Heyawake — rooms and white paths
665
+
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.
667
+
668
+ ```js
669
+ import { generateHeyawake, checkHeyawake, solveHeyawake } from "@johnmorrisdotca/kazu/heyawake";
670
+
671
+ const puzzle = generateHeyawake(5, 4, "easy", 17);
672
+ checkHeyawake(puzzle, puzzle.solution); // checks room counts and all three global rules
673
+ solveHeyawake(puzzle); // { count: 1, complete: true, ... }
674
+ ```
675
+
676
+ 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).
677
+
678
+ [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.
679
+
482
680
  ## Architecture
483
681
 
484
682
  The generators, the solvers, the check, the hint and the game are plain functions over short codes, with no
@@ -486,25 +684,203 @@ DOM. The drawing is SVG text in an entry of its own, so a server that only check
486
684
  the page's part (the mount and the element) is another.
487
685
 
488
686
  ```text
687
+ ├── hitori-draw-entry.ts
688
+ ├── hitori-entry.ts
689
+ ├── hitori-play-entry.ts
690
+ ├── hitori.constants.ts
691
+ ├── hitori.types.ts
692
+ ├── hitoriBoard.ts
693
+ ├── hitoriDraw.ts
694
+ ├── hitoriGame.ts
695
+ ├── hitoriGenerate.ts
696
+ ├── hitoriBuild.ts
697
+ ├── hitoriLogic.ts
698
+ ├── hitoriRate.ts
699
+ ├── hitoriMount.ts
700
+ ├── hitoriPlay.types.ts
701
+ ├── hitoriSolve.ts
702
+ ├── hitoriStrings.ts
703
+ ├── hitoriStyle.ts
704
+ ├── nurikabe-draw-entry.ts
705
+ ├── nurikabe-entry.ts
706
+ ├── nurikabe-play-entry.ts
707
+ ├── nurikabe.constants.ts
708
+ ├── nurikabe.types.ts
709
+ ├── nurikabeBoard.ts
710
+ ├── nurikabeDraw.ts
711
+ ├── nurikabeGame.ts
712
+ ├── nurikabeGenerate.ts
713
+ ├── nurikabeMount.ts
714
+ ├── nurikabePlay.types.ts
715
+ ├── nurikabeSolve.ts
716
+ ├── nurikabeStrings.ts
717
+ ├── nurikabeStyle.ts
718
+ ├── juosan-draw-entry.ts
719
+ ├── juosan-entry.ts
720
+ ├── juosan-play-entry.ts
721
+ ├── juosan.constants.ts
722
+ ├── juosan.types.ts
723
+ ├── juosanBoard.ts
724
+ ├── juosanDraw.ts
725
+ ├── juosanGame.ts
726
+ ├── juosanGenerate.ts
727
+ ├── juosanMount.ts
728
+ ├── juosanPlay.types.ts
729
+ ├── juosanSolve.ts
730
+ ├── juosanStrings.ts
731
+ ├── juosanStyle.ts
732
+ ├── masyu-draw-entry.ts
733
+ ├── masyu-entry.ts
734
+ ├── masyu-play-entry.ts
735
+ ├── masyu.constants.ts
736
+ ├── masyu.types.ts
737
+ ├── masyuBoard.ts
738
+ ├── masyuDraw.ts
739
+ ├── masyuGame.ts
740
+ ├── masyuGenerate.ts
741
+ ├── masyuMount.ts
742
+ ├── masyuPlay.types.ts
743
+ ├── masyuSolve.ts
744
+ ├── masyuStrings.ts
745
+ ├── masyuStyle.ts
746
+ ├── yajilin-draw-entry.ts
747
+ ├── yajilin-entry.ts
748
+ ├── yajilin-play-entry.ts
749
+ ├── yajilin.constants.ts
750
+ ├── yajilin.types.ts
751
+ ├── yajilinBoard.ts
752
+ ├── yajilinDraw.ts
753
+ ├── yajilinGame.ts
754
+ ├── yajilinGenerate.ts
755
+ ├── yajilinMount.ts
756
+ ├── yajilinPlay.types.ts
757
+ ├── yajilinSolve.ts
758
+ ├── yajilinStrings.ts
759
+ ├── yajilinStyle.ts
760
+ ├── fillomino-draw-entry.ts
761
+ ├── fillomino-entry.ts
762
+ ├── fillomino-play-entry.ts
763
+ ├── fillomino.constants.ts
764
+ ├── fillomino.types.ts
765
+ ├── fillominoBoard.ts
766
+ ├── fillominoDraw.ts
767
+ ├── fillominoGame.ts
768
+ ├── fillominoGenerate.ts
769
+ ├── fillominoBuild.ts
770
+ ├── fillominoLogic.ts
771
+ ├── fillominoRate.ts
772
+ ├── fillominoMount.ts
773
+ ├── fillominoPlay.types.ts
774
+ ├── fillominoSolve.ts
775
+ ├── fillominoStrings.ts
776
+ ├── fillominoStyle.ts
777
+ ├── fillominoWorker.ts
489
778
  ├── shikaku-draw-entry.ts
490
779
  ├── shikaku-entry.ts
491
780
  ├── shikaku-play-entry.ts
781
+ ├── kakuro-draw-entry.ts
782
+ ├── kakuro-entry.ts
783
+ ├── kakuro-play-entry.ts
784
+ ├── kakuro.constants.ts
785
+ ├── kakuro.types.ts
786
+ ├── kakuroBoard.ts
787
+ ├── kakuroDraw.ts
788
+ ├── kakuroGame.ts
789
+ ├── kakuroGenerate.ts
790
+ ├── kakuroBuild.ts
791
+ ├── kakuroLogic.ts
792
+ ├── kakuroRate.ts
793
+ ├── kakuroTemplate.ts
794
+ ├── kakuroMount.ts
795
+ ├── kakuroPlay.types.ts
796
+ ├── kakuroSolve.ts
797
+ ├── kakuroStrings.ts
798
+ ├── kakuroStyle.ts
492
799
  ├── shikaku.constants.ts
493
800
  ├── shikaku.types.ts
494
801
  ├── shikakuBoard.ts
495
802
  ├── shikakuDraw.ts
496
803
  ├── shikakuGame.ts
497
804
  ├── shikakuGenerate.ts
805
+ ├── shikakuBuild.ts
806
+ ├── shikakuLogic.ts
807
+ ├── shikakuRate.ts
808
+ ├── shikakuTemplate.ts
498
809
  ├── shikakuMount.ts
810
+ ├── shikakuPacks.ts
499
811
  ├── shikakuPlay.types.ts
500
812
  ├── shikakuSolve.ts
501
813
  ├── shikakuStrings.ts
502
814
  ├── shikakuStyle.ts
503
815
  ├── shikakuWorker.ts
816
+ ├── akari-draw-entry.ts
817
+ ├── akari-entry.ts
818
+ ├── akari-play-entry.ts
819
+ ├── akari.constants.ts
820
+ ├── akari.types.ts
821
+ ├── akariBoard.ts
822
+ ├── akariDraw.ts
823
+ ├── akariGame.ts
824
+ ├── akariGenerate.ts
825
+ ├── akariLogic.ts
826
+ ├── akariRate.ts
827
+ ├── akariTemplate.ts
828
+ ├── akariMount.ts
829
+ ├── akariPlay.types.ts
830
+ ├── akariSolve.ts
831
+ ├── akariStrings.ts
832
+ ├── akariStyle.ts
833
+ ├── slitherlink-draw-entry.ts
834
+ ├── slitherlink-entry.ts
835
+ ├── slitherlink-play-entry.ts
836
+ ├── slitherlink.constants.ts
837
+ ├── slitherlink.types.ts
838
+ ├── slitherlinkBoard.ts
839
+ ├── slitherlinkDraw.ts
840
+ ├── slitherlinkGame.ts
841
+ ├── slitherlinkGenerate.ts
842
+ ├── slitherlinkLogic.ts
843
+ ├── slitherlinkRate.ts
844
+ ├── slitherlinkTemplate.ts
845
+ ├── slitherlinkMount.ts
846
+ ├── slitherlinkPlay.types.ts
847
+ ├── slitherlinkSolve.ts
848
+ ├── slitherlinkStrings.ts
849
+ ├── slitherlinkStyle.ts
850
+ ├── ripple-draw-entry.ts
851
+ ├── ripple-entry.ts
852
+ ├── ripple-play-entry.ts
853
+ ├── ripple.constants.ts
854
+ ├── ripple.types.ts
855
+ ├── rippleBoard.ts
856
+ ├── rippleDraw.ts
857
+ ├── rippleGame.ts
858
+ ├── rippleGenerate.ts
859
+ ├── rippleMount.ts
860
+ ├── ripplePlay.types.ts
861
+ ├── rippleSolve.ts
862
+ ├── rippleStrings.ts
863
+ ├── rippleStyle.ts
864
+ ├── heyawake-draw-entry.ts
865
+ ├── heyawake-entry.ts
866
+ ├── heyawake-play-entry.ts
867
+ ├── heyawake.constants.ts
868
+ ├── heyawake.types.ts
869
+ ├── heyawakeBoard.ts
870
+ ├── heyawakeDraw.ts
871
+ ├── heyawakeGame.ts
872
+ ├── heyawakeGenerate.ts
873
+ ├── heyawakeMount.ts
874
+ ├── heyawakePlay.types.ts
875
+ ├── heyawakeSolve.ts
876
+ ├── heyawakeStrings.ts
877
+ ├── heyawakeStyle.ts
878
+ ├── heyawakeWorker.ts
504
879
  src/
505
880
  ├── index.ts the main entry: everything but the drawing and the page
506
881
  ├── kinds.ts the six puzzles' keys, sizes and levels, and the shape of a puzzle
507
882
  ├── random.ts the seeded random numbers every puzzle is made from
883
+ ├── csp.ts the one small engine under the six grid kinds: counting answers, and reasoning with and without supposing
508
884
  ├── cells.ts a grid of numbers as a string, 1 to 9 and A to G
509
885
  ├── layout.ts the groups that must each hold every number once: rows, columns, boxes, regions, diagonals, cages
510
886
  ├── groupSolve.ts the solver for puzzles made of groups: counting, singles, depth
@@ -568,7 +944,7 @@ Using Kazu in something? Open an *Add my project* issue and we will add you.
568
944
  ### The family
569
945
 
570
946
  <!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
571
- Kazu is one of nineteen packages, each made for the same site, each at
947
+ Kazu is one of twenty-two packages, each made for the same site, each at
572
948
  [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
573
949
 
574
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/).
@@ -590,8 +966,11 @@ Kazu is one of nineteen packages, each made for the same site, each at
590
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/).
591
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/).
592
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/).
593
972
 
594
- **This package is Kazu.** The demos of all nineteen share one header and footer, so each links the rest.
973
+ **This package is Kazu.** The demos of all twenty-two share one header and footer, so each links the rest.
595
974
  <!-- family:end -->
596
975
 
597
976
  ## Development
@@ -618,3 +997,38 @@ See [CHANGELOG.md](./CHANGELOG.md).
618
997
  ## Licence
619
998
 
620
999
  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.
1000
+
1001
+ ## Hitori
1002
+
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.
1004
+
1005
+ ```ts
1006
+ import { generateHitori, checkHitori, solveHitori } from "@johnmorrisdotca/kazu/hitori";
1007
+ import { drawHitori } from "@johnmorrisdotca/kazu/hitori/draw";
1008
+ import { mountHitori } from "@johnmorrisdotca/kazu/hitori/play";
1009
+
1010
+ const puzzle = generateHitori(9, 42, "hard"); // size, seed, level ("medium" if left out)
1011
+ checkHitori(puzzle, puzzle.solution); // { ok: true, errors: [] }
1012
+ solveHitori(puzzle); // count: 1, complete: true
1013
+ ```
1014
+
1015
+ `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.
1016
+
1017
+ Use `@johnmorrisdotca/kazu/hitori`, `@johnmorrisdotca/kazu/hitori/play`, or `@johnmorrisdotca/kazu/hitori/draw`.
1018
+ ## Nurikabe
1019
+
1020
+ 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.
1021
+
1022
+ ```ts
1023
+ import { generateNurikabe, checkNurikabe, solveNurikabe } from "@johnmorrisdotca/kazu/nurikabe";
1024
+ import { drawNurikabe } from "@johnmorrisdotca/kazu/nurikabe/draw";
1025
+ import { mountNurikabe } from "@johnmorrisdotca/kazu/nurikabe/play";
1026
+
1027
+ const puzzle = generateNurikabe(42);
1028
+ checkNurikabe(puzzle, puzzle.solution); // { ok: true, errors: [] }
1029
+ solveNurikabe(puzzle); // count: 1, complete: true
1030
+ ```
1031
+
1032
+ 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.
1033
+
1034
+ 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,13 @@
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 "./akariRate.ts";
7
+ export * from "./akariGame.ts";
8
+ export { drawAkari } from "./akariDraw.ts";
9
+ export type { AkariDrawOptions } from "./akariPlay.types.ts";
10
+ export { mountAkari } from "./akariMount.ts";
11
+ export { AKARI_PLAY_STYLE } from "./akariStyle.ts";
12
+ export { AKARI_STRINGS } from "./akariStrings.ts";
13
+ export type * from "./akariPlay.types.ts";