@fgv/ts-sudoku-lib 5.0.1-0 → 5.0.1-2
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/debug_killer.js +50 -0
- package/debug_test.js +88 -0
- package/dist/ts-sudoku-lib.d.ts +1505 -42
- package/lib/index.d.ts +3 -2
- package/lib/index.js +5 -3
- package/lib/packlets/collections/collections.d.ts +15 -8
- package/lib/packlets/collections/collections.js +41 -11
- package/lib/packlets/collections/data/puzzles.json +28 -33
- package/lib/packlets/common/common.d.ts +15 -0
- package/lib/packlets/common/common.js +25 -0
- package/lib/packlets/common/converters.d.ts +0 -6
- package/lib/packlets/common/converters.js +17 -22
- package/lib/packlets/common/ids.d.ts +8 -1
- package/lib/packlets/common/ids.js +83 -10
- package/lib/packlets/common/index.d.ts +3 -2
- package/lib/packlets/common/index.js +5 -2
- package/lib/packlets/common/logging.d.ts +7 -0
- package/lib/packlets/common/logging.js +35 -0
- package/lib/packlets/common/puzzle.d.ts +23 -13
- package/lib/packlets/common/puzzle.js +51 -25
- package/lib/packlets/common/puzzleDefinitions.d.ts +127 -0
- package/lib/packlets/common/puzzleDefinitions.js +251 -0
- package/lib/packlets/common/puzzleSession.d.ts +8 -0
- package/lib/packlets/common/puzzleSession.js +12 -0
- package/lib/packlets/common/puzzleState.js +3 -0
- package/lib/packlets/files/converters.d.ts +27 -0
- package/lib/packlets/files/converters.js +71 -0
- package/lib/packlets/{file/converters.d.ts → files/filesystem.d.ts} +3 -8
- package/lib/packlets/{file/converters.js → files/filesystem.js} +5 -14
- package/lib/packlets/files/index.d.ts +5 -0
- package/lib/packlets/{file → files}/index.js +3 -1
- package/lib/packlets/files/model.d.ts +32 -0
- package/lib/packlets/hints/baseHintProvider.d.ts +103 -0
- package/lib/packlets/hints/baseHintProvider.js +203 -0
- package/lib/packlets/hints/explanations.d.ts +105 -0
- package/lib/packlets/hints/explanations.js +276 -0
- package/lib/packlets/hints/hiddenSingles.d.ts +81 -0
- package/lib/packlets/hints/hiddenSingles.js +237 -0
- package/lib/packlets/hints/hintRegistry.d.ts +95 -0
- package/lib/packlets/hints/hintRegistry.js +226 -0
- package/lib/packlets/hints/hints.d.ts +144 -0
- package/lib/packlets/hints/hints.js +316 -0
- package/lib/packlets/hints/index.d.ts +10 -0
- package/lib/packlets/hints/index.js +55 -0
- package/lib/packlets/hints/interfaces.d.ts +121 -0
- package/lib/packlets/{file/model.js → hints/interfaces.js} +1 -1
- package/lib/packlets/hints/nakedSingles.d.ts +57 -0
- package/lib/packlets/hints/nakedSingles.js +202 -0
- package/lib/packlets/hints/puzzleSessionHints.d.ts +306 -0
- package/lib/packlets/hints/puzzleSessionHints.js +499 -0
- package/lib/packlets/hints/types.d.ts +102 -0
- package/lib/packlets/hints/types.js +46 -0
- package/lib/packlets/puzzles/anyPuzzle.d.ts +3 -3
- package/lib/packlets/puzzles/anyPuzzle.js +1 -1
- package/lib/packlets/puzzles/index.d.ts +4 -2
- package/lib/packlets/puzzles/index.js +20 -3
- package/lib/packlets/puzzles/internal/combinationCache.d.ts +29 -0
- package/lib/packlets/puzzles/internal/combinationCache.js +79 -0
- package/lib/packlets/puzzles/internal/combinationGenerator.d.ts +34 -0
- package/lib/packlets/puzzles/internal/combinationGenerator.js +146 -0
- package/lib/packlets/puzzles/internal/possibilityAnalyzer.d.ts +25 -0
- package/lib/packlets/puzzles/internal/possibilityAnalyzer.js +125 -0
- package/lib/packlets/puzzles/killerCombinations.d.ts +90 -0
- package/lib/packlets/puzzles/killerCombinations.js +226 -0
- package/lib/packlets/puzzles/killerCombinationsTypes.d.ts +17 -0
- package/lib/packlets/puzzles/killerCombinationsTypes.js +26 -0
- package/lib/packlets/puzzles/killerSudokuPuzzle.d.ts +2 -2
- package/lib/packlets/puzzles/killerSudokuPuzzle.js +15 -11
- package/lib/packlets/puzzles/sudokuPuzzle.d.ts +2 -2
- package/lib/packlets/puzzles/sudokuXPuzzle.d.ts +2 -2
- package/lib/packlets/puzzles/sudokuXPuzzle.js +8 -4
- package/package.json +5 -5
- package/sample-12x12-puzzle.js +72 -0
- package/sample-4x4-puzzles.js +68 -0
- package/sample-6x6-puzzle.js +61 -0
- package/temp_12x12_cells.txt +12 -0
- package/temp_correct.txt +12 -0
- package/test-grid-sizes.js +132 -0
- package/lib/packlets/common/model.d.ts +0 -15
- package/lib/packlets/file/index.d.ts +0 -4
- package/lib/packlets/file/model.d.ts +0 -9
- /package/lib/packlets/{common → files}/model.js +0 -0
|
@@ -22,14 +22,31 @@
|
|
|
22
22
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
23
|
* SOFTWARE.
|
|
24
24
|
*/
|
|
25
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
28
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
29
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(o, k2, desc);
|
|
32
|
+
}) : (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
o[k2] = m[k];
|
|
35
|
+
}));
|
|
36
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
37
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
38
|
+
};
|
|
25
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.SudokuX = exports.Sudoku = exports.Killer = exports.Any = void 0;
|
|
27
|
-
const anyPuzzle_1 = require("./anyPuzzle");
|
|
28
|
-
Object.defineProperty(exports, "Any", { enumerable: true, get: function () { return anyPuzzle_1.AnyPuzzle; } });
|
|
40
|
+
exports.SudokuX = exports.Sudoku = exports.KillerCombinations = exports.Killer = exports.Any = void 0;
|
|
29
41
|
const killerSudokuPuzzle_1 = require("./killerSudokuPuzzle");
|
|
30
42
|
Object.defineProperty(exports, "Killer", { enumerable: true, get: function () { return killerSudokuPuzzle_1.KillerSudokuPuzzle; } });
|
|
31
43
|
const sudokuPuzzle_1 = require("./sudokuPuzzle");
|
|
32
44
|
Object.defineProperty(exports, "Sudoku", { enumerable: true, get: function () { return sudokuPuzzle_1.SudokuPuzzle; } });
|
|
33
45
|
const sudokuXPuzzle_1 = require("./sudokuXPuzzle");
|
|
34
46
|
Object.defineProperty(exports, "SudokuX", { enumerable: true, get: function () { return sudokuXPuzzle_1.SudokuXPuzzle; } });
|
|
47
|
+
const killerCombinations_1 = require("./killerCombinations");
|
|
48
|
+
Object.defineProperty(exports, "KillerCombinations", { enumerable: true, get: function () { return killerCombinations_1.KillerCombinations; } });
|
|
49
|
+
const anyPuzzle_1 = require("./anyPuzzle");
|
|
50
|
+
Object.defineProperty(exports, "Any", { enumerable: true, get: function () { return anyPuzzle_1.AnyPuzzle; } });
|
|
51
|
+
__exportStar(require("./killerCombinationsTypes"), exports);
|
|
35
52
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { IKillerConstraints } from '../killerCombinationsTypes';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
export declare class CombinationCache {
|
|
6
|
+
private static readonly _cache;
|
|
7
|
+
private static readonly _maxCacheSize;
|
|
8
|
+
/**
|
|
9
|
+
* Generate cache key for combination parameters.
|
|
10
|
+
*/
|
|
11
|
+
static generateKey(cageSize: number, total: number, constraints?: IKillerConstraints): string;
|
|
12
|
+
/**
|
|
13
|
+
* Get cached combination result.
|
|
14
|
+
*/
|
|
15
|
+
static get(key: string): number[][] | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Store combination result in cache.
|
|
18
|
+
*/
|
|
19
|
+
static set(key: string, combinations: number[][]): void;
|
|
20
|
+
/**
|
|
21
|
+
* Clear the entire cache.
|
|
22
|
+
*/
|
|
23
|
+
static clear(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Get current cache size (for testing/debugging).
|
|
26
|
+
*/
|
|
27
|
+
static size(): number;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=combinationCache.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* MIT License
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2023 Erik Fortune
|
|
6
|
+
*
|
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
* furnished to do so, subject to the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
* copies or substantial portions of the Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
* SOFTWARE.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.CombinationCache = void 0;
|
|
27
|
+
/**
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
class CombinationCache {
|
|
31
|
+
/**
|
|
32
|
+
* Generate cache key for combination parameters.
|
|
33
|
+
*/
|
|
34
|
+
static generateKey(cageSize, total, constraints) {
|
|
35
|
+
var _a, _b, _c, _d;
|
|
36
|
+
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 : '';
|
|
37
|
+
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 : '';
|
|
38
|
+
return `${cageSize}:${total}:${excluded}:${required}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get cached combination result.
|
|
42
|
+
*/
|
|
43
|
+
static get(key) {
|
|
44
|
+
const result = this._cache.get(key);
|
|
45
|
+
if (result) {
|
|
46
|
+
// Deep clone to prevent mutation of cached data
|
|
47
|
+
return result.map((combination) => [...combination]);
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Store combination result in cache.
|
|
53
|
+
*/
|
|
54
|
+
static set(key, combinations) {
|
|
55
|
+
// Implement simple LRU by clearing cache when it gets too large
|
|
56
|
+
if (this._cache.size >= this._maxCacheSize) {
|
|
57
|
+
this.clear();
|
|
58
|
+
}
|
|
59
|
+
// Deep clone to prevent mutation of cached data
|
|
60
|
+
const clonedCombinations = combinations.map((combination) => [...combination]);
|
|
61
|
+
this._cache.set(key, clonedCombinations);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Clear the entire cache.
|
|
65
|
+
*/
|
|
66
|
+
static clear() {
|
|
67
|
+
this._cache.clear();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get current cache size (for testing/debugging).
|
|
71
|
+
*/
|
|
72
|
+
static size() {
|
|
73
|
+
return this._cache.size;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.CombinationCache = CombinationCache;
|
|
77
|
+
CombinationCache._cache = new Map();
|
|
78
|
+
CombinationCache._maxCacheSize = 1000;
|
|
79
|
+
//# sourceMappingURL=combinationCache.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { IKillerConstraints } from '../killerCombinationsTypes';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
export declare class CombinationGenerator {
|
|
6
|
+
/**
|
|
7
|
+
* Generates all possible combinations of unique numbers that sum to the target total.
|
|
8
|
+
* @param cageSize - Number of cells in the cage
|
|
9
|
+
* @param total - Target sum
|
|
10
|
+
* @param constraints - Optional constraints on included/excluded numbers
|
|
11
|
+
* @param maxValue - Maximum value allowed in cells (defaults to 9)
|
|
12
|
+
* @returns Array of combinations, each sorted in ascending order
|
|
13
|
+
*/
|
|
14
|
+
static generate(cageSize: number, total: number, constraints?: IKillerConstraints, maxValue?: number): number[][];
|
|
15
|
+
/**
|
|
16
|
+
* Recursive helper to generate combinations.
|
|
17
|
+
* @param availableNumbers - Numbers that can be used
|
|
18
|
+
* @param targetSize - Number of numbers needed
|
|
19
|
+
* @param targetSum - Target sum for the combination
|
|
20
|
+
* @param startIndex - Starting index in available numbers
|
|
21
|
+
* @param currentCombination - Current partial combination
|
|
22
|
+
* @param results - Array to collect results
|
|
23
|
+
*/
|
|
24
|
+
private static _generateCombinations;
|
|
25
|
+
/**
|
|
26
|
+
* Calculate minimum possible sum for remaining slots.
|
|
27
|
+
*/
|
|
28
|
+
private static _calculateMinSum;
|
|
29
|
+
/**
|
|
30
|
+
* Calculate maximum possible sum for remaining slots.
|
|
31
|
+
*/
|
|
32
|
+
private static _calculateMaxSum;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=combinationGenerator.d.ts.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* MIT License
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2023 Erik Fortune
|
|
6
|
+
*
|
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
* furnished to do so, subject to the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
* copies or substantial portions of the Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
* SOFTWARE.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.CombinationGenerator = void 0;
|
|
27
|
+
/**
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
class CombinationGenerator {
|
|
31
|
+
/**
|
|
32
|
+
* Generates all possible combinations of unique numbers that sum to the target total.
|
|
33
|
+
* @param cageSize - Number of cells in the cage
|
|
34
|
+
* @param total - Target sum
|
|
35
|
+
* @param constraints - Optional constraints on included/excluded numbers
|
|
36
|
+
* @param maxValue - Maximum value allowed in cells (defaults to 9)
|
|
37
|
+
* @returns Array of combinations, each sorted in ascending order
|
|
38
|
+
*/
|
|
39
|
+
static generate(cageSize, total, constraints, maxValue = 9) {
|
|
40
|
+
var _a, _b;
|
|
41
|
+
const results = [];
|
|
42
|
+
const excludedSet = new Set((_a = constraints === null || constraints === void 0 ? void 0 : constraints.excludedNumbers) !== null && _a !== void 0 ? _a : []);
|
|
43
|
+
const requiredNumbers = (_b = constraints === null || constraints === void 0 ? void 0 : constraints.requiredNumbers) !== null && _b !== void 0 ? _b : [];
|
|
44
|
+
const requiredSet = new Set(requiredNumbers);
|
|
45
|
+
// Validate that we can satisfy required numbers constraint
|
|
46
|
+
if (requiredNumbers.length > cageSize) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
// Check if required numbers sum is already too high
|
|
50
|
+
const requiredSum = requiredNumbers.reduce((sum, num) => sum + num, 0);
|
|
51
|
+
if (requiredSum > total) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
// Calculate remaining constraints after accounting for required numbers
|
|
55
|
+
const remainingSize = cageSize - requiredNumbers.length;
|
|
56
|
+
const remainingTotal = total - requiredSum;
|
|
57
|
+
if (remainingSize === 0) {
|
|
58
|
+
// Only required numbers should be present
|
|
59
|
+
/* c8 ignore next 3 - edge case: required numbers fill cage but sum doesn't match (tested via KillerCombinations) */
|
|
60
|
+
if (remainingTotal === 0) {
|
|
61
|
+
return [Array.from(requiredNumbers).sort()];
|
|
62
|
+
}
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
// Generate combinations for the remaining slots
|
|
66
|
+
const availableNumbers = [];
|
|
67
|
+
for (let i = 1; i <= maxValue; i++) {
|
|
68
|
+
if (!excludedSet.has(i) && !requiredSet.has(i)) {
|
|
69
|
+
availableNumbers.push(i);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
this._generateCombinations(availableNumbers, remainingSize, remainingTotal, 0, [], results);
|
|
73
|
+
// Add required numbers to each result and sort
|
|
74
|
+
return results.map((combination) => [...combination, ...requiredNumbers].sort((a, b) => a - b));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Recursive helper to generate combinations.
|
|
78
|
+
* @param availableNumbers - Numbers that can be used
|
|
79
|
+
* @param targetSize - Number of numbers needed
|
|
80
|
+
* @param targetSum - Target sum for the combination
|
|
81
|
+
* @param startIndex - Starting index in available numbers
|
|
82
|
+
* @param currentCombination - Current partial combination
|
|
83
|
+
* @param results - Array to collect results
|
|
84
|
+
*/
|
|
85
|
+
static _generateCombinations(availableNumbers, targetSize, targetSum, startIndex, currentCombination, results) {
|
|
86
|
+
// Base case: we have the right number of elements
|
|
87
|
+
if (currentCombination.length === targetSize) {
|
|
88
|
+
const sum = currentCombination.reduce((total, num) => total + num, 0);
|
|
89
|
+
if (sum === targetSum) {
|
|
90
|
+
results.push([...currentCombination]);
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Early termination: impossible to reach target
|
|
95
|
+
const remainingSlots = targetSize - currentCombination.length;
|
|
96
|
+
const currentSum = currentCombination.reduce((total, num) => total + num, 0);
|
|
97
|
+
const remainingSum = targetSum - currentSum;
|
|
98
|
+
// Check minimum possible sum with remaining slots
|
|
99
|
+
const minPossible = this._calculateMinSum(availableNumbers, startIndex, remainingSlots);
|
|
100
|
+
if (remainingSum < minPossible) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Check maximum possible sum with remaining slots
|
|
104
|
+
const maxPossible = this._calculateMaxSum(availableNumbers, startIndex, remainingSlots);
|
|
105
|
+
if (remainingSum > maxPossible) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Try each available number
|
|
109
|
+
for (let i = startIndex; i < availableNumbers.length; i++) {
|
|
110
|
+
const number = availableNumbers[i];
|
|
111
|
+
// Skip if adding this number would exceed our target
|
|
112
|
+
if (currentSum + number > targetSum) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
currentCombination.push(number);
|
|
116
|
+
this._generateCombinations(availableNumbers, targetSize, targetSum, i + 1, currentCombination, results);
|
|
117
|
+
currentCombination.pop();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Calculate minimum possible sum for remaining slots.
|
|
122
|
+
*/
|
|
123
|
+
static _calculateMinSum(availableNumbers, startIndex, remainingSlots) {
|
|
124
|
+
let sum = 0;
|
|
125
|
+
let count = 0;
|
|
126
|
+
for (let i = startIndex; i < availableNumbers.length && count < remainingSlots; i++) {
|
|
127
|
+
sum += availableNumbers[i];
|
|
128
|
+
count++;
|
|
129
|
+
}
|
|
130
|
+
return sum;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Calculate maximum possible sum for remaining slots.
|
|
134
|
+
*/
|
|
135
|
+
static _calculateMaxSum(availableNumbers, startIndex, remainingSlots) {
|
|
136
|
+
let sum = 0;
|
|
137
|
+
let count = 0;
|
|
138
|
+
for (let i = availableNumbers.length - 1; i >= startIndex && count < remainingSlots; i--) {
|
|
139
|
+
sum += availableNumbers[i];
|
|
140
|
+
count++;
|
|
141
|
+
}
|
|
142
|
+
return sum;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
exports.CombinationGenerator = CombinationGenerator;
|
|
146
|
+
//# sourceMappingURL=combinationGenerator.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Result } from '@fgv/ts-utils';
|
|
2
|
+
import { CellId, ICage, Puzzle, PuzzleState } from '../../common';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export declare class PossibilityAnalyzer {
|
|
7
|
+
/**
|
|
8
|
+
* Analyze possible values for each cell in a killer cage based on current puzzle state.
|
|
9
|
+
* @param puzzle - The puzzle instance
|
|
10
|
+
* @param state - Current puzzle state
|
|
11
|
+
* @param cage - The killer cage to analyze
|
|
12
|
+
* @param validCombinations - Pre-computed valid combinations for the cage
|
|
13
|
+
* @returns Map of CellId to possible number arrays
|
|
14
|
+
*/
|
|
15
|
+
static analyze(puzzle: Puzzle, state: PuzzleState, cage: ICage, validCombinations: number[][]): Result<Map<CellId, number[]>>;
|
|
16
|
+
/**
|
|
17
|
+
* Check if a value can be placed in a cell considering sudoku constraints.
|
|
18
|
+
* @param cell - The cell to check
|
|
19
|
+
* @param value - The value to place
|
|
20
|
+
* @param state - Current puzzle state
|
|
21
|
+
* @returns True if the value can be placed
|
|
22
|
+
*/
|
|
23
|
+
private static _canPlaceValueInCell;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=possibilityAnalyzer.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* MIT License
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2023 Erik Fortune
|
|
6
|
+
*
|
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
* furnished to do so, subject to the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
* copies or substantial portions of the Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
* SOFTWARE.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.PossibilityAnalyzer = void 0;
|
|
27
|
+
const ts_utils_1 = require("@fgv/ts-utils");
|
|
28
|
+
/**
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
class PossibilityAnalyzer {
|
|
32
|
+
/**
|
|
33
|
+
* Analyze possible values for each cell in a killer cage based on current puzzle state.
|
|
34
|
+
* @param puzzle - The puzzle instance
|
|
35
|
+
* @param state - Current puzzle state
|
|
36
|
+
* @param cage - The killer cage to analyze
|
|
37
|
+
* @param validCombinations - Pre-computed valid combinations for the cage
|
|
38
|
+
* @returns Map of CellId to possible number arrays
|
|
39
|
+
*/
|
|
40
|
+
static analyze(puzzle, state, cage, validCombinations) {
|
|
41
|
+
const possibilities = new Map();
|
|
42
|
+
// Get current values in the cage
|
|
43
|
+
// We need the concrete Cage class for containedValues method
|
|
44
|
+
const concreteCage = cage;
|
|
45
|
+
const currentValues = concreteCage.containedValues(state);
|
|
46
|
+
const emptyCells = [];
|
|
47
|
+
// Identify empty cells and initialize possibilities
|
|
48
|
+
for (const cellId of cage.cellIds) {
|
|
49
|
+
const cellContents = state.getCellContents(cellId).orDefault();
|
|
50
|
+
/* c8 ignore next 1 - ? is defense in depth */
|
|
51
|
+
if ((cellContents === null || cellContents === void 0 ? void 0 : cellContents.value) !== undefined) {
|
|
52
|
+
// Cell already has a value - set empty possibilities array
|
|
53
|
+
possibilities.set(cellId, []);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Empty cell
|
|
57
|
+
emptyCells.push(cellId);
|
|
58
|
+
possibilities.set(cellId, []);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Filter combinations that are compatible with current state
|
|
62
|
+
const compatibleCombinations = validCombinations.filter((combination) => {
|
|
63
|
+
// Check if combination contains all current values
|
|
64
|
+
for (const currentValue of currentValues) {
|
|
65
|
+
if (!combination.includes(currentValue)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Check if combination has the right number of remaining values
|
|
70
|
+
const remainingValues = combination.filter((value) => !currentValues.has(value));
|
|
71
|
+
return remainingValues.length === emptyCells.length;
|
|
72
|
+
});
|
|
73
|
+
// If no compatible combinations exist, return empty possibilities for all cells
|
|
74
|
+
if (compatibleCombinations.length === 0) {
|
|
75
|
+
return (0, ts_utils_1.succeed)(possibilities);
|
|
76
|
+
}
|
|
77
|
+
// For each empty cell, find which values are possible
|
|
78
|
+
for (const cellId of emptyCells) {
|
|
79
|
+
const cellPossibilities = new Set();
|
|
80
|
+
// Get the cell to check sudoku constraints
|
|
81
|
+
const cellResult = puzzle.getCell(cellId);
|
|
82
|
+
/* c8 ignore next 3 - defensive coding: protects against internal cage/puzzle state corruption */
|
|
83
|
+
if (cellResult.isFailure()) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const cell = cellResult.value;
|
|
87
|
+
// Check each compatible combination
|
|
88
|
+
for (const combination of compatibleCombinations) {
|
|
89
|
+
const remainingValues = combination.filter((value) => !currentValues.has(value));
|
|
90
|
+
// Check if each remaining value could go in this cell
|
|
91
|
+
for (const value of remainingValues) {
|
|
92
|
+
if (this._canPlaceValueInCell(cell, value, state)) {
|
|
93
|
+
cellPossibilities.add(value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
possibilities.set(cellId, Array.from(cellPossibilities).sort());
|
|
98
|
+
}
|
|
99
|
+
return (0, ts_utils_1.succeed)(possibilities);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Check if a value can be placed in a cell considering sudoku constraints.
|
|
103
|
+
* @param cell - The cell to check
|
|
104
|
+
* @param value - The value to place
|
|
105
|
+
* @param state - Current puzzle state
|
|
106
|
+
* @returns True if the value can be placed
|
|
107
|
+
*/
|
|
108
|
+
static _canPlaceValueInCell(cell, value, state) {
|
|
109
|
+
// Check all cages this cell belongs to for sudoku constraints
|
|
110
|
+
for (const cage of cell.cages) {
|
|
111
|
+
if (cage.cageType === 'killer') {
|
|
112
|
+
// Skip killer cage check - we're already analyzing within killer constraints
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
// Check if value already exists in this constraint cage (row, column, section, etc.)
|
|
116
|
+
const concreteCage = cage;
|
|
117
|
+
if (concreteCage.containsValue(value, state, [cell.id])) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.PossibilityAnalyzer = PossibilityAnalyzer;
|
|
125
|
+
//# sourceMappingURL=possibilityAnalyzer.js.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Result } from '@fgv/ts-utils';
|
|
2
|
+
import { CellId, ICage, Puzzle, PuzzleState } from '../common';
|
|
3
|
+
import { IKillerConstraints } from './killerCombinationsTypes';
|
|
4
|
+
/**
|
|
5
|
+
* Helper class providing UI assistance functions for killer sudoku puzzle solving.
|
|
6
|
+
* Generates possible totals, number combinations with constraints, and cell-specific
|
|
7
|
+
* possibilities based on current puzzle state.
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export declare class KillerCombinations {
|
|
11
|
+
/**
|
|
12
|
+
* Gets all mathematically possible totals for a given cage size.
|
|
13
|
+
*
|
|
14
|
+
* Uses the existing totalsByCageSize constant to determine the valid range
|
|
15
|
+
* of totals for the specified cage size and returns all integers in that range.
|
|
16
|
+
*
|
|
17
|
+
* @param cageSize - The number of cells in the cage (must be 1-9)
|
|
18
|
+
* @returns Result containing array of possible totals in ascending order
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* // Get possible totals for a 3-cell cage
|
|
23
|
+
* const result = KillerCombinations.getPossibleTotals(3);
|
|
24
|
+
* if (result.isSuccess()) {
|
|
25
|
+
* console.log(result.value); // [6, 7, 8, 9, ..., 24]
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
static getPossibleTotals(cageSize: number, maxValue?: number): Result<number[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Generates all possible number combinations that sum to the target total.
|
|
32
|
+
*
|
|
33
|
+
* Each combination contains unique numbers from 1-9 that sum exactly to the
|
|
34
|
+
* specified total. Combinations respect both excluded and required number
|
|
35
|
+
* constraints if provided.
|
|
36
|
+
*
|
|
37
|
+
* @param cageSize - The number of cells in the cage (must be 1-9)
|
|
38
|
+
* @param total - The target sum (must be valid for the cage size)
|
|
39
|
+
* @param constraints - Optional constraints on included/excluded numbers
|
|
40
|
+
* @returns Result containing array of combinations, each sorted in ascending order
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Get all combinations for a 3-cell cage with total 15
|
|
45
|
+
* const result = KillerCombinations.getCombinations(3, 15);
|
|
46
|
+
* if (result.isSuccess()) {
|
|
47
|
+
* console.log(result.value); // [[1,5,9], [1,6,8], [2,4,9], ...]
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* // With constraints - exclude 1 and 2, require 9
|
|
51
|
+
* const constrained = KillerCombinations.getCombinations(3, 15, {
|
|
52
|
+
* excludedNumbers: [1, 2],
|
|
53
|
+
* requiredNumbers: [9]
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
static getCombinations(cageSize: number, total: number, constraints?: IKillerConstraints, maxValue?: number): Result<number[][]>;
|
|
58
|
+
/**
|
|
59
|
+
* Determines possible values for each cell in a killer cage based on current puzzle state.
|
|
60
|
+
*
|
|
61
|
+
* Analyzes the current state of the puzzle and cage to determine which values
|
|
62
|
+
* are possible for each empty cell, considering both killer cage constraints
|
|
63
|
+
* and standard sudoku constraints (row, column, section uniqueness).
|
|
64
|
+
*
|
|
65
|
+
* @param puzzle - The puzzle instance
|
|
66
|
+
* @param state - Current puzzle state
|
|
67
|
+
* @param cage - The killer cage to analyze (must be of type 'killer')
|
|
68
|
+
* @returns Result containing map of CellId to possible number arrays
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const cage = puzzle.getCage(cageId).orThrow();
|
|
73
|
+
* const possibilities = KillerCombinations.getCellPossibilities(puzzle, state, cage);
|
|
74
|
+
* if (possibilities.isSuccess()) {
|
|
75
|
+
* for (const [cellId, values] of possibilities.value) {
|
|
76
|
+
* console.log(`Cell ${cellId} can have values: ${values.join(', ')}`);
|
|
77
|
+
* }
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
static getCellPossibilities(puzzle: Puzzle, state: PuzzleState, cage: ICage): Result<Map<CellId, number[]>>;
|
|
82
|
+
/**
|
|
83
|
+
* Validate constraint parameters.
|
|
84
|
+
* @param constraints - Constraints to validate
|
|
85
|
+
* @param maxValue - Maximum value allowed in cells
|
|
86
|
+
* @returns Result indicating success or failure with error message
|
|
87
|
+
*/
|
|
88
|
+
private static _validateConstraints;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=killerCombinations.d.ts.map
|