@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.
Files changed (82) hide show
  1. package/debug_killer.js +50 -0
  2. package/debug_test.js +88 -0
  3. package/dist/ts-sudoku-lib.d.ts +1505 -42
  4. package/lib/index.d.ts +3 -2
  5. package/lib/index.js +5 -3
  6. package/lib/packlets/collections/collections.d.ts +15 -8
  7. package/lib/packlets/collections/collections.js +41 -11
  8. package/lib/packlets/collections/data/puzzles.json +28 -33
  9. package/lib/packlets/common/common.d.ts +15 -0
  10. package/lib/packlets/common/common.js +25 -0
  11. package/lib/packlets/common/converters.d.ts +0 -6
  12. package/lib/packlets/common/converters.js +17 -22
  13. package/lib/packlets/common/ids.d.ts +8 -1
  14. package/lib/packlets/common/ids.js +83 -10
  15. package/lib/packlets/common/index.d.ts +3 -2
  16. package/lib/packlets/common/index.js +5 -2
  17. package/lib/packlets/common/logging.d.ts +7 -0
  18. package/lib/packlets/common/logging.js +35 -0
  19. package/lib/packlets/common/puzzle.d.ts +23 -13
  20. package/lib/packlets/common/puzzle.js +51 -25
  21. package/lib/packlets/common/puzzleDefinitions.d.ts +127 -0
  22. package/lib/packlets/common/puzzleDefinitions.js +251 -0
  23. package/lib/packlets/common/puzzleSession.d.ts +8 -0
  24. package/lib/packlets/common/puzzleSession.js +12 -0
  25. package/lib/packlets/common/puzzleState.js +3 -0
  26. package/lib/packlets/files/converters.d.ts +27 -0
  27. package/lib/packlets/files/converters.js +71 -0
  28. package/lib/packlets/{file/converters.d.ts → files/filesystem.d.ts} +3 -8
  29. package/lib/packlets/{file/converters.js → files/filesystem.js} +5 -14
  30. package/lib/packlets/files/index.d.ts +5 -0
  31. package/lib/packlets/{file → files}/index.js +3 -1
  32. package/lib/packlets/files/model.d.ts +32 -0
  33. package/lib/packlets/hints/baseHintProvider.d.ts +103 -0
  34. package/lib/packlets/hints/baseHintProvider.js +203 -0
  35. package/lib/packlets/hints/explanations.d.ts +105 -0
  36. package/lib/packlets/hints/explanations.js +276 -0
  37. package/lib/packlets/hints/hiddenSingles.d.ts +81 -0
  38. package/lib/packlets/hints/hiddenSingles.js +237 -0
  39. package/lib/packlets/hints/hintRegistry.d.ts +95 -0
  40. package/lib/packlets/hints/hintRegistry.js +226 -0
  41. package/lib/packlets/hints/hints.d.ts +144 -0
  42. package/lib/packlets/hints/hints.js +316 -0
  43. package/lib/packlets/hints/index.d.ts +10 -0
  44. package/lib/packlets/hints/index.js +55 -0
  45. package/lib/packlets/hints/interfaces.d.ts +121 -0
  46. package/lib/packlets/{file/model.js → hints/interfaces.js} +1 -1
  47. package/lib/packlets/hints/nakedSingles.d.ts +57 -0
  48. package/lib/packlets/hints/nakedSingles.js +202 -0
  49. package/lib/packlets/hints/puzzleSessionHints.d.ts +306 -0
  50. package/lib/packlets/hints/puzzleSessionHints.js +499 -0
  51. package/lib/packlets/hints/types.d.ts +102 -0
  52. package/lib/packlets/hints/types.js +46 -0
  53. package/lib/packlets/puzzles/anyPuzzle.d.ts +3 -3
  54. package/lib/packlets/puzzles/anyPuzzle.js +1 -1
  55. package/lib/packlets/puzzles/index.d.ts +4 -2
  56. package/lib/packlets/puzzles/index.js +20 -3
  57. package/lib/packlets/puzzles/internal/combinationCache.d.ts +29 -0
  58. package/lib/packlets/puzzles/internal/combinationCache.js +79 -0
  59. package/lib/packlets/puzzles/internal/combinationGenerator.d.ts +34 -0
  60. package/lib/packlets/puzzles/internal/combinationGenerator.js +146 -0
  61. package/lib/packlets/puzzles/internal/possibilityAnalyzer.d.ts +25 -0
  62. package/lib/packlets/puzzles/internal/possibilityAnalyzer.js +125 -0
  63. package/lib/packlets/puzzles/killerCombinations.d.ts +90 -0
  64. package/lib/packlets/puzzles/killerCombinations.js +226 -0
  65. package/lib/packlets/puzzles/killerCombinationsTypes.d.ts +17 -0
  66. package/lib/packlets/puzzles/killerCombinationsTypes.js +26 -0
  67. package/lib/packlets/puzzles/killerSudokuPuzzle.d.ts +2 -2
  68. package/lib/packlets/puzzles/killerSudokuPuzzle.js +15 -11
  69. package/lib/packlets/puzzles/sudokuPuzzle.d.ts +2 -2
  70. package/lib/packlets/puzzles/sudokuXPuzzle.d.ts +2 -2
  71. package/lib/packlets/puzzles/sudokuXPuzzle.js +8 -4
  72. package/package.json +5 -5
  73. package/sample-12x12-puzzle.js +72 -0
  74. package/sample-4x4-puzzles.js +68 -0
  75. package/sample-6x6-puzzle.js +61 -0
  76. package/temp_12x12_cells.txt +12 -0
  77. package/temp_correct.txt +12 -0
  78. package/test-grid-sizes.js +132 -0
  79. package/lib/packlets/common/model.d.ts +0 -15
  80. package/lib/packlets/file/index.d.ts +0 -4
  81. package/lib/packlets/file/model.d.ts +0 -9
  82. /package/lib/packlets/{common → files}/model.js +0 -0
