@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,117 @@
1
+ /**
2
+ * Subscription utilities
3
+ *
4
+ * These functions work with RevenueCat SDK types to convert to our domain models.
5
+ * The RevenueCat types are passed as generics to avoid direct SDK dependency.
6
+ */
7
+ /**
8
+ * Convert RevenueCat package to our Product interface
9
+ *
10
+ * @param pkg - RevenueCat package
11
+ * @returns Product object
12
+ */
13
+ export function convertPackageToProduct(pkg) {
14
+ const product = pkg.rcBillingProduct;
15
+ const subscriptionOption = product?.defaultSubscriptionOption;
16
+ const result = {
17
+ identifier: pkg.identifier,
18
+ price: product?.currentPrice?.amountMicros
19
+ ? (product.currentPrice.amountMicros / 1000000).toFixed(2)
20
+ : '0',
21
+ priceString: product?.currentPrice?.formattedPrice ?? '$0',
22
+ title: product?.title ?? pkg.identifier,
23
+ description: product?.description ?? '',
24
+ };
25
+ if (product?.identifier !== undefined) {
26
+ result.productId = product.identifier;
27
+ }
28
+ if (product?.normalPeriodDuration !== undefined) {
29
+ result.period = product.normalPeriodDuration;
30
+ }
31
+ if (subscriptionOption?.introPrice?.price?.formattedPrice !== undefined) {
32
+ result.introPrice = subscriptionOption.introPrice.price.formattedPrice;
33
+ }
34
+ if (subscriptionOption?.introPrice?.periodDuration !== undefined) {
35
+ result.introPricePeriod = subscriptionOption.introPrice.periodDuration;
36
+ }
37
+ if (subscriptionOption?.trial?.periodDuration !== undefined) {
38
+ result.freeTrialPeriod = subscriptionOption.trial.periodDuration;
39
+ }
40
+ return result;
41
+ }
42
+ /**
43
+ * Parse RevenueCat CustomerInfo into our Subscription interface
44
+ *
45
+ * @param customerInfo - RevenueCat customer info
46
+ * @returns Subscription object
47
+ */
48
+ export function parseCustomerInfo(customerInfo) {
49
+ const hasActiveEntitlement = Object.keys(customerInfo.entitlements.active).length > 0;
50
+ const activeEntitlement = Object.values(customerInfo.entitlements.active)[0];
51
+ if (hasActiveEntitlement && activeEntitlement) {
52
+ const subscription = { isActive: true };
53
+ if (activeEntitlement.expirationDate != null) {
54
+ subscription.expirationDate = new Date(activeEntitlement.expirationDate);
55
+ }
56
+ if (activeEntitlement.latestPurchaseDate != null) {
57
+ subscription.purchaseDate = new Date(activeEntitlement.latestPurchaseDate);
58
+ }
59
+ if (activeEntitlement.productIdentifier !== undefined) {
60
+ subscription.productIdentifier = activeEntitlement.productIdentifier;
61
+ }
62
+ if (activeEntitlement.willRenew !== undefined) {
63
+ subscription.willRenew = activeEntitlement.willRenew;
64
+ }
65
+ return subscription;
66
+ }
67
+ return { isActive: false };
68
+ }
69
+ /**
70
+ * Get display name for subscription period
71
+ *
72
+ * @param period - ISO 8601 duration string (e.g., "P1M", "P1Y")
73
+ * @returns Human-readable period name
74
+ */
75
+ export function getPeriodDisplayName(period) {
76
+ if (!period)
77
+ return '';
78
+ const periodMap = {
79
+ P1W: 'Weekly',
80
+ P1M: 'Monthly',
81
+ P3M: 'Quarterly',
82
+ P6M: 'Semi-Annual',
83
+ P1Y: 'Annual',
84
+ P12M: 'Annual',
85
+ };
86
+ return periodMap[period] ?? period;
87
+ }
88
+ /**
89
+ * Check if a plan is the "best value" (typically annual plans)
90
+ *
91
+ * @param period - ISO 8601 duration string
92
+ * @returns Whether this is typically the best value option
93
+ */
94
+ export function isBestValuePlan(period) {
95
+ return period === 'P1Y' || period === 'P12M';
96
+ }
97
+ /**
98
+ * Get RevenueCat purchase error message
99
+ *
100
+ * @param errorCode - RevenueCat error code
101
+ * @returns User-friendly error message
102
+ */
103
+ export function getRevenueCatErrorMessage(errorCode) {
104
+ switch (errorCode) {
105
+ case 1:
106
+ return 'Purchase cancelled';
107
+ case 2:
108
+ return 'Store problem occurred. Please try again later.';
109
+ case 3:
110
+ return 'Purchase not allowed on this device';
111
+ case 7:
112
+ return 'Network error. Please check your connection.';
113
+ default:
114
+ return 'Purchase failed. Please try again.';
115
+ }
116
+ }
117
+ //# sourceMappingURL=subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../src/utils/subscription.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Sudoku presenter - port of renderable/SudokuPresenter.kt
3
+ *
4
+ * Handles all display logic for the Sudoku board including:
5
+ * - Cell background colors based on selection state
6
+ * - Cell border colors for selected cells
7
+ * - Text colors (given, input, hint actions)
8
+ * - Hint visualization (areas, cells)
9
+ * - Pencilmark coloring
10
+ */
11
+ import type { SudokuCell } from '../types/sudoku';
12
+ import { type CellDisplayState, type HintCell, type HintStep, ThemeColor, type UIColorPalette } from '../types/display';
13
+ /**
14
+ * Convert ThemeColor to actual CSS color - matches RendererColor.color
15
+ */
16
+ export declare function themeColorToCSS(palette: UIColorPalette, themeColor: ThemeColor | null): string | null;
17
+ /**
18
+ * Get the color palette for the current theme
19
+ */
20
+ export declare function getColorPalette(isDarkMode: boolean): UIColorPalette;
21
+ /**
22
+ * Process hint areas and cells into a map of cell hints
23
+ * Matches BoardPresenter.calculateCellHints
24
+ */
25
+ export declare function calculateCellHints(hintStep: HintStep | null): Map<number, HintCell> | null;
26
+ export interface PresentBoardOptions {
27
+ /** The 81 cells of the board */
28
+ cells: SudokuCell[];
29
+ /** Currently selected cell index (0-80) or null */
30
+ selectedIndex: number | null;
31
+ /** Whether to show errors (compare input vs solution) */
32
+ showErrors: boolean;
33
+ /** Current hint step (if any) */
34
+ hintStep?: HintStep | null;
35
+ /** Set of cell indices with the selected digit (for digit highlighting) */
36
+ selectedDigitCells?: Set<number> | null;
37
+ }
38
+ /**
39
+ * Generate display states for all cells
40
+ * This is the main presenter function that matches the Kotlin renderable behavior
41
+ */
42
+ export declare function presentBoard(options: PresentBoardOptions): CellDisplayState[];
43
+ /**
44
+ * Get indices of cells that contain a specific digit (given or input)
45
+ * Used for digit highlighting mode
46
+ */
47
+ export declare function getCellsWithDigit(cells: SudokuCell[], digit: number): Set<number>;
48
+ /**
49
+ * Get the selected digit for digit highlighting mode
50
+ * Matches Kotlin SudokuInteractor.selectedDigit logic:
51
+ * - If selected cell has a given value: return given
52
+ * - If selected cell has a CORRECT input (input === solution): return input
53
+ * - Otherwise: return null (no digit highlighting)
54
+ */
55
+ export declare function getSelectedDigit(cells: SudokuCell[], selectedIndex: number | null): number | null;
56
+ /**
57
+ * Compute selectedDigitCells based on selected cell
58
+ * Returns Set of cell indices that have the same digit as selected cell
59
+ * Returns null if selected cell doesn't have a highlightable digit
60
+ */
61
+ export declare function computeSelectedDigitCells(cells: SudokuCell[], selectedIndex: number | null): Set<number> | null;
62
+ //# sourceMappingURL=sudokuPresenter.d.ts.map
@@ -0,0 +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,QAAQ,EACb,KAAK,QAAQ,EAGb,UAAU,EAGV,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AA0F1B;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,MAAM,GAAG,IAAI,CA4Bf;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,OAAO,GAAG,cAAc,CAEnE;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,QAAQ,GAAG,IAAI,GACxB,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,IAAI,CAoC9B;AAuMD,MAAM,WAAW,mBAAmB;IAClC,gCAAgC;IAChC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,mDAAmD;IACnD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,yDAAyD;IACzD,UAAU,EAAE,OAAO,CAAC;IACpB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,EAAE,CAuF7E;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,EAAE,EACnB,KAAK,EAAE,MAAM,GACZ,GAAG,CAAC,MAAM,CAAC,CAQb;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,UAAU,EAAE,EACnB,aAAa,EAAE,MAAM,GAAG,IAAI,GAC3B,MAAM,GAAG,IAAI,CAiBf;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,UAAU,EAAE,EACnB,aAAa,EAAE,MAAM,GAAG,IAAI,GAC3B,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAIpB"}
@@ -0,0 +1,403 @@
1
+ /**
2
+ * Sudoku presenter - port of renderable/SudokuPresenter.kt
3
+ *
4
+ * Handles all display logic for the Sudoku board including:
5
+ * - Cell background colors based on selection state
6
+ * - Cell border colors for selected cells
7
+ * - Text colors (given, input, hint actions)
8
+ * - Hint visualization (areas, cells)
9
+ * - Pencilmark coloring
10
+ */
11
+ import { blockOf, columnOf, rowOf } from '../types/sudoku';
12
+ import { SudokuColor, ThemeColor, UIColorDark, UIColorLight, } from '../types/display';
13
+ // =============================================================================
14
+ // Block Definition (matches PlayBoard.blocks)
15
+ // =============================================================================
16
+ /**
17
+ * Block class - matches Kotlin Block class
18
+ */
19
+ class Block {
20
+ constructor(index) {
21
+ this.index = index;
22
+ const blockRow = Math.floor(index / 3);
23
+ const blockCol = index % 3;
24
+ this.cellIndices = [];
25
+ for (let r = 0; r < 3; r++) {
26
+ for (let c = 0; c < 3; c++) {
27
+ const row = blockRow * 3 + r;
28
+ const col = blockCol * 3 + c;
29
+ this.cellIndices.push(row * 9 + col);
30
+ }
31
+ }
32
+ }
33
+ }
34
+ /**
35
+ * Row class - matches Kotlin Row class
36
+ */
37
+ class Row {
38
+ constructor(index) {
39
+ this.index = index;
40
+ this.cellIndices = Array.from({ length: 9 }, (_, col) => index * 9 + col);
41
+ }
42
+ }
43
+ /**
44
+ * Column class - matches Kotlin Column class
45
+ */
46
+ class Column {
47
+ constructor(index) {
48
+ this.index = index;
49
+ this.cellIndices = Array.from({ length: 9 }, (_, row) => row * 9 + index);
50
+ }
51
+ }
52
+ // =============================================================================
53
+ // Color Conversion (matches CellPresenter.colorOf)
54
+ // =============================================================================
55
+ /**
56
+ * Convert SudokuColor to ThemeColor - matches CellPresenter.colorOf
57
+ */
58
+ function sudokuColorToTheme(sudokuColor) {
59
+ switch (sudokuColor) {
60
+ case SudokuColor.BLUE:
61
+ return ThemeColor.SELECTED;
62
+ case SudokuColor.GREEN:
63
+ return ThemeColor.SUCCESS;
64
+ case SudokuColor.YELLOW:
65
+ return ThemeColor.WARNING_SECONDARY;
66
+ case SudokuColor.ORANGE:
67
+ return ThemeColor.WARNING;
68
+ case SudokuColor.RED:
69
+ return ThemeColor.ERROR;
70
+ case SudokuColor.WHITE:
71
+ return ThemeColor.BACKGROUND;
72
+ case SudokuColor.LIGHT_GRAY:
73
+ return ThemeColor.BACKGROUND_SECONDARY;
74
+ case SudokuColor.BLACK:
75
+ return ThemeColor.LABEL;
76
+ case SudokuColor.GRAY:
77
+ return ThemeColor.LABEL_SECONDARY;
78
+ case SudokuColor.CLEAR:
79
+ return ThemeColor.CLEAR;
80
+ default:
81
+ return null;
82
+ }
83
+ }
84
+ /**
85
+ * Convert ThemeColor to actual CSS color - matches RendererColor.color
86
+ */
87
+ export function themeColorToCSS(palette, themeColor) {
88
+ switch (themeColor) {
89
+ case ThemeColor.NONE:
90
+ case ThemeColor.CLEAR:
91
+ return palette.clear;
92
+ case ThemeColor.SELECTED:
93
+ return palette.systemPurple;
94
+ case ThemeColor.SUCCESS:
95
+ return palette.systemBlue;
96
+ case ThemeColor.WARNING_SECONDARY:
97
+ return palette.systemYellow;
98
+ case ThemeColor.WARNING:
99
+ return palette.systemOrange;
100
+ case ThemeColor.ERROR:
101
+ return palette.systemRed;
102
+ case ThemeColor.BACKGROUND:
103
+ return palette.systemBackground;
104
+ case ThemeColor.BACKGROUND_SECONDARY:
105
+ return palette.secondarySystemBackground;
106
+ case ThemeColor.LABEL:
107
+ return palette.label;
108
+ case ThemeColor.LABEL_SECONDARY:
109
+ return palette.secondaryLabel;
110
+ case ThemeColor.DISABLED:
111
+ return palette.systemGray;
112
+ default:
113
+ return null;
114
+ }
115
+ }
116
+ /**
117
+ * Get the color palette for the current theme
118
+ */
119
+ export function getColorPalette(isDarkMode) {
120
+ return isDarkMode ? UIColorDark : UIColorLight;
121
+ }
122
+ // =============================================================================
123
+ // Hint Processing (matches BoardPresenter.calculateCellHints)
124
+ // =============================================================================
125
+ /**
126
+ * Process hint areas and cells into a map of cell hints
127
+ * Matches BoardPresenter.calculateCellHints
128
+ */
129
+ export function calculateCellHints(hintStep) {
130
+ if (!hintStep)
131
+ return null;
132
+ const hintsCell = new Map();
133
+ // Process areas first
134
+ hintStep.areas?.forEach(area => {
135
+ let cellIndices;
136
+ switch (area.type) {
137
+ case 'block':
138
+ cellIndices = new Block(area.index).cellIndices;
139
+ break;
140
+ case 'row':
141
+ cellIndices = new Row(area.index).cellIndices;
142
+ break;
143
+ case 'column':
144
+ cellIndices = new Column(area.index).cellIndices;
145
+ break;
146
+ }
147
+ cellIndices.forEach(index => {
148
+ hintsCell.set(index, {
149
+ index,
150
+ color: area.color,
151
+ fill: true, // Areas always use fill
152
+ });
153
+ });
154
+ });
155
+ // Process cells (overrides areas)
156
+ hintStep.cells?.forEach(cell => {
157
+ hintsCell.set(cell.index, cell);
158
+ });
159
+ return hintsCell;
160
+ }
161
+ /**
162
+ * Determine background and border colors for a cell
163
+ * Matches BlockPresenter.color exactly
164
+ */
165
+ function getCellColors(cellIndex, cellBlockIndex, cellRowIndex, cellColumnIndex, cellHasValue, selectedIndex, selectedBlockIndex, selectedRowIndex, selectedColumnIndex, selectedDigitCells) {
166
+ // Default background based on block (checkerboard pattern)
167
+ const defaultBackgroundColor = cellBlockIndex % 2 === 0
168
+ ? ThemeColor.BACKGROUND_SECONDARY
169
+ : ThemeColor.BACKGROUND;
170
+ if (selectedIndex === null) {
171
+ return { backgroundColor: defaultBackgroundColor, borderColor: null };
172
+ }
173
+ if (selectedDigitCells !== null) {
174
+ // Digit highlighting mode
175
+ if (selectedDigitCells.has(cellIndex)) {
176
+ // Highlighted cells (have the same digit)
177
+ return { backgroundColor: ThemeColor.SELECTED, borderColor: null };
178
+ }
179
+ // Check if this cell is "impossible" (in same row/col/block as highlighted cells)
180
+ let isImpossible = cellHasValue;
181
+ if (!isImpossible) {
182
+ for (const highlightedIndex of selectedDigitCells) {
183
+ const hRow = rowOf(highlightedIndex);
184
+ const hCol = columnOf(highlightedIndex);
185
+ const hBlock = blockOf(highlightedIndex);
186
+ if (cellRowIndex === hRow ||
187
+ cellColumnIndex === hCol ||
188
+ cellBlockIndex === hBlock) {
189
+ isImpossible = true;
190
+ break;
191
+ }
192
+ }
193
+ }
194
+ if (isImpossible) {
195
+ return { backgroundColor: ThemeColor.LABEL_SECONDARY, borderColor: null };
196
+ }
197
+ // Possible cells
198
+ return { backgroundColor: defaultBackgroundColor, borderColor: null };
199
+ }
200
+ // Normal selection mode
201
+ if (selectedIndex === cellIndex) {
202
+ // Selected cell: default background + SELECTED border
203
+ return {
204
+ backgroundColor: defaultBackgroundColor,
205
+ borderColor: ThemeColor.SELECTED,
206
+ };
207
+ }
208
+ if (selectedBlockIndex === cellBlockIndex ||
209
+ selectedRowIndex === cellRowIndex ||
210
+ selectedColumnIndex === cellColumnIndex) {
211
+ // Same row, column or block: SELECTED background
212
+ return { backgroundColor: ThemeColor.SELECTED, borderColor: null };
213
+ }
214
+ // Other cells
215
+ return { backgroundColor: defaultBackgroundColor, borderColor: null };
216
+ }
217
+ /**
218
+ * Determine what digit and color to display in a cell
219
+ * Matches CellPresenter.present logic
220
+ */
221
+ function getCellText(cell, showErrors, hintCell) {
222
+ // Hint select action takes priority
223
+ if (hintCell?.actions?.select != null) {
224
+ return {
225
+ digit: hintCell.actions.select,
226
+ textColor: ThemeColor.SELECTED,
227
+ isGiven: false,
228
+ };
229
+ }
230
+ // Hint unselect action (wrong digit to remove)
231
+ if (hintCell?.actions?.unselect != null) {
232
+ return {
233
+ digit: hintCell.actions.unselect,
234
+ textColor: ThemeColor.ERROR,
235
+ isGiven: false,
236
+ };
237
+ }
238
+ // User input
239
+ if (cell.input !== null) {
240
+ let textColor;
241
+ if (showErrors) {
242
+ textColor =
243
+ cell.input === cell.solution ? ThemeColor.SUCCESS : ThemeColor.ERROR;
244
+ }
245
+ else {
246
+ textColor = ThemeColor.LABEL_SECONDARY;
247
+ }
248
+ return { digit: cell.input, textColor, isGiven: false };
249
+ }
250
+ // Given (original)
251
+ if (cell.given !== null) {
252
+ return { digit: cell.given, textColor: ThemeColor.LABEL, isGiven: true };
253
+ }
254
+ return { digit: null, textColor: null, isGiven: false };
255
+ }
256
+ // =============================================================================
257
+ // Pencilmark Logic (matches CellPresenter.present pencilmark section)
258
+ // =============================================================================
259
+ /**
260
+ * Get pencilmark display states
261
+ * Matches CellPresenter.present pencilmark logic
262
+ */
263
+ function getPencilmarks(cell, hintCell) {
264
+ const cellPencilmarks = cell.pencilmarks
265
+ ? new Set(cell.pencilmarks)
266
+ : new Set();
267
+ const adding = hintCell?.actions?.add ? new Set(hintCell.actions.add) : null;
268
+ const removing = hintCell?.actions?.remove
269
+ ? new Set(hintCell.actions.remove)
270
+ : null;
271
+ const highlighting = hintCell?.actions?.highlight
272
+ ? new Set(hintCell.actions.highlight)
273
+ : null;
274
+ const result = [];
275
+ for (let pencilmark = 1; pencilmark <= 9; pencilmark++) {
276
+ let color = null;
277
+ if (highlighting?.has(pencilmark)) {
278
+ color = ThemeColor.SELECTED;
279
+ }
280
+ else if (adding?.has(pencilmark)) {
281
+ color = ThemeColor.SUCCESS;
282
+ }
283
+ else if (removing?.has(pencilmark)) {
284
+ color = ThemeColor.ERROR;
285
+ }
286
+ else if (cellPencilmarks.has(pencilmark)) {
287
+ color = ThemeColor.LABEL_SECONDARY;
288
+ }
289
+ if (color !== null) {
290
+ result.push({ digit: pencilmark, color });
291
+ }
292
+ }
293
+ return result;
294
+ }
295
+ /**
296
+ * Generate display states for all cells
297
+ * This is the main presenter function that matches the Kotlin renderable behavior
298
+ */
299
+ export function presentBoard(options) {
300
+ const { cells, selectedIndex, showErrors, hintStep = null, selectedDigitCells = null, } = options;
301
+ // Calculate hint cell map
302
+ const cellHints = calculateCellHints(hintStep);
303
+ // Get selected cell properties (if any)
304
+ const _selectedCell = selectedIndex !== null ? cells[selectedIndex] : null;
305
+ void _selectedCell; // Reserved for future use
306
+ const selectedBlockIndex = selectedIndex !== null ? blockOf(selectedIndex) : null;
307
+ const selectedRowIndex = selectedIndex !== null ? rowOf(selectedIndex) : null;
308
+ const selectedColumnIndex = selectedIndex !== null ? columnOf(selectedIndex) : null;
309
+ // Generate display state for each cell
310
+ return cells.map((cell, index) => {
311
+ const cellBlockIndex = blockOf(index);
312
+ const cellRowIndex = rowOf(index);
313
+ const cellColumnIndex = columnOf(index);
314
+ const cellHasValue = cell.input !== null || cell.given !== null;
315
+ // Get hint for this cell
316
+ const hintCell = cellHints?.get(index) ?? null;
317
+ // Get base colors
318
+ const { backgroundColor, borderColor } = getCellColors(index, cellBlockIndex, cellRowIndex, cellColumnIndex, cellHasValue, selectedIndex, selectedBlockIndex, selectedRowIndex, selectedColumnIndex, selectedDigitCells);
319
+ // Apply hint colors if present
320
+ let finalBgColor = backgroundColor;
321
+ let finalBorderColor = borderColor;
322
+ if (hintCell) {
323
+ if (hintCell.fill) {
324
+ // Hint uses fill: override background
325
+ const hintBgColor = sudokuColorToTheme(hintCell.color);
326
+ if (hintBgColor !== null) {
327
+ finalBgColor = hintBgColor;
328
+ }
329
+ }
330
+ else {
331
+ // Hint uses border
332
+ const hintBdrColor = sudokuColorToTheme(hintCell.color ?? SudokuColor.BLUE);
333
+ if (hintBdrColor !== null) {
334
+ finalBorderColor = hintBdrColor;
335
+ }
336
+ }
337
+ }
338
+ // Get text info
339
+ const { digit, textColor, isGiven } = getCellText(cell, showErrors, hintCell);
340
+ // Get pencilmarks if no digit
341
+ const pencilmarks = digit === null ? getPencilmarks(cell, hintCell) : [];
342
+ return {
343
+ index,
344
+ backgroundColor: finalBgColor,
345
+ borderColor: finalBorderColor,
346
+ textColor,
347
+ digit,
348
+ isGiven,
349
+ pencilmarks,
350
+ };
351
+ });
352
+ }
353
+ // =============================================================================
354
+ // Utility: Get cells with a specific digit
355
+ // =============================================================================
356
+ /**
357
+ * Get indices of cells that contain a specific digit (given or input)
358
+ * Used for digit highlighting mode
359
+ */
360
+ export function getCellsWithDigit(cells, digit) {
361
+ const result = new Set();
362
+ cells.forEach((cell, index) => {
363
+ if (cell.given === digit || cell.input === digit) {
364
+ result.add(index);
365
+ }
366
+ });
367
+ return result;
368
+ }
369
+ /**
370
+ * Get the selected digit for digit highlighting mode
371
+ * Matches Kotlin SudokuInteractor.selectedDigit logic:
372
+ * - If selected cell has a given value: return given
373
+ * - If selected cell has a CORRECT input (input === solution): return input
374
+ * - Otherwise: return null (no digit highlighting)
375
+ */
376
+ export function getSelectedDigit(cells, selectedIndex) {
377
+ if (selectedIndex === null)
378
+ return null;
379
+ const cell = cells[selectedIndex];
380
+ if (!cell)
381
+ return null;
382
+ // Given value takes priority
383
+ if (cell.given !== null) {
384
+ return cell.given;
385
+ }
386
+ // Only correct input triggers digit highlighting
387
+ if (cell.input !== null && cell.input === cell.solution) {
388
+ return cell.input;
389
+ }
390
+ return null;
391
+ }
392
+ /**
393
+ * Compute selectedDigitCells based on selected cell
394
+ * Returns Set of cell indices that have the same digit as selected cell
395
+ * Returns null if selected cell doesn't have a highlightable digit
396
+ */
397
+ export function computeSelectedDigitCells(cells, selectedIndex) {
398
+ const selectedDigit = getSelectedDigit(cells, selectedIndex);
399
+ if (selectedDigit === null)
400
+ return null;
401
+ return getCellsWithDigit(cells, selectedDigit);
402
+ }
403
+ //# sourceMappingURL=sudokuPresenter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sudokuPresenter.js","sourceRoot":"","sources":["../../src/utils/sudokuPresenter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
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 { type SudokuCell } from '../types/sudoku';
8
+ export interface ScramblerProtocol {
9
+ scramble9(symmetrical: boolean): number[];
10
+ }
11
+ /**
12
+ * Scrambler implementation that matches the Kotlin version
13
+ */
14
+ export declare const Scrambler: ScramblerProtocol;
15
+ /**
16
+ * Non-scrambler that returns identity permutation
17
+ */
18
+ export declare const NonScrambler: ScramblerProtocol;
19
+ export interface ScrambleResult {
20
+ /** Scrambled cells array */
21
+ cells: SudokuCell[];
22
+ /** Digit mapping from original (1-9) to scrambled (1-9) */
23
+ digitMapping: Map<number, number>;
24
+ /** Reverse digit mapping from scrambled to original */
25
+ reverseDigitMapping: Map<number, number>;
26
+ }
27
+ /**
28
+ * Scramble a board using the given scrambler
29
+ *
30
+ * @param scrambler - Scrambler protocol implementation
31
+ * @param cells - Original 81 cells
32
+ * @param symmetrical - Whether to use symmetrical scrambling
33
+ * @returns Scrambled cells and digit mapping
34
+ */
35
+ export declare function scrambleBoard(scrambler: ScramblerProtocol, cells: SudokuCell[], symmetrical?: boolean): ScrambleResult;
36
+ /**
37
+ * Parse a puzzle string (81 chars of 0-9 or .) into cells
38
+ *
39
+ * @param puzzle - 81 character string
40
+ * @param solution - 81 character solution string (optional)
41
+ * @returns Array of 81 cells
42
+ */
43
+ export declare function parsePuzzleString(puzzle: string, solution?: string): SudokuCell[];
44
+ /**
45
+ * Convert cells to puzzle string
46
+ */
47
+ export declare function cellsToPuzzleString(cells: SudokuCell[]): string;
48
+ /**
49
+ * Convert cells to current state string (given + input)
50
+ */
51
+ export declare function cellsToStateString(cells: SudokuCell[]): string;
52
+ /**
53
+ * Convert cells to input-only string
54
+ */
55
+ export declare function cellsToInputString(cells: SudokuCell[]): string;
56
+ /**
57
+ * Convert cells to pencilmarks string (comma-separated)
58
+ * Matches Kotlin: pencilmarksParam - returns empty string for null pencilmarks
59
+ */
60
+ export declare function cellsToPencilmarksString(cells: SudokuCell[]): string;
61
+ //# sourceMappingURL=sudokuScrambler.d.ts.map