@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.
- package/CHANGELOG.md +23 -0
- package/README.md +429 -5
- package/dist/akari-draw-entry.d.ts +2 -0
- package/dist/akari-draw-entry.js +1 -0
- package/dist/akari-entry.d.ts +12 -0
- package/dist/akari-entry.js +10 -0
- package/dist/akari-play-entry.d.ts +2 -0
- package/dist/akari-play-entry.js +1 -0
- package/dist/akari.constants.d.ts +2 -0
- package/dist/akari.constants.js +2 -0
- package/dist/akari.types.d.ts +33 -0
- package/dist/akari.types.js +1 -0
- package/dist/akariBoard.d.ts +11 -0
- package/dist/akariBoard.js +96 -0
- package/dist/akariDraw.d.ts +4 -0
- package/dist/akariDraw.js +38 -0
- package/dist/akariGame.d.ts +15 -0
- package/dist/akariGame.js +71 -0
- package/dist/akariGenerate.d.ts +7 -0
- package/dist/akariGenerate.js +78 -0
- package/dist/akariMount.d.ts +3 -0
- package/dist/akariMount.js +214 -0
- package/dist/akariPlay.types.d.ts +25 -0
- package/dist/akariPlay.types.js +1 -0
- package/dist/akariSolve.d.ts +6 -0
- package/dist/akariSolve.js +106 -0
- package/dist/akariStrings.d.ts +98 -0
- package/dist/akariStrings.js +53 -0
- package/dist/akariStyle.d.ts +1 -0
- package/dist/akariStyle.js +9 -0
- package/dist/draw-entry.d.ts +1 -0
- package/dist/draw-entry.js +1 -0
- package/dist/fillomino-draw-entry.d.ts +2 -0
- package/dist/fillomino-draw-entry.js +1 -0
- package/dist/fillomino-entry.d.ts +8 -0
- package/dist/fillomino-entry.js +7 -0
- package/dist/fillomino-play-entry.d.ts +4 -0
- package/dist/fillomino-play-entry.js +3 -0
- package/dist/fillomino.constants.d.ts +5 -0
- package/dist/fillomino.constants.js +5 -0
- package/dist/fillomino.types.d.ts +30 -0
- package/dist/fillomino.types.js +1 -0
- package/dist/fillominoBoard.d.ts +6 -0
- package/dist/fillominoBoard.js +84 -0
- package/dist/fillominoDraw.d.ts +3 -0
- package/dist/fillominoDraw.js +31 -0
- package/dist/fillominoGame.d.ts +13 -0
- package/dist/fillominoGame.js +65 -0
- package/dist/fillominoGenerate.d.ts +3 -0
- package/dist/fillominoGenerate.js +125 -0
- package/dist/fillominoMount.d.ts +2 -0
- package/dist/fillominoMount.js +211 -0
- package/dist/fillominoPlay.types.d.ts +25 -0
- package/dist/fillominoPlay.types.js +1 -0
- package/dist/fillominoSolve.d.ts +6 -0
- package/dist/fillominoSolve.js +111 -0
- package/dist/fillominoStrings.d.ts +102 -0
- package/dist/fillominoStrings.js +13 -0
- package/dist/fillominoStyle.d.ts +1 -0
- package/dist/fillominoStyle.js +13 -0
- package/dist/fillominoWorker.d.ts +1 -0
- package/dist/fillominoWorker.js +10 -0
- package/dist/heyawake-draw-entry.d.ts +2 -0
- package/dist/heyawake-draw-entry.js +1 -0
- package/dist/heyawake-entry.d.ts +13 -0
- package/dist/heyawake-entry.js +11 -0
- package/dist/heyawake-play-entry.d.ts +2 -0
- package/dist/heyawake-play-entry.js +1 -0
- package/dist/heyawake.constants.d.ts +5 -0
- package/dist/heyawake.constants.js +5 -0
- package/dist/heyawake.types.d.ts +35 -0
- package/dist/heyawake.types.js +1 -0
- package/dist/heyawakeBoard.d.ts +7 -0
- package/dist/heyawakeBoard.js +116 -0
- package/dist/heyawakeDraw.d.ts +3 -0
- package/dist/heyawakeDraw.js +27 -0
- package/dist/heyawakeGame.d.ts +12 -0
- package/dist/heyawakeGame.js +51 -0
- package/dist/heyawakeGenerate.d.ts +2 -0
- package/dist/heyawakeGenerate.js +114 -0
- package/dist/heyawakeMount.d.ts +2 -0
- package/dist/heyawakeMount.js +106 -0
- package/dist/heyawakePlay.types.d.ts +24 -0
- package/dist/heyawakePlay.types.js +1 -0
- package/dist/heyawakeSolve.d.ts +5 -0
- package/dist/heyawakeSolve.js +52 -0
- package/dist/heyawakeStrings.d.ts +19 -0
- package/dist/heyawakeStrings.js +7 -0
- package/dist/heyawakeStyle.d.ts +1 -0
- package/dist/heyawakeStyle.js +2 -0
- package/dist/heyawakeWorker.d.ts +1 -0
- package/dist/heyawakeWorker.js +10 -0
- package/dist/hitori-draw-entry.d.ts +2 -0
- package/dist/hitori-draw-entry.js +1 -0
- package/dist/hitori-entry.d.ts +6 -0
- package/dist/hitori-entry.js +6 -0
- package/dist/hitori-play-entry.d.ts +4 -0
- package/dist/hitori-play-entry.js +3 -0
- package/dist/hitori.constants.d.ts +2 -0
- package/dist/hitori.constants.js +2 -0
- package/dist/hitori.types.d.ts +20 -0
- package/dist/hitori.types.js +1 -0
- package/dist/hitoriBoard.d.ts +8 -0
- package/dist/hitoriBoard.js +72 -0
- package/dist/hitoriDraw.d.ts +4 -0
- package/dist/hitoriDraw.js +25 -0
- package/dist/hitoriGame.d.ts +10 -0
- package/dist/hitoriGame.js +48 -0
- package/dist/hitoriGenerate.d.ts +3 -0
- package/dist/hitoriGenerate.js +163 -0
- package/dist/hitoriMount.d.ts +3 -0
- package/dist/hitoriMount.js +111 -0
- package/dist/hitoriPlay.types.d.ts +25 -0
- package/dist/hitoriPlay.types.js +1 -0
- package/dist/hitoriSolve.d.ts +6 -0
- package/dist/hitoriSolve.js +69 -0
- package/dist/hitoriStrings.d.ts +70 -0
- package/dist/hitoriStrings.js +17 -0
- package/dist/hitoriStyle.d.ts +1 -0
- package/dist/hitoriStyle.js +9 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/juosan-draw-entry.d.ts +2 -0
- package/dist/juosan-draw-entry.js +1 -0
- package/dist/juosan-entry.d.ts +11 -0
- package/dist/juosan-entry.js +10 -0
- package/dist/juosan-play-entry.d.ts +4 -0
- package/dist/juosan-play-entry.js +3 -0
- package/dist/juosan.constants.d.ts +4 -0
- package/dist/juosan.constants.js +4 -0
- package/dist/juosan.types.d.ts +33 -0
- package/dist/juosan.types.js +1 -0
- package/dist/juosanBoard.d.ts +7 -0
- package/dist/juosanBoard.js +102 -0
- package/dist/juosanDraw.d.ts +4 -0
- package/dist/juosanDraw.js +49 -0
- package/dist/juosanGame.d.ts +20 -0
- package/dist/juosanGame.js +86 -0
- package/dist/juosanGenerate.d.ts +3 -0
- package/dist/juosanGenerate.js +31 -0
- package/dist/juosanMount.d.ts +3 -0
- package/dist/juosanMount.js +206 -0
- package/dist/juosanPlay.types.d.ts +24 -0
- package/dist/juosanPlay.types.js +1 -0
- package/dist/juosanSolve.d.ts +3 -0
- package/dist/juosanSolve.js +82 -0
- package/dist/juosanStrings.d.ts +77 -0
- package/dist/juosanStrings.js +43 -0
- package/dist/juosanStyle.d.ts +1 -0
- package/dist/juosanStyle.js +1 -0
- package/dist/kakuro-draw-entry.d.ts +2 -0
- package/dist/kakuro-draw-entry.js +1 -0
- package/dist/kakuro-entry.d.ts +12 -0
- package/dist/kakuro-entry.js +10 -0
- package/dist/kakuro-play-entry.d.ts +4 -0
- package/dist/kakuro-play-entry.js +3 -0
- package/dist/kakuro.constants.d.ts +6 -0
- package/dist/kakuro.constants.js +6 -0
- package/dist/kakuro.types.d.ts +46 -0
- package/dist/kakuro.types.js +1 -0
- package/dist/kakuroBoard.d.ts +8 -0
- package/dist/kakuroBoard.js +117 -0
- package/dist/kakuroDraw.d.ts +10 -0
- package/dist/kakuroDraw.js +21 -0
- package/dist/kakuroGame.d.ts +12 -0
- package/dist/kakuroGame.js +74 -0
- package/dist/kakuroGenerate.d.ts +3 -0
- package/dist/kakuroGenerate.js +130 -0
- package/dist/kakuroMount.d.ts +2 -0
- package/dist/kakuroMount.js +109 -0
- package/dist/kakuroPlay.types.d.ts +20 -0
- package/dist/kakuroPlay.types.js +1 -0
- package/dist/kakuroSolve.d.ts +6 -0
- package/dist/kakuroSolve.js +85 -0
- package/dist/kakuroStrings.d.ts +82 -0
- package/dist/kakuroStrings.js +5 -0
- package/dist/kakuroStyle.d.ts +1 -0
- package/dist/kakuroStyle.js +1 -0
- package/dist/masyu-draw-entry.d.ts +2 -0
- package/dist/masyu-draw-entry.js +1 -0
- package/dist/masyu-entry.d.ts +6 -0
- package/dist/masyu-entry.js +6 -0
- package/dist/masyu-play-entry.d.ts +4 -0
- package/dist/masyu-play-entry.js +3 -0
- package/dist/masyu.constants.d.ts +2 -0
- package/dist/masyu.constants.js +2 -0
- package/dist/masyu.types.d.ts +46 -0
- package/dist/masyu.types.js +1 -0
- package/dist/masyuBoard.d.ts +10 -0
- package/dist/masyuBoard.js +88 -0
- package/dist/masyuDraw.d.ts +2 -0
- package/dist/masyuDraw.js +33 -0
- package/dist/masyuGame.d.ts +9 -0
- package/dist/masyuGame.js +46 -0
- package/dist/masyuGenerate.d.ts +3 -0
- package/dist/masyuGenerate.js +56 -0
- package/dist/masyuMount.d.ts +2 -0
- package/dist/masyuMount.js +118 -0
- package/dist/masyuPlay.types.d.ts +1 -0
- package/dist/masyuPlay.types.js +1 -0
- package/dist/masyuSolve.d.ts +6 -0
- package/dist/masyuSolve.js +88 -0
- package/dist/masyuStrings.d.ts +28 -0
- package/dist/masyuStrings.js +4 -0
- package/dist/masyuStyle.d.ts +1 -0
- package/dist/masyuStyle.js +11 -0
- package/dist/nurikabe-draw-entry.d.ts +2 -0
- package/dist/nurikabe-draw-entry.js +1 -0
- package/dist/nurikabe-entry.d.ts +6 -0
- package/dist/nurikabe-entry.js +6 -0
- package/dist/nurikabe-play-entry.d.ts +4 -0
- package/dist/nurikabe-play-entry.js +3 -0
- package/dist/nurikabe.constants.d.ts +2 -0
- package/dist/nurikabe.constants.js +2 -0
- package/dist/nurikabe.types.d.ts +20 -0
- package/dist/nurikabe.types.js +1 -0
- package/dist/nurikabeBoard.d.ts +8 -0
- package/dist/nurikabeBoard.js +72 -0
- package/dist/nurikabeDraw.d.ts +4 -0
- package/dist/nurikabeDraw.js +15 -0
- package/dist/nurikabeGame.d.ts +8 -0
- package/dist/nurikabeGame.js +40 -0
- package/dist/nurikabeGenerate.d.ts +3 -0
- package/dist/nurikabeGenerate.js +31 -0
- package/dist/nurikabeMount.d.ts +3 -0
- package/dist/nurikabeMount.js +99 -0
- package/dist/nurikabePlay.types.d.ts +25 -0
- package/dist/nurikabePlay.types.js +1 -0
- package/dist/nurikabeSolve.d.ts +6 -0
- package/dist/nurikabeSolve.js +75 -0
- package/dist/nurikabeStrings.d.ts +70 -0
- package/dist/nurikabeStrings.js +5 -0
- package/dist/nurikabeStyle.d.ts +1 -0
- package/dist/nurikabeStyle.js +9 -0
- package/dist/play-entry.d.ts +1 -0
- package/dist/play-entry.js +1 -0
- package/dist/ripple-draw-entry.d.ts +2 -0
- package/dist/ripple-draw-entry.js +1 -0
- package/dist/ripple-entry.d.ts +12 -0
- package/dist/ripple-entry.js +11 -0
- package/dist/ripple-play-entry.d.ts +4 -0
- package/dist/ripple-play-entry.js +3 -0
- package/dist/ripple.constants.d.ts +4 -0
- package/dist/ripple.constants.js +4 -0
- package/dist/ripple.types.d.ts +34 -0
- package/dist/ripple.types.js +1 -0
- package/dist/rippleBoard.d.ts +7 -0
- package/dist/rippleBoard.js +99 -0
- package/dist/rippleDraw.d.ts +3 -0
- package/dist/rippleDraw.js +32 -0
- package/dist/rippleGame.d.ts +13 -0
- package/dist/rippleGame.js +85 -0
- package/dist/rippleGenerate.d.ts +3 -0
- package/dist/rippleGenerate.js +57 -0
- package/dist/rippleMount.d.ts +2 -0
- package/dist/rippleMount.js +179 -0
- package/dist/ripplePlay.types.d.ts +23 -0
- package/dist/ripplePlay.types.js +1 -0
- package/dist/rippleSolve.d.ts +6 -0
- package/dist/rippleSolve.js +83 -0
- package/dist/rippleStrings.d.ts +86 -0
- package/dist/rippleStrings.js +5 -0
- package/dist/rippleStyle.d.ts +1 -0
- package/dist/rippleStyle.js +1 -0
- package/dist/shikaku-draw-entry.d.ts +2 -0
- package/dist/shikaku-draw-entry.js +1 -0
- package/dist/shikaku-entry.d.ts +8 -0
- package/dist/shikaku-entry.js +8 -0
- package/dist/shikaku-play-entry.d.ts +4 -0
- package/dist/shikaku-play-entry.js +3 -0
- package/dist/shikaku.constants.d.ts +5 -0
- package/dist/shikaku.constants.js +5 -0
- package/dist/shikaku.types.d.ts +36 -0
- package/dist/shikaku.types.js +1 -0
- package/dist/shikakuBoard.d.ts +9 -0
- package/dist/shikakuBoard.js +59 -0
- package/dist/shikakuDraw.d.ts +4 -0
- package/dist/shikakuDraw.js +23 -0
- package/dist/shikakuGame.d.ts +14 -0
- package/dist/shikakuGame.js +61 -0
- package/dist/shikakuGenerate.d.ts +3 -0
- package/dist/shikakuGenerate.js +43 -0
- package/dist/shikakuMount.d.ts +3 -0
- package/dist/shikakuMount.js +143 -0
- package/dist/shikakuPacks.d.ts +27 -0
- package/dist/shikakuPacks.js +26 -0
- package/dist/shikakuPlay.types.d.ts +26 -0
- package/dist/shikakuPlay.types.js +1 -0
- package/dist/shikakuSolve.d.ts +6 -0
- package/dist/shikakuSolve.js +54 -0
- package/dist/shikakuStrings.d.ts +102 -0
- package/dist/shikakuStrings.js +5 -0
- package/dist/shikakuStyle.d.ts +1 -0
- package/dist/shikakuStyle.js +12 -0
- package/dist/shikakuWorker.d.ts +1 -0
- package/dist/shikakuWorker.js +10 -0
- package/dist/slitherlink-draw-entry.d.ts +2 -0
- package/dist/slitherlink-draw-entry.js +1 -0
- package/dist/slitherlink-entry.d.ts +11 -0
- package/dist/slitherlink-entry.js +9 -0
- package/dist/slitherlink-play-entry.d.ts +4 -0
- package/dist/slitherlink-play-entry.js +3 -0
- package/dist/slitherlink.constants.d.ts +2 -0
- package/dist/slitherlink.constants.js +2 -0
- package/dist/slitherlink.types.d.ts +34 -0
- package/dist/slitherlink.types.js +1 -0
- package/dist/slitherlinkBoard.d.ts +12 -0
- package/dist/slitherlinkBoard.js +132 -0
- package/dist/slitherlinkDraw.d.ts +4 -0
- package/dist/slitherlinkDraw.js +51 -0
- package/dist/slitherlinkGame.d.ts +8 -0
- package/dist/slitherlinkGame.js +63 -0
- package/dist/slitherlinkGenerate.d.ts +3 -0
- package/dist/slitherlinkGenerate.js +101 -0
- package/dist/slitherlinkMount.d.ts +3 -0
- package/dist/slitherlinkMount.js +267 -0
- package/dist/slitherlinkPlay.types.d.ts +23 -0
- package/dist/slitherlinkPlay.types.js +1 -0
- package/dist/slitherlinkSolve.d.ts +6 -0
- package/dist/slitherlinkSolve.js +127 -0
- package/dist/slitherlinkStrings.d.ts +86 -0
- package/dist/slitherlinkStrings.js +47 -0
- package/dist/slitherlinkStyle.d.ts +1 -0
- package/dist/slitherlinkStyle.js +18 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/yajilin-draw-entry.d.ts +2 -0
- package/dist/yajilin-draw-entry.js +1 -0
- package/dist/yajilin-entry.d.ts +6 -0
- package/dist/yajilin-entry.js +6 -0
- package/dist/yajilin-play-entry.d.ts +4 -0
- package/dist/yajilin-play-entry.js +3 -0
- package/dist/yajilin.constants.d.ts +2 -0
- package/dist/yajilin.constants.js +2 -0
- package/dist/yajilin.types.d.ts +63 -0
- package/dist/yajilin.types.js +1 -0
- package/dist/yajilinBoard.d.ts +9 -0
- package/dist/yajilinBoard.js +91 -0
- package/dist/yajilinDraw.d.ts +2 -0
- package/dist/yajilinDraw.js +33 -0
- package/dist/yajilinGame.d.ts +15 -0
- package/dist/yajilinGame.js +58 -0
- package/dist/yajilinGenerate.d.ts +3 -0
- package/dist/yajilinGenerate.js +224 -0
- package/dist/yajilinMount.d.ts +2 -0
- package/dist/yajilinMount.js +162 -0
- package/dist/yajilinPlay.types.d.ts +1 -0
- package/dist/yajilinPlay.types.js +1 -0
- package/dist/yajilinSolve.d.ts +6 -0
- package/dist/yajilinSolve.js +82 -0
- package/dist/yajilinStrings.d.ts +32 -0
- package/dist/yajilinStrings.js +4 -0
- package/dist/yajilinStyle.d.ts +1 -0
- package/dist/yajilinStyle.js +12 -0
- package/package.json +196 -3
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export type KakuroLanguage = "en" | "ja";
|
|
2
|
+
export declare const KAKURO_STRINGS: {
|
|
3
|
+
readonly en: {
|
|
4
|
+
readonly title: "Kakuro";
|
|
5
|
+
readonly help: "Fill each white cell with 1–9. Digits in each across or down run add to its clue and cannot repeat. Arrows move; type a digit or use the number pad; P toggles pencil marks.";
|
|
6
|
+
readonly undo: "Undo";
|
|
7
|
+
readonly hint: "Hint";
|
|
8
|
+
readonly check: "Check";
|
|
9
|
+
readonly restart: "Restart";
|
|
10
|
+
readonly ready: "Choose a white cell.";
|
|
11
|
+
readonly won: "Every run is correct. Solved!";
|
|
12
|
+
readonly helped: "Solved with help.";
|
|
13
|
+
readonly checked: "No run errors so far. Keep filling.";
|
|
14
|
+
readonly wrong: "Some runs have a repeated digit or impossible total.";
|
|
15
|
+
readonly across: "Across";
|
|
16
|
+
readonly down: "Down";
|
|
17
|
+
readonly pencil: "Pencil marks";
|
|
18
|
+
readonly row: "Row";
|
|
19
|
+
readonly column: "column";
|
|
20
|
+
readonly blank: "blank";
|
|
21
|
+
readonly clear: "Clear";
|
|
22
|
+
};
|
|
23
|
+
readonly ja: {
|
|
24
|
+
readonly title: "カックロ";
|
|
25
|
+
readonly help: "白マスに1〜9を入れます。横または縦の連続した数字はヒントの合計になり、同じ数字は使えません。矢印または数字パッドで入力、Pでメモを切り替えます。";
|
|
26
|
+
readonly undo: "戻す";
|
|
27
|
+
readonly hint: "ヒント";
|
|
28
|
+
readonly check: "確認";
|
|
29
|
+
readonly restart: "やり直す";
|
|
30
|
+
readonly ready: "白マスを選んでください。";
|
|
31
|
+
readonly won: "すべての合計が合いました。完成!";
|
|
32
|
+
readonly helped: "ヒントを使って完成!";
|
|
33
|
+
readonly checked: "ここまでの合計に誤りはありません。続けてください。";
|
|
34
|
+
readonly wrong: "同じ数字または合計の誤りがあります。";
|
|
35
|
+
readonly across: "横";
|
|
36
|
+
readonly down: "縦";
|
|
37
|
+
readonly pencil: "メモ";
|
|
38
|
+
readonly row: "行";
|
|
39
|
+
readonly column: "列";
|
|
40
|
+
readonly blank: "空白";
|
|
41
|
+
readonly clear: "消す";
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export declare function kakuroWords(language?: KakuroLanguage): {
|
|
45
|
+
readonly title: "Kakuro";
|
|
46
|
+
readonly help: "Fill each white cell with 1–9. Digits in each across or down run add to its clue and cannot repeat. Arrows move; type a digit or use the number pad; P toggles pencil marks.";
|
|
47
|
+
readonly undo: "Undo";
|
|
48
|
+
readonly hint: "Hint";
|
|
49
|
+
readonly check: "Check";
|
|
50
|
+
readonly restart: "Restart";
|
|
51
|
+
readonly ready: "Choose a white cell.";
|
|
52
|
+
readonly won: "Every run is correct. Solved!";
|
|
53
|
+
readonly helped: "Solved with help.";
|
|
54
|
+
readonly checked: "No run errors so far. Keep filling.";
|
|
55
|
+
readonly wrong: "Some runs have a repeated digit or impossible total.";
|
|
56
|
+
readonly across: "Across";
|
|
57
|
+
readonly down: "Down";
|
|
58
|
+
readonly pencil: "Pencil marks";
|
|
59
|
+
readonly row: "Row";
|
|
60
|
+
readonly column: "column";
|
|
61
|
+
readonly blank: "blank";
|
|
62
|
+
readonly clear: "Clear";
|
|
63
|
+
} | {
|
|
64
|
+
readonly title: "カックロ";
|
|
65
|
+
readonly help: "白マスに1〜9を入れます。横または縦の連続した数字はヒントの合計になり、同じ数字は使えません。矢印または数字パッドで入力、Pでメモを切り替えます。";
|
|
66
|
+
readonly undo: "戻す";
|
|
67
|
+
readonly hint: "ヒント";
|
|
68
|
+
readonly check: "確認";
|
|
69
|
+
readonly restart: "やり直す";
|
|
70
|
+
readonly ready: "白マスを選んでください。";
|
|
71
|
+
readonly won: "すべての合計が合いました。完成!";
|
|
72
|
+
readonly helped: "ヒントを使って完成!";
|
|
73
|
+
readonly checked: "ここまでの合計に誤りはありません。続けてください。";
|
|
74
|
+
readonly wrong: "同じ数字または合計の誤りがあります。";
|
|
75
|
+
readonly across: "横";
|
|
76
|
+
readonly down: "縦";
|
|
77
|
+
readonly pencil: "メモ";
|
|
78
|
+
readonly row: "行";
|
|
79
|
+
readonly column: "列";
|
|
80
|
+
readonly blank: "空白";
|
|
81
|
+
readonly clear: "消す";
|
|
82
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const KAKURO_STRINGS = {
|
|
2
|
+
en: { title: "Kakuro", help: "Fill each white cell with 1–9. Digits in each across or down run add to its clue and cannot repeat. Arrows move; type a digit or use the number pad; P toggles pencil marks.", undo: "Undo", hint: "Hint", check: "Check", restart: "Restart", ready: "Choose a white cell.", won: "Every run is correct. Solved!", helped: "Solved with help.", checked: "No run errors so far. Keep filling.", wrong: "Some runs have a repeated digit or impossible total.", across: "Across", down: "Down", pencil: "Pencil marks", row: "Row", column: "column", blank: "blank", clear: "Clear" },
|
|
3
|
+
ja: { title: "カックロ", help: "白マスに1〜9を入れます。横または縦の連続した数字はヒントの合計になり、同じ数字は使えません。矢印または数字パッドで入力、Pでメモを切り替えます。", undo: "戻す", hint: "ヒント", check: "確認", restart: "やり直す", ready: "白マスを選んでください。", won: "すべての合計が合いました。完成!", helped: "ヒントを使って完成!", checked: "ここまでの合計に誤りはありません。続けてください。", wrong: "同じ数字または合計の誤りがあります。", across: "横", down: "縦", pencil: "メモ", row: "行", column: "列", blank: "空白", clear: "消す" }
|
|
4
|
+
};
|
|
5
|
+
export function kakuroWords(language = "en") { return KAKURO_STRINGS[language]; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const KAKURO_PLAY_STYLE = ".kk-root{max-width:760px;margin:auto;color:var(--ink,#1f2320);font:16px/1.5 system-ui,sans-serif}.kk-board{position:relative;width:100%;touch-action:manipulation}.kk-cells{position:absolute;inset:2px;display:grid}.kk-cell{border:0;background:transparent;min-width:0;min-height:0;padding:0;touch-action:manipulation}.kk-cell:focus-visible,.kk-pad button:focus-visible{outline:3px solid #b5452c;outline-offset:-3px}.kk-tools{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0}.kk-tools button,.kk-pad button{font:inherit;min-height:44px;padding:0 14px;border-radius:999px;border:1px solid #ddd6c6;background:#fbf8f1;color:inherit}.kk-pad{display:grid;grid-template-columns:repeat(5,minmax(44px,1fr));gap:8px;margin:12px 0}.kk-pad button{padding:0;touch-action:manipulation}.kk-status{min-height:1.5em}.kk-help{opacity:.8;font-size:14px}";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const KAKURO_PLAY_STYLE = `.kk-root{max-width:760px;margin:auto;color:var(--ink,#1f2320);font:16px/1.5 system-ui,sans-serif}.kk-board{position:relative;width:100%;touch-action:manipulation}.kk-cells{position:absolute;inset:2px;display:grid}.kk-cell{border:0;background:transparent;min-width:0;min-height:0;padding:0;touch-action:manipulation}.kk-cell:focus-visible,.kk-pad button:focus-visible{outline:3px solid #b5452c;outline-offset:-3px}.kk-tools{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0}.kk-tools button,.kk-pad button{font:inherit;min-height:44px;padding:0 14px;border-radius:999px;border:1px solid #ddd6c6;background:#fbf8f1;color:inherit}.kk-pad{display:grid;grid-template-columns:repeat(5,minmax(44px,1fr));gap:8px;margin:12px 0}.kk-pad button{padding:0;touch-action:manipulation}.kk-status{min-height:1.5em}.kk-help{opacity:.8;font-size:14px}`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { drawMasyu } from "./masyuDraw.js";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type MasyuBoard = {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
pearls: readonly number[];
|
|
5
|
+
};
|
|
6
|
+
export type MasyuPuzzle = MasyuBoard & {
|
|
7
|
+
seed: number;
|
|
8
|
+
solution: readonly number[];
|
|
9
|
+
};
|
|
10
|
+
export type MasyuCheck = {
|
|
11
|
+
ok: boolean;
|
|
12
|
+
errors: readonly number[];
|
|
13
|
+
};
|
|
14
|
+
export type MasyuSolve = {
|
|
15
|
+
count: number;
|
|
16
|
+
solution: readonly number[] | null;
|
|
17
|
+
complete: boolean;
|
|
18
|
+
nodes: number;
|
|
19
|
+
};
|
|
20
|
+
export type MasyuGame = {
|
|
21
|
+
board: MasyuBoard;
|
|
22
|
+
edges: readonly number[];
|
|
23
|
+
history: readonly (readonly number[])[];
|
|
24
|
+
helped: boolean;
|
|
25
|
+
};
|
|
26
|
+
export type MasyuDrawOptions = {
|
|
27
|
+
edges?: readonly number[];
|
|
28
|
+
selected?: number;
|
|
29
|
+
errors?: readonly number[];
|
|
30
|
+
material?: "ivory" | "wood" | "slate";
|
|
31
|
+
pieces?: "plain" | "tiles";
|
|
32
|
+
language?: "en" | "ja";
|
|
33
|
+
};
|
|
34
|
+
export type MasyuMountOptions = MasyuDrawOptions & {
|
|
35
|
+
board: MasyuBoard;
|
|
36
|
+
progress?: string;
|
|
37
|
+
onChange?: (game: MasyuGame) => void;
|
|
38
|
+
onFinish?: (game: MasyuGame) => void;
|
|
39
|
+
};
|
|
40
|
+
export type MasyuMount = {
|
|
41
|
+
game: () => MasyuGame;
|
|
42
|
+
progress: () => string;
|
|
43
|
+
set: (options: MasyuDrawOptions) => void;
|
|
44
|
+
restart: () => void;
|
|
45
|
+
destroy: () => void;
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MasyuBoard, MasyuCheck } from "./masyu.types.ts";
|
|
2
|
+
export declare const masyuCellCount: (board: MasyuBoard) => number;
|
|
3
|
+
export declare const masyuEdgeCount: (board: MasyuBoard) => number;
|
|
4
|
+
export declare function isMasyuBoard(value: unknown): value is MasyuBoard;
|
|
5
|
+
export declare function masyuNeighbors(board: MasyuBoard, cell: number): {
|
|
6
|
+
cell: number;
|
|
7
|
+
edge: number;
|
|
8
|
+
direction: number;
|
|
9
|
+
}[];
|
|
10
|
+
export declare function checkMasyu(board: MasyuBoard, lines: readonly number[]): MasyuCheck;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { MASYU_SIZES } from "./masyu.constants.js";
|
|
2
|
+
export const masyuCellCount = (board) => board.width * board.height;
|
|
3
|
+
export const masyuEdgeCount = (board) => board.height * (board.width - 1) + board.width * (board.height - 1);
|
|
4
|
+
export function isMasyuBoard(value) {
|
|
5
|
+
if (!value || typeof value !== "object")
|
|
6
|
+
return false;
|
|
7
|
+
const board = value;
|
|
8
|
+
return MASYU_SIZES.includes(board.width) && board.width === board.height
|
|
9
|
+
&& Array.isArray(board.pearls) && board.pearls.length === masyuCellCount(board)
|
|
10
|
+
&& board.pearls.every(pearl => pearl === 0 || pearl === 1 || pearl === 2)
|
|
11
|
+
&& board.pearls.some(Boolean);
|
|
12
|
+
}
|
|
13
|
+
export function masyuNeighbors(board, cell) {
|
|
14
|
+
const x = cell % board.width, y = Math.floor(cell / board.width), horizontal = board.height * (board.width - 1);
|
|
15
|
+
const result = [];
|
|
16
|
+
if (y > 0)
|
|
17
|
+
result.push({ cell: cell - board.width, edge: horizontal + (y - 1) * board.width + x, direction: 0 });
|
|
18
|
+
if (x < board.width - 1)
|
|
19
|
+
result.push({ cell: cell + 1, edge: y * (board.width - 1) + x, direction: 1 });
|
|
20
|
+
if (y < board.height - 1)
|
|
21
|
+
result.push({ cell: cell + board.width, edge: horizontal + y * board.width + x, direction: 2 });
|
|
22
|
+
if (x > 0)
|
|
23
|
+
result.push({ cell: cell - 1, edge: y * (board.width - 1) + x - 1, direction: 3 });
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
function pearlRule(board, edges, cell) {
|
|
27
|
+
const directions = masyuNeighbors(board, cell).filter(next => edges.has(next.edge)).map(next => next.direction);
|
|
28
|
+
if (directions.length !== 2)
|
|
29
|
+
return false;
|
|
30
|
+
const pearl = board.pearls[cell];
|
|
31
|
+
if (pearl === 1) {
|
|
32
|
+
if ((directions[0] + 2) % 4 !== directions[1])
|
|
33
|
+
return false;
|
|
34
|
+
return directions.some(direction => {
|
|
35
|
+
const next = masyuNeighbors(board, cell).find(item => item.direction === direction);
|
|
36
|
+
const nextDirections = masyuNeighbors(board, next.cell).filter(item => edges.has(item.edge)).map(item => item.direction);
|
|
37
|
+
return nextDirections.length === 2 && (nextDirections[0] + 2) % 4 !== nextDirections[1];
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (pearl === 2) {
|
|
41
|
+
if ((directions[0] + 2) % 4 === directions[1])
|
|
42
|
+
return false;
|
|
43
|
+
return directions.every(direction => {
|
|
44
|
+
const next = masyuNeighbors(board, cell).find(item => item.direction === direction);
|
|
45
|
+
const nextDirections = masyuNeighbors(board, next.cell).filter(item => edges.has(item.edge)).map(item => item.direction);
|
|
46
|
+
return nextDirections.length === 2 && (nextDirections[0] + 2) % 4 === nextDirections[1];
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
export function checkMasyu(board, lines) {
|
|
52
|
+
if (!isMasyuBoard(board))
|
|
53
|
+
throw new RangeError("Invalid Masyu board");
|
|
54
|
+
const edgeCount = masyuEdgeCount(board), edges = new Set(lines), errors = new Set();
|
|
55
|
+
if (edges.size !== lines.length || lines.some(edge => !Number.isInteger(edge) || edge < 0 || edge >= edgeCount)) {
|
|
56
|
+
return { ok: false, errors: [] };
|
|
57
|
+
}
|
|
58
|
+
const degrees = board.pearls.map((_, cell) => masyuNeighbors(board, cell).filter(next => edges.has(next.edge)).length);
|
|
59
|
+
for (let cell = 0; cell < degrees.length; cell += 1) {
|
|
60
|
+
if (degrees[cell] !== 0 && degrees[cell] !== 2 || (board.pearls[cell] > 0 && degrees[cell] !== 2))
|
|
61
|
+
errors.add(cell);
|
|
62
|
+
}
|
|
63
|
+
if (edges.size) {
|
|
64
|
+
const start = degrees.findIndex(degree => degree === 2), reached = new Set([start]), stack = [start];
|
|
65
|
+
while (stack.length) {
|
|
66
|
+
const cell = stack.pop();
|
|
67
|
+
for (const next of masyuNeighbors(board, cell))
|
|
68
|
+
if (edges.has(next.edge) && !reached.has(next.cell)) {
|
|
69
|
+
reached.add(next.cell);
|
|
70
|
+
stack.push(next.cell);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (let cell = 0; cell < degrees.length; cell += 1)
|
|
74
|
+
if (degrees[cell] === 2 && !reached.has(cell))
|
|
75
|
+
errors.add(cell);
|
|
76
|
+
if (reached.size !== edges.size)
|
|
77
|
+
for (const cell of reached)
|
|
78
|
+
errors.add(cell);
|
|
79
|
+
}
|
|
80
|
+
for (let cell = 0; cell < degrees.length; cell += 1) {
|
|
81
|
+
if (degrees[cell] === 2 && !pearlRule(board, edges, cell))
|
|
82
|
+
errors.add(cell);
|
|
83
|
+
}
|
|
84
|
+
const complete = edges.size > 0 && degrees.every(degree => degree === 0 || degree === 2)
|
|
85
|
+
&& degrees.every((degree, cell) => board.pearls[cell] === 0 || degree === 2)
|
|
86
|
+
&& degrees.filter(degree => degree === 2).length === edges.size;
|
|
87
|
+
return { ok: complete && errors.size === 0, errors: [...errors] };
|
|
88
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isMasyuBoard, masyuNeighbors } from "./masyuBoard.js";
|
|
2
|
+
import { MASYU_STRINGS } from "./masyuStrings.js";
|
|
3
|
+
export function drawMasyu(board, options = {}) {
|
|
4
|
+
if (!isMasyuBoard(board))
|
|
5
|
+
throw new RangeError("Invalid Masyu board");
|
|
6
|
+
const words = MASYU_STRINGS[options.language ?? "en"], step = 52, pad = 14;
|
|
7
|
+
const palette = { ivory: ["var(--kz-paper,#fbf8f1)", "var(--kz-ink,#1f2320)", "var(--kz-grid,#cfc6b2)"], wood: ["#e5cda6", "#493e2f", "#bca582"], slate: ["#262a27", "#ece8dc", "#454a44"] };
|
|
8
|
+
const [paper, ink, grid] = palette[options.material ?? "ivory"];
|
|
9
|
+
const edges = new Set(options.edges ?? []), parts = [];
|
|
10
|
+
for (let cell = 0; cell < board.pearls.length; cell += 1) {
|
|
11
|
+
const x = pad + (cell % board.width) * step, y = pad + Math.floor(cell / board.width) * step;
|
|
12
|
+
const active = options.selected === cell;
|
|
13
|
+
parts.push(`<rect x="${x - step / 2}" y="${y - step / 2}" width="${step}" height="${step}" fill="${active ? ink : "transparent"}" fill-opacity="${active ? ".08" : "1"}"/>`);
|
|
14
|
+
for (const next of masyuNeighbors(board, cell))
|
|
15
|
+
if (cell < next.cell && edges.has(next.edge)) {
|
|
16
|
+
const nx = pad + (next.cell % board.width) * step, ny = pad + Math.floor(next.cell / board.width) * step;
|
|
17
|
+
parts.push(`<line x1="${x}" y1="${y}" x2="${nx}" y2="${ny}" stroke="${options.errors?.includes(cell) ? "var(--kz-bad,#b5452c)" : ink}" stroke-width="5" stroke-linecap="round"/>`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
for (let cell = 0; cell < board.pearls.length; cell += 1) {
|
|
21
|
+
const pearl = board.pearls[cell];
|
|
22
|
+
if (!pearl)
|
|
23
|
+
continue;
|
|
24
|
+
const x = pad + (cell % board.width) * step, y = pad + Math.floor(cell / board.width) * step;
|
|
25
|
+
const radius = options.pieces === "tiles" ? 12 : 10;
|
|
26
|
+
parts.push(`<circle cx="${x}" cy="${y}" r="${radius}" fill="${pearl === 1 ? paper : ink}" stroke="${ink}" stroke-width="3" aria-label="${words.pearl[pearl]}"/>`);
|
|
27
|
+
}
|
|
28
|
+
for (let y = 0; y < board.height; y += 1)
|
|
29
|
+
for (let x = 0; x < board.width; x += 1) {
|
|
30
|
+
parts.push(`<circle cx="${pad + x * step}" cy="${pad + y * step}" r="2" fill="${grid}"/>`);
|
|
31
|
+
}
|
|
32
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${pad * 2 + (board.width - 1) * step} ${pad * 2 + (board.height - 1) * step}" role="img" aria-label="${words.title}" style="display:block;width:100%;height:auto;background:${paper}">${parts.join("")}</svg>`;
|
|
33
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MasyuBoard, MasyuGame } from "./masyu.types.ts";
|
|
2
|
+
export declare function newMasyu(board: MasyuBoard): MasyuGame;
|
|
3
|
+
export declare function toggleMasyuEdge(game: MasyuGame, edge: number): MasyuGame;
|
|
4
|
+
export declare function undoMasyu(game: MasyuGame): MasyuGame;
|
|
5
|
+
export declare function restartMasyu(game: MasyuGame): MasyuGame;
|
|
6
|
+
export declare function hintMasyu(game: MasyuGame): number | null;
|
|
7
|
+
export declare function masyuFinished(game: MasyuGame): boolean;
|
|
8
|
+
export declare function encodeMasyu(game: MasyuGame): string;
|
|
9
|
+
export declare function decodeMasyu(code: string): MasyuGame | null;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { checkMasyu, isMasyuBoard, masyuEdgeCount } from "./masyuBoard.js";
|
|
2
|
+
import { solveMasyu } from "./masyuSolve.js";
|
|
3
|
+
export function newMasyu(board) {
|
|
4
|
+
if (!isMasyuBoard(board))
|
|
5
|
+
throw new RangeError("Invalid Masyu board");
|
|
6
|
+
return { board: { width: board.width, height: board.height, pearls: [...board.pearls] }, edges: [], history: [], helped: false };
|
|
7
|
+
}
|
|
8
|
+
export function toggleMasyuEdge(game, edge) {
|
|
9
|
+
if (!Number.isInteger(edge) || edge < 0 || edge >= masyuEdgeCount(game.board))
|
|
10
|
+
return game;
|
|
11
|
+
const edges = game.edges.includes(edge) ? game.edges.filter(value => value !== edge) : [...game.edges, edge].sort((a, b) => a - b);
|
|
12
|
+
return { ...game, edges, history: [...game.history, [...game.edges]] };
|
|
13
|
+
}
|
|
14
|
+
export function undoMasyu(game) {
|
|
15
|
+
const previous = game.history.at(-1);
|
|
16
|
+
return previous ? { ...game, edges: [...previous], history: game.history.slice(0, -1) } : game;
|
|
17
|
+
}
|
|
18
|
+
export function restartMasyu(game) { return { ...newMasyu(game.board), helped: false }; }
|
|
19
|
+
export function hintMasyu(game) {
|
|
20
|
+
const result = solveMasyu(game.board);
|
|
21
|
+
if (!result.complete || result.count !== 1)
|
|
22
|
+
return null;
|
|
23
|
+
return result.solution.find(edge => !game.edges.includes(edge)) ?? null;
|
|
24
|
+
}
|
|
25
|
+
export function masyuFinished(game) { return checkMasyu(game.board, game.edges).ok; }
|
|
26
|
+
export function encodeMasyu(game) {
|
|
27
|
+
return JSON.stringify({ version: 1, board: game.board, edges: game.edges, helped: game.helped });
|
|
28
|
+
}
|
|
29
|
+
export function decodeMasyu(code) {
|
|
30
|
+
try {
|
|
31
|
+
if (code.length > 40000)
|
|
32
|
+
return null;
|
|
33
|
+
const value = JSON.parse(code);
|
|
34
|
+
if (value.version !== 1 || !isMasyuBoard(value.board) || !Array.isArray(value.edges)
|
|
35
|
+
|| typeof value.helped !== "boolean" || !value.edges.every((edge) => Number.isInteger(edge)))
|
|
36
|
+
return null;
|
|
37
|
+
const rawEdges = value.edges;
|
|
38
|
+
const edges = [...new Set(rawEdges)].sort((a, b) => a - b);
|
|
39
|
+
if (edges.length !== rawEdges.length || edges.some(edge => edge < 0 || edge >= masyuEdgeCount(value.board)))
|
|
40
|
+
return null;
|
|
41
|
+
return { ...newMasyu(value.board), edges, helped: value.helped };
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { isKazuSeed, seededRandom } from "./random.js";
|
|
2
|
+
import { checkMasyu, isMasyuBoard, masyuNeighbors } from "./masyuBoard.js";
|
|
3
|
+
import { solveMasyu } from "./masyuSolve.js";
|
|
4
|
+
const loops = [
|
|
5
|
+
[12, 17, 18, 19, 14, 9, 8, 7],
|
|
6
|
+
[14, 9, 4, 3, 2, 1, 6, 11, 12, 13],
|
|
7
|
+
[21, 20, 15, 10, 5, 0, 1, 2, 7, 12, 17, 22],
|
|
8
|
+
[19, 24, 23, 22, 17, 12, 7, 8, 9, 14],
|
|
9
|
+
];
|
|
10
|
+
function turnAt(path, index) {
|
|
11
|
+
const before = path[(index + path.length - 1) % path.length];
|
|
12
|
+
const cell = path[index];
|
|
13
|
+
const after = path[(index + 1) % path.length];
|
|
14
|
+
return Math.abs(cell - before) !== Math.abs(after - cell);
|
|
15
|
+
}
|
|
16
|
+
function makePearls(path) {
|
|
17
|
+
const pearls = Array(25).fill(0);
|
|
18
|
+
for (let index = 0; index < path.length; index += 1) {
|
|
19
|
+
if (turnAt(path, index))
|
|
20
|
+
pearls[path[index]] = 2;
|
|
21
|
+
else if (turnAt(path, (index + path.length - 1) % path.length)
|
|
22
|
+
|| turnAt(path, (index + 1) % path.length))
|
|
23
|
+
pearls[path[index]] = 1;
|
|
24
|
+
}
|
|
25
|
+
return pearls;
|
|
26
|
+
}
|
|
27
|
+
function makeEdges(board, path) {
|
|
28
|
+
const edges = path.map((cell, index) => {
|
|
29
|
+
const nextCell = path[(index + 1) % path.length];
|
|
30
|
+
return masyuNeighbors(board, cell).find(next => next.cell === nextCell)?.edge;
|
|
31
|
+
});
|
|
32
|
+
return edges.every((edge) => edge !== undefined) ? edges.sort((a, b) => a - b) : null;
|
|
33
|
+
}
|
|
34
|
+
/** Seeded original loop layouts; every output is accepted only after a completed uniqueness proof. */
|
|
35
|
+
export function generateMasyu(size = 5, seed = 1) {
|
|
36
|
+
if (size !== 5 || !isKazuSeed(seed))
|
|
37
|
+
throw new RangeError("Invalid Masyu settings");
|
|
38
|
+
const random = seededRandom(seed), base = loops[Math.floor(random() * loops.length)];
|
|
39
|
+
const turns = Math.floor(random() * 4), reflect = random() < 0.5;
|
|
40
|
+
const path = base.map(cell => {
|
|
41
|
+
let x = cell % 5, y = Math.floor(cell / 5);
|
|
42
|
+
if (reflect)
|
|
43
|
+
x = 4 - x;
|
|
44
|
+
for (let turn = 0; turn < turns; turn += 1)
|
|
45
|
+
[x, y] = [4 - y, x];
|
|
46
|
+
return y * 5 + x;
|
|
47
|
+
});
|
|
48
|
+
const board = { width: 5, height: 5, pearls: makePearls(path) };
|
|
49
|
+
const expected = makeEdges(board, path);
|
|
50
|
+
if (!expected || !isMasyuBoard(board) || !checkMasyu(board, expected).ok)
|
|
51
|
+
throw new Error("Invalid Masyu layout");
|
|
52
|
+
const proof = solveMasyu(board);
|
|
53
|
+
if (!proof.complete || proof.count !== 1)
|
|
54
|
+
throw new Error("No uniquely proved Masyu found within the generation budget; try another seed");
|
|
55
|
+
return { ...board, seed, solution: proof.solution };
|
|
56
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { checkMasyu, masyuNeighbors } from "./masyuBoard.js";
|
|
2
|
+
import { decodeMasyu, encodeMasyu, hintMasyu, newMasyu, restartMasyu, toggleMasyuEdge, undoMasyu } from "./masyuGame.js";
|
|
3
|
+
import { drawMasyu } from "./masyuDraw.js";
|
|
4
|
+
import { MASYU_PLAY_STYLE } from "./masyuStyle.js";
|
|
5
|
+
import { MASYU_STRINGS } from "./masyuStrings.js";
|
|
6
|
+
export function mountMasyu(element, initial) {
|
|
7
|
+
const document = element.ownerDocument, style = document.createElement("style");
|
|
8
|
+
style.textContent = MASYU_PLAY_STYLE;
|
|
9
|
+
const root = document.createElement("section");
|
|
10
|
+
root.className = "kazu-masyu";
|
|
11
|
+
const help = document.createElement("p"), board = document.createElement("div");
|
|
12
|
+
board.className = "km-board";
|
|
13
|
+
const tools = document.createElement("div");
|
|
14
|
+
tools.className = "km-tools";
|
|
15
|
+
const status = document.createElement("p");
|
|
16
|
+
status.className = "km-status";
|
|
17
|
+
status.setAttribute("aria-live", "polite");
|
|
18
|
+
root.append(help, board, tools, status);
|
|
19
|
+
element.replaceChildren(style, root);
|
|
20
|
+
let options = { ...initial };
|
|
21
|
+
let game = initial.progress ? decodeMasyu(initial.progress) : null;
|
|
22
|
+
if (!game || game.board.width !== initial.board.width || game.board.height !== initial.board.height
|
|
23
|
+
|| game.board.pearls.some((pearl, cell) => pearl !== initial.board.pearls[cell]))
|
|
24
|
+
game = newMasyu(initial.board);
|
|
25
|
+
let selected = 0, anchor = null, errors = [], message = "", dead = false;
|
|
26
|
+
const words = () => MASYU_STRINGS[options.language ?? "en"];
|
|
27
|
+
const copy = () => ({ ...game, board: { ...game.board, pearls: [...game.board.pearls] }, edges: [...game.edges], history: game.history.map(turn => [...turn]) });
|
|
28
|
+
const changed = () => { options.onChange?.(copy()); if (checkMasyu(game.board, game.edges).ok)
|
|
29
|
+
options.onFinish?.(copy()); };
|
|
30
|
+
const toggleBetween = (first, second) => {
|
|
31
|
+
const edge = masyuNeighbors(game.board, first).find(next => next.cell === second)?.edge;
|
|
32
|
+
if (edge === undefined)
|
|
33
|
+
return;
|
|
34
|
+
game = toggleMasyuEdge(game, edge);
|
|
35
|
+
anchor = second;
|
|
36
|
+
selected = second;
|
|
37
|
+
errors = [];
|
|
38
|
+
message = "";
|
|
39
|
+
render();
|
|
40
|
+
changed();
|
|
41
|
+
};
|
|
42
|
+
const render = () => {
|
|
43
|
+
if (dead)
|
|
44
|
+
return;
|
|
45
|
+
const w = words();
|
|
46
|
+
help.textContent = w.help;
|
|
47
|
+
board.innerHTML = drawMasyu(game.board, { ...options, edges: game.edges, selected, errors });
|
|
48
|
+
const controls = document.createElement("div");
|
|
49
|
+
controls.className = "km-cells";
|
|
50
|
+
controls.style.gridTemplateColumns = `repeat(${game.board.width}, 1fr)`;
|
|
51
|
+
controls.style.gridTemplateRows = `repeat(${game.board.height}, 1fr)`;
|
|
52
|
+
game.board.pearls.forEach((pearl, cell) => {
|
|
53
|
+
const button = document.createElement("button");
|
|
54
|
+
button.type = "button";
|
|
55
|
+
button.className = "km-cell";
|
|
56
|
+
button.dataset.cell = String(cell);
|
|
57
|
+
button.textContent = String(cell + 1);
|
|
58
|
+
button.setAttribute("aria-label", `${w.pearl[pearl]}, row ${Math.floor(cell / game.board.width) + 1}, column ${cell % game.board.width + 1}`);
|
|
59
|
+
button.setAttribute("aria-pressed", String(selected === cell));
|
|
60
|
+
button.tabIndex = selected === cell ? 0 : -1;
|
|
61
|
+
button.onclick = () => {
|
|
62
|
+
selected = cell;
|
|
63
|
+
if (anchor === null)
|
|
64
|
+
anchor = cell;
|
|
65
|
+
else if (anchor !== cell)
|
|
66
|
+
toggleBetween(anchor, cell);
|
|
67
|
+
render();
|
|
68
|
+
};
|
|
69
|
+
button.onkeydown = event => {
|
|
70
|
+
const x = cell % game.board.width, y = Math.floor(cell / game.board.width);
|
|
71
|
+
const moves = { ArrowUp: [0, -1], ArrowDown: [0, 1], ArrowLeft: [-1, 0], ArrowRight: [1, 0] };
|
|
72
|
+
if (event.key in moves) {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
const [dx, dy] = moves[event.key];
|
|
75
|
+
const nextX = x + dx, nextY = y + dy;
|
|
76
|
+
if (nextX >= 0 && nextX < game.board.width && nextY >= 0 && nextY < game.board.height) {
|
|
77
|
+
selected = nextY * game.board.width + nextX;
|
|
78
|
+
render();
|
|
79
|
+
board.querySelectorAll(".km-cell")[selected]?.focus();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (event.key === "Enter" || event.key === " ") {
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
if (anchor === null)
|
|
85
|
+
anchor = cell;
|
|
86
|
+
else if (anchor !== cell)
|
|
87
|
+
toggleBetween(anchor, cell);
|
|
88
|
+
render();
|
|
89
|
+
board.querySelectorAll(".km-cell")[selected]?.focus();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
controls.append(button);
|
|
93
|
+
});
|
|
94
|
+
board.append(controls);
|
|
95
|
+
tools.replaceChildren();
|
|
96
|
+
const add = (label, action) => { const button = document.createElement("button"); button.type = "button"; button.textContent = label; button.onclick = action; tools.append(button); };
|
|
97
|
+
add(w.undo, () => { game = undoMasyu(game); errors = []; render(); changed(); });
|
|
98
|
+
add(w.hint, () => { const edge = hintMasyu(game); if (edge === null)
|
|
99
|
+
message = w.bad;
|
|
100
|
+
else {
|
|
101
|
+
game = toggleMasyuEdge(game, edge);
|
|
102
|
+
game = { ...game, helped: true };
|
|
103
|
+
message = w.good;
|
|
104
|
+
changed();
|
|
105
|
+
} render(); });
|
|
106
|
+
add(w.check, () => { errors = checkMasyu(game.board, game.edges).errors; message = errors.length ? w.bad : w.good; render(); });
|
|
107
|
+
add(w.restart, () => { game = restartMasyu(game); anchor = null; errors = []; message = ""; render(); changed(); });
|
|
108
|
+
status.textContent = checkMasyu(game.board, game.edges).ok ? (game.helped ? w.helped : w.won) : message;
|
|
109
|
+
};
|
|
110
|
+
render();
|
|
111
|
+
return {
|
|
112
|
+
game: copy,
|
|
113
|
+
progress: () => encodeMasyu(game),
|
|
114
|
+
set: next => { options = { ...options, ...next }; render(); },
|
|
115
|
+
restart: () => { game = restartMasyu(game); anchor = null; render(); changed(); },
|
|
116
|
+
destroy: () => { dead = true; element.replaceChildren(); },
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { MasyuDrawOptions as MasyuDrawOptions, MasyuMount as MasyuMount, MasyuMountOptions as MasyuMountOptions } from "./masyu.types.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MasyuBoard, MasyuSolve } from "./masyu.types.ts";
|
|
2
|
+
/** Counts distinct single loops with local pearl rules; budget stops never claim a proof. */
|
|
3
|
+
export declare function solveMasyu(board: MasyuBoard, options?: {
|
|
4
|
+
limit?: number;
|
|
5
|
+
nodes?: number;
|
|
6
|
+
}): MasyuSolve;
|