@@ -0,0 +1,27 @@
1
+ import { Converter, Result } from '@fgv/ts-utils';
2
+ import { IPuzzlesFile, IPuzzleFileData, IPuzzleFileDimensions } from './model';
3
+ import { FileTree } from '@fgv/ts-json-base';
4
+ /**
5
+ * Converts puzzle file dimensions from JSON format
6
+ * @public
7
+ */
8
+ export declare const puzzleFileDimensions: Converter<IPuzzleFileDimensions>;
9
+ /**
10
+ * Converts puzzle file data from JSON format
11
+ * @public
12
+ */
13
+ export declare const puzzleFileData: Converter<IPuzzleFileData>;
14
+ /**
15
+ * Converts an arbitrary object to a {@link Files.Model.IPuzzlesFile | IPuzzlesFile}.
16
+ * @public
17
+ */
18
+ export declare const puzzlesFile: Converter<IPuzzlesFile>;
19
+ /**
20
+ * Loads a puzzles file from a `IFileTreeFileItem`.
21
+ * @param file - The `IFileTreeFileItem` to load.
22
+ * @returns `Success` with the resulting {@link Files.Model.IPuzzlesFile | IPuzzlesFile}, or `Failure` with
23
+ * details if an error occurs.
24
+ * @public
25
+ */
26
+ export declare function loadPuzzlesFile(file: FileTree.IFileTreeFileItem): Result<IPuzzlesFile>;
27
+ //# sourceMappingURL=converters.d.ts.map
@@ -0,0 +1,71 @@
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.puzzlesFile = exports.puzzleFileData = exports.puzzleFileDimensions = void 0;
27
+ exports.loadPuzzlesFile = loadPuzzlesFile;
28
+ const ts_utils_1 = require("@fgv/ts-utils");
29
+ const common_1 = require("../common");
30
+ /**
31
+ * Converts puzzle file dimensions from JSON format
32
+ * @public
33
+ */
34
+ exports.puzzleFileDimensions = ts_utils_1.Converters.strictObject({
35
+ cageWidthInCells: ts_utils_1.Converters.number,
36
+ cageHeightInCells: ts_utils_1.Converters.number,
37
+ boardWidthInCages: ts_utils_1.Converters.number,
38
+ boardHeightInCages: ts_utils_1.Converters.number
39
+ });
40
+ /**
41
+ * Converts puzzle file data from JSON format
42
+ * @public
43
+ */
44
+ exports.puzzleFileData = ts_utils_1.Converters.strictObject({
45
+ id: ts_utils_1.Converters.string,
46
+ description: ts_utils_1.Converters.string,
47
+ type: common_1.Converters.puzzleType,
48
+ level: ts_utils_1.Converters.number,
49
+ cells: ts_utils_1.Converters.oneOf([ts_utils_1.Converters.string, ts_utils_1.Converters.stringArray.map((s) => (0, ts_utils_1.succeed)(s.join('')))]),
50
+ dimensions: exports.puzzleFileDimensions
51
+ }, {
52
+ optionalFields: ['id']
53
+ });
54
+ /**
55
+ * Converts an arbitrary object to a {@link Files.Model.IPuzzlesFile | IPuzzlesFile}.
56
+ * @public
57
+ */
58
+ exports.puzzlesFile = ts_utils_1.Converters.strictObject({
59
+ puzzles: ts_utils_1.Converters.arrayOf(exports.puzzleFileData)
60
+ });
61
+ /**
62
+ * Loads a puzzles file from a `IFileTreeFileItem`.
63
+ * @param file - The `IFileTreeFileItem` to load.
64
+ * @returns `Success` with the resulting {@link Files.Model.IPuzzlesFile | IPuzzlesFile}, or `Failure` with
65
+ * details if an error occurs.
66
+ * @public
67
+ */
68
+ function loadPuzzlesFile(file) {
69
+ return file.getContents(exports.puzzlesFile);
70
+ }
71
+ //# sourceMappingURL=converters.js.map
@@ -1,17 +1,12 @@
1
- import { Converter, Result } from '@fgv/ts-utils';
1
+ import { Result } from '@fgv/ts-utils';
2
2
  import { IPuzzlesFile } from './model';
