@sudobility/sudojo_lib 0.0.4

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 (141) hide show
  1. package/dist/hooks/index.d.ts +27 -0
  2. package/dist/hooks/index.d.ts.map +1 -0
  3. package/dist/hooks/index.js +27 -0
  4. package/dist/hooks/index.js.map +1 -0
  5. package/dist/hooks/useDailyGame.d.ts +64 -0
  6. package/dist/hooks/useDailyGame.d.ts.map +1 -0
  7. package/dist/hooks/useDailyGame.js +120 -0
  8. package/dist/hooks/useDailyGame.js.map +1 -0
  9. package/dist/hooks/useGame.d.ts +104 -0
  10. package/dist/hooks/useGame.d.ts.map +1 -0
  11. package/dist/hooks/useGame.js +546 -0
  12. package/dist/hooks/useGame.js.map +1 -0
  13. package/dist/hooks/useGamePersistence.d.ts +63 -0
  14. package/dist/hooks/useGamePersistence.d.ts.map +1 -0
  15. package/dist/hooks/useGamePersistence.js +124 -0
  16. package/dist/hooks/useGamePersistence.js.map +1 -0
  17. package/dist/hooks/useGameTeaching.d.ts +89 -0
  18. package/dist/hooks/useGameTeaching.d.ts.map +1 -0
  19. package/dist/hooks/useGameTeaching.js +230 -0
  20. package/dist/hooks/useGameTeaching.js.map +1 -0
  21. package/dist/hooks/useGameTimer.d.ts +43 -0
  22. package/dist/hooks/useGameTimer.d.ts.map +1 -0
  23. package/dist/hooks/useGameTimer.js +68 -0
  24. package/dist/hooks/useGameTimer.js.map +1 -0
  25. package/dist/hooks/useHint.d.ts +105 -0
  26. package/dist/hooks/useHint.d.ts.map +1 -0
  27. package/dist/hooks/useHint.js +179 -0
  28. package/dist/hooks/useHint.js.map +1 -0
  29. package/dist/hooks/useLearning.d.ts +100 -0
  30. package/dist/hooks/useLearning.d.ts.map +1 -0
  31. package/dist/hooks/useLearning.js +127 -0
  32. package/dist/hooks/useLearning.js.map +1 -0
  33. package/dist/hooks/useLevelGame.d.ts +68 -0
  34. package/dist/hooks/useLevelGame.d.ts.map +1 -0
  35. package/dist/hooks/useLevelGame.js +128 -0
  36. package/dist/hooks/useLevelGame.js.map +1 -0
  37. package/dist/hooks/useLevels.d.ts +93 -0
  38. package/dist/hooks/useLevels.d.ts.map +1 -0
  39. package/dist/hooks/useLevels.js +98 -0
  40. package/dist/hooks/useLevels.js.map +1 -0
  41. package/dist/hooks/useLocalStorage.d.ts +26 -0
  42. package/dist/hooks/useLocalStorage.d.ts.map +1 -0
  43. package/dist/hooks/useLocalStorage.js +75 -0
  44. package/dist/hooks/useLocalStorage.js.map +1 -0
  45. package/dist/hooks/useSudoku.d.ts +125 -0
  46. package/dist/hooks/useSudoku.d.ts.map +1 -0
  47. package/dist/hooks/useSudoku.js +655 -0
  48. package/dist/hooks/useSudoku.js.map +1 -0
  49. package/dist/hooks/useTechniques.d.ts +92 -0
  50. package/dist/hooks/useTechniques.d.ts.map +1 -0
  51. package/dist/hooks/useTechniques.js +108 -0
  52. package/dist/hooks/useTechniques.js.map +1 -0
  53. package/dist/index.d.ts +42 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +64 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/test/setup.d.ts +2 -0
  58. package/dist/test/setup.d.ts.map +1 -0
  59. package/dist/test/setup.js +35 -0
  60. package/dist/test/setup.js.map +1 -0
  61. package/dist/types/display.d.ts +142 -0
  62. package/dist/types/display.d.ts.map +1 -0
  63. package/dist/types/display.js +94 -0
  64. package/dist/types/display.js.map +1 -0
  65. package/dist/types/game.d.ts +201 -0
  66. package/dist/types/game.d.ts.map +1 -0
  67. package/dist/types/game.js +26 -0
  68. package/dist/types/game.js.map +1 -0
  69. package/dist/types/gamePersistence.d.ts +31 -0
  70. package/dist/types/gamePersistence.d.ts.map +1 -0
  71. package/dist/types/gamePersistence.js +15 -0
  72. package/dist/types/gamePersistence.js.map +1 -0
  73. package/dist/types/index.d.ts +18 -0
  74. package/dist/types/index.d.ts.map +1 -0
  75. package/dist/types/index.js +11 -0
  76. package/dist/types/index.js.map +1 -0
  77. package/dist/types/progress.d.ts +43 -0
  78. package/dist/types/progress.d.ts.map +1 -0
  79. package/dist/types/progress.js +19 -0
  80. package/dist/types/progress.js.map +1 -0
  81. package/dist/types/settings.d.ts +19 -0
  82. package/dist/types/settings.d.ts.map +1 -0
  83. package/dist/types/settings.js +12 -0
  84. package/dist/types/settings.js.map +1 -0
  85. package/dist/types/subscription.d.ts +27 -0
  86. package/dist/types/subscription.d.ts.map +1 -0
  87. package/dist/types/subscription.js +8 -0
  88. package/dist/types/subscription.js.map +1 -0
  89. package/dist/types/sudoku.d.ts +147 -0
  90. package/dist/types/sudoku.d.ts.map +1 -0
  91. package/dist/types/sudoku.js +125 -0
  92. package/dist/types/sudoku.js.map +1 -0
  93. package/dist/utils/auth.d.ts +25 -0
  94. package/dist/utils/auth.d.ts.map +1 -0
  95. package/dist/utils/auth.js +47 -0
  96. package/dist/utils/auth.js.map +1 -0
  97. package/dist/utils/board.d.ts +116 -0
  98. package/dist/utils/board.d.ts.map +1 -0
  99. package/dist/utils/board.js +291 -0
  100. package/dist/utils/board.js.map +1 -0
  101. package/dist/utils/index.d.ts +18 -0
  102. package/dist/utils/index.d.ts.map +1 -0
  103. package/dist/utils/index.js +23 -0
  104. package/dist/utils/index.js.map +1 -0
  105. package/dist/utils/progress.d.ts +44 -0
  106. package/dist/utils/progress.d.ts.map +1 -0
  107. package/dist/utils/progress.js +104 -0
  108. package/dist/utils/progress.js.map +1 -0
  109. package/dist/utils/scramble.d.ts +35 -0
  110. package/dist/utils/scramble.d.ts.map +1 -0
  111. package/dist/utils/scramble.js +270 -0
  112. package/dist/utils/scramble.js.map +1 -0
  113. package/dist/utils/subscription.d.ts +82 -0
  114. package/dist/utils/subscription.d.ts.map +1 -0
  115. package/dist/utils/subscription.js +117 -0
  116. package/dist/utils/subscription.js.map +1 -0
  117. package/dist/utils/sudokuPresenter.d.ts +62 -0
  118. package/dist/utils/sudokuPresenter.d.ts.map +1 -0
  119. package/dist/utils/sudokuPresenter.js +403 -0
  120. package/dist/utils/sudokuPresenter.js.map +1 -0
  121. package/dist/utils/sudokuScrambler.d.ts +61 -0
  122. package/dist/utils/sudokuScrambler.d.ts.map +1 -0
  123. package/dist/utils/sudokuScrambler.js +223 -0
  124. package/dist/utils/sudokuScrambler.js.map +1 -0
  125. package/dist/utils/technique.d.ts +41 -0
  126. package/dist/utils/technique.d.ts.map +1 -0
  127. package/dist/utils/technique.js +106 -0
  128. package/dist/utils/technique.js.map +1 -0
  129. package/dist/utils/theme.d.ts +31 -0
  130. package/dist/utils/theme.d.ts.map +1 -0
  131. package/dist/utils/theme.js +40 -0
  132. package/dist/utils/theme.js.map +1 -0
  133. package/dist/utils/time.d.ts +22 -0
  134. package/dist/utils/time.d.ts.map +1 -0
  135. package/dist/utils/time.js +41 -0
  136. package/dist/utils/time.js.map +1 -0
  137. package/dist/utils/validation.d.ts +107 -0
  138. package/dist/utils/validation.d.ts.map +1 -0
  139. package/dist/utils/validation.js +248 -0
  140. package/dist/utils/validation.js.map +1 -0
  141. package/package.json +79 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sudokuScrambler.d.ts","sourceRoot":"","sources":["../../src/utils/sudokuScrambler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMnE,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,EAAE,CAAC;CAC3C;AA2BD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,iBAsBvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,iBAI1B,CAAC;AAsBF,MAAM,WAAW,cAAc;IAC7B,4BAA4B;IAC5B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,2DAA2D;IAC3D,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,uDAAuD;IACvD,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,iBAAiB,EAC5B,KAAK,EAAE,UAAU,EAAE,EACnB,WAAW,GAAE,OAAe,GAC3B,cAAc,CAuChB;AAkCD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAChB,UAAU,EAAE,CA2Bd;AAYD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAE/D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAI9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAE9D;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAUpE"}
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Sudoku board scrambler - ported from renderable/utils/Scrambler.kt
3
+ *
4
+ * Generates permutations for rows, columns, and digits to create unique puzzles
5
+ * while preserving Sudoku validity.
6
+ */
7
+ import { columnOf, rowOf } from '../types/sudoku';
8
+ // =============================================================================
9
+ // Random Utilities
10
+ // =============================================================================
11
+ /**
12
+ * Simple seeded random number generator
13
+ */
14
+ class SeededRandom {
15
+ constructor(seed) {
16
+ this.seed = seed;
17
+ }
18
+ nextInt() {
19
+ // Linear congruential generator
20
+ this.seed = (this.seed * 1664525 + 1013904223) >>> 0;
21
+ return this.seed;
22
+ }
23
+ }
24
+ // =============================================================================
25
+ // Main Scrambler
26
+ // =============================================================================
27
+ /**
28
+ * Scrambler implementation that matches the Kotlin version
29
+ */
30
+ export const Scrambler = {
31
+ scramble9(symmetrical) {
32
+ const random = new SeededRandom(Math.floor(Date.now() / 1000));
33
+ const block0 = scramble3(symmetrical, random);
34
+ const block1 = scramble3(symmetrical, random);
35
+ const block2 = symmetrical ? block0 : scramble3(symmetrical, random);
36
+ const blocks = [block0, block1, block2];
37
+ const board = scramble3(symmetrical, random);
38
+ const result = [];
39
+ for (let i = 0; i < 3; i++) {
40
+ const target = board[i] ?? 0;
41
+ const block = blocks[i] ?? [0, 1, 2];
42
+ for (let j = 0; j < 3; j++) {
43
+ result.push(target * 3 + (block[j] ?? 0));
44
+ }
45
+ }
46
+ return result;
47
+ },
48
+ };
49
+ /**
50
+ * Non-scrambler that returns identity permutation
51
+ */
52
+ export const NonScrambler = {
53
+ scramble9(_symmetrical) {
54
+ return [0, 1, 2, 3, 4, 5, 6, 7, 8];
55
+ },
56
+ };
57
+ /**
58
+ * Generates a permutation of [0, 1, 2]
59
+ */
60
+ function scramble3(symmetrical, random) {
61
+ if (symmetrical) {
62
+ const seed = random.nextInt();
63
+ // 50% chance for identity or reverse
64
+ return seed % 2 === 0 ? [0, 1, 2] : [2, 1, 0];
65
+ }
66
+ else {
67
+ // Generate 3 random values and sort to get permutation
68
+ const values = [random.nextInt(), random.nextInt(), random.nextInt()];
69
+ const sorted = [...values].sort((a, b) => a - b);
70
+ return sorted.map(v => values.indexOf(v));
71
+ }
72
+ }
73
+ /**
74
+ * Scramble a board using the given scrambler
75
+ *
76
+ * @param scrambler - Scrambler protocol implementation
77
+ * @param cells - Original 81 cells
78
+ * @param symmetrical - Whether to use symmetrical scrambling
79
+ * @returns Scrambled cells and digit mapping
80
+ */
81
+ export function scrambleBoard(scrambler, cells, symmetrical = false) {
82
+ const rows = scrambler.scramble9(symmetrical);
83
+ const columns = scrambler.scramble9(symmetrical);
84
+ const numbers = scrambler.scramble9(symmetrical);
85
+ // Create digit mapping (1-based to 1-based)
86
+ const digitMapping = new Map();
87
+ const reverseDigitMapping = new Map();
88
+ for (let i = 0; i < 9; i++) {
89
+ const original = i + 1;
90
+ const scrambled = (numbers[i] ?? i) + 1;
91
+ digitMapping.set(original, scrambled);
92
+ reverseDigitMapping.set(scrambled, original);
93
+ }
94
+ const scrambledCells = Array.from({ length: 81 }, (_, index) => {
95
+ const x = columnOf(index);
96
+ const y = rowOf(index);
97
+ const scrambledY = rows[y] ?? y;
98
+ const scrambledX = columns[x] ?? x;
99
+ const existing = cells[scrambledY * 9 + scrambledX];
100
+ if (!existing) {
101
+ throw new Error(`Missing cell at index ${scrambledY * 9 + scrambledX}`);
102
+ }
103
+ return {
104
+ index,
105
+ solution: mapDigit(existing.solution, numbers),
106
+ given: mapDigit(existing.given, numbers),
107
+ input: mapDigit(existing.input, numbers),
108
+ pencilmarks: mapPencilmarks(existing.pencilmarks, numbers),
109
+ };
110
+ });
111
+ return {
112
+ cells: scrambledCells,
113
+ digitMapping,
114
+ reverseDigitMapping,
115
+ };
116
+ }
117
+ /**
118
+ * Map a digit using the lookup array
119
+ * @param digit - Original digit (1-9) or null
120
+ * @param lookup - Permutation array (0-indexed, returns 0-indexed)
121
+ * @returns Mapped digit (1-9) or null
122
+ */
123
+ function mapDigit(digit, lookup) {
124
+ if (digit === null || digit === 0)
125
+ return null;
126
+ // digit is 1-based, lookup is 0-indexed
127
+ const mapped = lookup[digit - 1];
128
+ return mapped !== undefined ? mapped + 1 : digit;
129
+ }
130
+ /**
131
+ * Map pencilmarks array using the lookup array
132
+ * Matches Kotlin: map(digits: Array<Int>?, lookup: Array<Int>?): Array<Int>?
133
+ */
134
+ function mapPencilmarks(pencilmarks, lookup) {
135
+ if (pencilmarks === null)
136
+ return null;
137
+ return pencilmarks
138
+ .map(digit => mapDigit(digit, lookup))
139
+ .filter((d) => d !== null)
140
+ .sort((a, b) => a - b);
141
+ }
142
+ // =============================================================================
143
+ // Board Parsing
144
+ // =============================================================================
145
+ /**
146
+ * Parse a puzzle string (81 chars of 0-9 or .) into cells
147
+ *
148
+ * @param puzzle - 81 character string
149
+ * @param solution - 81 character solution string (optional)
150
+ * @returns Array of 81 cells
151
+ */
152
+ export function parsePuzzleString(puzzle, solution) {
153
+ if (puzzle.length !== 81) {
154
+ throw new Error(`Invalid puzzle string length: ${puzzle.length}`);
155
+ }
156
+ if (solution && solution.length !== 81) {
157
+ throw new Error(`Invalid solution string length: ${solution.length}`);
158
+ }
159
+ return Array.from({ length: 81 }, (_, index) => {
160
+ const givenChar = puzzle[index];
161
+ if (givenChar === undefined) {
162
+ throw new Error(`Missing puzzle character at index ${index}`);
163
+ }
164
+ const givenDigit = parseDigit(givenChar);
165
+ const solutionChar = solution?.[index];
166
+ const solutionDigit = solutionChar ? parseDigit(solutionChar) : null;
167
+ return {
168
+ index,
169
+ solution: solutionDigit,
170
+ given: givenDigit,
171
+ input: null,
172
+ pencilmarks: null,
173
+ };
174
+ });
175
+ }
176
+ /**
177
+ * Parse a character to a digit (1-9) or null
178
+ */
179
+ function parseDigit(char) {
180
+ if (char === '0' || char === '.')
181
+ return null;
182
+ const digit = parseInt(char, 10);
183
+ if (digit >= 1 && digit <= 9)
184
+ return digit;
185
+ return null;
186
+ }
187
+ /**
188
+ * Convert cells to puzzle string
189
+ */
190
+ export function cellsToPuzzleString(cells) {
191
+ return cells.map(cell => cell.given?.toString() ?? '0').join('');
192
+ }
193
+ /**
194
+ * Convert cells to current state string (given + input)
195
+ */
196
+ export function cellsToStateString(cells) {
197
+ return cells
198
+ .map(cell => (cell.given ?? cell.input)?.toString() ?? '0')
199
+ .join('');
200
+ }
201
+ /**
202
+ * Convert cells to input-only string
203
+ */
204
+ export function cellsToInputString(cells) {
205
+ return cells.map(cell => cell.input?.toString() ?? '0').join('');
206
+ }
207
+ /**
208
+ * Convert cells to pencilmarks string (comma-separated)
209
+ * Matches Kotlin: pencilmarksParam - returns empty string for null pencilmarks
210
+ */
211
+ export function cellsToPencilmarksString(cells) {
212
+ return cells
213
+ .map(cell => {
214
+ if (cell.pencilmarks !== null) {
215
+ return cell.pencilmarks.join('');
216
+ }
217
+ else {
218
+ return '';
219
+ }
220
+ })
221
+ .join(',');
222
+ }
223
+ //# sourceMappingURL=sudokuScrambler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sudokuScrambler.js","sourceRoot":"","sources":["../../src/utils/sudokuScrambler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAmB,MAAM,iBAAiB,CAAC;AAUnE,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;GAEG;AACH,MAAM,YAAY;IAGhB,YAAY,IAAY;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,OAAO;QACL,gCAAgC;QAChC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;CACF;AAED,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAEhF;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAsB;IAC1C,SAAS,CAAC,WAAoB;QAC5B,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;QAE/D,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACrE,MAAM,MAAM,GAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAEpD,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAsB;IAC7C,SAAS,CAAC,YAAqB;QAC7B,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,SAAS,SAAS,CAAC,WAAoB,EAAE,MAAoB;IAC3D,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;QAC9B,qCAAqC;QACrC,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAChD,CAAC;SAAM,CAAC;QACN,uDAAuD;QACvD,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACjD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAeD;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,SAA4B,EAC5B,KAAmB,EACnB,cAAuB,KAAK;IAE5B,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAEjD,4CAA4C;IAC5C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC/C,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACxC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACtC,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;QAC7D,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC1B,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,yBAAyB,UAAU,GAAG,CAAC,GAAG,UAAU,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED,OAAO;YACL,KAAK;YACL,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;YAC9C,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;YACxC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;YACxC,WAAW,EAAE,cAAc,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;SAC3D,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,cAAc;QACrB,YAAY;QACZ,mBAAmB;KACpB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,KAAoB,EAAE,MAAgB;IACtD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,wCAAwC;IACxC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACjC,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CACrB,WAA4B,EAC5B,MAAgB;IAEhB,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,WAAW;SACf,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;SACrC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;SACtC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED,gFAAgF;AAChF,gBAAgB;AAChB,gFAAgF;AAEhF;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAc,EACd,QAAiB;IAEjB,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,iCAAiC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,mCAAmC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;QAC7C,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,UAAU,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;QAEzC,MAAM,YAAY,GAAG,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,aAAa,GAAG,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAErE,OAAO;YACL,KAAK;YACL,QAAQ,EAAE,aAAa;YACvB,KAAK,EAAE,UAAU;YACjB,KAAK,EAAE,IAAI;YACX,WAAW,EAAE,IAAI;SAClB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACjC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAmB;IACrD,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACnE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,OAAO,KAAK;SACT,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC;SAC1D,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAAmB;IAC1D,OAAO,KAAK;SACT,GAAG,CAAC,IAAI,CAAC,EAAE;QACV,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Technique-related utilities
3
+ */
4
+ /**
5
+ * Convert technique title to icon filename
6
+ *
7
+ * @param title - Technique title (e.g., "Full House", "X-Wing")
8
+ * @returns Icon URL path
9
+ *
10
+ * @example
11
+ * getTechniqueIconUrl("Full House") // "/technique.full.house.svg"
12
+ * getTechniqueIconUrl("X-Wing") // "/technique.x.wing.svg"
13
+ * getTechniqueIconUrl("XY-Wing") // "/technique.xy.wing.svg"
14
+ */
15
+ export declare function getTechniqueIconUrl(title: string): string;
16
+ /** Map technique titles to HTML help file paths */
17
+ export declare const TECHNIQUE_TO_HELP_FILE: Record<string, string>;
18
+ /** Reverse mapping: HTML file name (lowercase) -> technique title */
19
+ export declare const HELP_FILE_TO_TECHNIQUE: Record<string, string>;
20
+ /**
21
+ * Get help file URL for a technique
22
+ *
23
+ * @param techniqueTitle - Technique title
24
+ * @returns Help file URL path or generated path if not in mapping
25
+ */
26
+ export declare function getHelpFileUrl(techniqueTitle: string): string;
27
+ /**
28
+ * Get technique title from help file name
29
+ *
30
+ * @param fileName - Help file name (e.g., "Full_House.html")
31
+ * @returns Technique title or undefined if not found
32
+ */
33
+ export declare function getTechniqueFromHelpFile(fileName: string): string | undefined;
34
+ /**
35
+ * Extract body content from HTML and clean up navigation links
36
+ *
37
+ * @param html - Raw HTML string
38
+ * @returns Cleaned HTML body content
39
+ */
40
+ export declare function extractBodyContent(html: string): string;
41
+ //# sourceMappingURL=technique.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"technique.d.ts","sourceRoot":"","sources":["../../src/utils/technique.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED,mDAAmD;AACnD,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyBzD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQzD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE7E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyBvD"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Technique-related utilities
3
+ */
4
+ /**
5
+ * Convert technique title to icon filename
6
+ *
7
+ * @param title - Technique title (e.g., "Full House", "X-Wing")
8
+ * @returns Icon URL path
9
+ *
10
+ * @example
11
+ * getTechniqueIconUrl("Full House") // "/technique.full.house.svg"
12
+ * getTechniqueIconUrl("X-Wing") // "/technique.x.wing.svg"
13
+ * getTechniqueIconUrl("XY-Wing") // "/technique.xy.wing.svg"
14
+ */
15
+ export function getTechniqueIconUrl(title) {
16
+ const normalized = title
17
+ .toLowerCase()
18
+ .replace(/\s+/g, '.') // spaces to dots
19
+ .replace(/-/g, '.'); // hyphens to dots
20
+ return `/technique.${normalized}.svg`;
21
+ }
22
+ /** Map technique titles to HTML help file paths */
23
+ export const TECHNIQUE_TO_HELP_FILE = {
24
+ 'Full House': 'Full_House.html',
25
+ 'Naked Single': 'Naked_Single.html',
26
+ 'Hidden Single': 'Hidden_Single.html',
27
+ 'Naked Pair': 'Naked_Pair.html',
28
+ 'Hidden Pair': 'Hidden_Pair.html',
29
+ 'Locked Candidates': 'Locked_Candidates.html',
30
+ 'Naked Triple': 'Naked_Triple.html',
31
+ 'Hidden Triple': 'Hidden_Triple.html',
32
+ 'Naked Quad': 'Nakded_Quad.html', // Note: typo in original file name
33
+ 'Hidden Quad': 'Hidden_Quad.html',
34
+ 'X-Wing': 'X-Wing.html',
35
+ Swordfish: 'Swordfish.html',
36
+ Jellyfish: 'Jellyfish.html',
37
+ Squirmbag: 'Squirmbag.html',
38
+ 'XY-Wing': 'XY-Wing.html',
39
+ 'XYZ-Wing': 'XYZ-Wing.html',
40
+ 'WXYZ-Wing': 'WXYZ-Wing.html',
41
+ 'Finned X-Wing': 'Finned_X-Wing.html',
42
+ 'Finned Swordfish': 'Finned_Swordfish.html',
43
+ 'Finned Jellyfish': 'Finned_Jellyfish.html',
44
+ 'Finned Squirmbag': 'Finned_Squirmbag.html',
45
+ 'Almost Locked Sets': 'Almost_Locked_Sets.html',
46
+ 'ALS Chain': 'ALS-Chain.html',
47
+ 'ALS-Chain': 'ALS-Chain.html',
48
+ };
49
+ /** Reverse mapping: HTML file name (lowercase) -> technique title */
50
+ export const HELP_FILE_TO_TECHNIQUE = Object.entries(TECHNIQUE_TO_HELP_FILE).reduce((acc, [title, file]) => {
51
+ acc[file.toLowerCase()] = title;
52
+ return acc;
53
+ }, {});
54
+ /**
55
+ * Get help file URL for a technique
56
+ *
57
+ * @param techniqueTitle - Technique title
58
+ * @returns Help file URL path or generated path if not in mapping
59
+ */
60
+ export function getHelpFileUrl(techniqueTitle) {
61
+ const fileName = TECHNIQUE_TO_HELP_FILE[techniqueTitle];
62
+ if (fileName) {
63
+ return `/help/${fileName}`;
64
+ }
65
+ // Try to generate file name from title (spaces to underscores, preserve hyphens)
66
+ const generatedFileName = `${techniqueTitle.replace(/\s+/g, '_')}.html`;
67
+ return `/help/${generatedFileName}`;
68
+ }
69
+ /**
70
+ * Get technique title from help file name
71
+ *
72
+ * @param fileName - Help file name (e.g., "Full_House.html")
73
+ * @returns Technique title or undefined if not found
74
+ */
75
+ export function getTechniqueFromHelpFile(fileName) {
76
+ return HELP_FILE_TO_TECHNIQUE[fileName.toLowerCase()];
77
+ }
78
+ /**
79
+ * Extract body content from HTML and clean up navigation links
80
+ *
81
+ * @param html - Raw HTML string
82
+ * @returns Cleaned HTML body content
83
+ */
84
+ export function extractBodyContent(html) {
85
+ const parser = new DOMParser();
86
+ const doc = parser.parseFromString(html, 'text/html');
87
+ // Remove "Back to Course Catalog" links
88
+ const links = doc.querySelectorAll('a[href="index.html"]');
89
+ links.forEach(link => {
90
+ const parent = link.closest('tr') ?? link.closest('td') ?? link.parentElement;
91
+ if (parent) {
92
+ parent.remove();
93
+ }
94
+ });
95
+ // Fix image paths - convert relative paths to absolute /help/ paths
96
+ const images = doc.querySelectorAll('img');
97
+ images.forEach(img => {
98
+ const src = img.getAttribute('src');
99
+ if (src && !src.startsWith('/') && !src.startsWith('http')) {
100
+ img.setAttribute('src', `/help/${src}`);
101
+ }
102
+ });
103
+ // Get the body content
104
+ return doc.body.innerHTML;
105
+ }
106
+ //# sourceMappingURL=technique.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"technique.js","sourceRoot":"","sources":["../../src/utils/technique.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Theme utilities
3
+ */
4
+ /** Available theme options */
5
+ export type ThemePreference = 'light' | 'dark' | 'system';
6
+ /** Resolved theme (without system option) */
7
+ export type ResolvedTheme = 'light' | 'dark';
8
+ /** Storage key for theme preference */
9
+ export declare const THEME_STORAGE_KEY = "sudojo-theme";
10
+ /**
11
+ * Detect system color scheme preference
12
+ *
13
+ * @returns 'dark' if system prefers dark mode, 'light' otherwise
14
+ */
15
+ export declare function getSystemTheme(): ResolvedTheme;
16
+ /**
17
+ * Resolve theme preference to actual theme
18
+ *
19
+ * @param preference - User's theme preference
20
+ * @param systemTheme - Current system theme
21
+ * @returns Resolved theme ('light' or 'dark')
22
+ */
23
+ export declare function resolveTheme(preference: ThemePreference, systemTheme: ResolvedTheme): ResolvedTheme;
24
+ /**
25
+ * Check if a value is a valid theme preference
26
+ *
27
+ * @param value - Value to check
28
+ * @returns Whether value is a valid theme preference
29
+ */
30
+ export declare function isValidThemePreference(value: unknown): value is ThemePreference;
31
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/utils/theme.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8BAA8B;AAC9B,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1D,6CAA6C;AAC7C,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,uCAAuC;AACvC,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,aAAa,CAK9C;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,aAAa,GACzB,aAAa,CAKf;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,eAAe,CAE1B"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Theme utilities
3
+ */
4
+ /** Storage key for theme preference */
5
+ export const THEME_STORAGE_KEY = 'sudojo-theme';
6
+ /**
7
+ * Detect system color scheme preference
8
+ *
9
+ * @returns 'dark' if system prefers dark mode, 'light' otherwise
10
+ */
11
+ export function getSystemTheme() {
12
+ if (typeof window === 'undefined')
13
+ return 'light';
14
+ return window.matchMedia('(prefers-color-scheme: dark)').matches
15
+ ? 'dark'
16
+ : 'light';
17
+ }
18
+ /**
19
+ * Resolve theme preference to actual theme
20
+ *
21
+ * @param preference - User's theme preference
22
+ * @param systemTheme - Current system theme
23
+ * @returns Resolved theme ('light' or 'dark')
24
+ */
25
+ export function resolveTheme(preference, systemTheme) {
26
+ if (preference === 'system') {
27
+ return systemTheme;
28
+ }
29
+ return preference;
30
+ }
31
+ /**
32
+ * Check if a value is a valid theme preference
33
+ *
34
+ * @param value - Value to check
35
+ * @returns Whether value is a valid theme preference
36
+ */
37
+ export function isValidThemePreference(value) {
38
+ return value === 'light' || value === 'dark' || value === 'system';
39
+ }
40
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/utils/theme.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,uCAAuC;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAEhD;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,OAAO,CAAC;IAClD,OAAO,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO;QAC9D,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,OAAO,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,UAA2B,EAC3B,WAA0B;IAE1B,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAc;IAEd,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,QAAQ,CAAC;AACrE,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Time formatting utilities
3
+ */
4
+ /**
5
+ * Format seconds into MM:SS or HH:MM:SS string
6
+ *
7
+ * @param seconds - Total seconds to format
8
+ * @returns Formatted time string
9
+ *
10
+ * @example
11
+ * formatTime(65) // "1:05"
12
+ * formatTime(3665) // "1:01:05"
13
+ */
14
+ export declare function formatTime(seconds: number): string;
15
+ /**
16
+ * Parse a time string back to seconds
17
+ *
18
+ * @param timeString - Time string in MM:SS or HH:MM:SS format
19
+ * @returns Total seconds
20
+ */
21
+ export declare function parseTime(timeString: string): number;
22
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASlD;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAWpD"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Time formatting utilities
3
+ */
4
+ /**
5
+ * Format seconds into MM:SS or HH:MM:SS string
6
+ *
7
+ * @param seconds - Total seconds to format
8
+ * @returns Formatted time string
9
+ *
10
+ * @example
11
+ * formatTime(65) // "1:05"
12
+ * formatTime(3665) // "1:01:05"
13
+ */
14
+ export function formatTime(seconds) {
15
+ const hours = Math.floor(seconds / 3600);
16
+ const minutes = Math.floor((seconds % 3600) / 60);
17
+ const secs = seconds % 60;
18
+ if (hours > 0) {
19
+ return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
20
+ }
21
+ return `${minutes}:${secs.toString().padStart(2, '0')}`;
22
+ }
23
+ /**
24
+ * Parse a time string back to seconds
25
+ *
26
+ * @param timeString - Time string in MM:SS or HH:MM:SS format
27
+ * @returns Total seconds
28
+ */
29
+ export function parseTime(timeString) {
30
+ const parts = timeString.split(':').map(Number);
31
+ if (parts.length === 2) {
32
+ const [minutes, secs] = parts;
33
+ return (minutes ?? 0) * 60 + (secs ?? 0);
34
+ }
35
+ if (parts.length === 3) {
36
+ const [hours, minutes, secs] = parts;
37
+ return (hours ?? 0) * 3600 + (minutes ?? 0) * 60 + (secs ?? 0);
38
+ }
39
+ return 0;
40
+ }
41
+ //# sourceMappingURL=time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/utils/time.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,OAAO,GAAG,EAAE,CAAC;IAE1B,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,OAAO,GAAG,KAAK,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC/F,CAAC;IACD,OAAO,GAAG,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,UAAkB;IAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;QAC9B,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;QACrC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Game validation utilities for checking mistakes, completion, and game state
3
+ */
4
+ import type { GameBoard, GameState } from '../types';
5
+ /**
6
+ * Checks if a cell value matches the solution
7
+ * @param board - GameBoard
8
+ * @param solution - 81-character solution string
9
+ * @param row - Row index
10
+ * @param col - Column index
11
+ * @returns true if the cell value matches the solution (or is empty)
12
+ */
13
+ export declare function isCellCorrect(board: GameBoard, solution: string, row: number, col: number): boolean;
14
+ /**
15
+ * Checks if a specific value would be correct for a cell
16
+ * @param solution - 81-character solution string
17
+ * @param row - Row index
18
+ * @param col - Column index
19
+ * @param value - Value to check (1-9)
20
+ * @returns true if the value matches the solution
21
+ */
22
+ export declare function isValueCorrect(solution: string, row: number, col: number, value: number): boolean;
23
+ /**
24
+ * Gets all cells with incorrect values
25
+ * @param board - GameBoard
26
+ * @param solution - 81-character solution string
27
+ * @returns Array of {row, column} positions with incorrect values
28
+ */
29
+ export declare function getIncorrectCells(board: GameBoard, solution: string): Array<{
30
+ row: number;
31
+ column: number;
32
+ }>;
33
+ /**
34
+ * Updates the error state of all cells based on the solution
35
+ * @param board - GameBoard to update
36
+ * @param solution - 81-character solution string
37
+ * @param showErrors - Whether to show errors
38
+ * @returns Updated GameBoard
39
+ */
40
+ export declare function updateCellErrors(board: GameBoard, solution: string, showErrors: boolean): GameBoard;
41
+ /**
42
+ * Checks if the game is complete (all cells filled correctly)
43
+ * @param board - GameBoard
44
+ * @param solution - 81-character solution string
45
+ * @returns true if all cells are filled with correct values
46
+ */
47
+ export declare function isGameComplete(board: GameBoard, solution: string): boolean;
48
+ /**
49
+ * Checks if the board is fully filled (regardless of correctness)
50
+ * @param board - GameBoard
51
+ * @returns true if all cells have values
52
+ */
53
+ export declare function isBoardFilled(board: GameBoard): boolean;
54
+ /**
55
+ * Counts the number of mistakes in the current board
56
+ * @param board - GameBoard
57
+ * @param solution - 81-character solution string
58
+ * @returns Number of incorrect cells
59
+ */
60
+ export declare function countMistakes(board: GameBoard, solution: string): number;
61
+ /**
62
+ * Updates highlight state for all cells based on selected cell
63
+ * @param board - GameBoard
64
+ * @param selectedRow - Selected row (or null if nothing selected)
65
+ * @param selectedCol - Selected column (or null if nothing selected)
66
+ * @param highlightRelated - Whether to highlight related cells
67
+ * @param highlightSameValue - Whether to highlight cells with same value
68
+ * @returns Updated GameBoard
69
+ */
70
+ export declare function updateCellHighlights(board: GameBoard, selectedRow: number | null, selectedCol: number | null, highlightRelated: boolean, highlightSameValue: boolean): GameBoard;
71
+ /**
72
+ * Clears all highlights from the board
73
+ * @param board - GameBoard
74
+ * @returns Board with no highlights
75
+ */
76
+ export declare function clearHighlights(board: GameBoard): GameBoard;
77
+ /**
78
+ * Auto-removes pencilmarks from related cells when a value is placed
79
+ * @param board - GameBoard
80
+ * @param row - Row where value was placed
81
+ * @param col - Column where value was placed
82
+ * @param value - Value that was placed
83
+ * @returns Updated GameBoard with pencilmarks removed
84
+ */
85
+ export declare function autoRemovePencilmarks(board: GameBoard, row: number, col: number, value: number): GameBoard;
86
+ /**
87
+ * Gets the progress percentage (filled cells / total empty cells in original puzzle)
88
+ * @param board - GameBoard
89
+ * @returns Progress percentage (0-100)
90
+ */
91
+ export declare function getProgressPercentage(board: GameBoard): number;
92
+ /**
93
+ * Validates the entire game state
94
+ * @param state - GameState
95
+ * @returns Object with validation results
96
+ */
97
+ export declare function validateGameState(state: GameState): {
98
+ isComplete: boolean;
99
+ isFilled: boolean;
100
+ mistakeCount: number;
101
+ progressPercentage: number;
102
+ incorrectCells: Array<{
103
+ row: number;
104
+ column: number;
105
+ }>;
106
+ };
107
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGrD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAST;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,OAAO,CAGT;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,MAAM,GACf,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBxC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,OAAO,GAClB,SAAS,CAoBX;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAe1E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CASvD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExE;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,gBAAgB,EAAE,OAAO,EACzB,kBAAkB,EAAE,OAAO,GAC1B,SAAS,CA2CX;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAS3D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,SAAS,CAwBX;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAqB9D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,GAAG;IACnD,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD,CAQA"}