@johnmorrisdotca/kazu 1.0.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 (355) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +429 -5
  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/draw-entry.d.ts +1 -0
  32. package/dist/draw-entry.js +1 -0
  33. package/dist/fillomino-draw-entry.d.ts +2 -0
  34. package/dist/fillomino-draw-entry.js +1 -0
  35. package/dist/fillomino-entry.d.ts +8 -0
  36. package/dist/fillomino-entry.js +7 -0
  37. package/dist/fillomino-play-entry.d.ts +4 -0
  38. package/dist/fillomino-play-entry.js +3 -0
  39. package/dist/fillomino.constants.d.ts +5 -0
  40. package/dist/fillomino.constants.js +5 -0
  41. package/dist/fillomino.types.d.ts +30 -0
  42. package/dist/fillomino.types.js +1 -0
  43. package/dist/fillominoBoard.d.ts +6 -0
  44. package/dist/fillominoBoard.js +84 -0
  45. package/dist/fillominoDraw.d.ts +3 -0
  46. package/dist/fillominoDraw.js +31 -0
  47. package/dist/fillominoGame.d.ts +13 -0
  48. package/dist/fillominoGame.js +65 -0
  49. package/dist/fillominoGenerate.d.ts +3 -0
  50. package/dist/fillominoGenerate.js +125 -0
  51. package/dist/fillominoMount.d.ts +2 -0
  52. package/dist/fillominoMount.js +211 -0
  53. package/dist/fillominoPlay.types.d.ts +25 -0
  54. package/dist/fillominoPlay.types.js +1 -0
  55. package/dist/fillominoSolve.d.ts +6 -0
  56. package/dist/fillominoSolve.js +111 -0
  57. package/dist/fillominoStrings.d.ts +102 -0
  58. package/dist/fillominoStrings.js +13 -0
  59. package/dist/fillominoStyle.d.ts +1 -0
  60. package/dist/fillominoStyle.js +13 -0
  61. package/dist/fillominoWorker.d.ts +1 -0
  62. package/dist/fillominoWorker.js +10 -0
  63. package/dist/heyawake-draw-entry.d.ts +2 -0
  64. package/dist/heyawake-draw-entry.js +1 -0
  65. package/dist/heyawake-entry.d.ts +13 -0
  66. package/dist/heyawake-entry.js +11 -0
  67. package/dist/heyawake-play-entry.d.ts +2 -0
  68. package/dist/heyawake-play-entry.js +1 -0
  69. package/dist/heyawake.constants.d.ts +5 -0
  70. package/dist/heyawake.constants.js +5 -0
  71. package/dist/heyawake.types.d.ts +35 -0
  72. package/dist/heyawake.types.js +1 -0
  73. package/dist/heyawakeBoard.d.ts +7 -0
  74. package/dist/heyawakeBoard.js +116 -0
  75. package/dist/heyawakeDraw.d.ts +3 -0
  76. package/dist/heyawakeDraw.js +27 -0
  77. package/dist/heyawakeGame.d.ts +12 -0
  78. package/dist/heyawakeGame.js +51 -0
  79. package/dist/heyawakeGenerate.d.ts +2 -0
  80. package/dist/heyawakeGenerate.js +114 -0
  81. package/dist/heyawakeMount.d.ts +2 -0
  82. package/dist/heyawakeMount.js +106 -0
  83. package/dist/heyawakePlay.types.d.ts +24 -0
  84. package/dist/heyawakePlay.types.js +1 -0
  85. package/dist/heyawakeSolve.d.ts +5 -0
  86. package/dist/heyawakeSolve.js +52 -0
  87. package/dist/heyawakeStrings.d.ts +19 -0
  88. package/dist/heyawakeStrings.js +7 -0
  89. package/dist/heyawakeStyle.d.ts +1 -0
  90. package/dist/heyawakeStyle.js +2 -0
  91. package/dist/heyawakeWorker.d.ts +1 -0
  92. package/dist/heyawakeWorker.js +10 -0
  93. package/dist/hitori-draw-entry.d.ts +2 -0
  94. package/dist/hitori-draw-entry.js +1 -0
  95. package/dist/hitori-entry.d.ts +6 -0
  96. package/dist/hitori-entry.js +6 -0
  97. package/dist/hitori-play-entry.d.ts +4 -0
  98. package/dist/hitori-play-entry.js +3 -0
  99. package/dist/hitori.constants.d.ts +2 -0
  100. package/dist/hitori.constants.js +2 -0
  101. package/dist/hitori.types.d.ts +20 -0
  102. package/dist/hitori.types.js +1 -0
  103. package/dist/hitoriBoard.d.ts +8 -0
  104. package/dist/hitoriBoard.js +72 -0
  105. package/dist/hitoriDraw.d.ts +4 -0
  106. package/dist/hitoriDraw.js +25 -0
  107. package/dist/hitoriGame.d.ts +10 -0
  108. package/dist/hitoriGame.js +48 -0
  109. package/dist/hitoriGenerate.d.ts +3 -0
  110. package/dist/hitoriGenerate.js +163 -0
  111. package/dist/hitoriMount.d.ts +3 -0
  112. package/dist/hitoriMount.js +111 -0
  113. package/dist/hitoriPlay.types.d.ts +25 -0
  114. package/dist/hitoriPlay.types.js +1 -0
  115. package/dist/hitoriSolve.d.ts +6 -0
  116. package/dist/hitoriSolve.js +69 -0
  117. package/dist/hitoriStrings.d.ts +70 -0
  118. package/dist/hitoriStrings.js +17 -0
  119. package/dist/hitoriStyle.d.ts +1 -0
  120. package/dist/hitoriStyle.js +9 -0
  121. package/dist/index.d.ts +12 -0
  122. package/dist/index.js +12 -0
  123. package/dist/juosan-draw-entry.d.ts +2 -0
  124. package/dist/juosan-draw-entry.js +1 -0
  125. package/dist/juosan-entry.d.ts +11 -0
  126. package/dist/juosan-entry.js +10 -0
  127. package/dist/juosan-play-entry.d.ts +4 -0
  128. package/dist/juosan-play-entry.js +3 -0
  129. package/dist/juosan.constants.d.ts +4 -0
  130. package/dist/juosan.constants.js +4 -0
  131. package/dist/juosan.types.d.ts +33 -0
  132. package/dist/juosan.types.js +1 -0
  133. package/dist/juosanBoard.d.ts +7 -0
  134. package/dist/juosanBoard.js +102 -0
  135. package/dist/juosanDraw.d.ts +4 -0
  136. package/dist/juosanDraw.js +49 -0
  137. package/dist/juosanGame.d.ts +20 -0
  138. package/dist/juosanGame.js +86 -0
  139. package/dist/juosanGenerate.d.ts +3 -0
  140. package/dist/juosanGenerate.js +31 -0
  141. package/dist/juosanMount.d.ts +3 -0
  142. package/dist/juosanMount.js +206 -0
  143. package/dist/juosanPlay.types.d.ts +24 -0
  144. package/dist/juosanPlay.types.js +1 -0
  145. package/dist/juosanSolve.d.ts +3 -0
  146. package/dist/juosanSolve.js +82 -0
  147. package/dist/juosanStrings.d.ts +77 -0
  148. package/dist/juosanStrings.js +43 -0
  149. package/dist/juosanStyle.d.ts +1 -0
  150. package/dist/juosanStyle.js +1 -0
  151. package/dist/kakuro-draw-entry.d.ts +2 -0
  152. package/dist/kakuro-draw-entry.js +1 -0
  153. package/dist/kakuro-entry.d.ts +12 -0
  154. package/dist/kakuro-entry.js +10 -0
  155. package/dist/kakuro-play-entry.d.ts +4 -0
  156. package/dist/kakuro-play-entry.js +3 -0
  157. package/dist/kakuro.constants.d.ts +6 -0
  158. package/dist/kakuro.constants.js +6 -0
  159. package/dist/kakuro.types.d.ts +46 -0
  160. package/dist/kakuro.types.js +1 -0
  161. package/dist/kakuroBoard.d.ts +8 -0
  162. package/dist/kakuroBoard.js +117 -0
  163. package/dist/kakuroDraw.d.ts +10 -0
  164. package/dist/kakuroDraw.js +21 -0
  165. package/dist/kakuroGame.d.ts +12 -0
  166. package/dist/kakuroGame.js +74 -0
  167. package/dist/kakuroGenerate.d.ts +3 -0
  168. package/dist/kakuroGenerate.js +130 -0
  169. package/dist/kakuroMount.d.ts +2 -0
  170. package/dist/kakuroMount.js +109 -0
  171. package/dist/kakuroPlay.types.d.ts +20 -0
  172. package/dist/kakuroPlay.types.js +1 -0
  173. package/dist/kakuroSolve.d.ts +6 -0
  174. package/dist/kakuroSolve.js +85 -0
  175. package/dist/kakuroStrings.d.ts +82 -0
  176. package/dist/kakuroStrings.js +5 -0
  177. package/dist/kakuroStyle.d.ts +1 -0
  178. package/dist/kakuroStyle.js +1 -0
  179. package/dist/masyu-draw-entry.d.ts +2 -0
  180. package/dist/masyu-draw-entry.js +1 -0
  181. package/dist/masyu-entry.d.ts +6 -0
  182. package/dist/masyu-entry.js +6 -0
  183. package/dist/masyu-play-entry.d.ts +4 -0
  184. package/dist/masyu-play-entry.js +3 -0
  185. package/dist/masyu.constants.d.ts +2 -0
  186. package/dist/masyu.constants.js +2 -0
  187. package/dist/masyu.types.d.ts +46 -0
  188. package/dist/masyu.types.js +1 -0
  189. package/dist/masyuBoard.d.ts +10 -0
  190. package/dist/masyuBoard.js +88 -0
  191. package/dist/masyuDraw.d.ts +2 -0
  192. package/dist/masyuDraw.js +33 -0
  193. package/dist/masyuGame.d.ts +9 -0
  194. package/dist/masyuGame.js +46 -0
  195. package/dist/masyuGenerate.d.ts +3 -0
  196. package/dist/masyuGenerate.js +56 -0
  197. package/dist/masyuMount.d.ts +2 -0
  198. package/dist/masyuMount.js +118 -0
  199. package/dist/masyuPlay.types.d.ts +1 -0
  200. package/dist/masyuPlay.types.js +1 -0
  201. package/dist/masyuSolve.d.ts +6 -0
  202. package/dist/masyuSolve.js +88 -0
  203. package/dist/masyuStrings.d.ts +28 -0
  204. package/dist/masyuStrings.js +4 -0
  205. package/dist/masyuStyle.d.ts +1 -0
  206. package/dist/masyuStyle.js +11 -0
  207. package/dist/nurikabe-draw-entry.d.ts +2 -0
  208. package/dist/nurikabe-draw-entry.js +1 -0
  209. package/dist/nurikabe-entry.d.ts +6 -0
  210. package/dist/nurikabe-entry.js +6 -0
  211. package/dist/nurikabe-play-entry.d.ts +4 -0
  212. package/dist/nurikabe-play-entry.js +3 -0
  213. package/dist/nurikabe.constants.d.ts +2 -0
  214. package/dist/nurikabe.constants.js +2 -0
  215. package/dist/nurikabe.types.d.ts +20 -0
  216. package/dist/nurikabe.types.js +1 -0
  217. package/dist/nurikabeBoard.d.ts +8 -0
  218. package/dist/nurikabeBoard.js +72 -0
  219. package/dist/nurikabeDraw.d.ts +4 -0
  220. package/dist/nurikabeDraw.js +15 -0
  221. package/dist/nurikabeGame.d.ts +8 -0
  222. package/dist/nurikabeGame.js +40 -0
  223. package/dist/nurikabeGenerate.d.ts +3 -0
  224. package/dist/nurikabeGenerate.js +31 -0
  225. package/dist/nurikabeMount.d.ts +3 -0
  226. package/dist/nurikabeMount.js +99 -0
  227. package/dist/nurikabePlay.types.d.ts +25 -0
  228. package/dist/nurikabePlay.types.js +1 -0
  229. package/dist/nurikabeSolve.d.ts +6 -0
  230. package/dist/nurikabeSolve.js +75 -0
  231. package/dist/nurikabeStrings.d.ts +70 -0
  232. package/dist/nurikabeStrings.js +5 -0
  233. package/dist/nurikabeStyle.d.ts +1 -0
  234. package/dist/nurikabeStyle.js +9 -0
  235. package/dist/play-entry.d.ts +1 -0
  236. package/dist/play-entry.js +1 -0
  237. package/dist/ripple-draw-entry.d.ts +2 -0
  238. package/dist/ripple-draw-entry.js +1 -0
  239. package/dist/ripple-entry.d.ts +12 -0
  240. package/dist/ripple-entry.js +11 -0
  241. package/dist/ripple-play-entry.d.ts +4 -0
  242. package/dist/ripple-play-entry.js +3 -0
  243. package/dist/ripple.constants.d.ts +4 -0
  244. package/dist/ripple.constants.js +4 -0
  245. package/dist/ripple.types.d.ts +34 -0
  246. package/dist/ripple.types.js +1 -0
  247. package/dist/rippleBoard.d.ts +7 -0
  248. package/dist/rippleBoard.js +99 -0
  249. package/dist/rippleDraw.d.ts +3 -0
  250. package/dist/rippleDraw.js +32 -0
  251. package/dist/rippleGame.d.ts +13 -0
  252. package/dist/rippleGame.js +85 -0
  253. package/dist/rippleGenerate.d.ts +3 -0
  254. package/dist/rippleGenerate.js +57 -0
  255. package/dist/rippleMount.d.ts +2 -0
  256. package/dist/rippleMount.js +179 -0
  257. package/dist/ripplePlay.types.d.ts +23 -0
  258. package/dist/ripplePlay.types.js +1 -0
  259. package/dist/rippleSolve.d.ts +6 -0
  260. package/dist/rippleSolve.js +83 -0
  261. package/dist/rippleStrings.d.ts +86 -0
  262. package/dist/rippleStrings.js +5 -0
  263. package/dist/rippleStyle.d.ts +1 -0
  264. package/dist/rippleStyle.js +1 -0
  265. package/dist/shikaku-draw-entry.d.ts +2 -0
  266. package/dist/shikaku-draw-entry.js +1 -0
  267. package/dist/shikaku-entry.d.ts +8 -0
  268. package/dist/shikaku-entry.js +8 -0
  269. package/dist/shikaku-play-entry.d.ts +4 -0
  270. package/dist/shikaku-play-entry.js +3 -0
  271. package/dist/shikaku.constants.d.ts +5 -0
  272. package/dist/shikaku.constants.js +5 -0
  273. package/dist/shikaku.types.d.ts +36 -0
  274. package/dist/shikaku.types.js +1 -0
  275. package/dist/shikakuBoard.d.ts +9 -0
  276. package/dist/shikakuBoard.js +59 -0
  277. package/dist/shikakuDraw.d.ts +4 -0
  278. package/dist/shikakuDraw.js +23 -0
  279. package/dist/shikakuGame.d.ts +14 -0
  280. package/dist/shikakuGame.js +61 -0
  281. package/dist/shikakuGenerate.d.ts +3 -0
  282. package/dist/shikakuGenerate.js +43 -0
  283. package/dist/shikakuMount.d.ts +3 -0
  284. package/dist/shikakuMount.js +143 -0
  285. package/dist/shikakuPacks.d.ts +27 -0
  286. package/dist/shikakuPacks.js +26 -0
  287. package/dist/shikakuPlay.types.d.ts +26 -0
  288. package/dist/shikakuPlay.types.js +1 -0
  289. package/dist/shikakuSolve.d.ts +6 -0
  290. package/dist/shikakuSolve.js +54 -0
  291. package/dist/shikakuStrings.d.ts +102 -0
  292. package/dist/shikakuStrings.js +5 -0
  293. package/dist/shikakuStyle.d.ts +1 -0
  294. package/dist/shikakuStyle.js +12 -0
  295. package/dist/shikakuWorker.d.ts +1 -0
  296. package/dist/shikakuWorker.js +10 -0
  297. package/dist/slitherlink-draw-entry.d.ts +2 -0
  298. package/dist/slitherlink-draw-entry.js +1 -0
  299. package/dist/slitherlink-entry.d.ts +11 -0
  300. package/dist/slitherlink-entry.js +9 -0
  301. package/dist/slitherlink-play-entry.d.ts +4 -0
  302. package/dist/slitherlink-play-entry.js +3 -0
  303. package/dist/slitherlink.constants.d.ts +2 -0
  304. package/dist/slitherlink.constants.js +2 -0
  305. package/dist/slitherlink.types.d.ts +34 -0
  306. package/dist/slitherlink.types.js +1 -0
  307. package/dist/slitherlinkBoard.d.ts +12 -0
  308. package/dist/slitherlinkBoard.js +132 -0
  309. package/dist/slitherlinkDraw.d.ts +4 -0
  310. package/dist/slitherlinkDraw.js +51 -0
  311. package/dist/slitherlinkGame.d.ts +8 -0
  312. package/dist/slitherlinkGame.js +63 -0
  313. package/dist/slitherlinkGenerate.d.ts +3 -0
  314. package/dist/slitherlinkGenerate.js +101 -0
  315. package/dist/slitherlinkMount.d.ts +3 -0
  316. package/dist/slitherlinkMount.js +267 -0
  317. package/dist/slitherlinkPlay.types.d.ts +23 -0
  318. package/dist/slitherlinkPlay.types.js +1 -0
  319. package/dist/slitherlinkSolve.d.ts +6 -0
  320. package/dist/slitherlinkSolve.js +127 -0
  321. package/dist/slitherlinkStrings.d.ts +86 -0
  322. package/dist/slitherlinkStrings.js +47 -0
  323. package/dist/slitherlinkStyle.d.ts +1 -0
  324. package/dist/slitherlinkStyle.js +18 -0
  325. package/dist/version.d.ts +1 -1
  326. package/dist/version.js +1 -1
  327. package/dist/yajilin-draw-entry.d.ts +2 -0
  328. package/dist/yajilin-draw-entry.js +1 -0
  329. package/dist/yajilin-entry.d.ts +6 -0
  330. package/dist/yajilin-entry.js +6 -0
  331. package/dist/yajilin-play-entry.d.ts +4 -0
  332. package/dist/yajilin-play-entry.js +3 -0
  333. package/dist/yajilin.constants.d.ts +2 -0
  334. package/dist/yajilin.constants.js +2 -0
  335. package/dist/yajilin.types.d.ts +63 -0
  336. package/dist/yajilin.types.js +1 -0
  337. package/dist/yajilinBoard.d.ts +9 -0
  338. package/dist/yajilinBoard.js +91 -0
  339. package/dist/yajilinDraw.d.ts +2 -0
  340. package/dist/yajilinDraw.js +33 -0
  341. package/dist/yajilinGame.d.ts +15 -0
  342. package/dist/yajilinGame.js +58 -0
  343. package/dist/yajilinGenerate.d.ts +3 -0
  344. package/dist/yajilinGenerate.js +224 -0
  345. package/dist/yajilinMount.d.ts +2 -0
  346. package/dist/yajilinMount.js +162 -0
  347. package/dist/yajilinPlay.types.d.ts +1 -0
  348. package/dist/yajilinPlay.types.js +1 -0
  349. package/dist/yajilinSolve.d.ts +6 -0
  350. package/dist/yajilinSolve.js +82 -0
  351. package/dist/yajilinStrings.d.ts +32 -0
  352. package/dist/yajilinStrings.js +4 -0
  353. package/dist/yajilinStyle.d.ts +1 -0
  354. package/dist/yajilinStyle.js +12 -0
  355. package/package.json +196 -3