3
- /**
4
- * Converts an arbitrary object to a {@link File.Model.IPuzzlesFile | IPuzzlesFile}.
5
- * @public
6
- */
7
- export declare const puzzlesFile: Converter<IPuzzlesFile>;
8
3
  /**
9
4
  * Loads an arbitrary JSON file and parses it to return a validated
10
- * {@link File.Model.IPuzzlesFile | IPuzzlesFile}.
5
+ * {@link Files.Model.IPuzzlesFile | IPuzzlesFile}.
11
6
  * @param path - String path to the file
12
7
  * @returns `Success` with the resulting file, or `Failure` with details if an
13
8
  * error occurs.
14
9
  * @public
15
10
  */
16
11
  export declare function loadJsonPuzzlesFileSync(path: string): Result<IPuzzlesFile>;
17
- //# sourceMappingURL=converters.d.ts.map
12
+ //# sourceMappingURL=filesystem.d.ts.map
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  * MIT License
4
4
  *
5
- * Copyright (c) 2023 Erik Fortune
5
+ * Copyright (c) 2025 Erik Fortune
6
6
  *
7
7
  * Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  * of this software and associated documentation files (the "Software"), to deal
@@ -23,27 +23,18 @@
23
23
  * SOFTWARE.
24
24
  */
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.puzzlesFile = void 0;
27
26
  exports.loadJsonPuzzlesFileSync = loadJsonPuzzlesFileSync;
28
- const ts_utils_1 = require("@fgv/ts-utils");
27
+ const converters_1 = require("./converters");
29
28
  const ts_json_base_1 = require("@fgv/ts-json-base");
