@fgv/ts-sudoku-lib 5.0.1-1 → 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,10 @@
|
|
|
1
|
+
export * from './types';
|
|
2
|
+
export * from './interfaces';
|
|
3
|
+
export * from './baseHintProvider';
|
|
4
|
+
export * from './hintRegistry';
|
|
5
|
+
export * from './nakedSingles';
|
|
6
|
+
export * from './hiddenSingles';
|
|
7
|
+
export * from './explanations';
|
|
8
|
+
export * from './hints';
|
|
9
|
+
export * from './puzzleSessionHints';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
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
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
// Core types and interfaces
|
|
41
|
+
__exportStar(require("./types"), exports);
|
|
42
|
+
__exportStar(require("./interfaces"), exports);
|
|
43
|
+
// Base classes and utilities
|
|
44
|
+
__exportStar(require("./baseHintProvider"), exports);
|
|
45
|
+
__exportStar(require("./hintRegistry"), exports);
|
|
46
|
+
// Technique implementations
|
|
47
|
+
__exportStar(require("./nakedSingles"), exports);
|
|
48
|
+
__exportStar(require("./hiddenSingles"), exports);
|
|
49
|
+
// Educational framework
|
|
50
|
+
__exportStar(require("./explanations"), exports);
|
|
51
|
+
// Main hint system
|
|
52
|
+
__exportStar(require("./hints"), exports);
|
|
53
|
+
// PuzzleSession integration
|
|
54
|
+
__exportStar(require("./puzzleSessionHints"), exports);
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Result } from '@fgv/ts-utils';
|
|
2
|
+
import { ICellState, Puzzle, PuzzleState } from '../common';
|
|
3
|
+
import { DifficultyLevel, IHint, IHintExplanation, IHintGenerationOptions, TechniqueId } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Interface for classes that can provide hints using a specific solving technique.
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export interface IHintProvider {
|
|
9
|
+
readonly techniqueId: TechniqueId;
|
|
10
|
+
readonly techniqueName: string;
|
|
11
|
+
readonly difficulty: DifficultyLevel;
|
|
12
|
+
readonly priority: number;
|
|
13
|
+
/**
|
|
14
|
+
* Determines if this provider can potentially generate hints for the given puzzle.
|
|
15
|
+
* This should be a fast check to avoid expensive hint generation when not applicable.
|
|
16
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
17
|
+
* @param state - The current puzzle state
|
|
18
|
+
* @returns true if this provider might be able to generate hints
|
|
19
|
+
*/
|
|
20
|
+
canProvideHints(puzzle: Puzzle, state: PuzzleState): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Generates all possible hints using this technique for the given puzzle.
|
|
23
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
24
|
+
* @param state - The current puzzle state
|
|
25
|
+
* @param options - Optional generation options
|
|
26
|
+
* @returns Result containing array of hints, or failure if generation fails
|
|
27
|
+
*/
|
|
28
|
+
generateHints(puzzle: Puzzle, state: PuzzleState, options?: IHintGenerationOptions): Result<readonly IHint[]>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Interface for managing and coordinating multiple hint providers.
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
export interface IHintRegistry {
|
|
35
|
+
/**
|
|
36
|
+
* Registers a new hint provider.
|
|
37
|
+
* @param provider - The provider to register
|
|
38
|
+
* @returns Result indicating success or failure of registration
|
|
39
|
+
*/
|
|
40
|
+
registerProvider(provider: IHintProvider): Result<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Unregisters a hint provider.
|
|
43
|
+
* @param techniqueId - The ID of the technique to unregister
|
|
44
|
+
* @returns Result indicating success or failure of unregistration
|
|
45
|
+
*/
|
|
46
|
+
unregisterProvider(techniqueId: TechniqueId): Result<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Gets a specific provider by technique ID.
|
|
49
|
+
* @param techniqueId - The ID of the technique
|
|
50
|
+
* @returns Result containing the provider, or failure if not found
|
|
51
|
+
*/
|
|
52
|
+
getProvider(techniqueId: TechniqueId): Result<IHintProvider>;
|
|
53
|
+
/**
|
|
54
|
+
* Gets all registered providers, optionally filtered by criteria.
|
|
55
|
+
* @param options - Optional filtering options
|
|
56
|
+
* @returns Array of providers matching the criteria
|
|
57
|
+
*/
|
|
58
|
+
getProviders(options?: IHintGenerationOptions): readonly IHintProvider[];
|
|
59
|
+
/**
|
|
60
|
+
* Generates hints using all applicable providers.
|
|
61
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
62
|
+
* @param state - The current puzzle state
|
|
63
|
+
* @param options - Optional generation options
|
|
64
|
+
* @returns Result containing array of hints from all providers
|
|
65
|
+
*/
|
|
66
|
+
generateAllHints(puzzle: Puzzle, state: PuzzleState, options?: IHintGenerationOptions): Result<readonly IHint[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Gets the best available hint based on difficulty and confidence.
|
|
69
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
70
|
+
* @param state - The current puzzle state
|
|
71
|
+
* @param options - Optional generation options
|
|
72
|
+
* @returns Result containing the best hint, or failure if no hints available
|
|
73
|
+
*/
|
|
74
|
+
getBestHint(puzzle: Puzzle, state: PuzzleState, options?: IHintGenerationOptions): Result<IHint>;
|
|
75
|
+
/**
|
|
76
|
+
* Gets all registered technique IDs.
|
|
77
|
+
* @returns Array of technique IDs
|
|
78
|
+
*/
|
|
79
|
+
getRegisteredTechniques(): readonly TechniqueId[];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Interface for hint explanation generation and formatting.
|
|
83
|
+
* @public
|
|
84
|
+
*/
|
|
85
|
+
export interface IHintExplanationProvider {
|
|
86
|
+
/**
|
|
87
|
+
* Gets the technique ID this explanation provider supports.
|
|
88
|
+
*/
|
|
89
|
+
readonly techniqueId: TechniqueId;
|
|
90
|
+
/**
|
|
91
|
+
* Generates explanations for a specific hint.
|
|
92
|
+
* @param hint - The hint to explain
|
|
93
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
94
|
+
* @param state - The puzzle state context
|
|
95
|
+
* @returns Result containing array of explanations at different levels
|
|
96
|
+
*/
|
|
97
|
+
generateExplanations(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<readonly IHintExplanation[]>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Interface for hint application and validation.
|
|
101
|
+
* @public
|
|
102
|
+
*/
|
|
103
|
+
export interface IHintApplicator {
|
|
104
|
+
/**
|
|
105
|
+
* Validates that a hint can be safely applied to the given state.
|
|
106
|
+
* @param hint - The hint to validate
|
|
107
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
108
|
+
* @param state - The current puzzle state
|
|
109
|
+
* @returns Result indicating validation success or failure with details
|
|
110
|
+
*/
|
|
111
|
+
canApplyHint(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Applies a hint to the puzzle state, generating the necessary cell updates.
|
|
114
|
+
* @param hint - The hint to apply
|
|
115
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
116
|
+
* @param state - The current puzzle state
|
|
117
|
+
* @returns Result containing the cell state updates needed to apply the hint
|
|
118
|
+
*/
|
|
119
|
+
applyHint(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<readonly ICellState[]>;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=interfaces.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Result } from '@fgv/ts-utils';
|
|
2
|
+
import { Puzzle, PuzzleState } from '../common';
|
|
3
|
+
import { BaseHintProvider } from './baseHintProvider';
|
|
4
|
+
import { IHint, IHintGenerationOptions } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Hint provider for the Naked Singles technique.
|
|
7
|
+
*
|
|
8
|
+
* A Naked Single occurs when a cell has only one possible candidate value
|
|
9
|
+
* based on the constraints of its row, column, and 3x3 box.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export declare class NakedSinglesProvider extends BaseHintProvider {
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new NakedSinglesProvider instance.
|
|
16
|
+
*/
|
|
17
|
+
constructor();
|
|
18
|
+
/**
|
|
19
|
+
* Determines if this provider can potentially generate hints for the given puzzle.
|
|
20
|
+
* Always returns true since naked singles can potentially exist in any incomplete puzzle.
|
|
21
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
22
|
+
* @param state - The current puzzle state
|
|
23
|
+
* @returns true if there are empty cells that might have naked singles
|
|
24
|
+
*/
|
|
25
|
+
canProvideHints(puzzle: Puzzle, state: PuzzleState): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Generates all naked single hints for the given puzzle.
|
|
28
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
29
|
+
* @param state - The current puzzle state
|
|
30
|
+
* @param options - Optional generation options
|
|
31
|
+
* @returns Result containing array of naked single hints
|
|
32
|
+
*/
|
|
33
|
+
generateHints(puzzle: Puzzle, state: PuzzleState, options?: IHintGenerationOptions): Result<readonly IHint[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Creates a hint for a specific naked single.
|
|
36
|
+
* @param cellId - The cell containing the naked single
|
|
37
|
+
* @param value - The single possible value for the cell
|
|
38
|
+
* @param state - The current puzzle state
|
|
39
|
+
* @returns A complete hint for this naked single
|
|
40
|
+
*/
|
|
41
|
+
private _createNakedSingleHint;
|
|
42
|
+
/**
|
|
43
|
+
* Finds cells in the same row, column, and box that constrain the given cell.
|
|
44
|
+
* @param cellId - The cell to analyze
|
|
45
|
+
* @param value - The value that will be placed
|
|
46
|
+
* @param state - The current puzzle state
|
|
47
|
+
* @param puzzle - The puzzle for dimensions
|
|
48
|
+
* @returns Array of cell IDs that are related to this naked single
|
|
49
|
+
*/
|
|
50
|
+
private _findRelatedCells;
|
|
51
|
+
/**
|
|
52
|
+
* Static factory method to create a new NakedSinglesProvider.
|
|
53
|
+
* @returns Result containing the new provider
|
|
54
|
+
*/
|
|
55
|
+
static create(): Result<NakedSinglesProvider>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=nakedSingles.d.ts.map
|
|
@@ -0,0 +1,202 @@
|
|
|
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.NakedSinglesProvider = void 0;
|
|
27
|
+
const ts_utils_1 = require("@fgv/ts-utils");
|
|
28
|
+
const common_1 = require("../common");
|
|
29
|
+
const baseHintProvider_1 = require("./baseHintProvider");
|
|
30
|
+
const types_1 = require("./types");
|
|
31
|
+
/**
|
|
32
|
+
* Hint provider for the Naked Singles technique.
|
|
33
|
+
*
|
|
34
|
+
* A Naked Single occurs when a cell has only one possible candidate value
|
|
35
|
+
* based on the constraints of its row, column, and 3x3 box.
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
class NakedSinglesProvider extends baseHintProvider_1.BaseHintProvider {
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new NakedSinglesProvider instance.
|
|
42
|
+
*/
|
|
43
|
+
constructor() {
|
|
44
|
+
super({
|
|
45
|
+
techniqueId: types_1.TechniqueIds.NAKED_SINGLES,
|
|
46
|
+
techniqueName: 'Naked Singles',
|
|
47
|
+
difficulty: 'beginner',
|
|
48
|
+
priority: 1, // Highest priority - should be checked first
|
|
49
|
+
defaultConfidence: types_1.ConfidenceLevels.HIGH
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Determines if this provider can potentially generate hints for the given puzzle.
|
|
54
|
+
* Always returns true since naked singles can potentially exist in any incomplete puzzle.
|
|
55
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
56
|
+
* @param state - The current puzzle state
|
|
57
|
+
* @returns true if there are empty cells that might have naked singles
|
|
58
|
+
*/
|
|
59
|
+
canProvideHints(puzzle, state) {
|
|
60
|
+
// Quick check: if there are empty cells, there might be naked singles
|
|
61
|
+
const emptyCells = this.getEmptyCells(puzzle, state);
|
|
62
|
+
return emptyCells.length > 0;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Generates all naked single hints for the given puzzle.
|
|
66
|
+
* @param puzzle - The puzzle structure containing constraints
|
|
67
|
+
* @param state - The current puzzle state
|
|
68
|
+
* @param options - Optional generation options
|
|
69
|
+
* @returns Result containing array of naked single hints
|
|
70
|
+
*/
|
|
71
|
+
generateHints(puzzle, state, options) {
|
|
72
|
+
return this.validateOptions(options).onSuccess(() => {
|
|
73
|
+
const hints = [];
|
|
74
|
+
const emptyCells = this.getEmptyCells(puzzle, state);
|
|
75
|
+
for (const cellId of emptyCells) {
|
|
76
|
+
const candidates = this.getCandidates(cellId, puzzle, state);
|
|
77
|
+
// A naked single has exactly one candidate
|
|
78
|
+
if (candidates.length === 1) {
|
|
79
|
+
const value = candidates[0];
|
|
80
|
+
const hint = this._createNakedSingleHint(cellId, value, state, puzzle);
|
|
81
|
+
hints.push(hint);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return (0, ts_utils_1.succeed)(this.filterHints(hints, options));
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Creates a hint for a specific naked single.
|
|
89
|
+
* @param cellId - The cell containing the naked single
|
|
90
|
+
* @param value - The single possible value for the cell
|
|
91
|
+
* @param state - The current puzzle state
|
|
92
|
+
* @returns A complete hint for this naked single
|
|
93
|
+
*/
|
|
94
|
+
_createNakedSingleHint(cellId, value, state, puzzle) {
|
|
95
|
+
// Create cell action to set the value
|
|
96
|
+
const cellAction = this.createCellAction(cellId, 'set-value', value, `Only possible value for this cell`);
|
|
97
|
+
// Find related cells that eliminate other candidates
|
|
98
|
+
const relatedCells = this._findRelatedCells(cellId, value, state, puzzle);
|
|
99
|
+
// Create relevant cells grouping
|
|
100
|
+
const relevantCells = this.createRelevantCells([cellId], // Primary: the cell with the naked single
|
|
101
|
+
relatedCells, // Secondary: cells that constrain this cell
|
|
102
|
+
[] // Affected: none for naked singles
|
|
103
|
+
);
|
|
104
|
+
// Create explanations at different levels
|
|
105
|
+
const explanations = [
|
|
106
|
+
this.createExplanation('brief', 'Naked Single', `Cell ${cellId} can only contain the value ${value}.`, [`Set ${cellId} = ${value}`], ['Look for cells with only one possible value']),
|
|
107
|
+
this.createExplanation('detailed', 'Naked Single Analysis', `Cell ${cellId} has only one possible candidate: ${value}. All other values (1-9) are ` +
|
|
108
|
+
`eliminated by existing numbers in the same row, column, or 3x3 box.`, [
|
|
109
|
+
`Examine cell ${cellId}`,
|
|
110
|
+
`Check which values 1-9 are already used in the same row, column, and 3x3 box`,
|
|
111
|
+
`Identify that only ${value} is not eliminated`,
|
|
112
|
+
`Set ${cellId} = ${value}`
|
|
113
|
+
], [
|
|
114
|
+
'Naked singles are the most basic solving technique',
|
|
115
|
+
'Always check for naked singles first',
|
|
116
|
+
'A cell with one candidate must contain that value'
|
|
117
|
+
]),
|
|
118
|
+
this.createExplanation('educational', 'Understanding Naked Singles', `A naked single occurs when a cell has only one possible value due to Sudoku's fundamental rules. ` +
|
|
119
|
+
`In cell ${cellId}, the value ${value} is the only number from 1-${puzzle.dimensions.maxValue} that doesn't already appear in the ` +
|
|
120
|
+
`same row, column, or section. This makes it a "naked" single because the solution is immediately visible.`, [
|
|
121
|
+
`Locate cell ${cellId} in the grid`,
|
|
122
|
+
`Scan the row containing ${cellId} and note which numbers 1-${puzzle.dimensions.maxValue} are already placed`,
|
|
123
|
+
`Scan the column containing ${cellId} and note which numbers 1-${puzzle.dimensions.maxValue} are already placed`,
|
|
124
|
+
`Scan the section containing ${cellId} and note which numbers 1-${puzzle.dimensions.maxValue} are already placed`,
|
|
125
|
+
`Combine all the "used" numbers from row, column, and section`,
|
|
126
|
+
`The remaining unused number is ${value}, which must go in ${cellId}`,
|
|
127
|
+
`Place ${value} in cell ${cellId}`
|
|
128
|
+
], [
|
|
129
|
+
'Naked singles are always correct - there is no risk in placing them',
|
|
130
|
+
'Start every solving session by finding all naked singles',
|
|
131
|
+
'Placing naked singles often reveals new naked singles in related cells',
|
|
132
|
+
'This technique forms the foundation for all other Sudoku solving methods'
|
|
133
|
+
])
|
|
134
|
+
];
|
|
135
|
+
return this.createHint([cellAction], relevantCells, explanations, types_1.ConfidenceLevels.HIGH);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Finds cells in the same row, column, and box that constrain the given cell.
|
|
139
|
+
* @param cellId - The cell to analyze
|
|
140
|
+
* @param value - The value that will be placed
|
|
141
|
+
* @param state - The current puzzle state
|
|
142
|
+
* @param puzzle - The puzzle for dimensions
|
|
143
|
+
* @returns Array of cell IDs that are related to this naked single
|
|
144
|
+
*/
|
|
145
|
+
_findRelatedCells(cellId, value, state, puzzle) {
|
|
146
|
+
const relatedCells = [];
|
|
147
|
+
// Parse the cell ID to get row and column
|
|
148
|
+
const coords = (0, common_1.parseCellId)(cellId.toString());
|
|
149
|
+
/* c8 ignore next 3 - defensive coding: handles corrupted cell ID, impractical to test via public API */
|
|
150
|
+
if (!coords) {
|
|
151
|
+
return relatedCells;
|
|
152
|
+
}
|
|
153
|
+
const { row, col } = coords;
|
|
154
|
+
const totalRows = puzzle.dimensions.totalRows;
|
|
155
|
+
const totalColumns = puzzle.dimensions.totalColumns;
|
|
156
|
+
const cageHeight = puzzle.dimensions.cageHeightInCells;
|
|
157
|
+
const cageWidth = puzzle.dimensions.cageWidthInCells;
|
|
158
|
+
// Find cells in the same row, column, and section that contain any value
|
|
159
|
+
// These are the cells that constrain our naked single
|
|
160
|
+
// Same row
|
|
161
|
+
for (let c = 0; c < totalColumns; c++) {
|
|
162
|
+
if (c !== col) {
|
|
163
|
+
const checkIdResult = common_1.Ids.cellId({ row, col: c });
|
|
164
|
+
if (checkIdResult.isSuccess() && state.hasValue(checkIdResult.value)) {
|
|
165
|
+
relatedCells.push(checkIdResult.value);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Same column
|
|
170
|
+
for (let r = 0; r < totalRows; r++) {
|
|
171
|
+
if (r !== row) {
|
|
172
|
+
const checkIdResult = common_1.Ids.cellId({ row: r, col });
|
|
173
|
+
if (checkIdResult.isSuccess() && state.hasValue(checkIdResult.value)) {
|
|
174
|
+
relatedCells.push(checkIdResult.value);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Same section/cage
|
|
179
|
+
const boxStartRow = Math.floor(row / cageHeight) * cageHeight;
|
|
180
|
+
const boxStartCol = Math.floor(col / cageWidth) * cageWidth;
|
|
181
|
+
for (let r = boxStartRow; r < boxStartRow + cageHeight; r++) {
|
|
182
|
+
for (let c = boxStartCol; c < boxStartCol + cageWidth; c++) {
|
|
183
|
+
if (r !== row || c !== col) {
|
|
184
|
+
const checkIdResult = common_1.Ids.cellId({ row: r, col: c });
|
|
185
|
+
if (checkIdResult.isSuccess() && state.hasValue(checkIdResult.value)) {
|
|
186
|
+
relatedCells.push(checkIdResult.value);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return relatedCells;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Static factory method to create a new NakedSinglesProvider.
|
|
195
|
+
* @returns Result containing the new provider
|
|
196
|
+
*/
|
|
197
|
+
static create() {
|
|
198
|
+
return (0, ts_utils_1.succeed)(new NakedSinglesProvider());
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
exports.NakedSinglesProvider = NakedSinglesProvider;
|
|
202
|
+
//# sourceMappingURL=nakedSingles.js.map
|