@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
|
@@ -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 {
|
|
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
|
|
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=
|
|
12
|
+
//# sourceMappingURL=filesystem.d.ts.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/*
|
|
3
3
|
* MIT License
|
|
4
4
|
*
|
|
5
|
-
* Copyright (c)
|
|
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
|
|
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
|
|
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,
|
|
38
|
+
return ts_json_base_1.JsonFile.convertJsonFileSync(path, converters_1.puzzlesFile);
|
|
48
39
|
}
|
|
49
|
-
//# sourceMappingURL=
|
|
40
|
+
//# sourceMappingURL=filesystem.js.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
|