@sudobility/sudojo_lib 0.0.201 → 0.0.203
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/dist/admin/adminApi.d.ts +34 -0
- package/dist/admin/adminApi.d.ts.map +1 -0
- package/dist/admin/adminApi.js +59 -0
- package/dist/admin/adminApi.js.map +1 -0
- package/dist/admin/boardRules.d.ts +75 -0
- package/dist/admin/boardRules.d.ts.map +1 -0
- package/dist/admin/boardRules.js +127 -0
- package/dist/admin/boardRules.js.map +1 -0
- package/dist/admin/boardStrings.d.ts +55 -0
- package/dist/admin/boardStrings.d.ts.map +1 -0
- package/dist/admin/boardStrings.js +99 -0
- package/dist/admin/boardStrings.js.map +1 -0
- package/dist/admin/examples.d.ts +58 -0
- package/dist/admin/examples.d.ts.map +1 -0
- package/dist/admin/examples.js +104 -0
- package/dist/admin/examples.js.map +1 -0
- package/dist/admin/extraction.d.ts +87 -0
- package/dist/admin/extraction.d.ts.map +1 -0
- package/dist/admin/extraction.js +141 -0
- package/dist/admin/extraction.js.map +1 -0
- package/dist/admin/index.d.ts +32 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +22 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/jobs.d.ts +108 -0
- package/dist/admin/jobs.d.ts.map +1 -0
- package/dist/admin/jobs.js +435 -0
- package/dist/admin/jobs.js.map +1 -0
- package/dist/admin/regenerateHints.d.ts +16 -0
- package/dist/admin/regenerateHints.d.ts.map +1 -0
- package/dist/admin/regenerateHints.js +43 -0
- package/dist/admin/regenerateHints.js.map +1 -0
- package/dist/admin/types.d.ts +42 -0
- package/dist/admin/types.d.ts.map +1 -0
- package/dist/admin/types.js +21 -0
- package/dist/admin/types.js.map +1 -0
- package/dist/admin/useAdminJob.d.ts +28 -0
- package/dist/admin/useAdminJob.d.ts.map +1 -0
- package/dist/admin/useAdminJob.js +99 -0
- package/dist/admin/useAdminJob.js.map +1 -0
- package/dist/admin/useAdminStats.d.ts +46 -0
- package/dist/admin/useAdminStats.d.ts.map +1 -0
- package/dist/admin/useAdminStats.js +91 -0
- package/dist/admin/useAdminStats.js.map +1 -0
- package/dist/admin/useBoardGenerator.d.ts +29 -0
- package/dist/admin/useBoardGenerator.d.ts.map +1 -0
- package/dist/admin/useBoardGenerator.js +38 -0
- package/dist/admin/useBoardGenerator.js.map +1 -0
- package/dist/admin/useBoardTechniquesUpdater.d.ts +14 -0
- package/dist/admin/useBoardTechniquesUpdater.d.ts.map +1 -0
- package/dist/admin/useBoardTechniquesUpdater.js +28 -0
- package/dist/admin/useBoardTechniquesUpdater.js.map +1 -0
- package/dist/admin/useExampleCreator.d.ts +29 -0
- package/dist/admin/useExampleCreator.d.ts.map +1 -0
- package/dist/admin/useExampleCreator.js +58 -0
- package/dist/admin/useExampleCreator.js.map +1 -0
- package/dist/admin/useSingleBoardExtractor.d.ts +18 -0
- package/dist/admin/useSingleBoardExtractor.d.ts.map +1 -0
- package/dist/admin/useSingleBoardExtractor.js +19 -0
- package/dist/admin/useSingleBoardExtractor.js.map +1 -0
- package/dist/admin/useTechniqueExtractor.d.ts +26 -0
- package/dist/admin/useTechniqueExtractor.d.ts.map +1 -0
- package/dist/admin/useTechniqueExtractor.js +37 -0
- package/dist/admin/useTechniqueExtractor.js.map +1 -0
- package/dist/context/SudojoApiContext.d.ts +58 -0
- package/dist/context/SudojoApiContext.d.ts.map +1 -0
- package/dist/context/SudojoApiContext.js +52 -0
- package/dist/context/SudojoApiContext.js.map +1 -0
- package/dist/context/SudojoApiProvider.d.ts +21 -0
- package/dist/context/SudojoApiProvider.d.ts.map +1 -0
- package/dist/context/SudojoApiProvider.js +15 -0
- package/dist/context/SudojoApiProvider.js.map +1 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +2 -0
- package/dist/context/index.js.map +1 -1
- package/dist/hooks/index.d.ts +24 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +22 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useBoardEntry.d.ts +65 -5
- package/dist/hooks/useBoardEntry.d.ts.map +1 -1
- package/dist/hooks/useBoardEntry.js +124 -27
- package/dist/hooks/useBoardEntry.js.map +1 -1
- package/dist/hooks/useBoardScan.d.ts +27 -0
- package/dist/hooks/useBoardScan.d.ts.map +1 -0
- package/dist/hooks/useBoardScan.js +27 -0
- package/dist/hooks/useBoardScan.js.map +1 -0
- package/dist/hooks/useCommunities.d.ts +6 -4
- package/dist/hooks/useCommunities.d.ts.map +1 -1
- package/dist/hooks/useCommunities.js +6 -1
- package/dist/hooks/useCommunities.js.map +1 -1
- package/dist/hooks/useCompletionTrigger.d.ts +23 -0
- package/dist/hooks/useCompletionTrigger.d.ts.map +1 -0
- package/dist/hooks/useCompletionTrigger.js +39 -0
- package/dist/hooks/useCompletionTrigger.js.map +1 -0
- package/dist/hooks/useDailyGame.d.ts +11 -6
- package/dist/hooks/useDailyGame.d.ts.map +1 -1
- package/dist/hooks/useDailyGame.js +20 -61
- package/dist/hooks/useDailyGame.js.map +1 -1
- package/dist/hooks/useDeleteAccount.d.ts +72 -0
- package/dist/hooks/useDeleteAccount.d.ts.map +1 -0
- package/dist/hooks/useDeleteAccount.js +71 -0
- package/dist/hooks/useDeleteAccount.js.map +1 -0
- package/dist/hooks/useEnteredGameSession.d.ts +73 -0
- package/dist/hooks/useEnteredGameSession.d.ts.map +1 -0
- package/dist/hooks/useEnteredGameSession.js +155 -0
- package/dist/hooks/useEnteredGameSession.js.map +1 -0
- package/dist/hooks/useGameSession.d.ts +8 -4
- package/dist/hooks/useGameSession.d.ts.map +1 -1
- package/dist/hooks/useGameSession.js +4 -1
- package/dist/hooks/useGameSession.js.map +1 -1
- package/dist/hooks/useGameTeaching.d.ts.map +1 -1
- package/dist/hooks/useGameTeaching.js +11 -13
- package/dist/hooks/useGameTeaching.js.map +1 -1
- package/dist/hooks/useHint.d.ts +7 -7
- package/dist/hooks/useHint.d.ts.map +1 -1
- package/dist/hooks/useHint.js +18 -9
- package/dist/hooks/useHint.js.map +1 -1
- package/dist/hooks/useIsSiteAdmin.d.ts +39 -0
- package/dist/hooks/useIsSiteAdmin.d.ts.map +1 -0
- package/dist/hooks/useIsSiteAdmin.js +31 -0
- package/dist/hooks/useIsSiteAdmin.js.map +1 -0
- package/dist/hooks/useLearning.d.ts +8 -8
- package/dist/hooks/useLearning.d.ts.map +1 -1
- package/dist/hooks/useLearning.js +5 -2
- package/dist/hooks/useLearning.js.map +1 -1
- package/dist/hooks/useLevelGame.d.ts +7 -7
- package/dist/hooks/useLevelGame.d.ts.map +1 -1
- package/dist/hooks/useLevelGame.js +13 -49
- package/dist/hooks/useLevelGame.js.map +1 -1
- package/dist/hooks/useLevels.d.ts +8 -8
- package/dist/hooks/useLevels.d.ts.map +1 -1
- package/dist/hooks/useLevels.js +7 -3
- package/dist/hooks/useLevels.js.map +1 -1
- package/dist/hooks/usePracticeGame.d.ts +53 -0
- package/dist/hooks/usePracticeGame.d.ts.map +1 -0
- package/dist/hooks/usePracticeGame.js +72 -0
- package/dist/hooks/usePracticeGame.js.map +1 -0
- package/dist/hooks/usePuzzleDistribution.d.ts +2 -0
- package/dist/hooks/usePuzzleDistribution.d.ts.map +1 -1
- package/dist/hooks/usePuzzleDistribution.js +4 -14
- package/dist/hooks/usePuzzleDistribution.js.map +1 -1
- package/dist/hooks/usePuzzleProgress.d.ts +37 -0
- package/dist/hooks/usePuzzleProgress.d.ts.map +1 -0
- package/dist/hooks/usePuzzleProgress.js +48 -0
- package/dist/hooks/usePuzzleProgress.js.map +1 -0
- package/dist/hooks/usePuzzleSession.d.ts +215 -0
- package/dist/hooks/usePuzzleSession.d.ts.map +1 -0
- package/dist/hooks/usePuzzleSession.js +358 -0
- package/dist/hooks/usePuzzleSession.js.map +1 -0
- package/dist/hooks/useResumeGame.d.ts +70 -0
- package/dist/hooks/useResumeGame.d.ts.map +1 -0
- package/dist/hooks/useResumeGame.js +123 -0
- package/dist/hooks/useResumeGame.js.map +1 -0
- package/dist/hooks/useStrategies.d.ts +39 -0
- package/dist/hooks/useStrategies.d.ts.map +1 -0
- package/dist/hooks/useStrategies.js +34 -0
- package/dist/hooks/useStrategies.js.map +1 -0
- package/dist/hooks/useSudoku.d.ts +5 -0
- package/dist/hooks/useSudoku.d.ts.map +1 -1
- package/dist/hooks/useSudoku.js +7 -1
- package/dist/hooks/useSudoku.js.map +1 -1
- package/dist/hooks/useTechniqueByPath.d.ts +46 -0
- package/dist/hooks/useTechniqueByPath.d.ts.map +1 -0
- package/dist/hooks/useTechniqueByPath.js +47 -0
- package/dist/hooks/useTechniqueByPath.js.map +1 -0
- package/dist/hooks/useTechniqueExample.d.ts +60 -0
- package/dist/hooks/useTechniqueExample.d.ts.map +1 -0
- package/dist/hooks/useTechniqueExample.js +63 -0
- package/dist/hooks/useTechniqueExample.js.map +1 -0
- package/dist/hooks/useTechniques.d.ts +8 -8
- package/dist/hooks/useTechniques.d.ts.map +1 -1
- package/dist/hooks/useTechniques.js +9 -21
- package/dist/hooks/useTechniques.js.map +1 -1
- package/dist/index.d.ts +418 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +276 -10
- package/dist/index.js.map +1 -1
- package/dist/types/currentGame.d.ts +9 -1
- package/dist/types/currentGame.d.ts.map +1 -1
- package/dist/types/currentGame.js.map +1 -1
- package/dist/utils/auth.d.ts +6 -0
- package/dist/utils/auth.d.ts.map +1 -1
- package/dist/utils/auth.js +8 -0
- package/dist/utils/auth.js.map +1 -1
- package/dist/utils/community.d.ts +64 -0
- package/dist/utils/community.d.ts.map +1 -0
- package/dist/utils/community.js +66 -0
- package/dist/utils/community.js.map +1 -0
- package/dist/utils/date.d.ts +37 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +68 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/entityTranslate.d.ts +120 -0
- package/dist/utils/entityTranslate.d.ts.map +1 -0
- package/dist/utils/entityTranslate.js +146 -0
- package/dist/utils/entityTranslate.js.map +1 -0
- package/dist/utils/gameFetchStatus.d.ts +74 -0
- package/dist/utils/gameFetchStatus.d.ts.map +1 -0
- package/dist/utils/gameFetchStatus.js +118 -0
- package/dist/utils/gameFetchStatus.js.map +1 -0
- package/dist/utils/gamification.d.ts +36 -0
- package/dist/utils/gamification.d.ts.map +1 -0
- package/dist/utils/gamification.js +33 -0
- package/dist/utils/gamification.js.map +1 -0
- package/dist/utils/hintAccess.d.ts +64 -0
- package/dist/utils/hintAccess.d.ts.map +1 -0
- package/dist/utils/hintAccess.js +57 -0
- package/dist/utils/hintAccess.js.map +1 -0
- package/dist/utils/index.d.ts +31 -10
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +20 -9
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/language.d.ts +123 -0
- package/dist/utils/language.d.ts.map +1 -0
- package/dist/utils/language.js +95 -0
- package/dist/utils/language.js.map +1 -0
- package/dist/utils/localizedHint.d.ts +60 -0
- package/dist/utils/localizedHint.d.ts.map +1 -1
- package/dist/utils/localizedHint.js +90 -0
- package/dist/utils/localizedHint.js.map +1 -1
- package/dist/utils/progress.d.ts +10 -1
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +24 -0
- package/dist/utils/progress.js.map +1 -1
- package/dist/utils/scannedBoard.d.ts +79 -0
- package/dist/utils/scannedBoard.d.ts.map +1 -0
- package/dist/utils/scannedBoard.js +96 -0
- package/dist/utils/scannedBoard.js.map +1 -0
- package/dist/utils/shareUrl.d.ts +38 -15
- package/dist/utils/shareUrl.d.ts.map +1 -1
- package/dist/utils/shareUrl.js +75 -26
- package/dist/utils/shareUrl.js.map +1 -1
- package/dist/utils/subscription.d.ts +118 -0
- package/dist/utils/subscription.d.ts.map +1 -1
- package/dist/utils/subscription.js +212 -0
- package/dist/utils/subscription.js.map +1 -1
- package/dist/utils/sudokuPresenter.d.ts +42 -0
- package/dist/utils/sudokuPresenter.d.ts.map +1 -1
- package/dist/utils/sudokuPresenter.js +114 -1
- package/dist/utils/sudokuPresenter.js.map +1 -1
- package/dist/utils/sudokuScrambler.d.ts +8 -0
- package/dist/utils/sudokuScrambler.d.ts.map +1 -1
- package/dist/utils/sudokuScrambler.js +12 -0
- package/dist/utils/sudokuScrambler.js.map +1 -1
- package/dist/utils/technique.d.ts +112 -1
- package/dist/utils/technique.d.ts.map +1 -1
- package/dist/utils/technique.js +293 -1
- package/dist/utils/technique.js.map +1 -1
- package/dist/utils/techniqueBitmask.d.ts +31 -0
- package/dist/utils/techniqueBitmask.d.ts.map +1 -0
- package/dist/utils/techniqueBitmask.js +53 -0
- package/dist/utils/techniqueBitmask.js.map +1 -0
- package/dist/utils/techniqueExample.d.ts +42 -0
- package/dist/utils/techniqueExample.d.ts.map +1 -0
- package/dist/utils/techniqueExample.js +57 -0
- package/dist/utils/techniqueExample.js.map +1 -0
- package/dist/utils/theme.d.ts +2 -0
- package/dist/utils/theme.d.ts.map +1 -1
- package/dist/utils/theme.js +2 -0
- package/dist/utils/theme.js.map +1 -1
- package/package.json +7 -7
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - Hint visualization (areas, cells)
|
|
9
9
|
* - Pencilmark coloring
|
|
10
10
|
*/
|
|
11
|
+
import type { SolverHintStep } from '@sudobility/sudojo_types';
|
|
11
12
|
import type { SudokuCell } from '../types/sudoku';
|
|
12
13
|
import { type CellDisplayState, type DisplayCellGroup, type DisplayLink, type HintCell, type HintStep, SudokuColor, ThemeColor, type UIColorPalette } from '../types/display';
|
|
13
14
|
/**
|
|
@@ -84,6 +85,47 @@ export declare function convertSolverCellGroup(group: {
|
|
|
84
85
|
color: string;
|
|
85
86
|
cells: number[][];
|
|
86
87
|
}): DisplayCellGroup;
|
|
88
|
+
/**
|
|
89
|
+
* Map a solver color string to a SudokuColor, case-insensitively.
|
|
90
|
+
*
|
|
91
|
+
* Only the five hint colors the solver uses for highlighting are mapped
|
|
92
|
+
* (`blue`, `green`, `yellow`, `orange`, `red`); anything else, including
|
|
93
|
+
* `none`, `clear`, `gray`, `white` and `black`, returns null.
|
|
94
|
+
*
|
|
95
|
+
* This is sudojo_app_rn's validated mapping (`hintConversion.ts`). sudojo_ui's
|
|
96
|
+
* canvas instead cast the raw string, so `gray` became `SudokuColor.GRAY` there
|
|
97
|
+
* (a gray border on unfilled cells). With null, presentBoard falls back to its
|
|
98
|
+
* defaults: no fill override, and a blue border for an unfilled cell.
|
|
99
|
+
*/
|
|
100
|
+
export declare function solverColorToSudokuColor(color: string | null | undefined): SudokuColor | null;
|
|
101
|
+
/**
|
|
102
|
+
* Parse a solver digit string (e.g. `"125"`) into digits, dropping anything
|
|
103
|
+
* that is not 1-9. Used for the `add` / `remove` / `highlight` cell actions.
|
|
104
|
+
*/
|
|
105
|
+
export declare function parseHintDigits(value: string | null | undefined): number[];
|
|
106
|
+
/**
|
|
107
|
+
* Convert a solver hint step (string actions, row/column cells) into the
|
|
108
|
+
* display format presentBoard takes (flat indices, digit arrays, SudokuColor).
|
|
109
|
+
*
|
|
110
|
+
* - Colors go through {@link solverColorToSudokuColor}.
|
|
111
|
+
* - Links and groups are converted with convertSolverLink /
|
|
112
|
+
* convertSolverCellGroup; empty lists become null.
|
|
113
|
+
* - `areas` / `cells` may be null on the wire (the autopencil hint); they
|
|
114
|
+
* become null here too.
|
|
115
|
+
*
|
|
116
|
+
* Shared by the web (sudojo_ui) and RN canvases.
|
|
117
|
+
*/
|
|
118
|
+
export declare function convertSolverHintStep(step: SolverHintStep | null | undefined): HintStep | null;
|
|
119
|
+
/**
|
|
120
|
+
* Whether a hint step is a conflict hint (any link of type `'conflict'`).
|
|
121
|
+
* Accepts either a display step or a raw solver step. presentBoard uses it to
|
|
122
|
+
* color the orange source cells' digits as warnings.
|
|
123
|
+
*/
|
|
124
|
+
export declare function isConflictHintStep(step: {
|
|
125
|
+
links?: ReadonlyArray<{
|
|
126
|
+
type: string;
|
|
127
|
+
}> | null;
|
|
128
|
+
} | null | undefined): boolean;
|
|
87
129
|
/**
|
|
88
130
|
* Export sudokuColorToTheme for external use
|
|
89
131
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sudokuPresenter.d.ts","sourceRoot":"","sources":["../../src/utils/sudokuPresenter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"sudokuPresenter.d.ts","sourceRoot":"","sources":["../../src/utils/sudokuPresenter.ts"],"names":[],"mappings":"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"}
|
|
@@ -299,7 +299,7 @@ function getPencilmarks(cell, hintCell) {
|
|
|
299
299
|
export function presentBoard(options) {
|
|
300
300
|
const { cells, selectedIndex, showErrors, hintStep = null, selectedDigitCells = null, } = options;
|
|
301
301
|
// Detect conflict hint (has links with type 'conflict')
|
|
302
|
-
const isConflictHint = hintStep
|
|
302
|
+
const isConflictHint = isConflictHintStep(hintStep);
|
|
303
303
|
// Calculate hint cell map
|
|
304
304
|
const cellHints = calculateCellHints(hintStep);
|
|
305
305
|
// Get selected cell properties (if any)
|
|
@@ -439,6 +439,119 @@ export function convertSolverCellGroup(group) {
|
|
|
439
439
|
cellIndices: group.cells.map(cell => (cell[0] ?? 0) * 9 + (cell[1] ?? 0)),
|
|
440
440
|
};
|
|
441
441
|
}
|
|
442
|
+
// =============================================================================
|
|
443
|
+
// Solver Hint Step Conversion
|
|
444
|
+
// =============================================================================
|
|
445
|
+
/**
|
|
446
|
+
* Map a solver color string to a SudokuColor, case-insensitively.
|
|
447
|
+
*
|
|
448
|
+
* Only the five hint colors the solver uses for highlighting are mapped
|
|
449
|
+
* (`blue`, `green`, `yellow`, `orange`, `red`); anything else, including
|
|
450
|
+
* `none`, `clear`, `gray`, `white` and `black`, returns null.
|
|
451
|
+
*
|
|
452
|
+
* This is sudojo_app_rn's validated mapping (`hintConversion.ts`). sudojo_ui's
|
|
453
|
+
* canvas instead cast the raw string, so `gray` became `SudokuColor.GRAY` there
|
|
454
|
+
* (a gray border on unfilled cells). With null, presentBoard falls back to its
|
|
455
|
+
* defaults: no fill override, and a blue border for an unfilled cell.
|
|
456
|
+
*/
|
|
457
|
+
export function solverColorToSudokuColor(color) {
|
|
458
|
+
if (!color)
|
|
459
|
+
return null;
|
|
460
|
+
switch (color.toLowerCase()) {
|
|
461
|
+
case 'blue':
|
|
462
|
+
return SudokuColor.BLUE;
|
|
463
|
+
case 'green':
|
|
464
|
+
return SudokuColor.GREEN;
|
|
465
|
+
case 'yellow':
|
|
466
|
+
return SudokuColor.YELLOW;
|
|
467
|
+
case 'orange':
|
|
468
|
+
return SudokuColor.ORANGE;
|
|
469
|
+
case 'red':
|
|
470
|
+
return SudokuColor.RED;
|
|
471
|
+
default:
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Parse a solver digit string (e.g. `"125"`) into digits, dropping anything
|
|
477
|
+
* that is not 1-9. Used for the `add` / `remove` / `highlight` cell actions.
|
|
478
|
+
*/
|
|
479
|
+
export function parseHintDigits(value) {
|
|
480
|
+
if (!value)
|
|
481
|
+
return [];
|
|
482
|
+
return value
|
|
483
|
+
.split('')
|
|
484
|
+
.map(d => parseInt(d, 10))
|
|
485
|
+
.filter(n => !isNaN(n) && n !== 0);
|
|
486
|
+
}
|
|
487
|
+
/** Parse a single-digit action (`select` / `unselect`); 0 or junk → null. */
|
|
488
|
+
function parseHintDigit(value) {
|
|
489
|
+
if (!value)
|
|
490
|
+
return null;
|
|
491
|
+
return parseInt(value, 10) || null;
|
|
492
|
+
}
|
|
493
|
+
function convertSolverHintArea(area) {
|
|
494
|
+
return {
|
|
495
|
+
type: area.type,
|
|
496
|
+
color: solverColorToSudokuColor(area.color),
|
|
497
|
+
index: area.index,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
function convertSolverHintCell(cell) {
|
|
501
|
+
const result = {
|
|
502
|
+
index: cell.row * 9 + cell.column,
|
|
503
|
+
color: solverColorToSudokuColor(cell.color),
|
|
504
|
+
fill: cell.fill,
|
|
505
|
+
};
|
|
506
|
+
if (cell.actions) {
|
|
507
|
+
// Matches Kotlin: select = if (select != 0) select else null
|
|
508
|
+
result.actions = {
|
|
509
|
+
select: parseHintDigit(cell.actions.select),
|
|
510
|
+
unselect: parseHintDigit(cell.actions.unselect),
|
|
511
|
+
add: cell.actions.add ? parseHintDigits(cell.actions.add) : null,
|
|
512
|
+
remove: cell.actions.remove ? parseHintDigits(cell.actions.remove) : null,
|
|
513
|
+
highlight: cell.actions.highlight
|
|
514
|
+
? parseHintDigits(cell.actions.highlight)
|
|
515
|
+
: null,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
return result;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Convert a solver hint step (string actions, row/column cells) into the
|
|
522
|
+
* display format presentBoard takes (flat indices, digit arrays, SudokuColor).
|
|
523
|
+
*
|
|
524
|
+
* - Colors go through {@link solverColorToSudokuColor}.
|
|
525
|
+
* - Links and groups are converted with convertSolverLink /
|
|
526
|
+
* convertSolverCellGroup; empty lists become null.
|
|
527
|
+
* - `areas` / `cells` may be null on the wire (the autopencil hint); they
|
|
528
|
+
* become null here too.
|
|
529
|
+
*
|
|
530
|
+
* Shared by the web (sudojo_ui) and RN canvases.
|
|
531
|
+
*/
|
|
532
|
+
export function convertSolverHintStep(step) {
|
|
533
|
+
if (!step)
|
|
534
|
+
return null;
|
|
535
|
+
return {
|
|
536
|
+
title: step.title,
|
|
537
|
+
text: step.text,
|
|
538
|
+
areas: step.areas?.map(convertSolverHintArea) ?? null,
|
|
539
|
+
cells: step.cells?.map(convertSolverHintCell) ?? null,
|
|
540
|
+
links: step.links?.length ? step.links.map(convertSolverLink) : null,
|
|
541
|
+
groups: step.groups?.length
|
|
542
|
+
? step.groups.map(convertSolverCellGroup)
|
|
543
|
+
: null,
|
|
544
|
+
digit: step.digit ?? null,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Whether a hint step is a conflict hint (any link of type `'conflict'`).
|
|
549
|
+
* Accepts either a display step or a raw solver step. presentBoard uses it to
|
|
550
|
+
* color the orange source cells' digits as warnings.
|
|
551
|
+
*/
|
|
552
|
+
export function isConflictHintStep(step) {
|
|
553
|
+
return step?.links?.some(link => link.type === 'conflict') ?? false;
|
|
554
|
+
}
|
|
442
555
|
/**
|
|
443
556
|
* Export sudokuColorToTheme for external use
|
|
444
557
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sudokuPresenter.js","sourceRoot":"","sources":["../../src/utils/sudokuPresenter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Sudoku presenter - port of renderable/SudokuPresenter.kt\n *\n * Handles all display logic for the Sudoku board including:\n * - Cell background colors based on selection state\n * - Cell border colors for selected cells\n * - Text colors (given, input, hint actions)\n * - Hint visualization (areas, cells)\n * - Pencilmark coloring\n */\n\nimport type { SudokuCell } from '../types/sudoku';\nimport { blockOf, columnOf, rowOf } from '../types/sudoku';\nimport {\n type CellDisplayState,\n type DisplayCellGroup,\n type DisplayLink,\n type HintCell,\n type HintStep,\n type PencilmarkDisplayState,\n SudokuColor,\n ThemeColor,\n UIColorDark,\n UIColorLight,\n type UIColorPalette,\n} from '../types/display';\n\n// =============================================================================\n// Block Definition (matches PlayBoard.blocks)\n// =============================================================================\n\n/**\n * Block class - matches Kotlin Block class\n */\nclass Block {\n readonly index: number;\n readonly cellIndices: number[];\n\n constructor(index: number) {\n this.index = index;\n const blockRow = Math.floor(index / 3);\n const blockCol = index % 3;\n this.cellIndices = [];\n for (let r = 0; r < 3; r++) {\n for (let c = 0; c < 3; c++) {\n const row = blockRow * 3 + r;\n const col = blockCol * 3 + c;\n this.cellIndices.push(row * 9 + col);\n }\n }\n }\n}\n\n/**\n * Row class - matches Kotlin Row class\n */\nclass Row {\n readonly index: number;\n readonly cellIndices: number[];\n\n constructor(index: number) {\n this.index = index;\n this.cellIndices = Array.from({ length: 9 }, (_, col) => index * 9 + col);\n }\n}\n\n/**\n * Column class - matches Kotlin Column class\n */\nclass Column {\n readonly index: number;\n readonly cellIndices: number[];\n\n constructor(index: number) {\n this.index = index;\n this.cellIndices = Array.from({ length: 9 }, (_, row) => row * 9 + index);\n }\n}\n\n// =============================================================================\n// Color Conversion (matches CellPresenter.colorOf)\n// =============================================================================\n\n/**\n * Convert SudokuColor to ThemeColor - matches CellPresenter.colorOf\n */\nfunction sudokuColorToTheme(\n sudokuColor: SudokuColor | null\n): ThemeColor | null {\n switch (sudokuColor) {\n case SudokuColor.BLUE:\n return ThemeColor.SELECTED;\n case SudokuColor.GREEN:\n return ThemeColor.SUCCESS;\n case SudokuColor.YELLOW:\n return ThemeColor.WARNING_SECONDARY;\n case SudokuColor.ORANGE:\n return ThemeColor.WARNING;\n case SudokuColor.RED:\n return ThemeColor.ERROR;\n case SudokuColor.WHITE:\n return ThemeColor.BACKGROUND;\n case SudokuColor.LIGHT_GRAY:\n return ThemeColor.BACKGROUND_SECONDARY;\n case SudokuColor.BLACK:\n return ThemeColor.LABEL;\n case SudokuColor.GRAY:\n return ThemeColor.LABEL_SECONDARY;\n case SudokuColor.CLEAR:\n return ThemeColor.CLEAR;\n default:\n return null;\n }\n}\n\n/**\n * Convert ThemeColor to actual CSS color - matches RendererColor.color\n */\nexport function themeColorToCSS(\n palette: UIColorPalette,\n themeColor: ThemeColor | null\n): string | null {\n switch (themeColor) {\n case ThemeColor.NONE:\n case ThemeColor.CLEAR:\n return palette.clear;\n case ThemeColor.SELECTED:\n return palette.systemPurple;\n case ThemeColor.SUCCESS:\n return palette.systemBlue;\n case ThemeColor.WARNING_SECONDARY:\n return palette.systemYellow;\n case ThemeColor.WARNING:\n return palette.systemOrange;\n case ThemeColor.ERROR:\n return palette.systemRed;\n case ThemeColor.BACKGROUND:\n return palette.systemBackground;\n case ThemeColor.BACKGROUND_SECONDARY:\n return palette.secondarySystemBackground;\n case ThemeColor.LABEL:\n return palette.label;\n case ThemeColor.LABEL_SECONDARY:\n return palette.secondaryLabel;\n case ThemeColor.DISABLED:\n return palette.systemGray;\n default:\n return null;\n }\n}\n\n/**\n * Get the color palette for the current theme\n */\nexport function getColorPalette(isDarkMode: boolean): UIColorPalette {\n return isDarkMode ? UIColorDark : UIColorLight;\n}\n\n// =============================================================================\n// Hint Processing (matches BoardPresenter.calculateCellHints)\n// =============================================================================\n\n/**\n * Process hint areas and cells into a map of cell hints\n * Matches BoardPresenter.calculateCellHints\n */\nexport function calculateCellHints(\n hintStep: HintStep | null\n): Map<number, HintCell> | null {\n if (!hintStep) return null;\n\n const hintsCell = new Map<number, HintCell>();\n\n // Process areas first\n hintStep.areas?.forEach(area => {\n let cellIndices: number[];\n\n switch (area.type) {\n case 'block':\n cellIndices = new Block(area.index).cellIndices;\n break;\n case 'row':\n cellIndices = new Row(area.index).cellIndices;\n break;\n case 'column':\n cellIndices = new Column(area.index).cellIndices;\n break;\n }\n\n cellIndices.forEach(index => {\n hintsCell.set(index, {\n index,\n color: area.color,\n fill: true, // Areas always use fill\n });\n });\n });\n\n // Process cells (overrides areas)\n hintStep.cells?.forEach(cell => {\n hintsCell.set(cell.index, cell);\n });\n\n return hintsCell;\n}\n\n// =============================================================================\n// Cell Color Logic (matches BlockPresenter.color)\n// =============================================================================\n\ninterface CellColorResult {\n backgroundColor: ThemeColor;\n borderColor: ThemeColor | null;\n}\n\n/**\n * Determine background and border colors for a cell\n * Matches BlockPresenter.color exactly\n */\nfunction getCellColors(\n cellIndex: number,\n cellBlockIndex: number,\n cellRowIndex: number,\n cellColumnIndex: number,\n cellHasValue: boolean,\n selectedIndex: number | null,\n selectedBlockIndex: number | null,\n selectedRowIndex: number | null,\n selectedColumnIndex: number | null,\n selectedDigitCells: Set<number> | null\n): CellColorResult {\n // Default background based on block (checkerboard pattern)\n const defaultBackgroundColor =\n cellBlockIndex % 2 === 0\n ? ThemeColor.BACKGROUND_SECONDARY\n : ThemeColor.BACKGROUND;\n\n if (selectedIndex === null) {\n return { backgroundColor: defaultBackgroundColor, borderColor: null };\n }\n\n if (selectedDigitCells !== null) {\n // Digit highlighting mode\n if (selectedDigitCells.has(cellIndex)) {\n // Highlighted cells (have the same digit)\n return { backgroundColor: ThemeColor.SELECTED, borderColor: null };\n }\n\n // Check if this cell is \"impossible\" (in same row/col/block as highlighted cells)\n let isImpossible = cellHasValue;\n if (!isImpossible) {\n for (const highlightedIndex of selectedDigitCells) {\n const hRow = rowOf(highlightedIndex);\n const hCol = columnOf(highlightedIndex);\n const hBlock = blockOf(highlightedIndex);\n if (\n cellRowIndex === hRow ||\n cellColumnIndex === hCol ||\n cellBlockIndex === hBlock\n ) {\n isImpossible = true;\n break;\n }\n }\n }\n\n if (isImpossible) {\n return { backgroundColor: ThemeColor.LABEL_SECONDARY, borderColor: null };\n }\n\n // Possible cells\n return { backgroundColor: defaultBackgroundColor, borderColor: null };\n }\n\n // Normal selection mode\n if (selectedIndex === cellIndex) {\n // Selected cell: default background + SELECTED border\n return {\n backgroundColor: defaultBackgroundColor,\n borderColor: ThemeColor.SELECTED,\n };\n }\n\n if (\n selectedBlockIndex === cellBlockIndex ||\n selectedRowIndex === cellRowIndex ||\n selectedColumnIndex === cellColumnIndex\n ) {\n // Same row, column or block: SELECTED background\n return { backgroundColor: ThemeColor.SELECTED, borderColor: null };\n }\n\n // Other cells\n return { backgroundColor: defaultBackgroundColor, borderColor: null };\n}\n\n// =============================================================================\n// Cell Text Logic (matches CellPresenter.present)\n// =============================================================================\n\ninterface CellTextResult {\n digit: number | null;\n textColor: ThemeColor | null;\n isGiven: boolean;\n}\n\n/**\n * Determine what digit and color to display in a cell\n * Matches CellPresenter.present logic\n */\nfunction getCellText(\n cell: SudokuCell,\n showErrors: boolean,\n hintCell: HintCell | null\n): CellTextResult {\n // Hint select action takes priority\n if (hintCell?.actions?.select != null) {\n return {\n digit: hintCell.actions.select,\n textColor: ThemeColor.SELECTED,\n isGiven: false,\n };\n }\n\n // Hint unselect action (wrong digit to remove)\n if (hintCell?.actions?.unselect != null) {\n return {\n digit: hintCell.actions.unselect,\n textColor: ThemeColor.ERROR,\n isGiven: false,\n };\n }\n\n // User input\n if (cell.input !== null) {\n let textColor: ThemeColor;\n if (showErrors) {\n textColor =\n cell.input === cell.solution ? ThemeColor.SUCCESS : ThemeColor.ERROR;\n } else {\n textColor = ThemeColor.LABEL_SECONDARY;\n }\n return { digit: cell.input, textColor, isGiven: false };\n }\n\n // Given (original)\n if (cell.given !== null) {\n return { digit: cell.given, textColor: ThemeColor.LABEL, isGiven: true };\n }\n\n return { digit: null, textColor: null, isGiven: false };\n}\n\n// =============================================================================\n// Pencilmark Logic (matches CellPresenter.present pencilmark section)\n// =============================================================================\n\n/**\n * Get pencilmark display states\n * Matches CellPresenter.present pencilmark logic\n */\nfunction getPencilmarks(\n cell: SudokuCell,\n hintCell: HintCell | null\n): PencilmarkDisplayState[] {\n const cellPencilmarks = cell.pencilmarks\n ? new Set(cell.pencilmarks)\n : new Set<number>();\n const adding = hintCell?.actions?.add ? new Set(hintCell.actions.add) : null;\n const removing = hintCell?.actions?.remove\n ? new Set(hintCell.actions.remove)\n : null;\n const highlighting = hintCell?.actions?.highlight\n ? new Set(hintCell.actions.highlight)\n : null;\n\n const result: PencilmarkDisplayState[] = [];\n\n for (let pencilmark = 1; pencilmark <= 9; pencilmark++) {\n let color: ThemeColor | null = null;\n\n if (highlighting?.has(pencilmark)) {\n color = ThemeColor.SELECTED;\n } else if (adding?.has(pencilmark)) {\n color = ThemeColor.SUCCESS;\n } else if (removing?.has(pencilmark)) {\n color = ThemeColor.ERROR;\n } else if (cellPencilmarks.has(pencilmark)) {\n color = ThemeColor.LABEL_SECONDARY;\n }\n\n if (color !== null) {\n result.push({ digit: pencilmark, color });\n }\n }\n\n return result;\n}\n\n// =============================================================================\n// Main Presenter Function\n// =============================================================================\n\nexport interface PresentBoardOptions {\n /** The 81 cells of the board */\n cells: SudokuCell[];\n /** Currently selected cell index (0-80) or null */\n selectedIndex: number | null;\n /** Whether to show errors (compare input vs solution) */\n showErrors: boolean;\n /** Current hint step (if any) */\n hintStep?: HintStep | null;\n /** Set of cell indices with the selected digit (for digit highlighting) */\n selectedDigitCells?: Set<number> | null;\n}\n\n/**\n * Generate display states for all cells\n * This is the main presenter function that matches the Kotlin renderable behavior\n */\nexport function presentBoard(options: PresentBoardOptions): CellDisplayState[] {\n const {\n cells,\n selectedIndex,\n showErrors,\n hintStep = null,\n selectedDigitCells = null,\n } = options;\n\n // Detect conflict hint (has links with type 'conflict')\n const isConflictHint =\n hintStep?.links?.some(link => link.type === 'conflict') ?? false;\n\n // Calculate hint cell map\n const cellHints = calculateCellHints(hintStep);\n\n // Get selected cell properties (if any)\n const _selectedCell = selectedIndex !== null ? cells[selectedIndex] : null;\n void _selectedCell; // Reserved for future use\n const selectedBlockIndex =\n selectedIndex !== null ? blockOf(selectedIndex) : null;\n const selectedRowIndex = selectedIndex !== null ? rowOf(selectedIndex) : null;\n const selectedColumnIndex =\n selectedIndex !== null ? columnOf(selectedIndex) : null;\n\n // Generate display state for each cell\n return cells.map((cell, index) => {\n const cellBlockIndex = blockOf(index);\n const cellRowIndex = rowOf(index);\n const cellColumnIndex = columnOf(index);\n const cellHasValue = cell.input !== null || cell.given !== null;\n\n // Get hint for this cell\n const hintCell = cellHints?.get(index) ?? null;\n\n // Get base colors\n const { backgroundColor, borderColor } = getCellColors(\n index,\n cellBlockIndex,\n cellRowIndex,\n cellColumnIndex,\n cellHasValue,\n selectedIndex,\n selectedBlockIndex,\n selectedRowIndex,\n selectedColumnIndex,\n selectedDigitCells\n );\n\n // Apply hint colors if present\n let finalBgColor = backgroundColor;\n let finalBorderColor = borderColor;\n\n if (hintCell) {\n if (hintCell.fill) {\n // Hint uses fill: override background\n const hintBgColor = sudokuColorToTheme(hintCell.color);\n if (hintBgColor !== null) {\n finalBgColor = hintBgColor;\n }\n } else {\n // Hint uses border\n const hintBdrColor = sudokuColorToTheme(\n hintCell.color ?? SudokuColor.BLUE\n );\n if (hintBdrColor !== null) {\n finalBorderColor = hintBdrColor;\n }\n }\n }\n\n // Get text info\n const { digit, textColor, isGiven } = getCellText(\n cell,\n showErrors,\n hintCell\n );\n\n // In conflict hints, override textColor for orange source cells\n let finalTextColor = textColor;\n if (\n isConflictHint &&\n hintCell &&\n !hintCell.fill &&\n hintCell.color === SudokuColor.ORANGE\n ) {\n finalTextColor = ThemeColor.WARNING;\n }\n\n // Get pencilmarks if no digit\n const pencilmarks = digit === null ? getPencilmarks(cell, hintCell) : [];\n\n return {\n index,\n backgroundColor: finalBgColor,\n borderColor: finalBorderColor,\n textColor: finalTextColor,\n digit,\n isGiven,\n pencilmarks,\n };\n });\n}\n\n// =============================================================================\n// Utility: Get cells with a specific digit\n// =============================================================================\n\n/**\n * Get indices of cells that contain a specific digit (given or input)\n * Used for digit highlighting mode\n */\nexport function getCellsWithDigit(\n cells: SudokuCell[],\n digit: number\n): Set<number> {\n const result = new Set<number>();\n cells.forEach((cell, index) => {\n if (cell.given === digit || cell.input === digit) {\n result.add(index);\n }\n });\n return result;\n}\n\n/**\n * Get the selected digit for digit highlighting mode\n * Matches Kotlin SudokuInteractor.selectedDigit logic:\n * - If selected cell has a given value: return given\n * - If selected cell has a CORRECT input (input === solution): return input\n * - Otherwise: return null (no digit highlighting)\n */\nexport function getSelectedDigit(\n cells: SudokuCell[],\n selectedIndex: number | null\n): number | null {\n if (selectedIndex === null) return null;\n\n const cell = cells[selectedIndex];\n if (!cell) return null;\n\n // Given value takes priority\n if (cell.given !== null) {\n return cell.given;\n }\n\n // Only correct input triggers digit highlighting\n if (cell.input !== null && cell.input === cell.solution) {\n return cell.input;\n }\n\n return null;\n}\n\n/**\n * Compute selectedDigitCells based on selected cell\n * Returns Set of cell indices that have the same digit as selected cell\n * Returns null if selected cell doesn't have a highlightable digit\n */\nexport function computeSelectedDigitCells(\n cells: SudokuCell[],\n selectedIndex: number | null\n): Set<number> | null {\n const selectedDigit = getSelectedDigit(cells, selectedIndex);\n if (selectedDigit === null) return null;\n return getCellsWithDigit(cells, selectedDigit);\n}\n\n// =============================================================================\n// Link and Group Conversion\n// =============================================================================\n\n/**\n * Convert a solver link to display link format\n * Converts row/col coordinates to flat cell index\n */\nexport function convertSolverLink(link: {\n fromRow: number;\n fromCol: number;\n toRow: number;\n toCol: number;\n type: 'strong' | 'weak' | 'conflict';\n digit: number;\n}): DisplayLink {\n return {\n fromIndex: link.fromRow * 9 + link.fromCol,\n toIndex: link.toRow * 9 + link.toCol,\n type: link.type,\n digit: link.digit,\n };\n}\n\n/**\n * Convert a solver cell group to display format\n * Converts row/col pairs to flat cell indices and SolverColor to ThemeColor\n */\nexport function convertSolverCellGroup(group: {\n name: string;\n color: string;\n cells: number[][];\n}): DisplayCellGroup {\n // Map solver color string to SudokuColor enum\n const sudokuColor = Object.values(SudokuColor).find(\n c => c.toLowerCase() === group.color.toLowerCase()\n ) as SudokuColor | undefined;\n\n return {\n name: group.name,\n color:\n sudokuColorToTheme(sudokuColor ?? SudokuColor.BLUE) ??\n ThemeColor.SELECTED,\n cellIndices: group.cells.map(cell => (cell[0] ?? 0) * 9 + (cell[1] ?? 0)),\n };\n}\n\n/**\n * Export sudokuColorToTheme for external use\n */\nexport { sudokuColorToTheme };\n"]}
|
|
1
|
+
{"version":3,"file":"sudokuPresenter.js","sourceRoot":"","sources":["../../src/utils/sudokuPresenter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Sudoku presenter - port of renderable/SudokuPresenter.kt\n *\n * Handles all display logic for the Sudoku board including:\n * - Cell background colors based on selection state\n * - Cell border colors for selected cells\n * - Text colors (given, input, hint actions)\n * - Hint visualization (areas, cells)\n * - Pencilmark coloring\n */\n\nimport type {\n SolverHintArea,\n SolverHintCell,\n SolverHintStep,\n} from '@sudobility/sudojo_types';\nimport type { SudokuCell } from '../types/sudoku';\nimport { blockOf, columnOf, rowOf } from '../types/sudoku';\nimport {\n type CellDisplayState,\n type DisplayCellGroup,\n type DisplayLink,\n type HintArea,\n type HintCell,\n type HintStep,\n type PencilmarkDisplayState,\n SudokuColor,\n ThemeColor,\n UIColorDark,\n UIColorLight,\n type UIColorPalette,\n} from '../types/display';\n\n// =============================================================================\n// Block Definition (matches PlayBoard.blocks)\n// =============================================================================\n\n/**\n * Block class - matches Kotlin Block class\n */\nclass Block {\n readonly index: number;\n readonly cellIndices: number[];\n\n constructor(index: number) {\n this.index = index;\n const blockRow = Math.floor(index / 3);\n const blockCol = index % 3;\n this.cellIndices = [];\n for (let r = 0; r < 3; r++) {\n for (let c = 0; c < 3; c++) {\n const row = blockRow * 3 + r;\n const col = blockCol * 3 + c;\n this.cellIndices.push(row * 9 + col);\n }\n }\n }\n}\n\n/**\n * Row class - matches Kotlin Row class\n */\nclass Row {\n readonly index: number;\n readonly cellIndices: number[];\n\n constructor(index: number) {\n this.index = index;\n this.cellIndices = Array.from({ length: 9 }, (_, col) => index * 9 + col);\n }\n}\n\n/**\n * Column class - matches Kotlin Column class\n */\nclass Column {\n readonly index: number;\n readonly cellIndices: number[];\n\n constructor(index: number) {\n this.index = index;\n this.cellIndices = Array.from({ length: 9 }, (_, row) => row * 9 + index);\n }\n}\n\n// =============================================================================\n// Color Conversion (matches CellPresenter.colorOf)\n// =============================================================================\n\n/**\n * Convert SudokuColor to ThemeColor - matches CellPresenter.colorOf\n */\nfunction sudokuColorToTheme(\n sudokuColor: SudokuColor | null\n): ThemeColor | null {\n switch (sudokuColor) {\n case SudokuColor.BLUE:\n return ThemeColor.SELECTED;\n case SudokuColor.GREEN:\n return ThemeColor.SUCCESS;\n case SudokuColor.YELLOW:\n return ThemeColor.WARNING_SECONDARY;\n case SudokuColor.ORANGE:\n return ThemeColor.WARNING;\n case SudokuColor.RED:\n return ThemeColor.ERROR;\n case SudokuColor.WHITE:\n return ThemeColor.BACKGROUND;\n case SudokuColor.LIGHT_GRAY:\n return ThemeColor.BACKGROUND_SECONDARY;\n case SudokuColor.BLACK:\n return ThemeColor.LABEL;\n case SudokuColor.GRAY:\n return ThemeColor.LABEL_SECONDARY;\n case SudokuColor.CLEAR:\n return ThemeColor.CLEAR;\n default:\n return null;\n }\n}\n\n/**\n * Convert ThemeColor to actual CSS color - matches RendererColor.color\n */\nexport function themeColorToCSS(\n palette: UIColorPalette,\n themeColor: ThemeColor | null\n): string | null {\n switch (themeColor) {\n case ThemeColor.NONE:\n case ThemeColor.CLEAR:\n return palette.clear;\n case ThemeColor.SELECTED:\n return palette.systemPurple;\n case ThemeColor.SUCCESS:\n return palette.systemBlue;\n case ThemeColor.WARNING_SECONDARY:\n return palette.systemYellow;\n case ThemeColor.WARNING:\n return palette.systemOrange;\n case ThemeColor.ERROR:\n return palette.systemRed;\n case ThemeColor.BACKGROUND:\n return palette.systemBackground;\n case ThemeColor.BACKGROUND_SECONDARY:\n return palette.secondarySystemBackground;\n case ThemeColor.LABEL:\n return palette.label;\n case ThemeColor.LABEL_SECONDARY:\n return palette.secondaryLabel;\n case ThemeColor.DISABLED:\n return palette.systemGray;\n default:\n return null;\n }\n}\n\n/**\n * Get the color palette for the current theme\n */\nexport function getColorPalette(isDarkMode: boolean): UIColorPalette {\n return isDarkMode ? UIColorDark : UIColorLight;\n}\n\n// =============================================================================\n// Hint Processing (matches BoardPresenter.calculateCellHints)\n// =============================================================================\n\n/**\n * Process hint areas and cells into a map of cell hints\n * Matches BoardPresenter.calculateCellHints\n */\nexport function calculateCellHints(\n hintStep: HintStep | null\n): Map<number, HintCell> | null {\n if (!hintStep) return null;\n\n const hintsCell = new Map<number, HintCell>();\n\n // Process areas first\n hintStep.areas?.forEach(area => {\n let cellIndices: number[];\n\n switch (area.type) {\n case 'block':\n cellIndices = new Block(area.index).cellIndices;\n break;\n case 'row':\n cellIndices = new Row(area.index).cellIndices;\n break;\n case 'column':\n cellIndices = new Column(area.index).cellIndices;\n break;\n }\n\n cellIndices.forEach(index => {\n hintsCell.set(index, {\n index,\n color: area.color,\n fill: true, // Areas always use fill\n });\n });\n });\n\n // Process cells (overrides areas)\n hintStep.cells?.forEach(cell => {\n hintsCell.set(cell.index, cell);\n });\n\n return hintsCell;\n}\n\n// =============================================================================\n// Cell Color Logic (matches BlockPresenter.color)\n// =============================================================================\n\ninterface CellColorResult {\n backgroundColor: ThemeColor;\n borderColor: ThemeColor | null;\n}\n\n/**\n * Determine background and border colors for a cell\n * Matches BlockPresenter.color exactly\n */\nfunction getCellColors(\n cellIndex: number,\n cellBlockIndex: number,\n cellRowIndex: number,\n cellColumnIndex: number,\n cellHasValue: boolean,\n selectedIndex: number | null,\n selectedBlockIndex: number | null,\n selectedRowIndex: number | null,\n selectedColumnIndex: number | null,\n selectedDigitCells: Set<number> | null\n): CellColorResult {\n // Default background based on block (checkerboard pattern)\n const defaultBackgroundColor =\n cellBlockIndex % 2 === 0\n ? ThemeColor.BACKGROUND_SECONDARY\n : ThemeColor.BACKGROUND;\n\n if (selectedIndex === null) {\n return { backgroundColor: defaultBackgroundColor, borderColor: null };\n }\n\n if (selectedDigitCells !== null) {\n // Digit highlighting mode\n if (selectedDigitCells.has(cellIndex)) {\n // Highlighted cells (have the same digit)\n return { backgroundColor: ThemeColor.SELECTED, borderColor: null };\n }\n\n // Check if this cell is \"impossible\" (in same row/col/block as highlighted cells)\n let isImpossible = cellHasValue;\n if (!isImpossible) {\n for (const highlightedIndex of selectedDigitCells) {\n const hRow = rowOf(highlightedIndex);\n const hCol = columnOf(highlightedIndex);\n const hBlock = blockOf(highlightedIndex);\n if (\n cellRowIndex === hRow ||\n cellColumnIndex === hCol ||\n cellBlockIndex === hBlock\n ) {\n isImpossible = true;\n break;\n }\n }\n }\n\n if (isImpossible) {\n return { backgroundColor: ThemeColor.LABEL_SECONDARY, borderColor: null };\n }\n\n // Possible cells\n return { backgroundColor: defaultBackgroundColor, borderColor: null };\n }\n\n // Normal selection mode\n if (selectedIndex === cellIndex) {\n // Selected cell: default background + SELECTED border\n return {\n backgroundColor: defaultBackgroundColor,\n borderColor: ThemeColor.SELECTED,\n };\n }\n\n if (\n selectedBlockIndex === cellBlockIndex ||\n selectedRowIndex === cellRowIndex ||\n selectedColumnIndex === cellColumnIndex\n ) {\n // Same row, column or block: SELECTED background\n return { backgroundColor: ThemeColor.SELECTED, borderColor: null };\n }\n\n // Other cells\n return { backgroundColor: defaultBackgroundColor, borderColor: null };\n}\n\n// =============================================================================\n// Cell Text Logic (matches CellPresenter.present)\n// =============================================================================\n\ninterface CellTextResult {\n digit: number | null;\n textColor: ThemeColor | null;\n isGiven: boolean;\n}\n\n/**\n * Determine what digit and color to display in a cell\n * Matches CellPresenter.present logic\n */\nfunction getCellText(\n cell: SudokuCell,\n showErrors: boolean,\n hintCell: HintCell | null\n): CellTextResult {\n // Hint select action takes priority\n if (hintCell?.actions?.select != null) {\n return {\n digit: hintCell.actions.select,\n textColor: ThemeColor.SELECTED,\n isGiven: false,\n };\n }\n\n // Hint unselect action (wrong digit to remove)\n if (hintCell?.actions?.unselect != null) {\n return {\n digit: hintCell.actions.unselect,\n textColor: ThemeColor.ERROR,\n isGiven: false,\n };\n }\n\n // User input\n if (cell.input !== null) {\n let textColor: ThemeColor;\n if (showErrors) {\n textColor =\n cell.input === cell.solution ? ThemeColor.SUCCESS : ThemeColor.ERROR;\n } else {\n textColor = ThemeColor.LABEL_SECONDARY;\n }\n return { digit: cell.input, textColor, isGiven: false };\n }\n\n // Given (original)\n if (cell.given !== null) {\n return { digit: cell.given, textColor: ThemeColor.LABEL, isGiven: true };\n }\n\n return { digit: null, textColor: null, isGiven: false };\n}\n\n// =============================================================================\n// Pencilmark Logic (matches CellPresenter.present pencilmark section)\n// =============================================================================\n\n/**\n * Get pencilmark display states\n * Matches CellPresenter.present pencilmark logic\n */\nfunction getPencilmarks(\n cell: SudokuCell,\n hintCell: HintCell | null\n): PencilmarkDisplayState[] {\n const cellPencilmarks = cell.pencilmarks\n ? new Set(cell.pencilmarks)\n : new Set<number>();\n const adding = hintCell?.actions?.add ? new Set(hintCell.actions.add) : null;\n const removing = hintCell?.actions?.remove\n ? new Set(hintCell.actions.remove)\n : null;\n const highlighting = hintCell?.actions?.highlight\n ? new Set(hintCell.actions.highlight)\n : null;\n\n const result: PencilmarkDisplayState[] = [];\n\n for (let pencilmark = 1; pencilmark <= 9; pencilmark++) {\n let color: ThemeColor | null = null;\n\n if (highlighting?.has(pencilmark)) {\n color = ThemeColor.SELECTED;\n } else if (adding?.has(pencilmark)) {\n color = ThemeColor.SUCCESS;\n } else if (removing?.has(pencilmark)) {\n color = ThemeColor.ERROR;\n } else if (cellPencilmarks.has(pencilmark)) {\n color = ThemeColor.LABEL_SECONDARY;\n }\n\n if (color !== null) {\n result.push({ digit: pencilmark, color });\n }\n }\n\n return result;\n}\n\n// =============================================================================\n// Main Presenter Function\n// =============================================================================\n\nexport interface PresentBoardOptions {\n /** The 81 cells of the board */\n cells: SudokuCell[];\n /** Currently selected cell index (0-80) or null */\n selectedIndex: number | null;\n /** Whether to show errors (compare input vs solution) */\n showErrors: boolean;\n /** Current hint step (if any) */\n hintStep?: HintStep | null;\n /** Set of cell indices with the selected digit (for digit highlighting) */\n selectedDigitCells?: Set<number> | null;\n}\n\n/**\n * Generate display states for all cells\n * This is the main presenter function that matches the Kotlin renderable behavior\n */\nexport function presentBoard(options: PresentBoardOptions): CellDisplayState[] {\n const {\n cells,\n selectedIndex,\n showErrors,\n hintStep = null,\n selectedDigitCells = null,\n } = options;\n\n // Detect conflict hint (has links with type 'conflict')\n const isConflictHint = isConflictHintStep(hintStep);\n\n // Calculate hint cell map\n const cellHints = calculateCellHints(hintStep);\n\n // Get selected cell properties (if any)\n const _selectedCell = selectedIndex !== null ? cells[selectedIndex] : null;\n void _selectedCell; // Reserved for future use\n const selectedBlockIndex =\n selectedIndex !== null ? blockOf(selectedIndex) : null;\n const selectedRowIndex = selectedIndex !== null ? rowOf(selectedIndex) : null;\n const selectedColumnIndex =\n selectedIndex !== null ? columnOf(selectedIndex) : null;\n\n // Generate display state for each cell\n return cells.map((cell, index) => {\n const cellBlockIndex = blockOf(index);\n const cellRowIndex = rowOf(index);\n const cellColumnIndex = columnOf(index);\n const cellHasValue = cell.input !== null || cell.given !== null;\n\n // Get hint for this cell\n const hintCell = cellHints?.get(index) ?? null;\n\n // Get base colors\n const { backgroundColor, borderColor } = getCellColors(\n index,\n cellBlockIndex,\n cellRowIndex,\n cellColumnIndex,\n cellHasValue,\n selectedIndex,\n selectedBlockIndex,\n selectedRowIndex,\n selectedColumnIndex,\n selectedDigitCells\n );\n\n // Apply hint colors if present\n let finalBgColor = backgroundColor;\n let finalBorderColor = borderColor;\n\n if (hintCell) {\n if (hintCell.fill) {\n // Hint uses fill: override background\n const hintBgColor = sudokuColorToTheme(hintCell.color);\n if (hintBgColor !== null) {\n finalBgColor = hintBgColor;\n }\n } else {\n // Hint uses border\n const hintBdrColor = sudokuColorToTheme(\n hintCell.color ?? SudokuColor.BLUE\n );\n if (hintBdrColor !== null) {\n finalBorderColor = hintBdrColor;\n }\n }\n }\n\n // Get text info\n const { digit, textColor, isGiven } = getCellText(\n cell,\n showErrors,\n hintCell\n );\n\n // In conflict hints, override textColor for orange source cells\n let finalTextColor = textColor;\n if (\n isConflictHint &&\n hintCell &&\n !hintCell.fill &&\n hintCell.color === SudokuColor.ORANGE\n ) {\n finalTextColor = ThemeColor.WARNING;\n }\n\n // Get pencilmarks if no digit\n const pencilmarks = digit === null ? getPencilmarks(cell, hintCell) : [];\n\n return {\n index,\n backgroundColor: finalBgColor,\n borderColor: finalBorderColor,\n textColor: finalTextColor,\n digit,\n isGiven,\n pencilmarks,\n };\n });\n}\n\n// =============================================================================\n// Utility: Get cells with a specific digit\n// =============================================================================\n\n/**\n * Get indices of cells that contain a specific digit (given or input)\n * Used for digit highlighting mode\n */\nexport function getCellsWithDigit(\n cells: SudokuCell[],\n digit: number\n): Set<number> {\n const result = new Set<number>();\n cells.forEach((cell, index) => {\n if (cell.given === digit || cell.input === digit) {\n result.add(index);\n }\n });\n return result;\n}\n\n/**\n * Get the selected digit for digit highlighting mode\n * Matches Kotlin SudokuInteractor.selectedDigit logic:\n * - If selected cell has a given value: return given\n * - If selected cell has a CORRECT input (input === solution): return input\n * - Otherwise: return null (no digit highlighting)\n */\nexport function getSelectedDigit(\n cells: SudokuCell[],\n selectedIndex: number | null\n): number | null {\n if (selectedIndex === null) return null;\n\n const cell = cells[selectedIndex];\n if (!cell) return null;\n\n // Given value takes priority\n if (cell.given !== null) {\n return cell.given;\n }\n\n // Only correct input triggers digit highlighting\n if (cell.input !== null && cell.input === cell.solution) {\n return cell.input;\n }\n\n return null;\n}\n\n/**\n * Compute selectedDigitCells based on selected cell\n * Returns Set of cell indices that have the same digit as selected cell\n * Returns null if selected cell doesn't have a highlightable digit\n */\nexport function computeSelectedDigitCells(\n cells: SudokuCell[],\n selectedIndex: number | null\n): Set<number> | null {\n const selectedDigit = getSelectedDigit(cells, selectedIndex);\n if (selectedDigit === null) return null;\n return getCellsWithDigit(cells, selectedDigit);\n}\n\n// =============================================================================\n// Link and Group Conversion\n// =============================================================================\n\n/**\n * Convert a solver link to display link format\n * Converts row/col coordinates to flat cell index\n */\nexport function convertSolverLink(link: {\n fromRow: number;\n fromCol: number;\n toRow: number;\n toCol: number;\n type: 'strong' | 'weak' | 'conflict';\n digit: number;\n}): DisplayLink {\n return {\n fromIndex: link.fromRow * 9 + link.fromCol,\n toIndex: link.toRow * 9 + link.toCol,\n type: link.type,\n digit: link.digit,\n };\n}\n\n/**\n * Convert a solver cell group to display format\n * Converts row/col pairs to flat cell indices and SolverColor to ThemeColor\n */\nexport function convertSolverCellGroup(group: {\n name: string;\n color: string;\n cells: number[][];\n}): DisplayCellGroup {\n // Map solver color string to SudokuColor enum\n const sudokuColor = Object.values(SudokuColor).find(\n c => c.toLowerCase() === group.color.toLowerCase()\n ) as SudokuColor | undefined;\n\n return {\n name: group.name,\n color:\n sudokuColorToTheme(sudokuColor ?? SudokuColor.BLUE) ??\n ThemeColor.SELECTED,\n cellIndices: group.cells.map(cell => (cell[0] ?? 0) * 9 + (cell[1] ?? 0)),\n };\n}\n\n// =============================================================================\n// Solver Hint Step Conversion\n// =============================================================================\n\n/**\n * Map a solver color string to a SudokuColor, case-insensitively.\n *\n * Only the five hint colors the solver uses for highlighting are mapped\n * (`blue`, `green`, `yellow`, `orange`, `red`); anything else, including\n * `none`, `clear`, `gray`, `white` and `black`, returns null.\n *\n * This is sudojo_app_rn's validated mapping (`hintConversion.ts`). sudojo_ui's\n * canvas instead cast the raw string, so `gray` became `SudokuColor.GRAY` there\n * (a gray border on unfilled cells). With null, presentBoard falls back to its\n * defaults: no fill override, and a blue border for an unfilled cell.\n */\nexport function solverColorToSudokuColor(\n color: string | null | undefined\n): SudokuColor | null {\n if (!color) return null;\n switch (color.toLowerCase()) {\n case 'blue':\n return SudokuColor.BLUE;\n case 'green':\n return SudokuColor.GREEN;\n case 'yellow':\n return SudokuColor.YELLOW;\n case 'orange':\n return SudokuColor.ORANGE;\n case 'red':\n return SudokuColor.RED;\n default:\n return null;\n }\n}\n\n/**\n * Parse a solver digit string (e.g. `\"125\"`) into digits, dropping anything\n * that is not 1-9. Used for the `add` / `remove` / `highlight` cell actions.\n */\nexport function parseHintDigits(value: string | null | undefined): number[] {\n if (!value) return [];\n return value\n .split('')\n .map(d => parseInt(d, 10))\n .filter(n => !isNaN(n) && n !== 0);\n}\n\n/** Parse a single-digit action (`select` / `unselect`); 0 or junk → null. */\nfunction parseHintDigit(value: string | null | undefined): number | null {\n if (!value) return null;\n return parseInt(value, 10) || null;\n}\n\nfunction convertSolverHintArea(area: SolverHintArea): HintArea {\n return {\n type: area.type as HintArea['type'],\n color: solverColorToSudokuColor(area.color),\n index: area.index,\n };\n}\n\nfunction convertSolverHintCell(cell: SolverHintCell): HintCell {\n const result: HintCell = {\n index: cell.row * 9 + cell.column,\n color: solverColorToSudokuColor(cell.color),\n fill: cell.fill,\n };\n if (cell.actions) {\n // Matches Kotlin: select = if (select != 0) select else null\n result.actions = {\n select: parseHintDigit(cell.actions.select),\n unselect: parseHintDigit(cell.actions.unselect),\n add: cell.actions.add ? parseHintDigits(cell.actions.add) : null,\n remove: cell.actions.remove ? parseHintDigits(cell.actions.remove) : null,\n highlight: cell.actions.highlight\n ? parseHintDigits(cell.actions.highlight)\n : null,\n };\n }\n return result;\n}\n\n/**\n * Convert a solver hint step (string actions, row/column cells) into the\n * display format presentBoard takes (flat indices, digit arrays, SudokuColor).\n *\n * - Colors go through {@link solverColorToSudokuColor}.\n * - Links and groups are converted with convertSolverLink /\n * convertSolverCellGroup; empty lists become null.\n * - `areas` / `cells` may be null on the wire (the autopencil hint); they\n * become null here too.\n *\n * Shared by the web (sudojo_ui) and RN canvases.\n */\nexport function convertSolverHintStep(\n step: SolverHintStep | null | undefined\n): HintStep | null {\n if (!step) return null;\n return {\n title: step.title,\n text: step.text,\n areas: step.areas?.map(convertSolverHintArea) ?? null,\n cells: step.cells?.map(convertSolverHintCell) ?? null,\n links: step.links?.length ? step.links.map(convertSolverLink) : null,\n groups: step.groups?.length\n ? step.groups.map(convertSolverCellGroup)\n : null,\n digit: step.digit ?? null,\n };\n}\n\n/**\n * Whether a hint step is a conflict hint (any link of type `'conflict'`).\n * Accepts either a display step or a raw solver step. presentBoard uses it to\n * color the orange source cells' digits as warnings.\n */\nexport function isConflictHintStep(\n step: { links?: ReadonlyArray<{ type: string }> | null } | null | undefined\n): boolean {\n return step?.links?.some(link => link.type === 'conflict') ?? false;\n}\n\n/**\n * Export sudokuColorToTheme for external use\n */\nexport { sudokuColorToTheme };\n"]}
|
|
@@ -49,6 +49,14 @@ export declare function cellsToPuzzleString(cells: SudokuCell[]): string;
|
|
|
49
49
|
* Convert cells to current state string (given + input)
|
|
50
50
|
*/
|
|
51
51
|
export declare function cellsToStateString(cells: SudokuCell[]): string;
|
|
52
|
+
/**
|
|
53
|
+
* Convert cells to solution string ('0' where a cell has no solution)
|
|
54
|
+
*/
|
|
55
|
+
export declare function cellsToSolutionString(cells: SudokuCell[]): string;
|
|
56
|
+
/**
|
|
57
|
+
* Count the givens (clues) on a board.
|
|
58
|
+
*/
|
|
59
|
+
export declare function countClues(cells: SudokuCell[]): number;
|
|
52
60
|
/**
|
|
53
61
|
* Convert cells to input-only string
|
|
54
62
|
*/
|
|
@@ -1 +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,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1D;AA2BD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,iBAsBvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,iBAI1B,CAAC;AAiCF,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,CAwChB;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"}
|
|
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,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1D;AA2BD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,iBAsBvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,iBAI1B,CAAC;AAiCF,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,CAwChB;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,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAEjE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAEtD;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"}
|
|
@@ -209,6 +209,18 @@ export function cellsToStateString(cells) {
|
|
|
209
209
|
.map(cell => (cell.given ?? cell.input)?.toString() ?? '0')
|
|
210
210
|
.join('');
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Convert cells to solution string ('0' where a cell has no solution)
|
|
214
|
+
*/
|
|
215
|
+
export function cellsToSolutionString(cells) {
|
|
216
|
+
return cells.map(cell => cell.solution?.toString() ?? '0').join('');
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Count the givens (clues) on a board.
|
|
220
|
+
*/
|
|
221
|
+
export function countClues(cells) {
|
|
222
|
+
return cells.filter(cell => cell.given !== null).length;
|
|
223
|
+
}
|
|
212
224
|
/**
|
|
213
225
|
* Convert cells to input-only string
|
|
214
226
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sudokuScrambler.js","sourceRoot":"","sources":["../../src/utils/sudokuScrambler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Sudoku board scrambler - ported from renderable/utils/Scrambler.kt\n *\n * Generates permutations for rows, columns, and digits to create unique puzzles\n * while preserving Sudoku validity.\n */\n\nimport { columnOf, rowOf, type SudokuCell } from '../types/sudoku';\n\n// =============================================================================\n// Scrambler Protocol\n// =============================================================================\n\nexport interface ScramblerProtocol {\n scramble9(symmetrical: boolean, seed?: number): number[];\n}\n\n// =============================================================================\n// Random Utilities\n// =============================================================================\n\n/**\n * Simple seeded random number generator\n */\nclass SeededRandom {\n private seed: number;\n\n constructor(seed: number) {\n this.seed = seed;\n }\n\n nextInt(): number {\n // Linear congruential generator\n this.seed = (this.seed * 1664525 + 1013904223) >>> 0;\n return this.seed;\n }\n}\n\n// =============================================================================\n// Main Scrambler\n// =============================================================================\n\n/**\n * Scrambler implementation that matches the Kotlin version\n */\nexport const Scrambler: ScramblerProtocol = {\n scramble9(symmetrical: boolean, seed?: number): number[] {\n const random = new SeededRandom(seed ?? Math.floor(Date.now() / 1000));\n\n const block0 = scramble3(symmetrical, random);\n const block1 = scramble3(symmetrical, random);\n const block2 = symmetrical ? block0 : scramble3(symmetrical, random);\n const blocks: number[][] = [block0, block1, block2];\n\n const board = scramble3(symmetrical, random);\n const result: number[] = [];\n\n for (let i = 0; i < 3; i++) {\n const target = board[i] ?? 0;\n const block = blocks[i] ?? [0, 1, 2];\n for (let j = 0; j < 3; j++) {\n result.push(target * 3 + (block[j] ?? 0));\n }\n }\n\n return result;\n },\n};\n\n/**\n * Non-scrambler that returns identity permutation\n */\nexport const NonScrambler: ScramblerProtocol = {\n scramble9(_symmetrical: boolean, _seed?: number): number[] {\n return [0, 1, 2, 3, 4, 5, 6, 7, 8];\n },\n};\n\nfunction hashCells(cells: SudokuCell[]): number {\n let hash = 2166136261;\n for (const cell of cells) {\n const given = cell.given ?? 0;\n const solution = cell.solution ?? 0;\n hash ^= (given * 11 + solution) & 0xff;\n hash = Math.imul(hash, 16777619) >>> 0;\n }\n return hash >>> 0;\n}\n\n/**\n * Generates a permutation of [0, 1, 2]\n */\nfunction scramble3(symmetrical: boolean, random: SeededRandom): number[] {\n if (symmetrical) {\n const seed = random.nextInt();\n // 50% chance for identity or reverse\n return seed % 2 === 0 ? [0, 1, 2] : [2, 1, 0];\n } else {\n // Generate 3 random values and sort to get permutation\n const values = [random.nextInt(), random.nextInt(), random.nextInt()];\n const sorted = [...values].sort((a, b) => a - b);\n return sorted.map(v => values.indexOf(v));\n }\n}\n\n// =============================================================================\n// Board Scrambling\n// =============================================================================\n\nexport interface ScrambleResult {\n /** Scrambled cells array */\n cells: SudokuCell[];\n /** Digit mapping from original (1-9) to scrambled (1-9) */\n digitMapping: Map<number, number>;\n /** Reverse digit mapping from scrambled to original */\n reverseDigitMapping: Map<number, number>;\n}\n\n/**\n * Scramble a board using the given scrambler\n *\n * @param scrambler - Scrambler protocol implementation\n * @param cells - Original 81 cells\n * @param symmetrical - Whether to use symmetrical scrambling\n * @returns Scrambled cells and digit mapping\n */\nexport function scrambleBoard(\n scrambler: ScramblerProtocol,\n cells: SudokuCell[],\n symmetrical: boolean = false\n): ScrambleResult {\n const seedBase = hashCells(cells);\n const rows = scrambler.scramble9(symmetrical, seedBase);\n const columns = scrambler.scramble9(symmetrical, seedBase + 1);\n const numbers = scrambler.scramble9(symmetrical, seedBase + 2);\n\n // Create digit mapping (1-based to 1-based)\n const digitMapping = new Map<number, number>();\n const reverseDigitMapping = new Map<number, number>();\n for (let i = 0; i < 9; i++) {\n const original = i + 1;\n const scrambled = (numbers[i] ?? i) + 1;\n digitMapping.set(original, scrambled);\n reverseDigitMapping.set(scrambled, original);\n }\n\n const scrambledCells = Array.from({ length: 81 }, (_, index) => {\n const x = columnOf(index);\n const y = rowOf(index);\n const scrambledY = rows[y] ?? y;\n const scrambledX = columns[x] ?? x;\n const existing = cells[scrambledY * 9 + scrambledX];\n if (!existing) {\n throw new Error(`Missing cell at index ${scrambledY * 9 + scrambledX}`);\n }\n\n return {\n index,\n solution: mapDigit(existing.solution, numbers),\n given: mapDigit(existing.given, numbers),\n input: mapDigit(existing.input, numbers),\n pencilmarks: mapPencilmarks(existing.pencilmarks, numbers),\n };\n });\n\n return {\n cells: scrambledCells,\n digitMapping,\n reverseDigitMapping,\n };\n}\n\n/**\n * Map a digit using the lookup array\n * @param digit - Original digit (1-9) or null\n * @param lookup - Permutation array (0-indexed, returns 0-indexed)\n * @returns Mapped digit (1-9) or null\n */\nfunction mapDigit(digit: number | null, lookup: number[]): number | null {\n if (digit === null || digit === 0) return null;\n // digit is 1-based, lookup is 0-indexed\n const mapped = lookup[digit - 1];\n return mapped !== undefined ? mapped + 1 : digit;\n}\n\n/**\n * Map pencilmarks array using the lookup array\n * Matches Kotlin: map(digits: Array<Int>?, lookup: Array<Int>?): Array<Int>?\n */\nfunction mapPencilmarks(\n pencilmarks: number[] | null,\n lookup: number[]\n): number[] | null {\n if (pencilmarks === null) return null;\n return pencilmarks\n .map(digit => mapDigit(digit, lookup))\n .filter((d): d is number => d !== null)\n .sort((a, b) => a - b);\n}\n\n// =============================================================================\n// Board Parsing\n// =============================================================================\n\n/**\n * Parse a puzzle string (81 chars of 0-9 or .) into cells\n *\n * @param puzzle - 81 character string\n * @param solution - 81 character solution string (optional)\n * @returns Array of 81 cells\n */\nexport function parsePuzzleString(\n puzzle: string,\n solution?: string\n): SudokuCell[] {\n if (puzzle.length !== 81) {\n throw new Error(`Invalid puzzle string length: ${puzzle.length}`);\n }\n\n if (solution && solution.length !== 81) {\n throw new Error(`Invalid solution string length: ${solution.length}`);\n }\n\n return Array.from({ length: 81 }, (_, index) => {\n const givenChar = puzzle[index];\n if (givenChar === undefined) {\n throw new Error(`Missing puzzle character at index ${index}`);\n }\n const givenDigit = parseDigit(givenChar);\n\n const solutionChar = solution?.[index];\n const solutionDigit = solutionChar ? parseDigit(solutionChar) : null;\n\n return {\n index,\n solution: solutionDigit,\n given: givenDigit,\n input: null,\n pencilmarks: null,\n };\n });\n}\n\n/**\n * Parse a character to a digit (1-9) or null\n */\nfunction parseDigit(char: string): number | null {\n if (char === '0' || char === '.') return null;\n const digit = parseInt(char, 10);\n if (digit >= 1 && digit <= 9) return digit;\n return null;\n}\n\n/**\n * Convert cells to puzzle string\n */\nexport function cellsToPuzzleString(cells: SudokuCell[]): string {\n return cells.map(cell => cell.given?.toString() ?? '0').join('');\n}\n\n/**\n * Convert cells to current state string (given + input)\n */\nexport function cellsToStateString(cells: SudokuCell[]): string {\n return cells\n .map(cell => (cell.given ?? cell.input)?.toString() ?? '0')\n .join('');\n}\n\n/**\n * Convert cells to input-only string\n */\nexport function cellsToInputString(cells: SudokuCell[]): string {\n return cells.map(cell => cell.input?.toString() ?? '0').join('');\n}\n\n/**\n * Convert cells to pencilmarks string (comma-separated)\n * Matches Kotlin: pencilmarksParam - returns empty string for null pencilmarks\n */\nexport function cellsToPencilmarksString(cells: SudokuCell[]): string {\n return cells\n .map(cell => {\n if (cell.pencilmarks !== null) {\n return cell.pencilmarks.join('');\n } else {\n return '';\n }\n })\n .join(',');\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sudokuScrambler.js","sourceRoot":"","sources":["../../src/utils/sudokuScrambler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Sudoku board scrambler - ported from renderable/utils/Scrambler.kt\n *\n * Generates permutations for rows, columns, and digits to create unique puzzles\n * while preserving Sudoku validity.\n */\n\nimport { columnOf, rowOf, type SudokuCell } from '../types/sudoku';\n\n// =============================================================================\n// Scrambler Protocol\n// =============================================================================\n\nexport interface ScramblerProtocol {\n scramble9(symmetrical: boolean, seed?: number): number[];\n}\n\n// =============================================================================\n// Random Utilities\n// =============================================================================\n\n/**\n * Simple seeded random number generator\n */\nclass SeededRandom {\n private seed: number;\n\n constructor(seed: number) {\n this.seed = seed;\n }\n\n nextInt(): number {\n // Linear congruential generator\n this.seed = (this.seed * 1664525 + 1013904223) >>> 0;\n return this.seed;\n }\n}\n\n// =============================================================================\n// Main Scrambler\n// =============================================================================\n\n/**\n * Scrambler implementation that matches the Kotlin version\n */\nexport const Scrambler: ScramblerProtocol = {\n scramble9(symmetrical: boolean, seed?: number): number[] {\n const random = new SeededRandom(seed ?? Math.floor(Date.now() / 1000));\n\n const block0 = scramble3(symmetrical, random);\n const block1 = scramble3(symmetrical, random);\n const block2 = symmetrical ? block0 : scramble3(symmetrical, random);\n const blocks: number[][] = [block0, block1, block2];\n\n const board = scramble3(symmetrical, random);\n const result: number[] = [];\n\n for (let i = 0; i < 3; i++) {\n const target = board[i] ?? 0;\n const block = blocks[i] ?? [0, 1, 2];\n for (let j = 0; j < 3; j++) {\n result.push(target * 3 + (block[j] ?? 0));\n }\n }\n\n return result;\n },\n};\n\n/**\n * Non-scrambler that returns identity permutation\n */\nexport const NonScrambler: ScramblerProtocol = {\n scramble9(_symmetrical: boolean, _seed?: number): number[] {\n return [0, 1, 2, 3, 4, 5, 6, 7, 8];\n },\n};\n\nfunction hashCells(cells: SudokuCell[]): number {\n let hash = 2166136261;\n for (const cell of cells) {\n const given = cell.given ?? 0;\n const solution = cell.solution ?? 0;\n hash ^= (given * 11 + solution) & 0xff;\n hash = Math.imul(hash, 16777619) >>> 0;\n }\n return hash >>> 0;\n}\n\n/**\n * Generates a permutation of [0, 1, 2]\n */\nfunction scramble3(symmetrical: boolean, random: SeededRandom): number[] {\n if (symmetrical) {\n const seed = random.nextInt();\n // 50% chance for identity or reverse\n return seed % 2 === 0 ? [0, 1, 2] : [2, 1, 0];\n } else {\n // Generate 3 random values and sort to get permutation\n const values = [random.nextInt(), random.nextInt(), random.nextInt()];\n const sorted = [...values].sort((a, b) => a - b);\n return sorted.map(v => values.indexOf(v));\n }\n}\n\n// =============================================================================\n// Board Scrambling\n// =============================================================================\n\nexport interface ScrambleResult {\n /** Scrambled cells array */\n cells: SudokuCell[];\n /** Digit mapping from original (1-9) to scrambled (1-9) */\n digitMapping: Map<number, number>;\n /** Reverse digit mapping from scrambled to original */\n reverseDigitMapping: Map<number, number>;\n}\n\n/**\n * Scramble a board using the given scrambler\n *\n * @param scrambler - Scrambler protocol implementation\n * @param cells - Original 81 cells\n * @param symmetrical - Whether to use symmetrical scrambling\n * @returns Scrambled cells and digit mapping\n */\nexport function scrambleBoard(\n scrambler: ScramblerProtocol,\n cells: SudokuCell[],\n symmetrical: boolean = false\n): ScrambleResult {\n const seedBase = hashCells(cells);\n const rows = scrambler.scramble9(symmetrical, seedBase);\n const columns = scrambler.scramble9(symmetrical, seedBase + 1);\n const numbers = scrambler.scramble9(symmetrical, seedBase + 2);\n\n // Create digit mapping (1-based to 1-based)\n const digitMapping = new Map<number, number>();\n const reverseDigitMapping = new Map<number, number>();\n for (let i = 0; i < 9; i++) {\n const original = i + 1;\n const scrambled = (numbers[i] ?? i) + 1;\n digitMapping.set(original, scrambled);\n reverseDigitMapping.set(scrambled, original);\n }\n\n const scrambledCells = Array.from({ length: 81 }, (_, index) => {\n const x = columnOf(index);\n const y = rowOf(index);\n const scrambledY = rows[y] ?? y;\n const scrambledX = columns[x] ?? x;\n const existing = cells[scrambledY * 9 + scrambledX];\n if (!existing) {\n throw new Error(`Missing cell at index ${scrambledY * 9 + scrambledX}`);\n }\n\n return {\n index,\n solution: mapDigit(existing.solution, numbers),\n given: mapDigit(existing.given, numbers),\n input: mapDigit(existing.input, numbers),\n pencilmarks: mapPencilmarks(existing.pencilmarks, numbers),\n };\n });\n\n return {\n cells: scrambledCells,\n digitMapping,\n reverseDigitMapping,\n };\n}\n\n/**\n * Map a digit using the lookup array\n * @param digit - Original digit (1-9) or null\n * @param lookup - Permutation array (0-indexed, returns 0-indexed)\n * @returns Mapped digit (1-9) or null\n */\nfunction mapDigit(digit: number | null, lookup: number[]): number | null {\n if (digit === null || digit === 0) return null;\n // digit is 1-based, lookup is 0-indexed\n const mapped = lookup[digit - 1];\n return mapped !== undefined ? mapped + 1 : digit;\n}\n\n/**\n * Map pencilmarks array using the lookup array\n * Matches Kotlin: map(digits: Array<Int>?, lookup: Array<Int>?): Array<Int>?\n */\nfunction mapPencilmarks(\n pencilmarks: number[] | null,\n lookup: number[]\n): number[] | null {\n if (pencilmarks === null) return null;\n return pencilmarks\n .map(digit => mapDigit(digit, lookup))\n .filter((d): d is number => d !== null)\n .sort((a, b) => a - b);\n}\n\n// =============================================================================\n// Board Parsing\n// =============================================================================\n\n/**\n * Parse a puzzle string (81 chars of 0-9 or .) into cells\n *\n * @param puzzle - 81 character string\n * @param solution - 81 character solution string (optional)\n * @returns Array of 81 cells\n */\nexport function parsePuzzleString(\n puzzle: string,\n solution?: string\n): SudokuCell[] {\n if (puzzle.length !== 81) {\n throw new Error(`Invalid puzzle string length: ${puzzle.length}`);\n }\n\n if (solution && solution.length !== 81) {\n throw new Error(`Invalid solution string length: ${solution.length}`);\n }\n\n return Array.from({ length: 81 }, (_, index) => {\n const givenChar = puzzle[index];\n if (givenChar === undefined) {\n throw new Error(`Missing puzzle character at index ${index}`);\n }\n const givenDigit = parseDigit(givenChar);\n\n const solutionChar = solution?.[index];\n const solutionDigit = solutionChar ? parseDigit(solutionChar) : null;\n\n return {\n index,\n solution: solutionDigit,\n given: givenDigit,\n input: null,\n pencilmarks: null,\n };\n });\n}\n\n/**\n * Parse a character to a digit (1-9) or null\n */\nfunction parseDigit(char: string): number | null {\n if (char === '0' || char === '.') return null;\n const digit = parseInt(char, 10);\n if (digit >= 1 && digit <= 9) return digit;\n return null;\n}\n\n/**\n * Convert cells to puzzle string\n */\nexport function cellsToPuzzleString(cells: SudokuCell[]): string {\n return cells.map(cell => cell.given?.toString() ?? '0').join('');\n}\n\n/**\n * Convert cells to current state string (given + input)\n */\nexport function cellsToStateString(cells: SudokuCell[]): string {\n return cells\n .map(cell => (cell.given ?? cell.input)?.toString() ?? '0')\n .join('');\n}\n\n/**\n * Convert cells to solution string ('0' where a cell has no solution)\n */\nexport function cellsToSolutionString(cells: SudokuCell[]): string {\n return cells.map(cell => cell.solution?.toString() ?? '0').join('');\n}\n\n/**\n * Count the givens (clues) on a board.\n */\nexport function countClues(cells: SudokuCell[]): number {\n return cells.filter(cell => cell.given !== null).length;\n}\n\n/**\n * Convert cells to input-only string\n */\nexport function cellsToInputString(cells: SudokuCell[]): string {\n return cells.map(cell => cell.input?.toString() ?? '0').join('');\n}\n\n/**\n * Convert cells to pencilmarks string (comma-separated)\n * Matches Kotlin: pencilmarksParam - returns empty string for null pencilmarks\n */\nexport function cellsToPencilmarksString(cells: SudokuCell[]): string {\n return cells\n .map(cell => {\n if (cell.pencilmarks !== null) {\n return cell.pencilmarks.join('');\n } else {\n return '';\n }\n })\n .join(',');\n}\n"]}
|
|
@@ -1,5 +1,116 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Technique
|
|
2
|
+
* Technique and strategy utilities: path aliases, dependencies, grouping,
|
|
3
|
+
* slug lists, difficulty tiers, and the localized learning content.
|
|
3
4
|
*/
|
|
5
|
+
import type { Strategy, Technique } from '@sudobility/sudojo_types';
|
|
6
|
+
import type { TranslateFunction } from './localizedHint';
|
|
4
7
|
export { getTechniqueIconUrl } from '@sudobility/sudojo_types';
|
|
8
|
+
/**
|
|
9
|
+
* The public slug for a technique path (URL, share link, locale key).
|
|
10
|
+
* `3d-medusa` → `medusa-coloring`; other paths are returned unchanged.
|
|
11
|
+
*/
|
|
12
|
+
export declare function toCanonicalTechniquePath(path: string): string;
|
|
13
|
+
export declare function toCanonicalTechniquePath(path: string | null | undefined): string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* The API's `path` for a technique slug. `medusa-coloring` → `3d-medusa`;
|
|
16
|
+
* other paths are returned unchanged.
|
|
17
|
+
*/
|
|
18
|
+
export declare function toApiTechniquePath(path: string): string;
|
|
19
|
+
export declare function toApiTechniquePath(path: string | null | undefined): string | undefined;
|
|
20
|
+
/** Whether two technique paths name the same technique, aliases included. */
|
|
21
|
+
export declare function isSameTechniquePath(a: string | null | undefined, b: string | null | undefined): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Find a technique by path, accepting either the API path or the canonical
|
|
24
|
+
* slug (`3d-medusa` and `medusa-coloring` both find 3D Medusa).
|
|
25
|
+
*/
|
|
26
|
+
export declare function findTechniqueByPath<T extends Pick<Technique, 'path'>>(techniques: readonly T[], path: string | null | undefined): T | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Parse a technique's comma-separated `dependencies` field into technique ids.
|
|
29
|
+
* Blank and non-numeric entries are dropped.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseTechniqueDependencies(dependencies: string | null | undefined): number[];
|
|
32
|
+
/**
|
|
33
|
+
* The techniques a technique builds on ("Requires"), in the order of `all`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getTechniqueDependencies<T extends Pick<Technique, 'technique'>>(technique: Pick<Technique, 'dependencies'> | null | undefined, all: readonly T[]): T[];
|
|
36
|
+
/**
|
|
37
|
+
* The techniques that list this one as a dependency ("Learn after"),
|
|
38
|
+
* in the order of `all`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getDependentTechniques<T extends Pick<Technique, 'technique' | 'dependencies'>>(technique: Pick<Technique, 'technique'> | null | undefined, all: readonly T[]): T[];
|
|
41
|
+
/** Sort techniques by level (unassigned first), then by technique number. */
|
|
42
|
+
export declare function sortTechniquesByLevel<T extends Pick<Technique, 'technique' | 'level'>>(techniques: readonly T[]): T[];
|
|
43
|
+
/**
|
|
44
|
+
* Group techniques by level, each group sorted by technique number, keys in
|
|
45
|
+
* ascending level order. This is what useTechniques' `techniquesByLevel` returns.
|
|
46
|
+
*
|
|
47
|
+
* @param options.includeUnassigned - Keep techniques with no level under key 0
|
|
48
|
+
* (default true, like useTechniques). The web TechniquesPage drops them: pass false.
|
|
49
|
+
*/
|
|
50
|
+
export declare function groupTechniquesByLevel<T extends Pick<Technique, 'technique' | 'level'>>(techniques: readonly T[], options?: {
|
|
51
|
+
includeUnassigned?: boolean;
|
|
52
|
+
}): Map<number, T[]>;
|
|
53
|
+
/**
|
|
54
|
+
* The techniques in a strategy, sorted by technique number (as the web
|
|
55
|
+
* StrategiesPage and the RN strategy screens list them).
|
|
56
|
+
*/
|
|
57
|
+
export declare function getTechniquesForStrategy<T extends Pick<Technique, 'technique' | 'strategy_id'>>(techniques: readonly T[], strategyId: number | null | undefined): T[];
|
|
58
|
+
/** Find a strategy by its URL stub. */
|
|
59
|
+
export declare function findStrategyByStub<S extends Pick<Strategy, 'stub'>>(strategies: readonly S[], stub: string | null | undefined): S | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Public slugs of all 60 techniques, in footer / sitemap order (canonical
|
|
62
|
+
* slugs, e.g. `medusa-coloring`). Same list as sudojo_app's useFooterConfig
|
|
63
|
+
* and SitemapPage.
|
|
64
|
+
*/
|
|
65
|
+
export declare const TECHNIQUE_SLUGS: readonly ["full-house", "hidden-single", "naked-single", "hidden-pair", "naked-pair", "locked-candidates", "hidden-triple", "naked-triple", "hidden-quad", "naked-quad", "x-wing", "swordfish", "jellyfish", "xy-wing", "finned-x-wing", "squirmbag", "finned-swordfish", "finned-jellyfish", "xyz-wing", "wxyz-wing", "almost-locked-sets", "finned-squirmbag", "als-chain", "skyscraper", "two-string-kite", "empty-rectangle", "simple-coloring", "w-wing", "remote-pairs", "unique-rectangle-type-1", "unique-rectangle-type-2", "bug-1", "sue-de-coq", "als-xz", "x-cycles", "forcing-chains", "medusa-coloring", "crane", "unique-rectangle-type-3", "unique-rectangle-type-4", "unique-rectangle-type-5", "x-chain", "xy-chain", "vwxyz-wing", "uvwxyz-wing", "tuvwxyz-wing", "stuvwxyz-wing", "aic", "forcing-net", "avoidable-rectangle", "sashimi-x-wing", "sashimi-swordfish", "sashimi-jellyfish", "hidden-unique-rectangle", "firework", "death-blossom", "franken-x-wing", "franken-swordfish", "franken-jellyfish", "grouped-x-cycles"];
|
|
66
|
+
/** A public technique slug. */
|
|
67
|
+
export type TechniqueSlug = (typeof TECHNIQUE_SLUGS)[number];
|
|
68
|
+
/** Stubs of the 17 strategies, in difficulty order (sudojo_app's footer list). */
|
|
69
|
+
export declare const STRATEGY_SLUGS: readonly ["naked-subsets", "hidden-subsets", "locked-candidates", "basic-fish", "finned-sashimi-fish", "franken-fish", "single-digit-patterns", "wings", "unique-rectangles", "coloring", "remote-pairs", "almost-locked-sets", "bug-plus-one", "firework", "single-digit-chains", "multi-digit-chains", "forcing"];
|
|
70
|
+
/** A strategy stub. */
|
|
71
|
+
export type StrategySlug = (typeof STRATEGY_SLUGS)[number];
|
|
72
|
+
/** Difficulty tier of a strategy, shown as a badge. */
|
|
73
|
+
export type StrategyDifficultyTier = 'beginner' | 'intermediate' | 'advanced' | 'expert' | 'master';
|
|
74
|
+
/**
|
|
75
|
+
* Tier for a strategy's `difficulty`: ≤3 beginner, ≤5 intermediate,
|
|
76
|
+
* ≤9 advanced, ≤12 expert, otherwise master.
|
|
77
|
+
*/
|
|
78
|
+
export declare function getStrategyDifficultyTier(difficulty: number): StrategyDifficultyTier;
|
|
79
|
+
/** i18n key (default namespace) for a strategy's tier, e.g. `strategy.expert`. */
|
|
80
|
+
export declare function getStrategyDifficultyKey(difficulty: number): string;
|
|
81
|
+
/** One section of a strategy's localized description. */
|
|
82
|
+
export interface StrategySection {
|
|
83
|
+
/** Section title */
|
|
84
|
+
title: string;
|
|
85
|
+
/** Section body ('' when missing) */
|
|
86
|
+
text: string;
|
|
87
|
+
/** Non-blank lines of `text` */
|
|
88
|
+
paragraphs: string[];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Collect the sections of a strategy from the `strategies` namespace:
|
|
92
|
+
* `${stub}.1.title` / `${stub}.1.text`, `${stub}.2.*`, ... up to 20, stopping
|
|
93
|
+
* at the first missing title.
|
|
94
|
+
*
|
|
95
|
+
* @param t - Translate function for the `strategies` namespace
|
|
96
|
+
*/
|
|
97
|
+
export declare function getStrategySections(t: TranslateFunction, stub: string | null | undefined): StrategySection[];
|
|
98
|
+
/** A technique's localized learning content from the `techniques` namespace. */
|
|
99
|
+
export interface TechniqueContent {
|
|
100
|
+
/** `${path}.overview`, or '' */
|
|
101
|
+
overview: string;
|
|
102
|
+
/** `${path}.how-it-works`, one entry per line, leading "1. " numbering removed */
|
|
103
|
+
howItWorks: string[];
|
|
104
|
+
/** `${path}.tips`, split into sentences */
|
|
105
|
+
tips: string[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Read a technique's overview, how-it-works steps and tips.
|
|
109
|
+
*
|
|
110
|
+
* The path is canonicalized (`3d-medusa` → `medusa-coloring`), which is how
|
|
111
|
+
* `techniques.json` is keyed.
|
|
112
|
+
*
|
|
113
|
+
* @param t - Translate function for the `techniques` namespace
|
|
114
|
+
*/
|
|
115
|
+
export declare function parseTechniqueContent(t: TranslateFunction, path: string | null | undefined): TechniqueContent;
|
|
5
116
|
//# sourceMappingURL=technique.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"technique.d.ts","sourceRoot":"","sources":["../../src/utils/technique.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"technique.d.ts","sourceRoot":"","sources":["../../src/utils/technique.ts"],"names":[],"mappings":"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"}
|