@@ -0,0 +1,61 @@
1
+ import { checkShikaku, isShikakuBoard, shikakuCells } from "./shikakuBoard.js";
2
+ import { solveShikaku } from "./shikakuSolve.js";
3
+ /** Begin with a copy of just the public clues; a generated answer is never kept in play. */
4
+ export function newShikaku(board) {
5
+ if (!isShikakuBoard(board))
6
+ throw new RangeError("Invalid Shikaku board");
7
+ return { board: { width: board.width, height: board.height, clues: [...board.clues] }, rectangles: [], history: [], helped: false };
8
+ }
9
+ /** Place a rectangle, replacing any it touches. Local rule errors are allowed for the player to check. */
10
+ export function placeShikaku(game, rectangle) {
11
+ const cells = shikakuCells(game.board, rectangle);
12
+ if (!cells)
13
+ return game;
14
+ const covered = new Set(cells);
15
+ return { ...game, rectangles: [...game.rectangles.filter(r => !shikakuCells(game.board, r).some(c => covered.has(c))), { ...rectangle }],
16
+ history: [...game.history, game.rectangles.map(r => ({ ...r }))] };
17
+ }
18
+ /** Remove the rectangle containing a cell. */
19
+ export function removeShikaku(game, cell) {
20
+ const rectangles = game.rectangles.filter(r => !shikakuCells(game.board, r).includes(cell));
21
+ return rectangles.length === game.rectangles.length ? game : { ...game, rectangles, history: [...game.history, game.rectangles] };
22
+ }
23
+ export function undoShikaku(game) {
24
+ const previous = game.history.at(-1);
25
+ return previous ? { ...game, rectangles: previous.map(r => ({ ...r })), history: game.history.slice(0, -1) } : game;
26
+ }
27
+ /** A rectangle is offered only when the remaining partition has one proved answer. */
28
+ export function hintShikaku(game) {
29
+ const solved = solveShikaku(game.board, game.rectangles);
30
+ if (!solved.complete || solved.count !== 1)
31
+ return null;
32
+ return solved.solution.find(r => !game.rectangles.some(p => p.x === r.x && p.y === r.y && p.width === r.width && p.height === r.height)) ?? null;
33
+ }
34
+ export function shikakuFinished(game) { return checkShikaku(game.board, game.rectangles).ok; }
35
+ /** Versioned public puzzle and placement data, with no hidden answer. */
36
+ export function encodeShikaku(game) {
37
+ return JSON.stringify({ version: 1, board: game.board, rectangles: game.rectangles, helped: game.helped });
38
+ }
39
+ export function decodeShikaku(code) {
40
+ try {
41
+ if (code.length > 40000)
42
+ return null;
43
+ const value = JSON.parse(code);
44
+ if (value.version !== 1 || !isShikakuBoard(value.board) || !Array.isArray(value.rectangles)
45
+ || value.rectangles.length > value.board.clues.length || typeof value.helped !== "boolean")
46
+ return null;
47
+ let game = newShikaku(value.board);
48
+ for (const r of value.rectangles) {
49
+ if (!shikakuCells(game.board, r))
50
+ return null;
51
+ const next = placeShikaku(game, r);
52
+ if (next.rectangles.length !== game.rectangles.length + 1)
53
+ return null;
54
+ game = next;
55
+ }
56
+ return { ...game, history: [], helped: value.helped };
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
@@ -0,0 +1,3 @@
1
+ import type { ShikakuLevel, ShikakuPuzzle } from "./shikaku.types.ts";
2
+ /** Seeded partitions with an independently counted, unique answer. Levels choose rectangle sizes, not a promised human rating. */
3
+ export declare function generateShikaku(width?: number, height?: number, level?: ShikakuLevel, seed?: number): ShikakuPuzzle;
@@ -0,0 +1,43 @@
1
+ import { SHIKAKU_LEVELS, SHIKAKU_MOST_ATTEMPTS } from "./shikaku.constants.js";
2
+ import { isShikakuBoard, shikakuCells } from "./shikakuBoard.js";
3
+ import { solveShikaku } from "./shikakuSolve.js";
4
+ import { isKazuSeed, seededRandom } from "./random.js";
5
+ /** Seeded partitions with an independently counted, unique answer. Levels choose rectangle sizes, not a promised human rating. */
6
+ export function generateShikaku(width = 7, height = width, level = "medium", seed = 1) {
7
+ if (![width, height].every(n => Number.isInteger(n) && n >= 2 && n <= 16))
8
+ throw new RangeError("Invalid Shikaku dimensions");
9
+ const empty = { width, height, clues: Array(width * height).fill(0) };
10
+ if (!isKazuSeed(seed) || !SHIKAKU_LEVELS.includes(level)
11
+ || !isShikakuBoard({ ...empty, clues: [width * height, ...empty.clues.slice(1)] }))
12
+ throw new RangeError("Invalid Shikaku settings");
13
+ const random = seededRandom(seed), maximum = { easy: 5, medium: 9, hard: 15 }[level];
14
+ for (let attempt = 0; attempt < SHIKAKU_MOST_ATTEMPTS; attempt += 1) {
15
+ const solution = [];
16
+ const split = (r) => {
17
+ if (r.width * r.height <= maximum && (r.width * r.height < 3 || random() < .6)) {
18
+ solution.push(r);
19
+ return;
20
+ }
21
+ const vertical = r.height === 1 || (r.width > 1 && random() < .5);
22
+ const cut = 1 + Math.floor(random() * ((vertical ? r.width : r.height) - 1));
23
+ if (vertical) {
24
+ split({ ...r, width: cut });
25
+ split({ ...r, x: r.x + cut, width: r.width - cut });
26
+ }
27
+ else {
28
+ split({ ...r, height: cut });
29
+ split({ ...r, y: r.y + cut, height: r.height - cut });
30
+ }
31
+ };
32
+ split({ x: 0, y: 0, width, height });
33
+ const clues = [...empty.clues];
34
+ for (const r of solution) {
35
+ const cells = shikakuCells(empty, r);
36
+ clues[cells[Math.floor(random() * cells.length)]] = cells.length;
37
+ }
38
+ const board = { width, height, clues }, counted = solveShikaku(board);
39
+ if (counted.complete && counted.count === 1)
40
+ return { ...board, seed, level, solution: counted.solution };
41
+ }
42
+ throw new Error("No unique Shikaku found within the generation budget; try another seed");
43
+ }
@@ -0,0 +1,3 @@
1
+ import type { ShikakuMount, ShikakuMountOptions } from "./shikakuPlay.types.ts";
2
+ /** A rectangle player in any element; mount once, update appearance with set, destroy on unmount. */
3
+ export declare function mountShikaku(host: HTMLElement, initial: ShikakuMountOptions): ShikakuMount;
@@ -0,0 +1,143 @@
1
+ import { checkShikaku, shikakuCells } from "./shikakuBoard.js";
2
+ import { decodeShikaku, encodeShikaku, hintShikaku, newShikaku, placeShikaku, removeShikaku, shikakuFinished, undoShikaku } from "./shikakuGame.js";
3
+ import { drawShikaku } from "./shikakuDraw.js";
4
+ import { KAZU_STYLE } from "./style.js";
5
+ import { SHIKAKU_PLAY_STYLE } from "./shikakuStyle.js";
6
+ import { shikakuWords } from "./shikakuStrings.js";
7
+ /** A rectangle player in any element; mount once, update appearance with set, destroy on unmount. */
8
+ export function mountShikaku(host, initial) {
9
+ const doc = host.ownerDocument;
10
+ let options = { ...initial }, game = initial.progress ? decodeShikaku(initial.progress) : null;
11
+ game ?? (game = newShikaku(initial.board));
12
+ let selected = 0, anchor = null, errors = [], message = "", finished = shikakuFinished(game), dead = false;
13
+ const root = doc.createElement("div"), style = doc.createElement("style"), board = doc.createElement("div"), status = doc.createElement("p"), tools = doc.createElement("div"), help = doc.createElement("p");
14
+ root.className = "ks-root";
15
+ style.textContent = KAZU_STYLE.replace(/\.kazu/g, ".ks-palette") + SHIKAKU_PLAY_STYLE;
16
+ board.className = "ks-board";
17
+ status.className = "ks-status";
18
+ status.setAttribute("role", "status");
19
+ tools.className = "ks-tools";
20
+ help.className = "ks-help";
21
+ root.append(style, board, status, tools, help);
22
+ host.append(root);
23
+ const copy = () => ({ ...game, board: { ...game.board, clues: [...game.board.clues] }, rectangles: game.rectangles.map(r => ({ ...r })), history: game.history.map(turn => turn.map(r => ({ ...r }))) });
24
+ const words = () => shikakuWords(options.language);
25
+ const button = (text, action, disabled = false) => {
26
+ const b = doc.createElement("button");
27
+ b.type = "button";
28
+ b.textContent = text;
29
+ b.disabled = disabled;
30
+ b.onclick = action;
31
+ tools.append(b);
32
+ };
33
+ const changed = () => {
34
+ errors = [];
35
+ message = "";
36
+ anchor = null;
37
+ const won = shikakuFinished(game);
38
+ render();
39
+ options.onChange?.(copy());
40
+ root.dispatchEvent(new CustomEvent("shikaku-change", { detail: copy(), bubbles: true }));
41
+ if (won && !finished) {
42
+ options.onFinish?.(copy());
43
+ root.dispatchEvent(new CustomEvent("shikaku-finish", { detail: copy(), bubbles: true }));
44
+ }
45
+ finished = won;
46
+ };
47
+ const choose = (cell) => {
48
+ if (finished)
49
+ return;
50
+ selected = cell;
51
+ if (anchor === null) {
52
+ anchor = cell;
53
+ message = "";
54
+ render();
55
+ return;
56
+ }
57
+ const a = anchor, width = game.board.width, ax = a % width, ay = Math.floor(a / width), x = cell % width, y = Math.floor(cell / width);
58
+ game = placeShikaku(game, { x: Math.min(ax, x), y: Math.min(ay, y), width: Math.abs(ax - x) + 1, height: Math.abs(ay - y) + 1 });
59
+ changed();
60
+ };
61
+ const remove = () => { game = removeShikaku(game, selected); changed(); };
62
+ const restart = () => { game = newShikaku(game.board); finished = false; changed(); };
63
+ const render = () => {
64
+ if (dead)
65
+ return;
66
+ const w = words(), b = game.board, focused = board.contains(doc.activeElement);
67
+ board.classList.toggle("ks-palette", (options.material ?? "ivory") === "ivory");
68
+ board.innerHTML = drawShikaku(b, { ...options, rectangles: game.rectangles, selected, anchor, errors });
69
+ const cells = doc.createElement("div");
70
+ cells.className = "ks-cells";
71
+ cells.style.gridTemplateColumns = `repeat(${b.width},1fr)`;
72
+ for (let c = 0; c < b.clues.length; c += 1) {
73
+ const cell = doc.createElement("button");
74
+ cell.type = "button";
75
+ cell.className = "ks-cell";
76
+ cell.dataset.cell = String(c);
77
+ cell.tabIndex = c === selected ? 0 : -1;
78
+ const inside = game.rectangles.find(r => shikakuCells(b, r).includes(c));
79
+ cell.setAttribute("aria-label", `${w.row} ${Math.floor(c / b.width) + 1}, ${w.column} ${c % b.width + 1}: ${b.clues[c] ? `${w.clue} ${b.clues[c]}` : w.blank}${inside ? `, ${w.covered}` : ""}`);
80
+ cell.onclick = () => choose(c);
81
+ cell.onkeydown = e => {
82
+ const steps = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -b.width, ArrowDown: b.width };
83
+ if (e.key in steps) {
84
+ e.preventDefault();
85
+ selected = Math.max(0, Math.min(b.clues.length - 1, c + steps[e.key]));
86
+ render();
87
+ }
88
+ else if (e.key === "Escape") {
89
+ e.preventDefault();
90
+ anchor = null;
91
+ render();
92
+ }
93
+ else if (e.key === "Delete" || e.key === "Backspace") {
94
+ e.preventDefault();
95
+ selected = c;
96
+ remove();
97
+ }
98
+ };
99
+ cells.append(cell);
100
+ }
101
+ board.append(cells);
102
+ if (focused)
103
+ board.querySelector(`[data-cell="${selected}"]`)?.focus();
104
+ status.textContent = shikakuFinished(game) ? (game.helped ? w.helped : w.won) : message || (anchor === null ? w.ready : w.corner);
105
+ help.textContent = w.help;
106
+ tools.replaceChildren();
107
+ button(w.undo, () => { game = undoShikaku(game); finished = shikakuFinished(game); changed(); }, !game.history.length);
108
+ button(w.remove, remove, finished || !game.rectangles.some(r => shikakuCells(b, r).includes(selected)));
109
+ button(w.cancel, () => { anchor = null; message = ""; render(); }, anchor === null);
110
+ button(w.check, () => { errors = checkShikaku(b, game.rectangles).errors; message = errors.length ? w.wrong : w.checked; render(); });
111
+ button(w.hint, () => {
112
+ const r = hintShikaku(game);
113
+ if (!r) {
114
+ message = w.noHint;
115
+ render();
116
+ return;
117
+ }
118
+ game = { ...placeShikaku(game, r), helped: true };
119
+ changed();
120
+ message = w.hintWhy;
121
+ render();
122
+ }, finished);
123
+ button(w.restart, restart);
124
+ button(w.expand, () => {
125
+ if (root.parentElement !== host)
126
+ return;
127
+ const dialog = doc.createElement("dialog"), close = doc.createElement("button");
128
+ dialog.className = "ks-dialog";
129
+ close.className = "ks-close";
130
+ close.textContent = w.close;
131
+ close.onclick = () => dialog.close();
132
+ dialog.append(close, root);
133
+ host.append(dialog);
134
+ dialog.onclose = () => { host.append(root); dialog.remove(); };
135
+ dialog.showModal();
136
+ });
137
+ };
138
+ render();
139
+ return { game: copy, progress: () => encodeShikaku(game), restart,
140
+ set: (next) => { if (next.language && next.language !== options.language)
141
+ message = ""; options = { ...options, ...next }; render(); },
142
+ destroy: () => { dead = true; const dialog = root.closest("dialog"); root.remove(); dialog?.remove(); } };
143
+ }
@@ -0,0 +1,27 @@
1
+ import type { ShikakuLevel, ShikakuPuzzle } from "./shikaku.types.ts";
2
+ export type ShikakuPackKey = "square" | "wide" | "tall";
3
+ export type ShikakuChallenge = {
4
+ pack: ShikakuPackKey;
5
+ packTitle: string;
6
+ name: string;
7
+ level: ShikakuLevel;
8
+ width: number;
9
+ height: number;
10
+ seed: number;
11
+ };
12
+ export type ShikakuChallengePuzzle = ShikakuChallenge & {
13
+ puzzle: ShikakuPuzzle;
14
+ };
15
+ /** Three short, named challenge routes using the regular seeded unique-puzzle generator. */
16
+ export declare const SHIKAKU_CHALLENGE_PACKS: Readonly<Record<ShikakuPackKey, {
17
+ title: string;
18
+ description: string;
19
+ width: number;
20
+ height: number;
21
+ challenges: readonly {
22
+ name: string;
23
+ level: ShikakuLevel;
24
+ seed: number;
25
+ }[];
26
+ }>>;
27
+ export declare function generateShikakuChallenge(pack: ShikakuPackKey, challenge?: number): ShikakuChallengePuzzle;
@@ -0,0 +1,26 @@
1
+ import { generateShikaku } from "./shikakuGenerate.js";
2
+ /** Three short, named challenge routes using the regular seeded unique-puzzle generator. */
3
+ export const SHIKAKU_CHALLENGE_PACKS = {
4
+ square: {
5
+ title: "The Courtyard", description: "A compact square route.", width: 7, height: 7,
6
+ challenges: [{ name: "First Cut", level: "easy", seed: 1 }, { name: "Crossing", level: "medium", seed: 2 }, { name: "Mosaic", level: "hard", seed: 3 }],
7
+ },
8
+ wide: {
9
+ title: "The Long Table", description: "Three puzzles across a wide board.", width: 10, height: 6,
10
+ challenges: [{ name: "Harbour", level: "easy", seed: 1 }, { name: "Causeway", level: "medium", seed: 2 }, { name: "Headland", level: "hard", seed: 3 }],
11
+ },
12
+ tall: {
13
+ title: "The Narrow Garden", description: "A tall route with long edges.", width: 6, height: 10,
14
+ challenges: [{ name: "Gate", level: "easy", seed: 1 }, { name: "Pavilion", level: "medium", seed: 2 }, { name: "Lantern Walk", level: "hard", seed: 3 }],
15
+ },
16
+ };
17
+ export function generateShikakuChallenge(pack, challenge = 1) {
18
+ const definition = SHIKAKU_CHALLENGE_PACKS[pack], entry = definition?.challenges[challenge - 1];
19
+ if (!definition || !entry)
20
+ throw new RangeError("Unknown Shikaku challenge");
21
+ return {
22
+ pack, packTitle: definition.title, name: entry.name, level: entry.level,
23
+ width: definition.width, height: definition.height, seed: entry.seed,
24
+ puzzle: generateShikaku(definition.width, definition.height, entry.level, entry.seed),
25
+ };
26
+ }
@@ -0,0 +1,26 @@
1
+ import type { ShikakuBoard, ShikakuGame } from "./shikaku.types.ts";
2
+ export type ShikakuMaterial = "ivory" | "wood" | "slate";
3
+ export type ShikakuPieces = "ink" | "tiles";
4
+ export type ShikakuLanguage = "en" | "ja";
5
+ export type ShikakuDrawOptions = {
6
+ rectangles?: ShikakuGame["rectangles"];
7
+ selected?: number | null;
8
+ anchor?: number | null;
9
+ errors?: readonly number[];
10
+ material?: ShikakuMaterial;
11
+ pieces?: ShikakuPieces;
12
+ language?: ShikakuLanguage;
13
+ };
14
+ export type ShikakuMountOptions = ShikakuDrawOptions & {
15
+ board: ShikakuBoard;
16
+ progress?: string;
17
+ onChange?: (game: ShikakuGame) => void;
18
+ onFinish?: (game: ShikakuGame) => void;
19
+ };
20
+ export type ShikakuMount = {
21
+ game: () => ShikakuGame;
22
+ progress: () => string;
23
+ set: (options: ShikakuDrawOptions) => void;
24
+ restart: () => void;
25
+ destroy: () => void;
26
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { ShikakuBoard, ShikakuRectangle, ShikakuSolve } from "./shikaku.types.ts";
2
+ /** Exact cover over cells. A bounded search reports incompleteness instead of claiming uniqueness. */
3
+ export declare function solveShikaku(board: ShikakuBoard, placed?: readonly ShikakuRectangle[], options?: {
4
+ limit?: number;
5
+ nodes?: number;
6
+ }): ShikakuSolve;
@@ -0,0 +1,54 @@
1
+ import { SHIKAKU_MOST_NODES } from "./shikaku.constants.js";
2
+ import { checkShikaku, isShikakuBoard, shikakuCandidates, shikakuCells } from "./shikakuBoard.js";
3
+ /** Exact cover over cells. A bounded search reports incompleteness instead of claiming uniqueness. */
4
+ export function solveShikaku(board, placed = [], options = {}) {
5
+ if (!isShikakuBoard(board))
6
+ throw new RangeError("Invalid Shikaku board");
7
+ const limit = options.limit ?? 2, budget = options.nodes ?? SHIKAKU_MOST_NODES;
8
+ if (!Number.isInteger(limit) || limit < 1 || !Number.isInteger(budget) || budget < 1)
9
+ throw new RangeError("Invalid search bounds");
10
+ const maskOf = (r) => shikakuCells(board, r).reduce((m, c) => m | 1n << BigInt(c), 0n);
11
+ if (checkShikaku(board, placed).errors.length)
12
+ return { count: 0, solution: null, complete: true, nodes: 0 };
13
+ const all = (1n << BigInt(board.clues.length)) - 1n;
14
+ let occupied = 0n;
15
+ for (const r of placed)
16
+ occupied |= maskOf(r);
17
+ const candidates = board.clues.flatMap((n, c) => n ? shikakuCandidates(board, c).map(r => ({ r, mask: maskOf(r) })) : []);
18
+ const byCell = board.clues.map((_, c) => candidates.filter(r => (r.mask & 1n << BigInt(c)) !== 0n));
19
+ let count = 0, nodes = 0, complete = true, solution = null;
20
+ const visit = (mask, rectangles) => {
21
+ if (++nodes > budget) {
22
+ complete = false;
23
+ return;
24
+ }
25
+ if (mask === all) {
26
+ count += 1;
27
+ solution ?? (solution = rectangles.map(r => ({ ...r })));
28
+ return;
29
+ }
30
+ let choices = null;
31
+ for (let c = 0; c < board.clues.length; c += 1) {
32
+ if ((mask & 1n << BigInt(c)) !== 0n)
33
+ continue;
34
+ const available = byCell[c].filter(r => (mask & r.mask) === 0n);
35
+ if (!available.length)
36
+ return;
37
+ if (!choices || available.length < choices.length)
38
+ choices = available;
39
+ if (choices.length === 1)
40
+ break;
41
+ }
42
+ for (const next of choices ?? []) {
43
+ visit(mask | next.mask, [...rectangles, next.r]);
44
+ if (!complete)
45
+ return;
46
+ if (count >= limit) {
47
+ complete = false;
48
+ return;
49
+ }
50
+ }
51
+ };
52
+ visit(occupied, placed);
53
+ return { count, solution, complete, nodes };
54
+ }
@@ -0,0 +1,102 @@
1
+ import type { ShikakuLanguage } from "./shikakuPlay.types.ts";
2
+ export declare const SHIKAKU_STRINGS: {
3
+ readonly en: {
4
+ readonly title: "Shikaku";
5
+ readonly help: "Choose two opposite corners to draw a rectangle. Each rectangle must contain one number equal to its area. Cover the whole board. Arrow keys move; Enter chooses a corner; Delete removes a rectangle; Escape cancels.";
6
+ readonly undo: "Undo";
7
+ readonly hint: "Hint";
8
+ readonly check: "Check";
9
+ readonly restart: "Restart";
10
+ readonly cancel: "Cancel";
11
+ readonly expand: "Just the board";
12
+ readonly close: "Close";
13
+ readonly ready: "Choose the first corner.";
14
+ readonly corner: "Choose the opposite corner.";
15
+ readonly won: "Every rectangle fits. Solved!";
16
+ readonly helped: "Every rectangle fits. Solved with help.";
17
+ readonly hintWhy: "This rectangle occurs in the only remaining complete partition.";
18
+ readonly noHint: "No proved hint: undo a conflicting rectangle or continue drawing.";
19
+ readonly checked: "All drawn rectangles follow the rules. Keep filling the board.";
20
+ readonly wrong: "Some rectangles need changing: each must hold one number equal to its area.";
21
+ readonly remove: "Remove selected rectangle";
22
+ readonly row: "Row";
23
+ readonly column: "column";
24
+ readonly clue: "area";
25
+ readonly blank: "no clue";
26
+ readonly covered: "in a rectangle";
27
+ };
28
+ readonly ja: {
29
+ readonly title: "四角に切れ";
30
+ readonly help: "向かい合う二つの角を選んで長方形を描きます。各長方形に数字を一つ入れ、その数字と面積を同じにします。盤全体を埋めましょう。矢印キーで移動、Enterで角を選び、Deleteで長方形を消し、Escapeで取り消します。";
31
+ readonly undo: "戻す";
32
+ readonly hint: "ヒント";
33
+ readonly check: "確かめる";
34
+ readonly restart: "やり直す";
35
+ readonly cancel: "取り消す";
36
+ readonly expand: "盤だけ";
37
+ readonly close: "閉じる";
38
+ readonly ready: "最初の角を選びます。";
39
+ readonly corner: "向かいの角を選びます。";
40
+ readonly won: "全部の長方形が合いました。完成!";
41
+ readonly helped: "ヒントを使って完成!";
42
+ readonly hintWhy: "残った唯一の答えに、この長方形があります。";
43
+ readonly noHint: "確かなヒントがありません。合わない長方形を戻すか、続けて描きましょう。";
44
+ readonly checked: "描いた長方形はルールに合っています。残りも埋めましょう。";
45
+ readonly wrong: "直す長方形があります。数字を一つ含み、その数字と面積を同じにします。";
46
+ readonly remove: "選んだ長方形を消す";
47
+ readonly row: "行";
48
+ readonly column: "列";
49
+ readonly clue: "面積";
50
+ readonly blank: "数字なし";
51
+ readonly covered: "長方形の中";
52
+ };
53
+ };
54
+ export declare function shikakuWords(language?: ShikakuLanguage): {
55
+ readonly title: "Shikaku";
56
+ readonly help: "Choose two opposite corners to draw a rectangle. Each rectangle must contain one number equal to its area. Cover the whole board. Arrow keys move; Enter chooses a corner; Delete removes a rectangle; Escape cancels.";
57
+ readonly undo: "Undo";
58
+ readonly hint: "Hint";
59
+ readonly check: "Check";
60
+ readonly restart: "Restart";
61
+ readonly cancel: "Cancel";
62
+ readonly expand: "Just the board";
63
+ readonly close: "Close";
64
+ readonly ready: "Choose the first corner.";
65
+ readonly corner: "Choose the opposite corner.";
66
+ readonly won: "Every rectangle fits. Solved!";
67
+ readonly helped: "Every rectangle fits. Solved with help.";
68
+ readonly hintWhy: "This rectangle occurs in the only remaining complete partition.";
69
+ readonly noHint: "No proved hint: undo a conflicting rectangle or continue drawing.";
70
+ readonly checked: "All drawn rectangles follow the rules. Keep filling the board.";
71
+ readonly wrong: "Some rectangles need changing: each must hold one number equal to its area.";
72
+ readonly remove: "Remove selected rectangle";
73
+ readonly row: "Row";
74
+ readonly column: "column";
75
+ readonly clue: "area";
76
+ readonly blank: "no clue";
77
+ readonly covered: "in a rectangle";
78
+ } | {
79
+ readonly title: "四角に切れ";
80
+ readonly help: "向かい合う二つの角を選んで長方形を描きます。各長方形に数字を一つ入れ、その数字と面積を同じにします。盤全体を埋めましょう。矢印キーで移動、Enterで角を選び、Deleteで長方形を消し、Escapeで取り消します。";
81
+ readonly undo: "戻す";
82
+ readonly hint: "ヒント";
83
+ readonly check: "確かめる";
84
+ readonly restart: "やり直す";
85
+ readonly cancel: "取り消す";
86
+ readonly expand: "盤だけ";
87
+ readonly close: "閉じる";
88
+ readonly ready: "最初の角を選びます。";
89
+ readonly corner: "向かいの角を選びます。";
90
+ readonly won: "全部の長方形が合いました。完成!";
91
+ readonly helped: "ヒントを使って完成!";
92
+ readonly hintWhy: "残った唯一の答えに、この長方形があります。";
93
+ readonly noHint: "確かなヒントがありません。合わない長方形を戻すか、続けて描きましょう。";
94
+ readonly checked: "描いた長方形はルールに合っています。残りも埋めましょう。";
95
+ readonly wrong: "直す長方形があります。数字を一つ含み、その数字と面積を同じにします。";
96
+ readonly remove: "選んだ長方形を消す";
97
+ readonly row: "行";
98
+ readonly column: "列";
99
+ readonly clue: "面積";
100
+ readonly blank: "数字なし";
101
+ readonly covered: "長方形の中";
102
+ };
@@ -0,0 +1,5 @@
1
+ export const SHIKAKU_STRINGS = {
2
+ en: { title: "Shikaku", help: "Choose two opposite corners to draw a rectangle. Each rectangle must contain one number equal to its area. Cover the whole board. Arrow keys move; Enter chooses a corner; Delete removes a rectangle; Escape cancels.", undo: "Undo", hint: "Hint", check: "Check", restart: "Restart", cancel: "Cancel", expand: "Just the board", close: "Close", ready: "Choose the first corner.", corner: "Choose the opposite corner.", won: "Every rectangle fits. Solved!", helped: "Every rectangle fits. Solved with help.", hintWhy: "This rectangle occurs in the only remaining complete partition.", noHint: "No proved hint: undo a conflicting rectangle or continue drawing.", checked: "All drawn rectangles follow the rules. Keep filling the board.", wrong: "Some rectangles need changing: each must hold one number equal to its area.", remove: "Remove selected rectangle", row: "Row", column: "column", clue: "area", blank: "no clue", covered: "in a rectangle" },
3
+ ja: { title: "四角に切れ", help: "向かい合う二つの角を選んで長方形を描きます。各長方形に数字を一つ入れ、その数字と面積を同じにします。盤全体を埋めましょう。矢印キーで移動、Enterで角を選び、Deleteで長方形を消し、Escapeで取り消します。", undo: "戻す", hint: "ヒント", check: "確かめる", restart: "やり直す", cancel: "取り消す", expand: "盤だけ", close: "閉じる", ready: "最初の角を選びます。", corner: "向かいの角を選びます。", won: "全部の長方形が合いました。完成!", helped: "ヒントを使って完成!", hintWhy: "残った唯一の答えに、この長方形があります。", noHint: "確かなヒントがありません。合わない長方形を戻すか、続けて描きましょう。", checked: "描いた長方形はルールに合っています。残りも埋めましょう。", wrong: "直す長方形があります。数字を一つ含み、その数字と面積を同じにします。", remove: "選んだ長方形を消す", row: "行", column: "列", clue: "面積", blank: "数字なし", covered: "長方形の中" }
4
+ };
5
+ export function shikakuWords(language = "en") { return SHIKAKU_STRINGS[language]; }
@@ -0,0 +1 @@
1
+ export declare const SHIKAKU_PLAY_STYLE = "\n.ks-root{color:var(--ks-ink,#1f2320);font:16px/1.5 var(--font,system-ui,sans-serif);max-width:680px;margin:auto}\n.ks-board{position:relative;width:100%;min-width:240px;touch-action:manipulation}\n.ks-cells{position:absolute;inset:0;display:grid;padding:2px}\n.ks-cell{background:transparent;border:0;min-width:0;min-height:0;padding:0;cursor:crosshair;touch-action:manipulation}\n.ks-cell:focus-visible{outline:3px solid var(--accent,#b5452c);outline-offset:-4px;border-radius:4px}\n.ks-tools{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0}\n.ks-tools button,.ks-close{font:inherit;border:1px solid var(--ks-rule,#ddd6c6);border-radius:999px;min-height:44px;padding:0 14px;background:var(--ks-surface,#fbf8f1);color:var(--ks-ink,#1f2320);cursor:pointer}\n.ks-tools button:disabled{opacity:.45;cursor:default}.ks-status{min-height:1.5em}.ks-help{font-size:14px;opacity:.8}\n.ks-dialog{border:0;border-radius:14px;padding:24px;width:min(86vw,760px);max-height:90vh;overflow:auto;background:var(--surface,#fbf8f1);--ks-ink:var(--ink,#1f2320);--ks-surface:var(--surface,#fbf8f1);--ks-rule:var(--rule,#ddd6c6)}.ks-dialog::backdrop{background:#172c28bb}\n.ks-close{float:right;margin-bottom:10px}.ks-dialog .ks-root{clear:both}\n";
@@ -0,0 +1,12 @@
1
+ export const SHIKAKU_PLAY_STYLE = `
2
+ .ks-root{color:var(--ks-ink,#1f2320);font:16px/1.5 var(--font,system-ui,sans-serif);max-width:680px;margin:auto}
3
+ .ks-board{position:relative;width:100%;min-width:240px;touch-action:manipulation}
4
+ .ks-cells{position:absolute;inset:0;display:grid;padding:2px}
5
+ .ks-cell{background:transparent;border:0;min-width:0;min-height:0;padding:0;cursor:crosshair;touch-action:manipulation}
6
+ .ks-cell:focus-visible{outline:3px solid var(--accent,#b5452c);outline-offset:-4px;border-radius:4px}
7
+ .ks-tools{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0}
8
+ .ks-tools button,.ks-close{font:inherit;border:1px solid var(--ks-rule,#ddd6c6);border-radius:999px;min-height:44px;padding:0 14px;background:var(--ks-surface,#fbf8f1);color:var(--ks-ink,#1f2320);cursor:pointer}
9
+ .ks-tools button:disabled{opacity:.45;cursor:default}.ks-status{min-height:1.5em}.ks-help{font-size:14px;opacity:.8}
10
+ .ks-dialog{border:0;border-radius:14px;padding:24px;width:min(86vw,760px);max-height:90vh;overflow:auto;background:var(--surface,#fbf8f1);--ks-ink:var(--ink,#1f2320);--ks-surface:var(--surface,#fbf8f1);--ks-rule:var(--rule,#ddd6c6)}.ks-dialog::backdrop{background:#172c28bb}
11
+ .ks-close{float:right;margin-bottom:10px}.ks-dialog .ks-root{clear:both}
12
+ `;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import { generateShikaku } from "./shikakuGenerate.js";
2
+ self.onmessage = (event) => {
3
+ try {
4
+ const { width, height, level, seed } = event.data;
5
+ self.postMessage({ puzzle: generateShikaku(width, height, level, seed) });
6
+ }
7
+ catch (error) {
8
+ self.postMessage({ error: error instanceof Error ? error.message : String(error) });
9
+ }
10
+ };
@@ -0,0 +1,2 @@
1
+ export { drawSlitherlink } from "./slitherlinkDraw.ts";
2
+ export type { SlitherlinkDrawOptions } from "./slitherlinkPlay.types.ts";
@@ -0,0 +1 @@
1
+ export { drawSlitherlink } from "./slitherlinkDraw.js";
@@ -0,0 +1,11 @@
1
+ /** Slitherlink uses a dedicated edge model and a single-loop constraint. */
2
+ export * from "./slitherlink.types.ts";
3
+ export * from "./slitherlink.constants.ts";
4
+ export * from "./slitherlinkBoard.ts";
5
+ export * from "./slitherlinkSolve.ts";
6
+ export * from "./slitherlinkGenerate.ts";
7
+ export * from "./slitherlinkGame.ts";
8
+ export { drawSlitherlink } from "./slitherlinkDraw.ts";
9
+ export type { SlitherlinkDrawOptions } from "./slitherlinkPlay.types.ts";
10
+ export { mountSlitherlink } from "./slitherlinkMount.ts";
11
+ export type * from "./slitherlinkPlay.types.ts";
@@ -0,0 +1,9 @@
1
+ /** Slitherlink uses a dedicated edge model and a single-loop constraint. */
2
+ export * from "./slitherlink.types.js";
3
+ export * from "./slitherlink.constants.js";
4
+ export * from "./slitherlinkBoard.js";
5
+ export * from "./slitherlinkSolve.js";
6
+ export * from "./slitherlinkGenerate.js";
7
+ export * from "./slitherlinkGame.js";
8
+ export { drawSlitherlink } from "./slitherlinkDraw.js";
9
+ export { mountSlitherlink } from "./slitherlinkMount.js";
@@ -0,0 +1,4 @@
1
+ export { mountSlitherlink } from "./slitherlinkMount.ts";
2
+ export { SLITHERLINK_PLAY_STYLE } from "./slitherlinkStyle.ts";
3
+ export { SLITHERLINK_STRINGS } from "./slitherlinkStrings.ts";
4
+ export type * from "./slitherlinkPlay.types.ts";
@@ -0,0 +1,3 @@
1
+ export { mountSlitherlink } from "./slitherlinkMount.js";
2
+ export { SLITHERLINK_PLAY_STYLE } from "./slitherlinkStyle.js";
3
+ export { SLITHERLINK_STRINGS } from "./slitherlinkStrings.js";
@@ -0,0 +1,2 @@
1
+ export declare const SLITHERLINK_MOST_SIDE = 10;
2
+ export declare const SLITHERLINK_MOST_NODES = 300000;
@@ -0,0 +1,2 @@
1
+ export const SLITHERLINK_MOST_SIDE = 10;
2
+ export const SLITHERLINK_MOST_NODES = 300000;
@@ -0,0 +1,34 @@
1
+ export type SlitherlinkBoard = {
2
+ width: number;
3
+ height: number;
4
+ clues: readonly (number | null)[];
5
+ };
6
+ export type SlitherlinkPuzzle = SlitherlinkBoard & {
7
+ seed: number;
8
+ solution: readonly number[];
9
+ };
10
+ export type SlitherlinkCheck = {
11
+ ok: boolean;
12
+ errors: readonly number[];
13
+ clues: readonly number[];
14
+ vertices: readonly number[];
15
+ loops: number;
16
+ };
17
+ export type SlitherlinkProgress = {
18
+ ok: boolean;
19
+ clues: readonly number[];
20
+ vertices: readonly number[];
21
+ loops: number;
22
+ };
23
+ export type SlitherlinkSolve = {
24
+ count: number;
25
+ solution: readonly number[] | null;
26
+ complete: boolean;
27
+ nodes: number;
28
+ };
29
+ export type SlitherlinkGame = {
30
+ board: SlitherlinkBoard;
31
+ edges: readonly number[];
32
+ history: readonly (readonly number[])[];
33
+ helped: boolean;
34
+ };
@@ -0,0 +1 @@
1
+ export {};