@fgv/ts-sudoku-lib 5.0.1-9 → 5.0.1

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 (55) hide show
  1. package/dist/index.browser.js +37 -0
  2. package/dist/index.js +30 -0
  3. package/dist/packlets/collections/collections.js +136 -0
  4. package/dist/packlets/collections/data/puzzles.json +56 -0
  5. package/dist/packlets/collections/index.js +25 -0
  6. package/dist/packlets/common/cage.js +83 -0
  7. package/dist/packlets/common/cell.js +88 -0
  8. package/dist/packlets/common/common.js +70 -0
  9. package/dist/packlets/common/converters.js +65 -0
  10. package/dist/packlets/common/ids.js +152 -0
  11. package/dist/packlets/common/index.js +36 -0
  12. package/dist/packlets/common/logging.js +32 -0
  13. package/dist/packlets/common/public.js +25 -0
  14. package/dist/packlets/common/puzzle.js +411 -0
  15. package/dist/packlets/common/puzzleDefinitions.js +247 -0
  16. package/dist/packlets/common/puzzleSession.js +369 -0
  17. package/dist/packlets/common/puzzleState.js +99 -0
  18. package/dist/packlets/files/converters.js +67 -0
  19. package/dist/packlets/files/fileTreeHelpers.js +37 -0
  20. package/dist/packlets/files/filesystem.js +37 -0
  21. package/dist/packlets/files/index.browser.js +32 -0
  22. package/dist/packlets/files/index.js +30 -0
  23. package/dist/packlets/files/model.js +25 -0
  24. package/dist/packlets/hints/baseHintProvider.js +199 -0
  25. package/dist/packlets/hints/explanations.js +270 -0
  26. package/dist/packlets/hints/hiddenSingles.js +233 -0
  27. package/dist/packlets/hints/hintRegistry.js +222 -0
  28. package/dist/packlets/hints/hints.js +311 -0
  29. package/dist/packlets/hints/index.js +39 -0
  30. package/dist/packlets/hints/interfaces.js +25 -0
  31. package/dist/packlets/hints/nakedSingles.js +198 -0
  32. package/dist/packlets/hints/puzzleSessionHints.js +495 -0
  33. package/dist/packlets/hints/types.js +43 -0
  34. package/dist/packlets/puzzles/anyPuzzle.js +47 -0
  35. package/dist/packlets/puzzles/index.js +31 -0
  36. package/dist/packlets/puzzles/internal/combinationCache.js +75 -0
  37. package/dist/packlets/puzzles/internal/combinationGenerator.js +142 -0
  38. package/dist/packlets/puzzles/internal/possibilityAnalyzer.js +121 -0
  39. package/dist/packlets/puzzles/killerCombinations.js +222 -0
  40. package/dist/packlets/puzzles/killerCombinationsTypes.js +25 -0
  41. package/dist/packlets/puzzles/killerSudokuPuzzle.js +136 -0
  42. package/dist/packlets/puzzles/sudokuPuzzle.js +43 -0
  43. package/dist/packlets/puzzles/sudokuXPuzzle.js +65 -0
  44. package/dist/test/unit/helpers/puzzleBuilders.js +149 -0
  45. package/dist/ts-sudoku-lib.d.ts +12 -7
  46. package/dist/tsdoc-metadata.json +1 -1
  47. package/lib/index.browser.d.ts +7 -0
  48. package/lib/index.browser.js +78 -0
  49. package/lib/packlets/files/fileTreeHelpers.d.ts +13 -0
  50. package/lib/packlets/files/fileTreeHelpers.js +40 -0
  51. package/lib/packlets/files/index.browser.d.ts +5 -0
  52. package/lib/packlets/files/index.browser.js +70 -0
  53. package/lib/packlets/files/index.d.ts +3 -2
  54. package/lib/packlets/files/index.js +7 -4
  55. package/package.json +19 -5
