@johnmorrisdotca/kazu 1.0.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 (76) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +21 -0
  3. package/README.md +568 -0
  4. package/dist/cells.d.ts +28 -0
  5. package/dist/cells.js +65 -0
  6. package/dist/check.d.ts +15 -0
  7. package/dist/check.js +189 -0
  8. package/dist/clock.d.ts +2 -0
  9. package/dist/clock.js +9 -0
  10. package/dist/conflicts.d.ts +14 -0
  11. package/dist/conflicts.js +79 -0
  12. package/dist/draw-entry.d.ts +14 -0
  13. package/dist/draw-entry.js +10 -0
  14. package/dist/draw.d.ts +47 -0
  15. package/dist/draw.js +219 -0
  16. package/dist/element-define.d.ts +1 -0
  17. package/dist/element-define.js +13 -0
  18. package/dist/element.d.ts +48 -0
  19. package/dist/element.js +150 -0
  20. package/dist/game.d.ts +65 -0
  21. package/dist/game.js +120 -0
  22. package/dist/generate.d.ts +11 -0
  23. package/dist/generate.js +35 -0
  24. package/dist/geometry.d.ts +33 -0
  25. package/dist/geometry.js +27 -0
  26. package/dist/givens.d.ts +30 -0
  27. package/dist/givens.js +43 -0
  28. package/dist/groupSolve.d.ts +68 -0
  29. package/dist/groupSolve.js +284 -0
  30. package/dist/hint.d.ts +39 -0
  31. package/dist/hint.js +158 -0
  32. package/dist/index.d.ts +36 -0
  33. package/dist/index.js +31 -0
  34. package/dist/jigsaw.d.ts +21 -0
  35. package/dist/jigsaw.js +144 -0
  36. package/dist/kinds.d.ts +51 -0
  37. package/dist/kinds.js +40 -0
  38. package/dist/layout.d.ts +63 -0
  39. package/dist/layout.js +128 -0
  40. package/dist/moreOrLess.d.ts +7 -0
  41. package/dist/moreOrLess.js +90 -0
  42. package/dist/moreOrLessCode.d.ts +22 -0
  43. package/dist/moreOrLessCode.js +52 -0
  44. package/dist/moreOrLessSolve.d.ts +45 -0
  45. package/dist/moreOrLessSolve.js +180 -0
  46. package/dist/mount.d.ts +117 -0
  47. package/dist/mount.js +558 -0
  48. package/dist/names.d.ts +41 -0
  49. package/dist/names.js +163 -0
  50. package/dist/numberPlace.d.ts +14 -0
  51. package/dist/numberPlace.js +123 -0
  52. package/dist/play-entry.d.ts +9 -0
  53. package/dist/play-entry.js +8 -0
  54. package/dist/playStyle.d.ts +11 -0
  55. package/dist/playStyle.js +47 -0
  56. package/dist/progress.d.ts +29 -0
  57. package/dist/progress.js +96 -0
  58. package/dist/random.d.ts +25 -0
  59. package/dist/random.js +42 -0
  60. package/dist/solve.d.ts +22 -0
  61. package/dist/solve.js +60 -0
  62. package/dist/strings.d.ts +17 -0
  63. package/dist/strings.js +143 -0
  64. package/dist/style.d.ts +13 -0
  65. package/dist/style.js +61 -0
  66. package/dist/sumCages.d.ts +60 -0
  67. package/dist/sumCages.js +190 -0
  68. package/dist/towers.d.ts +3 -0
  69. package/dist/towers.js +48 -0
  70. package/dist/towersCode.d.ts +31 -0
  71. package/dist/towersCode.js +79 -0
  72. package/dist/towersSolve.d.ts +65 -0
  73. package/dist/towersSolve.js +276 -0
  74. package/dist/version.d.ts +2 -0
  75. package/dist/version.js +2 -0
  76. package/package.json +104 -0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * A GRID OF NUMBERS AS A STRING: for an address, a POST body, and a kept run.