30
- const common_1 = require("../common");
31
- /**
32
- * Converts an arbitrary object to a {@link File.Model.IPuzzlesFile | IPuzzlesFile}.
33
- * @public
34
- */
35
- exports.puzzlesFile = ts_utils_1.Converters.strictObject({
36
- puzzles: ts_utils_1.Converters.arrayOf(common_1.Converters.puzzleDescription)
37
- });
38
29
  /**
39
30
  * Loads an arbitrary JSON file and parses it to return a validated
40
- * {@link File.Model.IPuzzlesFile | IPuzzlesFile}.
31
+ * {@link Files.Model.IPuzzlesFile | IPuzzlesFile}.
41
32
  * @param path - String path to the file
42
33
  * @returns `Success` with the resulting file, or `Failure` with details if an
43
34
  * error occurs.
44
35
  * @public
45
36
  */
46
37
  function loadJsonPuzzlesFileSync(path) {
47
- return ts_json_base_1.JsonFile.convertJsonFileSync(path, exports.puzzlesFile);
38
+ return ts_json_base_1.JsonFile.convertJsonFileSync(path, converters_1.puzzlesFile);
48
39
  }
49
- //# sourceMappingURL=converters.js.map
40
+ //# sourceMappingURL=filesystem.js.map
@@ -0,0 +1,5 @@
1
+ import * as Converters from './converters';
2
+ import * as Model from './model';
3
+ import * as FileSystem from './filesystem';
4
+ export { Converters, Model, FileSystem };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -56,9 +56,11 @@ var __importStar = (this && this.__importStar) || (function () {
56
56
  };
57
57
  })();
58
58
  Object.defineProperty(exports, "__esModule", { value: true });
59
- exports.Model = exports.Converters = void 0;
59
+ exports.FileSystem = exports.Model = exports.Converters = void 0;
60
60
  const Converters = __importStar(require("./converters"));
61
61
  exports.Converters = Converters;
62
62
  const Model = __importStar(require("./model"));
63
63
  exports.Model = Model;