@@ -0,0 +1,43 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ /**
25
+ * Standard technique identifiers for built-in solving techniques.
26
+ * @public
27
+ */
28
+ export const TechniqueIds = {
29
+ NAKED_SINGLES: 'naked-singles',
30
+ HIDDEN_SINGLES: 'hidden-singles'
31
+ };
32
+ /**
33
+ * Standard confidence levels as branded types.
34
+ * @public
35
+ */
36
+ export const ConfidenceLevels = {
37
+ LOW: 1,
38
+ MEDIUM_LOW: 2,
39
+ MEDIUM: 3,
40
+ MEDIUM_HIGH: 4,
41
+ HIGH: 5
42
+ };
43
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,47 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ import { fail } from '@fgv/ts-utils';
25
+ import { KillerSudokuPuzzle } from './killerSudokuPuzzle';
26
+ import { SudokuPuzzle } from './sudokuPuzzle';
27
+ import { SudokuXPuzzle } from './sudokuXPuzzle';
28
+ /**
29
+ * Static class to instantiate any puzzle from a {@link IPuzzleDefinition | puzzle definition}.
30
+ * @internal
31
+ */
32
+ export class AnyPuzzle {
33
+ static create(puzzle) {
34
+ switch (puzzle.type) {
35
+ case 'sudoku':
36
+ return SudokuPuzzle.create(puzzle);
37
+ case 'sudoku-x':
38
+ return SudokuXPuzzle.create(puzzle);
39
+ case 'killer-sudoku':
40
+ return KillerSudokuPuzzle.create(puzzle);
41
+ /* c8 ignore next 2 */
42
+ default:
43
+ return fail(`Puzzle '${puzzle.description}' unsupported type ${puzzle.type}`);
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=anyPuzzle.js.map
@@ -0,0 +1,31 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ import { KillerSudokuPuzzle as Killer } from './killerSudokuPuzzle';
25
+ import { SudokuPuzzle as Sudoku } from './sudokuPuzzle';
26
+ import { SudokuXPuzzle as SudokuX } from './sudokuXPuzzle';
27
+ import { KillerCombinations } from './killerCombinations';
28
+ import { AnyPuzzle as Any } from './anyPuzzle';
29
+ export * from './killerCombinationsTypes';
30
+ export { Any, Killer, KillerCombinations, Sudoku, SudokuX };
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,75 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ /**
25
+ * @internal
26
+ */
27
+ export class CombinationCache {
28
+ /**
29
+ * Generate cache key for combination parameters.
30
+ */
31
+ static generateKey(cageSize, total, constraints) {
32
+ var _a, _b, _c, _d;
33
+ const excluded = (_b = (_a = constraints === null || constraints === void 0 ? void 0 : constraints.excludedNumbers) === null || _a === void 0 ? void 0 : _a.slice().sort().join(',')) !== null && _b !== void 0 ? _b : '';
34
+ const required = (_d = (_c = constraints === null || constraints === void 0 ? void 0 : constraints.requiredNumbers) === null || _c === void 0 ? void 0 : _c.slice().sort().join(',')) !== null && _d !== void 0 ? _d : '';
35
+ return `${cageSize}:${total}:${excluded}:${required}`;
36
+ }
37
+ /**
38
+ * Get cached combination result.
39
+ */
40
+ static get(key) {
41
+ const result = this._cache.get(key);
42
+ if (result) {
43
+ // Deep clone to prevent mutation of cached data
44
+ return result.map((combination) => [...combination]);
45
+ }
46
+ return undefined;
47
+ }
48
+ /**
49
+ * Store combination result in cache.
50
+ */
51
+ static set(key, combinations) {
52
+ // Implement simple LRU by clearing cache when it gets too large
53
+ if (this._cache.size >= this._maxCacheSize) {
54
+ this.clear();
55
+ }
56
+ // Deep clone to prevent mutation of cached data
57
+ const clonedCombinations = combinations.map((combination) => [...combination]);
58
+ this._cache.set(key, clonedCombinations);
59
+ }
60
+ /**
61
+ * Clear the entire cache.
62
+ */
63
+ static clear() {
64
+ this._cache.clear();
65
+ }
66
+ /**
67
+ * Get current cache size (for testing/debugging).
68
+ */
69
+ static size() {
70
+ return this._cache.size;
71
+ }
72
+ }
73
+ CombinationCache._cache = new Map();
74
+ CombinationCache._maxCacheSize = 1000;
75
+ //# sourceMappingURL=combinationCache.js.map
@@ -0,0 +1,142 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ /**
25
+ * @internal
26
+ */
27
+ export class CombinationGenerator {
28
+ /**
29
+ * Generates all possible combinations of unique numbers that sum to the target total.
30
+ * @param cageSize - Number of cells in the cage
31
+ * @param total - Target sum
32
+ * @param constraints - Optional constraints on included/excluded numbers
33
+ * @param maxValue - Maximum value allowed in cells (defaults to 9)
34
+ * @returns Array of combinations, each sorted in ascending order
35
+ */
36
+ static generate(cageSize, total, constraints, maxValue = 9) {
37
+ var _a, _b;
38
+ const results = [];
39
+ const excludedSet = new Set((_a = constraints === null || constraints === void 0 ? void 0 : constraints.excludedNumbers) !== null && _a !== void 0 ? _a : []);
40
+ const requiredNumbers = (_b = constraints === null || constraints === void 0 ? void 0 : constraints.requiredNumbers) !== null && _b !== void 0 ? _b : [];
41
+ const requiredSet = new Set(requiredNumbers);
42
+ // Validate that we can satisfy required numbers constraint
43
+ if (requiredNumbers.length > cageSize) {
44
+ return [];
45
+ }
46
+ // Check if required numbers sum is already too high
47
+ const requiredSum = requiredNumbers.reduce((sum, num) => sum + num, 0);
48
+ if (requiredSum > total) {
49
+ return [];
50
+ }
51
+ // Calculate remaining constraints after accounting for required numbers
52
+ const remainingSize = cageSize - requiredNumbers.length;
53
+ const remainingTotal = total - requiredSum;
54
+ if (remainingSize === 0) {
55
+ // Only required numbers should be present
56
+ /* c8 ignore next 3 - edge case: required numbers fill cage but sum doesn't match (tested via KillerCombinations) */
57
+ if (remainingTotal === 0) {
58
+ return [Array.from(requiredNumbers).sort()];
59
+ }
60
+ return [];
61
+ }
62
+ // Generate combinations for the remaining slots
63
+ const availableNumbers = [];
64
+ for (let i = 1; i <= maxValue; i++) {
65
+ if (!excludedSet.has(i) && !requiredSet.has(i)) {
66
+ availableNumbers.push(i);
67
+ }
68
+ }
69
+ this._generateCombinations(availableNumbers, remainingSize, remainingTotal, 0, [], results);
70
+ // Add required numbers to each result and sort
71
+ return results.map((combination) => [...combination, ...requiredNumbers].sort((a, b) => a - b));
72
+ }
73
+ /**
74
+ * Recursive helper to generate combinations.
75
+ * @param availableNumbers - Numbers that can be used
76
+ * @param targetSize - Number of numbers needed
77
+ * @param targetSum - Target sum for the combination
78
+ * @param startIndex - Starting index in available numbers
79
+ * @param currentCombination - Current partial combination
80
+ * @param results - Array to collect results
81
+ */
82
+ static _generateCombinations(availableNumbers, targetSize, targetSum, startIndex, currentCombination, results) {
83
+ // Base case: we have the right number of elements
84
+ if (currentCombination.length === targetSize) {
85
+ const sum = currentCombination.reduce((total, num) => total + num, 0);
86
+ if (sum === targetSum) {
87
+ results.push([...currentCombination]);
88
+ }
89
+ return;
90
+ }
91
+ // Early termination: impossible to reach target
92
+ const remainingSlots = targetSize - currentCombination.length;
93
+ const currentSum = currentCombination.reduce((total, num) => total + num, 0);
94
+ const remainingSum = targetSum - currentSum;
95
+ // Check minimum possible sum with remaining slots
96
+ const minPossible = this._calculateMinSum(availableNumbers, startIndex, remainingSlots);
97
+ if (remainingSum < minPossible) {
98
+ return;
99
+ }
100
+ // Check maximum possible sum with remaining slots
101
+ const maxPossible = this._calculateMaxSum(availableNumbers, startIndex, remainingSlots);
102
+ if (remainingSum > maxPossible) {
103
+ return;
104
+ }
105
+ // Try each available number
106
+ for (let i = startIndex; i < availableNumbers.length; i++) {
107
+ const number = availableNumbers[i];
108
+ // Skip if adding this number would exceed our target
109
+ if (currentSum + number > targetSum) {
110
+ continue;
111
+ }
112
+ currentCombination.push(number);
113
+ this._generateCombinations(availableNumbers, targetSize, targetSum, i + 1, currentCombination, results);
114
+ currentCombination.pop();
115
+ }
116
+ }
117
+ /**
118
+ * Calculate minimum possible sum for remaining slots.
119
+ */
120
+ static _calculateMinSum(availableNumbers, startIndex, remainingSlots) {
121
+ let sum = 0;
122
+ let count = 0;
123
+ for (let i = startIndex; i < availableNumbers.length && count < remainingSlots; i++) {
124
+ sum += availableNumbers[i];
125
+ count++;
126
+ }
127
+ return sum;
128
+ }
129
+ /**
130
+ * Calculate maximum possible sum for remaining slots.
131
+ */
132
+ static _calculateMaxSum(availableNumbers, startIndex, remainingSlots) {
133
+ let sum = 0;
134
+ let count = 0;
135
+ for (let i = availableNumbers.length - 1; i >= startIndex && count < remainingSlots; i--) {
136
+ sum += availableNumbers[i];
137
+ count++;
138
+ }
139
+ return sum;
140
+ }
141
+ }
142
+ //# sourceMappingURL=combinationGenerator.js.map
@@ -0,0 +1,121 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ import { succeed } from '@fgv/ts-utils';
25
+ /**
26
+ * @internal
27
+ */
28
+ export class PossibilityAnalyzer {
29
+ /**
30
+ * Analyze possible values for each cell in a killer cage based on current puzzle state.
31
+ * @param puzzle - The puzzle instance
32
+ * @param state - Current puzzle state
33
+ * @param cage - The killer cage to analyze
34
+ * @param validCombinations - Pre-computed valid combinations for the cage
35
+ * @returns Map of CellId to possible number arrays
36
+ */
37
+ static analyze(puzzle, state, cage, validCombinations) {
38
+ const possibilities = new Map();
39
+ // Get current values in the cage
40
+ // We need the concrete Cage class for containedValues method
41
+ const concreteCage = cage;
42
+ const currentValues = concreteCage.containedValues(state);
43
+ const emptyCells = [];
44
+ // Identify empty cells and initialize possibilities
45
+ for (const cellId of cage.cellIds) {
46
+ const cellContents = state.getCellContents(cellId).orDefault();
47
+ /* c8 ignore next 1 - ? is defense in depth */
48
+ if ((cellContents === null || cellContents === void 0 ? void 0 : cellContents.value) !== undefined) {
49
+ // Cell already has a value - set empty possibilities array
50
+ possibilities.set(cellId, []);
51
+ }
52
+ else {
53
+ // Empty cell
54
+ emptyCells.push(cellId);
55
+ possibilities.set(cellId, []);
56
+ }
57
+ }
58
+ // Filter combinations that are compatible with current state
59
+ const compatibleCombinations = validCombinations.filter((combination) => {
60
+ // Check if combination contains all current values
61
+ for (const currentValue of currentValues) {
62
+ if (!combination.includes(currentValue)) {
63
+ return false;
64
+ }
65
+ }
66
+ // Check if combination has the right number of remaining values
67
+ const remainingValues = combination.filter((value) => !currentValues.has(value));
68
+ return remainingValues.length === emptyCells.length;
69
+ });
70
+ // If no compatible combinations exist, return empty possibilities for all cells
71
+ if (compatibleCombinations.length === 0) {
72
+ return succeed(possibilities);
73
+ }
74
+ // For each empty cell, find which values are possible
75
+ for (const cellId of emptyCells) {
76
+ const cellPossibilities = new Set();
77
+ // Get the cell to check sudoku constraints
78
+ const cellResult = puzzle.getCell(cellId);
79
+ /* c8 ignore next 3 - defensive coding: protects against internal cage/puzzle state corruption */
80
+ if (cellResult.isFailure()) {
81
+ continue;
82
+ }
83
+ const cell = cellResult.value;
84
+ // Check each compatible combination
85
+ for (const combination of compatibleCombinations) {
86
+ const remainingValues = combination.filter((value) => !currentValues.has(value));
87
+ // Check if each remaining value could go in this cell
88
+ for (const value of remainingValues) {
89
+ if (this._canPlaceValueInCell(cell, value, state)) {
90
+ cellPossibilities.add(value);
91
+ }
92
+ }
93
+ }
94
+ possibilities.set(cellId, Array.from(cellPossibilities).sort());
95
+ }
96
+ return succeed(possibilities);
97
+ }
98
+ /**
99
+ * Check if a value can be placed in a cell considering sudoku constraints.
100
+ * @param cell - The cell to check
101
+ * @param value - The value to place
102
+ * @param state - Current puzzle state
103
+ * @returns True if the value can be placed
104
+ */
105
+ static _canPlaceValueInCell(cell, value, state) {
106
+ // Check all cages this cell belongs to for sudoku constraints
107
+ for (const cage of cell.cages) {
108
+ if (cage.cageType === 'killer') {
109
+ // Skip killer cage check - we're already analyzing within killer constraints
110
+ continue;
111
+ }
112
+ // Check if value already exists in this constraint cage (row, column, section, etc.)
113
+ const concreteCage = cage;
114
+ if (concreteCage.containsValue(value, state, [cell.id])) {
115
+ return false;
116
+ }
117
+ }
118
+ return true;
119
+ }
120
+ }
121
+ //# sourceMappingURL=possibilityAnalyzer.js.map