@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,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
@@ -23,4 +23,4 @@
23
23
  * SOFTWARE.
24
24
  */
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- //# sourceMappingURL=model.js.map
26
+ //# sourceMappingURL=interfaces.js.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