64
+ const FileSystem = __importStar(require("./filesystem"));
65
+ exports.FileSystem = FileSystem;
64
66
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,32 @@
1
+ import { PuzzleType } from '../common';
2
+ /**
3
+ * Dimensional configuration for puzzles
4
+ * @public
5
+ */
6
+ export interface IPuzzleFileDimensions {
7
+ cageWidthInCells: number;
8
+ cageHeightInCells: number;
9
+ boardWidthInCages: number;
10
+ boardHeightInCages: number;
11
+ }
12
+ /**
13
+ * Input format for puzzle data in JSON files.
14
+ * Contains only the essential data needed to create an IPuzzleDefinition.
15
+ * @public
16
+ */
17
+ export interface IPuzzleFileData {
18
+ id?: string;
19
+ description: string;
20
+ type: PuzzleType;
21
+ level: number;
22
+ cells: string;
23
+ dimensions: IPuzzleFileDimensions;
24
+ }
25
+ /**
26
+ * Parsed file containing a collection of puzzles.
27
+ * @public
28
+ */
29
+ export interface IPuzzlesFile {
30
+ puzzles: IPuzzleFileData[];
31
+ }
32
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1,103 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { CellId, Puzzle, PuzzleState } from '../common';
3
+ import { IHintProvider } from './interfaces';
4
+ import { ConfidenceLevel, DifficultyLevel, ExplanationLevel, ICellAction, IHint, IHintExplanation, IHintGenerationOptions, IRelevantCells, TechniqueId } from './types';
5
+ /**
6
+ * Configuration for a {@link Hints.BaseHintProvider | BaseHintProvider} instance.
7
+ * @public
8
+ */
9
+ export interface IBaseHintProviderConfig {
10
+ readonly techniqueId: TechniqueId;
11
+ readonly techniqueName: string;
12
+ readonly difficulty: DifficultyLevel;
13
+ readonly priority: number;
14
+ readonly defaultConfidence?: ConfidenceLevel;
15
+ }
16
+ /**
17
+ * Abstract base class providing common functionality for hint providers.
18
+ * @public
19
+ */
20
+ export declare abstract class BaseHintProvider implements IHintProvider {
21
+ readonly techniqueId: TechniqueId;
22
+ readonly techniqueName: string;
23
+ readonly difficulty: DifficultyLevel;
24
+ readonly priority: number;
25
+ protected readonly defaultConfidence: ConfidenceLevel;
26
+ protected constructor(config: IBaseHintProviderConfig);
27
+ /**
28
+ * Abstract method to be implemented by concrete providers.
29
+ * Determines if this provider can potentially generate hints for the given puzzle.
30
+ */
31
+ abstract canProvideHints(puzzle: Puzzle, state: PuzzleState): boolean;
32
+ /**
33
+ * Abstract method to be implemented by concrete providers.
34
+ * Generates all possible hints using this technique for the given puzzle.
35
+ */
36
+ abstract generateHints(puzzle: Puzzle, state: PuzzleState, options?: IHintGenerationOptions): Result<readonly IHint[]>;
37
+ /**
38
+ * Utility method to create a hint with consistent structure.
39
+ * @param cellActions - The actions to be performed on cells
40
+ * @param relevantCells - The cells relevant to understanding the hint
41
+ * @param explanations - The explanations for the hint
42
+ * @param confidence - The confidence level for this hint
43
+ * @returns A complete hint object
44
+ */
45
+ protected createHint(cellActions: readonly ICellAction[], relevantCells: IRelevantCells, explanations: readonly IHintExplanation[], confidence?: ConfidenceLevel): IHint;
46
+ /**
47
+ * Utility method to create cell actions.
48
+ * @param cellId - The ID of the cell to act upon
49
+ * @param action - The type of action to perform
50
+ * @param value - Optional value for set-value actions
51
+ * @param reason - Optional reason for the action
52
+ * @returns A cell action object
53
+ */
54
+ protected createCellAction(cellId: CellId, action: ICellAction['action'], value?: number, reason?: string): ICellAction;
55
+ /**
56
+ * Utility method to create relevant cells grouping.
57
+ * @param primary - Primary cells that are the focus of the hint
58
+ * @param secondary - Secondary cells that provide context
59
+ * @param affected - Cells that will be affected by applying the hint
60
+ * @returns A relevant cells object
61
+ */
62
+ protected createRelevantCells(primary: readonly CellId[], secondary?: readonly CellId[], affected?: readonly CellId[]): IRelevantCells;
63
+ /**
64
+ * Utility method to create hint explanations.
65
+ * @param level - The level of detail for the explanation
66
+ * @param title - The title of the explanation
67
+ * @param description - The main description
68
+ * @param steps - Optional step-by-step instructions
69
+ * @param tips - Optional tips for understanding the technique
70
+ * @returns A hint explanation object
71
+ */
72
+ protected createExplanation(level: ExplanationLevel, title: string, description: string, steps?: readonly string[], tips?: readonly string[]): IHintExplanation;
73
+ /**
74
+ * Filters hints based on generation options.
75
+ * @param hints - The hints to filter
76
+ * @param options - The filtering options
77
+ * @returns Filtered array of hints
78
+ */
79
+ protected filterHints(hints: readonly IHint[], options?: IHintGenerationOptions): readonly IHint[];
80
+ /**
81
+ * Validates generation options for consistency.
82
+ * @param options - The options to validate
83
+ * @returns Result indicating validation success or failure
84
+ */
85
+ protected validateOptions(options?: IHintGenerationOptions): Result<void>;
86
+ /**
87
+ * Gets all empty cells in the puzzle.
88
+ * @param puzzle - The puzzle structure
89
+ * @param state - The puzzle state to analyze
90
+ * @returns Array of cell IDs that are empty
91
+ */
92
+ protected getEmptyCells(puzzle: Puzzle, state: PuzzleState): CellId[];
93
+ /**
94
+ * Gets the possible candidate values for a specific cell using cage-based constraints.
95
+ * This implementation works with any puzzle variant by checking all applicable cages.
96
+ * @param cellId - The cell to analyze
97
+ * @param puzzle - The puzzle structure containing constraints
98
+ * @param state - The current puzzle state
99
+ * @returns Array of possible values for the cell
100
+ */
101
+ protected getCandidates(cellId: CellId, puzzle: Puzzle, state: PuzzleState): number[];
102
+ }
103
+ //# sourceMappingURL=baseHintProvider.d.ts.map
@@ -0,0 +1,203 @@
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.BaseHintProvider = void 0;
27
+ const ts_utils_1 = require("@fgv/ts-utils");
28
+ const types_1 = require("./types");
29
+ /**
30
+ * Abstract base class providing common functionality for hint providers.
31
+ * @public
32
+ */
33
+ class BaseHintProvider {
34
+ constructor(config) {
35
+ var _a;
36
+ this.techniqueId = config.techniqueId;
37
+ this.techniqueName = config.techniqueName;
38
+ this.difficulty = config.difficulty;
39
+ this.priority = config.priority;
40
+ this.defaultConfidence = (_a = config.defaultConfidence) !== null && _a !== void 0 ? _a : types_1.ConfidenceLevels.MEDIUM;
41
+ }
42
+ /**
43
+ * Utility method to create a hint with consistent structure.
44
+ * @param cellActions - The actions to be performed on cells
45
+ * @param relevantCells - The cells relevant to understanding the hint
46
+ * @param explanations - The explanations for the hint
47
+ * @param confidence - The confidence level for this hint
48
+ * @returns A complete hint object
49
+ */
50
+ createHint(cellActions, relevantCells, explanations, confidence) {
51
+ /* c8 ignore next 1 - defense in depth */
52
+ confidence = confidence !== null && confidence !== void 0 ? confidence : this.defaultConfidence;
53
+ return {
54
+ techniqueId: this.techniqueId,
55
+ techniqueName: this.techniqueName,
56
+ difficulty: this.difficulty,
57
+ confidence,
58
+ cellActions,
59
+ relevantCells,
60
+ explanations,
61
+ priority: this.priority
62
+ };
63
+ }
64
+ /**
65
+ * Utility method to create cell actions.
66
+ * @param cellId - The ID of the cell to act upon
67
+ * @param action - The type of action to perform
68
+ * @param value - Optional value for set-value actions
69
+ * @param reason - Optional reason for the action
70
+ * @returns A cell action object
71
+ */
72
+ createCellAction(cellId, action, value, reason) {
73
+ return {
74
+ cellId,
75
+ action,
76
+ value,
77
+ reason
78
+ };
79
+ }
80
+ /**
81
+ * Utility method to create relevant cells grouping.
82
+ * @param primary - Primary cells that are the focus of the hint
83
+ * @param secondary - Secondary cells that provide context
84
+ * @param affected - Cells that will be affected by applying the hint
85
+ * @returns A relevant cells object
86
+ */
87
+ createRelevantCells(primary, secondary = [], affected = []) {
88
+ return {
89
+ primary,
90
+ secondary,
91
+ affected
92
+ };
93
+ }
94
+ /**
95
+ * Utility method to create hint explanations.
96
+ * @param level - The level of detail for the explanation
97
+ * @param title - The title of the explanation
98
+ * @param description - The main description
99
+ * @param steps - Optional step-by-step instructions
100
+ * @param tips - Optional tips for understanding the technique
101
+ * @returns A hint explanation object
102
+ */
103
+ createExplanation(level, title, description, steps, tips) {
104
+ return {
105
+ level,
106
+ title,
107
+ description,
108
+ steps,
109
+ tips
110
+ };
111
+ }
112
+ /**
113
+ * Filters hints based on generation options.
114
+ * @param hints - The hints to filter
115
+ * @param options - The filtering options
116
+ * @returns Filtered array of hints
117
+ */
118
+ filterHints(hints, options) {
119
+ if (!options) {
120
+ return hints;
121
+ }
122
+ let filtered = [...hints];
123
+ // Filter by minimum confidence
124
+ if (options.minConfidence) {
125
+ filtered = filtered.filter((hint) => hint.confidence >= options.minConfidence);
126
+ }
127
+ // Filter by enabled techniques
128
+ if (options.enabledTechniques && options.enabledTechniques.length > 0) {
129
+ filtered = filtered.filter((hint) => options.enabledTechniques.includes(hint.techniqueId));
130
+ }
131
+ // Limit number of hints
132
+ if (options.maxHints && options.maxHints > 0) {
133
+ // Sort by confidence (descending) then priority (ascending)
134
+ filtered.sort((a, b) => {
135
+ if (a.confidence !== b.confidence) {
136
+ return b.confidence - a.confidence;
137
+ }
138
+ return a.priority - b.priority;
139
+ });
140
+ filtered = filtered.slice(0, options.maxHints);
141
+ }
142
+ return filtered;
143
+ }
144
+ /**
145
+ * Validates generation options for consistency.
146
+ * @param options - The options to validate
147
+ * @returns Result indicating validation success or failure
148
+ */
149
+ validateOptions(options) {
150
+ if (!options) {
151
+ return (0, ts_utils_1.succeed)(undefined);
152
+ }
153
+ if (options.maxHints !== undefined && options.maxHints < 0) {
154
+ return (0, ts_utils_1.fail)('maxHints cannot be negative');
155
+ }
156
+ if (options.minConfidence !== undefined && (options.minConfidence < 1 || options.minConfidence > 5)) {
157
+ return (0, ts_utils_1.fail)('minConfidence must be between 1 and 5');
158
+ }
159
+ return (0, ts_utils_1.succeed)(undefined);
160
+ }
161
+ /**
162
+ * Gets all empty cells in the puzzle.
163
+ * @param puzzle - The puzzle structure
164
+ * @param state - The puzzle state to analyze
165
+ * @returns Array of cell IDs that are empty
166
+ */
167
+ getEmptyCells(puzzle, state) {
168
+ return puzzle.getEmptyCells(state).map((cell) => cell.id);
169
+ }
170
+ /**
171
+ * Gets the possible candidate values for a specific cell using cage-based constraints.
172
+ * This implementation works with any puzzle variant by checking all applicable cages.
173
+ * @param cellId - The cell to analyze
174
+ * @param puzzle - The puzzle structure containing constraints
175
+ * @param state - The current puzzle state
176
+ * @returns Array of possible values for the cell
177
+ */
178
+ getCandidates(cellId, puzzle, state) {
179
+ if (state.hasValue(cellId)) {
180
+ return [];
181
+ }
182
+ // Get maximum value based on grid size (typically sqrt of total cells)
183
+ const maxValue = Math.sqrt(puzzle.numRows * puzzle.numColumns);
184
+ // Find all cages that contain this cell
185
+ const applicableCages = puzzle.cages.filter((cage) => cage.containsCell(cellId));
186
+ // Collect all values that are forbidden due to cage constraints
187
+ const usedValues = new Set();
188
+ for (const cage of applicableCages) {
189
+ const cageValues = cage.containedValues(state);
190
+ cageValues.forEach((value) => usedValues.add(value));
191
+ }
192
+ // Generate candidates (values not used in any applicable cage)
193
+ const candidates = [];
194
+ for (let value = 1; value <= maxValue; value++) {
195
+ if (!usedValues.has(value)) {
196
+ candidates.push(value);
197
+ }
198
+ }
199
+ return candidates;
200
+ }
201
+ }
202
+ exports.BaseHintProvider = BaseHintProvider;
203
+ //# sourceMappingURL=baseHintProvider.js.map
@@ -0,0 +1,105 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { Puzzle, PuzzleState } from '../common';
3
+ import { IHintExplanationProvider } from './interfaces';
4
+ import { ExplanationLevel, IHint, IHintExplanation, TechniqueId } from './types';
5
+ /**
6
+ * Registry for managing hint explanation providers.
7
+ * @public
8
+ */
9
+ export declare class ExplanationRegistry {
10
+ private readonly _providers;
11
+ /**
12
+ * Creates a new ExplanationRegistry instance.
13
+ */
14
+ constructor();
15
+ /**
16
+ * Registers a new explanation provider.
17
+ * @param provider - The provider to register
18
+ * @returns Result indicating success or failure of registration
19
+ */
20
+ registerProvider(provider: IHintExplanationProvider): Result<void>;
21
+ /**
22
+ * Gets explanations for a specific hint.
23
+ * @param hint - The hint to explain
24
+ * @param puzzle - The puzzle structure containing constraints
25
+ * @param state - The puzzle state context
26
+ * @returns Result containing the explanations
27
+ */
28
+ getExplanations(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<readonly IHintExplanation[]>;
29
+ /**
30
+ * Gets a specific explanation at the requested level.
31
+ * @param hint - The hint to explain
32
+ * @param level - The desired explanation level
33
+ * @param state - The puzzle state context
34
+ * @returns Result containing the explanation at the specified level
35
+ */
36
+ getExplanationAtLevel(hint: IHint, level: ExplanationLevel, puzzle: Puzzle, state: PuzzleState): Result<IHintExplanation>;
37
+ }
38
+ /**
39
+ * Utility class for formatting and displaying hint explanations.
40
+ * @public
41
+ */
42
+ export declare class ExplanationFormatter {
43
+ /**
44
+ * Formats a hint explanation as a readable string.
45
+ * @param explanation - The explanation to format
46
+ * @param includeSteps - Whether to include step-by-step instructions
47
+ * @param includeTips - Whether to include tips
48
+ * @returns Formatted explanation string
49
+ */
50
+ static formatExplanation(explanation: IHintExplanation, includeSteps?: boolean, includeTips?: boolean): string;
51
+ /**
52
+ * Formats all explanations for a hint as a structured string.
53
+ * @param explanations - The explanations to format
54
+ * @returns Formatted explanations string
55
+ */
56
+ static formatAllExplanations(explanations: readonly IHintExplanation[]): string;
57
+ /**
58
+ * Creates a summary of available explanation levels.
59
+ * @param explanations - The explanations to summarize
60
+ * @returns Summary string
61
+ */
62
+ static createLevelSummary(explanations: readonly IHintExplanation[]): string;
63
+ }
64
+ /**
65
+ * Educational content manager for Sudoku solving techniques.
66
+ * @public
67
+ */
68
+ export declare class EducationalContent {
69
+ private static readonly _techniqueIntroductions;
70
+ private static readonly _techniqueRelationships;
71
+ /**
72
+ * Gets an introduction to a specific technique.
73
+ * @param techniqueId - The technique to introduce
74
+ * @returns Result containing the introduction text
75
+ */
76
+ static getTechniqueIntroduction(techniqueId: TechniqueId): Result<string>;
77
+ /**
78
+ * Gets relationship information for a technique.
79
+ * @param techniqueId - The technique to get relationships for
80
+ * @returns Result containing the relationship information
81
+ */
82
+ static getTechniqueRelationships(techniqueId: TechniqueId): Result<readonly string[]>;
83
+ /**
84
+ * Gets a complete educational overview for a technique.
85
+ * @param techniqueId - The technique to describe
86
+ * @returns Result containing the complete overview
87
+ */
88
+ static getTechniqueOverview(techniqueId: TechniqueId): Result<string>;
89
+ /**
90
+ * Gets general Sudoku solving advice.
91
+ * @returns Array of general solving tips
92
+ */
93
+ static getGeneralSolvingTips(): readonly string[];
94
+ /**
95
+ * Gets difficulty progression advice.
96
+ * @returns Advice for progressing through difficulty levels
97
+ */
98
+ static getDifficultyProgression(): readonly string[];
99
+ /**
100
+ * Creates a complete educational guide for beginners.
101
+ * @returns Comprehensive beginner guide
102
+ */
103
+ static createBeginnerGuide(): string;
104
+ }
105
+ //# sourceMappingURL=explanations.d.ts.map