3
+ *
4
+ * Row-major, one character per cell: a digit for a value, `.` for an empty cell. Past nine the
5
+ * values are letters, A for 10 up to G for 16, as a 16×16 Sudoku is printed (`symbolOf`), so one
6
+ * character is still one cell. Upper case only in a code, so one grid has one spelling. These are
7
+ * the spellings itsutsu.com has always stored, and they decode here unchanged.
8
+ */
9
+ export declare const EMPTY_CELL = ".";
10
+ /** How a value is written, in a code and on a cell: 1–9, then A–G. */
11
+ export declare function symbolOf(value: number): string;
12
+ /** The value a symbol names, or 0 for one that is not 1–9 or A–G (either case). */
13
+ export declare function valueOfSymbol(symbol: string): number;
14
+ /** `[0, 3, 0, 1]` → `".3.1"`. */
15
+ export declare function encodeCells(cells: readonly number[]): string;
16
+ /**
17
+ * `".3.1"` at a side of 2 → `[0, 3, 0, 1]`, or null for a string that is not a grid of that size:
18
+ * the wrong length, a value past the side, a stray character. Null rather than a grid with holes,
19
+ * because a grid with holes is a grid.
20
+ */
21
+ export declare function decodeCells(code: string, size: number): number[] | null;
22
+ /** A short fingerprint of a puzzle's givens (FNV-1a, eight hex characters): the same puzzle, however it was kept, has the same one. Not a credential. */
23
+ export declare function kazuHash(givens: string): string;
24
+ /**
25
+ * The number a tap on the chosen cell puts in it: one more, and after the largest the cell empties,
26
+ * and then it starts again at 1. A tap on a cell that is not chosen still only chooses it.
27
+ */
28
+ export declare function stepEntry(value: number, size: number): number;
package/dist/cells.js ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * A GRID OF NUMBERS AS A STRING: for an address, a POST body, and a kept run.
3
+ *
4
+ * Row-major, one character per cell: a digit for a value, `.` for an empty cell. Past nine the
5
+ * values are letters, A for 10 up to G for 16, as a 16×16 Sudoku is printed (`symbolOf`), so one
6
+ * character is still one cell. Upper case only in a code, so one grid has one spelling. These are
7
+ * the spellings itsutsu.com has always stored, and they decode here unchanged.
8
+ */
9
+ export const EMPTY_CELL = ".";
10
+ /** The letters after 9, in order: A is 10, G is 16. */
11
+ const PAST_NINE = "ABCDEFG";
12
+ /** How a value is written, in a code and on a cell: 1–9, then A–G. */
13
+ export function symbolOf(value) {
14
+ return value <= 9 ? String(value) : PAST_NINE[value - 10];
15
+ }
16
+ /** The value a symbol names, or 0 for one that is not 1–9 or A–G (either case). */
17
+ export function valueOfSymbol(symbol) {
18
+ if (symbol.length !== 1)
19
+ return 0;
20
+ if (symbol >= "1" && symbol <= "9")
21
+ return Number(symbol);
22
+ const at = PAST_NINE.indexOf(symbol.toUpperCase());
23
+ return at === -1 ? 0 : at + 10;
24
+ }
25
+ /** `[0, 3, 0, 1]` → `".3.1"`. */
26
+ export function encodeCells(cells) {
27
+ return cells.map((value) => (value === 0 ? EMPTY_CELL : symbolOf(value))).join("");
28
+ }
29
+ /**
30
+ * `".3.1"` at a side of 2 → `[0, 3, 0, 1]`, or null for a string that is not a grid of that size:
31
+ * the wrong length, a value past the side, a stray character. Null rather than a grid with holes,
32
+ * because a grid with holes is a grid.
33
+ */
34
+ export function decodeCells(code, size) {
35
+ if (typeof code !== "string" || code.length !== size * size)
36
+ return null;
37
+ const cells = [];
38
+ for (const character of code) {
39
+ if (character === EMPTY_CELL) {
40
+ cells.push(0);
41
+ continue;
42
+ }
43
+ const value = character === character.toUpperCase() ? valueOfSymbol(character) : 0;
44
+ if (value < 1 || value > size)
45
+ return null;
46
+ cells.push(value);
47
+ }
48
+ return cells;
49
+ }
50
+ /** A short fingerprint of a puzzle's givens (FNV-1a, eight hex characters): the same puzzle, however it was kept, has the same one. Not a credential. */
51
+ export function kazuHash(givens) {
52
+ let hash = 0x811c9dc5;
53
+ for (let i = 0; i < givens.length; i += 1) {
54
+ hash ^= givens.charCodeAt(i);
55
+ hash = Math.imul(hash, 0x01000193);
56
+ }
57
+ return (hash >>> 0).toString(16).padStart(8, "0");
58
+ }
59
+ /**
60
+ * The number a tap on the chosen cell puts in it: one more, and after the largest the cell empties,
61
+ * and then it starts again at 1. A tap on a cell that is not chosen still only chooses it.
62
+ */
63
+ export function stepEntry(value, size) {
64
+ return value >= size ? 0 : value + 1;
65
+ }
@@ -0,0 +1,15 @@
1
+ import { type KazuCheck, type KazuKind } from "./kinds.ts";
2
+ /**
3
+ * Whether an answer solves a puzzle: O(cells), no search, nothing remembered between calls. A browser
4
+ * runs it to say "done"; a server runs it before it believes a solve, so a grid that is right is
5
+ * accepted and a grid that was merely posted is not. It refuses rather than repairs: a grid of the
6
+ * wrong size, a value out of range or a given moved is a "no" with its reason, never a best guess at
7
+ * what was meant.
8
+ *
9
+ * The rules of each puzzle are restated here rather than shared with the solver on purpose: the
10
+ * solver is what MADE the puzzle, and a check that reads the solver's mind proves only that the
11
+ * solver agrees with itself.
12
+ */
13
+ export declare function checkKazu(kind: KazuKind, size: number, givens: string, answer: string): KazuCheck;
14
+ /** Whether a puzzle's givens are a well-formed puzzle of that kind and side at all (a Jigsaw's regions sound, every code readable). */
15
+ export declare function isKazuGivens(kind: KazuKind, size: number, givens: string): boolean;
package/dist/check.js ADDED
@@ -0,0 +1,189 @@
1
+ import { decodeCells } from "./cells.js";
2
+ import { readGivens } from "./givens.js";
3
+ import { decodeJigsaw } from "./jigsaw.js";
4
+ import { isKazuSize } from "./kinds.js";
5
+ import { boxedLayout, regionLayout, regionsAreSound } from "./layout.js";
6
+ import { decodeMoreOrLess } from "./moreOrLessCode.js";
7
+ import { decodeKiller } from "./sumCages.js";
8
+ import { decodeTowers, lineFrom, TOWER_SIDES } from "./towersCode.js";
9
+ /**
10
+ * Whether an answer solves a puzzle: O(cells), no search, nothing remembered between calls. A browser
11
+ * runs it to say "done"; a server runs it before it believes a solve, so a grid that is right is
12
+ * accepted and a grid that was merely posted is not. It refuses rather than repairs: a grid of the
13
+ * wrong size, a value out of range or a given moved is a "no" with its reason, never a best guess at
14
+ * what was meant.
15
+ *
16
+ * The rules of each puzzle are restated here rather than shared with the solver on purpose: the
17
+ * solver is what MADE the puzzle, and a check that reads the solver's mind proves only that the
18
+ * solver agrees with itself.
19
+ */
20
+ export function checkKazu(kind, size, givens, answer) {
21
+ if (!isKazuSize(kind, size))
22
+ return { ok: false, reason: `no ${kind} at ${size}` };
23
+ switch (kind) {
24
+ case "number-place":
25
+ return checkOnLayout(boxedLayout(size), decodeCells(givens, size), answer);
26
+ case "diagonal":
27
+ return checkOnLayout(boxedLayout(size, true), decodeCells(givens, size), answer);
28
+ case "jigsaw":
29
+ return checkJigsaw(size, givens, answer);
30
+ case "sum-cages":
31
+ return checkSumCages(size, givens, answer);
32
+ case "more-or-less":
33
+ return checkMoreOrLess(size, givens, answer);
34
+ case "towers":
35
+ return checkTowers(size, givens, answer);
36
+ default:
37
+ return { ok: false, reason: `no check for ${String(kind)}` };
38
+ }
39
+ }
40
+ /**
41
+ * A Jigsaw is checked against the regions it was handed, in its givens. They must be sound (`size`
42
+ * joined regions of `size` cells) or the grid is refused before it is read: regions of one cell each
43
+ * would make any grid whose rows and columns are right look like an answer.
44
+ */
45
+ function checkJigsaw(size, givens, answer) {
46
+ const asked = decodeJigsaw(givens, size);
47
+ if (asked === null)
48
+ return { ok: false, reason: "the givens are not a grid with regions" };
49
+ if (!regionsAreSound(size, asked.regions))
50
+ return { ok: false, reason: "the regions do not divide the grid" };
51
+ return checkOnLayout(regionLayout(size, asked.regions), asked.cells, answer);
52
+ }
53
+ /**
54
+ * Sum Cages: a Sudoku grid, and every cage it was handed holds no number twice and adds to its sum.
55
+ * The cages come from the givens, as a Jigsaw's regions do; each cell is in exactly one, which the
56
+ * code's shape already guarantees.
57
+ */
58
+ function checkSumCages(size, givens, answer) {
59
+ const asked = decodeKiller(givens, size);
60
+ if (asked === null)
61
+ return { ok: false, reason: "the givens are not a grid with cages" };
62
+ const plain = checkOnLayout(boxedLayout(size), asked.cells, answer);
63
+ if (!plain.ok)
64
+ return plain;
65
+ const filled = decodeCells(answer, size);
66
+ for (const [at, cage] of asked.cages.entries()) {
67
+ const values = cage.cells.map((index) => filled[index]);
68
+ if (new Set(values).size !== values.length)
69
+ return { ok: false, reason: `cage ${at + 1} repeats a number` };
70
+ if (values.reduce((total, value) => total + value, 0) !== cage.sum)
71
+ return { ok: false, reason: `cage ${at + 1} does not add to ${cage.sum}` };
72
+ }
73
+ return { ok: true };
74
+ }
75
+ /** Every group of the layout holds every number once, and no given was changed. One pass over the cells. */
76
+ function checkOnLayout(layout, asked, answer) {
77
+ const { size } = layout;
78
+ const filled = decodeCells(answer, size);
79
+ if (asked === null)
80
+ return { ok: false, reason: "the givens are not a grid" };
81
+ if (filled === null)
82
+ return { ok: false, reason: "the answer is not a grid" };
83
+ if (filled.some((value) => value === 0))
84
+ return { ok: false, reason: "the answer has empty cells" };
85
+ for (let index = 0; index < asked.length; index += 1) {
86
+ if (asked[index] !== 0 && asked[index] !== filled[index])
87
+ return { ok: false, reason: "a given was changed" };
88
+ }
89
+ const seen = new Array(layout.groups.length).fill(0);
90
+ for (let index = 0; index < filled.length; index += 1) {
91
+ const bit = 1 << filled[index];
92
+ for (const group of layout.groupsOf[index]) {
93
+ if (seen[group] & bit)
94
+ return { ok: false, reason: `${groupName(layout, group)} repeats a number` };
95
+ seen[group] |= bit;
96
+ }
97
+ }
98
+ return { ok: true };
99
+ }
100
+ /** "row 3", "column 5", "box 2", "region 4", "a diagonal": the groups in `layout.ts`'s order. */
101
+ function groupName(layout, group) {
102
+ const { size } = layout;
103
+ if (group < size)
104
+ return `row ${group + 1}`;
105
+ if (group < 2 * size)
106
+ return `column ${group - size + 1}`;
107
+ if (group < 3 * size)
108
+ return `${layout.regionWord} ${group - 2 * size + 1}`;
109
+ return "a diagonal";
110
+ }
111
+ function checkMoreOrLess(size, givens, answer) {
112
+ const asked = decodeMoreOrLess(givens, size);
113
+ if (asked === null)
114
+ return { ok: false, reason: "the givens are not a grid with marks" };
115
+ const square = checkLatinSquare(size, asked.cells, answer);
116
+ if (!square.ok)
117
+ return square;
118
+ const filled = decodeCells(answer, size);
119
+ for (const mark of asked.marks) {
120
+ if (!(filled[mark.less] < filled[mark.more]))
121
+ return { ok: false, reason: "a mark is not true" };
122
+ }
123
+ return { ok: true };
124
+ }
125
+ /** Every row and column a permutation of 1..size, nothing empty, and every given where it was: More or Less and Towers alike. */
126
+ function checkLatinSquare(size, asked, answer) {
127
+ const filled = decodeCells(answer, size);
128
+ if (filled === null)
129
+ return { ok: false, reason: "the answer is not a grid" };
130
+ if (filled.some((value) => value === 0))
131
+ return { ok: false, reason: "the answer has empty cells" };
132
+ for (let index = 0; index < asked.length; index += 1) {
133
+ if (asked[index] !== 0 && asked[index] !== filled[index])
134
+ return { ok: false, reason: "a given was changed" };
135
+ }
136
+ const rows = Array.from({ length: size }, () => 0);
137
+ const cols = Array.from({ length: size }, () => 0);
138
+ for (let index = 0; index < filled.length; index += 1) {
139
+ const bit = 1 << filled[index];
140
+ const row = Math.floor(index / size);
141
+ const col = index % size;
142
+ if (rows[row] & bit)
143
+ return { ok: false, reason: `row ${row + 1} repeats a number` };
144
+ if (cols[col] & bit)
145
+ return { ok: false, reason: `column ${col + 1} repeats a number` };
146
+ rows[row] |= bit;
147
+ cols[col] |= bit;
148
+ }
149
+ return { ok: true };
150
+ }
151
+ /**
152
+ * Towers: a Latin square that keeps its givens, and from every clue exactly that many towers show.
153
+ * The counting is written out here rather than taken from `towersSeen`, which the solver that made
154
+ * the puzzle uses; only where each clue looks from is shared, because that is the spelling.
155
+ */
156
+ function checkTowers(size, givens, answer) {
157
+ const asked = decodeTowers(givens, size);
158
+ if (asked === null)
159
+ return { ok: false, reason: "the givens are not a square with clues" };
160
+ const square = checkLatinSquare(size, asked.cells, answer);
161
+ if (!square.ok)
162
+ return square;
163
+ const filled = decodeCells(answer, size);
164
+ for (const side of TOWER_SIDES) {
165
+ for (let at = 0; at < size; at += 1) {
166
+ const clue = asked.clues[side][at];
167
+ if (clue === 0)
168
+ continue;
169
+ let tallest = 0;
170
+ let seen = 0;
171
+ for (const index of lineFrom(side, at, size)) {
172
+ if (filled[index] > tallest) {
173
+ tallest = filled[index];
174
+ seen += 1;
175
+ }
176
+ }
177
+ if (seen !== clue)
178
+ return { ok: false, reason: `the ${side} clue ${clue} sees ${seen}` };
179
+ }
180
+ }
181
+ return { ok: true };
182
+ }
183
+ /** Whether a puzzle's givens are a well-formed puzzle of that kind and side at all (a Jigsaw's regions sound, every code readable). */
184
+ export function isKazuGivens(kind, size, givens) {
185
+ const read = readGivens(kind, size, givens);
186
+ if (read === null)
187
+ return false;
188
+ return kind !== "jigsaw" || (read.regions !== null && regionsAreSound(size, read.regions));
189
+ }
@@ -0,0 +1,2 @@
1
+ /** A time taken, as a clock shows it: `m:ss`, and `h:mm:ss` past an hour. */
2
+ export declare function kazuClockText(ms: number): string;
package/dist/clock.js ADDED
@@ -0,0 +1,9 @@
1
+ /** A time taken, as a clock shows it: `m:ss`, and `h:mm:ss` past an hour. */
2
+ export function kazuClockText(ms) {
3
+ const seconds = Math.max(0, Math.floor(ms / 1000));
4
+ const s = seconds % 60;
5
+ const m = Math.floor(seconds / 60) % 60;
6
+ const h = Math.floor(seconds / 3600);
7
+ const two = (n) => String(n).padStart(2, "0");
8
+ return h > 0 ? `${h}:${two(m)}:${two(s)}` : `${m}:${two(s)}`;
9
+ }
@@ -0,0 +1,14 @@
1
+ import type { KazuGivens } from "./givens.ts";
2
+ /**
3
+ * THE CELLS THAT BREAK A RULE RIGHT NOW, read from the rules alone: no answer is needed, so it works
4
+ * on any puzzle, and it names a mistake the moment it is made rather than when it is checked.
5
+ *
6
+ * `values` is the whole grid as it stands: the printed numbers and what has been written, row-major,
7
+ * 0 for empty. A cell is in conflict when it shares a row, a column, a box, a region, a diagonal or a
8
+ * cage with a cell holding the same number; when a cage is full and does not add to its sum, or already
9
+ * adds to more; when a more-than mark between two filled cells is not true; or when the cells from a
10
+ * Towers clue show more towers than the clue says (or, once the line is full, any other number).
11
+ * An empty cell is never in conflict, and a grid with no conflict is not thereby right: it may
12
+ * simply not be finished.
13
+ */
14
+ export declare function conflictsOf(givens: KazuGivens, values: readonly number[]): number[];
@@ -0,0 +1,79 @@
1
+ import { layoutOfGivens } from "./givens.js";
2
+ import { lineFrom, TOWER_SIDES } from "./towersCode.js";
3
+ /**
4
+ * THE CELLS THAT BREAK A RULE RIGHT NOW, read from the rules alone: no answer is needed, so it works
5
+ * on any puzzle, and it names a mistake the moment it is made rather than when it is checked.
6
+ *
7
+ * `values` is the whole grid as it stands: the printed numbers and what has been written, row-major,
8
+ * 0 for empty. A cell is in conflict when it shares a row, a column, a box, a region, a diagonal or a
9
+ * cage with a cell holding the same number; when a cage is full and does not add to its sum, or already
10
+ * adds to more; when a more-than mark between two filled cells is not true; or when the cells from a
11
+ * Towers clue show more towers than the clue says (or, once the line is full, any other number).
12
+ * An empty cell is never in conflict, and a grid with no conflict is not thereby right: it may
13
+ * simply not be finished.
14
+ */
15
+ export function conflictsOf(givens, values) {
16
+ const { size } = givens;
17
+ const bad = new Set();
18
+ const layout = layoutOfGivens(givens);
19
+ const groups = layout !== null
20
+ ? layout.groups
21
+ : [...Array.from({ length: size }, (_, r) => Array.from({ length: size }, (_, c) => r * size + c)), ...Array.from({ length: size }, (_, c) => Array.from({ length: size }, (_, r) => r * size + c))];
22
+ for (const group of groups) {
23
+ const first = new Map();
24
+ for (const index of group) {
25
+ const value = values[index];
26
+ if (value === 0)
27
+ continue;
28
+ const was = first.get(value);
29
+ if (was === undefined)
30
+ first.set(value, index);
31
+ else {
32
+ bad.add(was);
33
+ bad.add(index);
34
+ }
35
+ }
36
+ }
37
+ for (const cage of givens.cages ?? []) {
38
+ const filled = cage.cells.filter((index) => values[index] !== 0);
39
+ const total = filled.reduce((sum, index) => sum + values[index], 0);
40
+ if (total > cage.sum || (filled.length === cage.cells.length && total !== cage.sum))
41
+ for (const index of filled)
42
+ bad.add(index);
43
+ }
44
+ for (const mark of givens.marks) {
45
+ const less = values[mark.less];
46
+ const more = values[mark.more];
47
+ if (less !== 0 && more !== 0 && !(less < more)) {
48
+ bad.add(mark.less);
49
+ bad.add(mark.more);
50
+ }
51
+ }
52
+ if (givens.clues !== null) {
53
+ for (const side of TOWER_SIDES) {
54
+ for (let at = 0; at < size; at += 1) {
55
+ const clue = givens.clues[side][at];
56
+ if (clue === 0)
57
+ continue;
58
+ const line = lineFrom(side, at, size);
59
+ let tallest = 0;
60
+ let seen = 0;
61
+ let reached = 0;
62
+ for (const index of line) {
63
+ const value = values[index];
64
+ if (value === 0)
65
+ break;
66
+ reached += 1;
67
+ if (value > tallest) {
68
+ tallest = value;
69
+ seen += 1;
70
+ }
71
+ }
72
+ if (seen > clue || (reached === size && seen !== clue))
73
+ for (const index of line.slice(0, reached))
74
+ bad.add(index);
75
+ }
76
+ }
77
+ }
78
+ return [...bad].sort((a, b) => a - b);
79
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Kazu's drawing: a puzzle as SVG text, with the style that gives it its look, and where everything sits
3
+ * in it (so a page can tell which cell a finger is over). A separate entry
4
+ * (`@johnmorrisdotca/kazu/draw`), so a server that only checks an answer never loads any of it.
5
+ */
6
+ export { drawKazu, kazuCellName, kazuWhere } from "./draw.ts";
7
+ export type { KazuDrawOptions } from "./draw.ts";
8
+ export { kazuGeometry, KAZU_CELL } from "./geometry.ts";
9
+ export type { KazuGeometry } from "./geometry.ts";
10
+ export { KAZU_STYLE } from "./style.ts";
11
+ export { KAZU_STRINGS, kazuLanguageOf, kazuNameOf, kazuSay } from "./strings.ts";
12
+ export type { KazuLanguage } from "./strings.ts";
13
+ export { KAZU_NAMES, KAZU_SIZE_NAMES } from "./names.ts";
14
+ export type { KazuName } from "./names.ts";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Kazu's drawing: a puzzle as SVG text, with the style that gives it its look, and where everything sits
3
+ * in it (so a page can tell which cell a finger is over). A separate entry
4
+ * (`@johnmorrisdotca/kazu/draw`), so a server that only checks an answer never loads any of it.
5
+ */
6
+ export { drawKazu, kazuCellName, kazuWhere } from "./draw.js";
7
+ export { kazuGeometry, KAZU_CELL } from "./geometry.js";
8
+ export { KAZU_STYLE } from "./style.js";
9
+ export { KAZU_STRINGS, kazuLanguageOf, kazuNameOf, kazuSay } from "./strings.js";
10
+ export { KAZU_NAMES, KAZU_SIZE_NAMES } from "./names.js";
package/dist/draw.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ import type { KazuKind } from "./kinds.ts";
2
+ import { type KazuLanguage } from "./strings.ts";
3
+ /** What a drawing shows beyond the puzzle itself. Every part is optional: a puzzle alone is its printed grid. */
4
+ export type KazuDrawOptions = {
5
+ /** What the player has written, row-major, 0 for empty. */
6
+ entries?: readonly number[];
7
+ /** Pencil marks, a bit mask a cell: bit `v` is the note `v` (`notesOf`). */
8
+ notes?: readonly number[];
9
+ /** The chosen cell. */
10
+ selected?: number | null;
11
+ /** Wash the chosen cell's row, column and group, and every cell holding its number. */
12
+ peers?: boolean;
13
+ /** Cells that break a rule, drawn in red with their numbers (`conflictsOf`). */
14
+ conflicts?: readonly number[];
15
+ /** Cells Check flagged wrong. */
16
+ wrong?: readonly number[];
17
+ /** The cell a hint pointed at. */
18
+ hint?: number | null;
19
+ /** A faint wash of green, and `data-solved="true"`. */
20
+ done?: boolean;
21
+ /** Put a transparent square over every cell, each with its `data-cell`, for a page to press on. `mountKazu` does. */
22
+ interactive?: boolean;
23
+ /** What a screen reader hears. */
24
+ language?: KazuLanguage;
25
+ /** A description instead of "Sudoku puzzle, 9 by 9". */
26
+ label?: string;
27
+ /** Put `KAZU_STYLE` inside, so the drawing stands alone as an image. */
28
+ style?: boolean;
29
+ /** Draw the wooden frame round the paper. Default false. */
30
+ frame?: boolean;
31
+ };
32
+ /** The words for the groups a number is ruled out by, for a hint's line: "its row, column and box". */
33
+ export declare function kazuWhere(kind: KazuKind, language: KazuLanguage): string;
34
+ /** A cell as a screen reader names it: "row 3, column 5". */
35
+ export declare function kazuCellName(size: number, index: number, language: KazuLanguage): string;
36
+ /**
37
+ * A PUZZLE AS SVG TEXT: the grid with its printed numbers, whatever has been written in it, pencil
38
+ * marks, and everything the kind of puzzle prints: the heavier rules round the boxes or a Jigsaw's
39
+ * regions, the shaded diagonals, a cage's dashed outline with its sum, the more-than marks between
40
+ * cells, and the tower clues round the edge. The chosen cell, the cells that break a rule, the cells
41
+ * Check flagged and the cell a hint pointed at are washed in colour.
42
+ *
43
+ * Returns the `<svg>` as a string: put it in a page, a file or an image, with nothing to load. It is
44
+ * one steady square whatever is drawn, so nothing moves as numbers are written. Nothing in it can be
45
+ * selected or dragged. Null for givens that are not a puzzle of that kind and side.
46
+ */
47
+ export declare function drawKazu(kind: KazuKind, size: number, givensCode: string, options?: KazuDrawOptions): string | null;