@fgv/ts-sudoku-lib 5.1.0-2 → 5.1.0-20
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/dist/index.browser.js.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/packlets/collections/collections.js +1 -1
- package/dist/packlets/collections/collections.js.map +1 -0
- package/dist/packlets/collections/index.js.map +1 -0
- package/dist/packlets/common/cage.js.map +1 -0
- package/dist/packlets/common/cell.js.map +1 -0
- package/dist/packlets/common/common.js.map +1 -0
- package/dist/packlets/common/converters.js.map +1 -0
- package/dist/packlets/common/ids.js.map +1 -0
- package/dist/packlets/common/index.js.map +1 -0
- package/dist/packlets/common/logging.js.map +1 -0
- package/dist/packlets/common/public.js.map +1 -0
- package/dist/packlets/common/puzzle.js +1 -1
- package/dist/packlets/common/puzzle.js.map +1 -0
- package/dist/packlets/common/puzzleDefinitions.js.map +1 -0
- package/dist/packlets/common/puzzleSession.js.map +1 -0
- package/dist/packlets/common/puzzleState.js.map +1 -0
- package/dist/packlets/files/converters.js.map +1 -0
- package/dist/packlets/files/fileTreeHelpers.js.map +1 -0
- package/dist/packlets/files/filesystem.js.map +1 -0
- package/dist/packlets/files/index.browser.js.map +1 -0
- package/dist/packlets/files/index.js.map +1 -0
- package/dist/packlets/files/model.js.map +1 -0
- package/dist/packlets/hints/baseHintProvider.js.map +1 -0
- package/dist/packlets/hints/explanations.js.map +1 -0
- package/dist/packlets/hints/hiddenSingles.js.map +1 -0
- package/dist/packlets/hints/hintRegistry.js.map +1 -0
- package/dist/packlets/hints/hints.js.map +1 -0
- package/dist/packlets/hints/index.js.map +1 -0
- package/dist/packlets/hints/interfaces.js.map +1 -0
- package/dist/packlets/hints/nakedSingles.js.map +1 -0
- package/dist/packlets/hints/puzzleSessionHints.js.map +1 -0
- package/dist/packlets/hints/types.js.map +1 -0
- package/dist/packlets/puzzles/anyPuzzle.js.map +1 -0
- package/dist/packlets/puzzles/index.js.map +1 -0
- package/dist/packlets/puzzles/internal/combinationCache.js.map +1 -0
- package/dist/packlets/puzzles/internal/combinationGenerator.js.map +1 -0
- package/dist/packlets/puzzles/internal/possibilityAnalyzer.js.map +1 -0
- package/dist/packlets/puzzles/killerCombinations.js.map +1 -0
- package/dist/packlets/puzzles/killerCombinationsTypes.js.map +1 -0
- package/dist/packlets/puzzles/killerSudokuPuzzle.js.map +1 -0
- package/dist/packlets/puzzles/sudokuPuzzle.js.map +1 -0
- package/dist/packlets/puzzles/sudokuXPuzzle.js.map +1 -0
- package/dist/ts-sudoku-lib.d.ts +8 -8
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/index.browser.d.ts.map +1 -0
- package/lib/index.browser.js.map +1 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js.map +1 -0
- package/lib/packlets/collections/collections.d.ts +1 -1
- package/lib/packlets/collections/collections.d.ts.map +1 -0
- package/lib/packlets/collections/collections.js +1 -1
- package/lib/packlets/collections/collections.js.map +1 -0
- package/lib/packlets/collections/index.d.ts.map +1 -0
- package/lib/packlets/collections/index.js.map +1 -0
- package/lib/packlets/common/cage.d.ts.map +1 -0
- package/lib/packlets/common/cage.js.map +1 -0
- package/lib/packlets/common/cell.d.ts.map +1 -0
- package/lib/packlets/common/cell.js.map +1 -0
- package/lib/packlets/common/common.d.ts +5 -5
- package/lib/packlets/common/common.d.ts.map +1 -0
- package/lib/packlets/common/common.js.map +1 -0
- package/lib/packlets/common/converters.d.ts.map +1 -0
- package/lib/packlets/common/converters.js.map +1 -0
- package/lib/packlets/common/ids.d.ts.map +1 -0
- package/lib/packlets/common/ids.js.map +1 -0
- package/lib/packlets/common/index.d.ts.map +1 -0
- package/lib/packlets/common/index.js.map +1 -0
- package/lib/packlets/common/logging.d.ts.map +1 -0
- package/lib/packlets/common/logging.js.map +1 -0
- package/lib/packlets/common/public.d.ts +1 -1
- package/lib/packlets/common/public.d.ts.map +1 -0
- package/lib/packlets/common/public.js.map +1 -0
- package/lib/packlets/common/puzzle.d.ts +1 -1
- package/lib/packlets/common/puzzle.d.ts.map +1 -0
- package/lib/packlets/common/puzzle.js +1 -1
- package/lib/packlets/common/puzzle.js.map +1 -0
- package/lib/packlets/common/puzzleDefinitions.d.ts.map +1 -0
- package/lib/packlets/common/puzzleDefinitions.js.map +1 -0
- package/lib/packlets/common/puzzleSession.d.ts.map +1 -0
- package/lib/packlets/common/puzzleSession.js.map +1 -0
- package/lib/packlets/common/puzzleState.d.ts.map +1 -0
- package/lib/packlets/common/puzzleState.js.map +1 -0
- package/lib/packlets/files/converters.d.ts.map +1 -0
- package/lib/packlets/files/converters.js.map +1 -0
- package/lib/packlets/files/fileTreeHelpers.d.ts.map +1 -0
- package/lib/packlets/files/fileTreeHelpers.js.map +1 -0
- package/lib/packlets/files/filesystem.d.ts.map +1 -0
- package/lib/packlets/files/filesystem.js.map +1 -0
- package/lib/packlets/files/index.browser.d.ts.map +1 -0
- package/lib/packlets/files/index.browser.js.map +1 -0
- package/lib/packlets/files/index.d.ts.map +1 -0
- package/lib/packlets/files/index.js.map +1 -0
- package/lib/packlets/files/model.d.ts.map +1 -0
- package/lib/packlets/files/model.js.map +1 -0
- package/lib/packlets/hints/baseHintProvider.d.ts.map +1 -0
- package/lib/packlets/hints/baseHintProvider.js.map +1 -0
- package/lib/packlets/hints/explanations.d.ts.map +1 -0
- package/lib/packlets/hints/explanations.js.map +1 -0
- package/lib/packlets/hints/hiddenSingles.d.ts.map +1 -0
- package/lib/packlets/hints/hiddenSingles.js.map +1 -0
- package/lib/packlets/hints/hintRegistry.d.ts.map +1 -0
- package/lib/packlets/hints/hintRegistry.js.map +1 -0
- package/lib/packlets/hints/hints.d.ts.map +1 -0
- package/lib/packlets/hints/hints.js.map +1 -0
- package/lib/packlets/hints/index.d.ts.map +1 -0
- package/lib/packlets/hints/index.js.map +1 -0
- package/lib/packlets/hints/interfaces.d.ts.map +1 -0
- package/lib/packlets/hints/interfaces.js.map +1 -0
- package/lib/packlets/hints/nakedSingles.d.ts.map +1 -0
- package/lib/packlets/hints/nakedSingles.js.map +1 -0
- package/lib/packlets/hints/puzzleSessionHints.d.ts.map +1 -0
- package/lib/packlets/hints/puzzleSessionHints.js.map +1 -0
- package/lib/packlets/hints/types.d.ts.map +1 -0
- package/lib/packlets/hints/types.js.map +1 -0
- package/lib/packlets/puzzles/anyPuzzle.d.ts.map +1 -0
- package/lib/packlets/puzzles/anyPuzzle.js.map +1 -0
- package/lib/packlets/puzzles/index.d.ts.map +1 -0
- package/lib/packlets/puzzles/index.js.map +1 -0
- package/lib/packlets/puzzles/internal/combinationCache.d.ts.map +1 -0
- package/lib/packlets/puzzles/internal/combinationCache.js.map +1 -0
- package/lib/packlets/puzzles/internal/combinationGenerator.d.ts.map +1 -0
- package/lib/packlets/puzzles/internal/combinationGenerator.js.map +1 -0
- package/lib/packlets/puzzles/internal/possibilityAnalyzer.d.ts.map +1 -0
- package/lib/packlets/puzzles/internal/possibilityAnalyzer.js.map +1 -0
- package/lib/packlets/puzzles/killerCombinations.d.ts.map +1 -0
- package/lib/packlets/puzzles/killerCombinations.js.map +1 -0
- package/lib/packlets/puzzles/killerCombinationsTypes.d.ts.map +1 -0
- package/lib/packlets/puzzles/killerCombinationsTypes.js.map +1 -0
- package/lib/packlets/puzzles/killerSudokuPuzzle.d.ts.map +1 -0
- package/lib/packlets/puzzles/killerSudokuPuzzle.js.map +1 -0
- package/lib/packlets/puzzles/sudokuPuzzle.d.ts.map +1 -0
- package/lib/packlets/puzzles/sudokuPuzzle.js.map +1 -0
- package/lib/packlets/puzzles/sudokuXPuzzle.d.ts.map +1 -0
- package/lib/packlets/puzzles/sudokuXPuzzle.js.map +1 -0
- package/package.json +23 -21
- package/debug_test.js +0 -88
- package/dist/test/unit/helpers/puzzleBuilders.js +0 -149
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hintRegistry.js","sourceRoot":"","sources":["../../../src/packlets/hints/hintRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, fail, succeed, mapResults } from '@fgv/ts-utils';\nimport { Puzzle, PuzzleState } from '../common';\nimport { IHintProvider, IHintRegistry } from './interfaces';\nimport { DifficultyLevel, IHint, IHintGenerationOptions, TechniqueId } from './types';\n\n/**\n * Implementation of the hint registry that manages and coordinates multiple hint providers.\n * @public\n */\nexport class HintRegistry implements IHintRegistry {\n private readonly _providers: Map<TechniqueId, IHintProvider>;\n\n /**\n * Creates a new HintRegistry instance.\n */\n public constructor() {\n this._providers = new Map();\n }\n\n /**\n * Creates a new HintRegistry with the specified providers pre-registered.\n * @param providers - The providers to register\n * @returns Result containing the new registry or failure if registration fails\n */\n public static create(providers?: readonly IHintProvider[]): Result<HintRegistry> {\n const registry = new HintRegistry();\n\n if (providers && providers.length > 0) {\n for (const provider of providers) {\n const result = registry.registerProvider(provider);\n if (result.isFailure()) {\n return fail(`Failed to register provider ${provider.techniqueId}: ${result.message}`);\n }\n }\n }\n\n return succeed(registry);\n }\n\n /**\n * Registers a new hint provider.\n * @param provider - The provider to register\n * @returns Result indicating success or failure of registration\n */\n public registerProvider(provider: IHintProvider): Result<void> {\n if (this._providers.has(provider.techniqueId)) {\n return fail(`Provider for technique ${provider.techniqueId} is already registered`);\n }\n\n this._providers.set(provider.techniqueId, provider);\n return succeed(undefined);\n }\n\n /**\n * Unregisters a hint provider.\n * @param techniqueId - The ID of the technique to unregister\n * @returns Result indicating success or failure of unregistration\n */\n public unregisterProvider(techniqueId: TechniqueId): Result<void> {\n if (!this._providers.has(techniqueId)) {\n return fail(`No provider registered for technique ${techniqueId}`);\n }\n\n this._providers.delete(techniqueId);\n return succeed(undefined);\n }\n\n /**\n * Gets a specific provider by technique ID.\n * @param techniqueId - The ID of the technique\n * @returns Result containing the provider, or failure if not found\n */\n public getProvider(techniqueId: TechniqueId): Result<IHintProvider> {\n const provider = this._providers.get(techniqueId);\n return provider ? succeed(provider) : fail(`No provider found for technique ${techniqueId}`);\n }\n\n /**\n * Gets all registered providers, optionally filtered by criteria.\n * @param options - Optional filtering options\n * @returns Array of providers matching the criteria\n */\n public getProviders(options?: IHintGenerationOptions): readonly IHintProvider[] {\n const providers = Array.from(this._providers.values());\n\n if (!options) {\n return providers;\n }\n\n let filtered = providers;\n\n // Filter by enabled techniques\n if (options.enabledTechniques && options.enabledTechniques.length > 0) {\n filtered = filtered.filter((provider) => options.enabledTechniques!.includes(provider.techniqueId));\n }\n\n // Filter by preferred difficulty (exact match for now)\n if (options.preferredDifficulty) {\n filtered = filtered.filter((provider) => provider.difficulty === options.preferredDifficulty);\n }\n\n return filtered;\n }\n\n /**\n * Generates hints using all applicable providers.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @param options - Optional generation options\n * @returns Result containing array of hints from all providers\n */\n public generateAllHints(\n puzzle: Puzzle,\n state: PuzzleState,\n options?: IHintGenerationOptions\n ): Result<readonly IHint[]> {\n const applicableProviders = this.getProviders(options).filter((provider) =>\n provider.canProvideHints(puzzle, state)\n );\n\n if (applicableProviders.length === 0) {\n return succeed([]);\n }\n\n // Generate hints from all applicable providers\n const hintResults = applicableProviders.map((provider) => provider.generateHints(puzzle, state, options));\n\n return mapResults(hintResults).onSuccess((allHints) => {\n // Flatten the array of hint arrays\n const flatHints = allHints.flat();\n\n // Sort by priority (ascending) then confidence (descending)\n const sortedHints = flatHints.sort((a, b) => {\n if (a.priority !== b.priority) {\n return a.priority - b.priority;\n }\n return b.confidence - a.confidence;\n });\n\n // Apply global filtering options\n return this._applyGlobalFiltering(sortedHints, options);\n });\n }\n\n /**\n * Gets the best available hint based on difficulty and confidence.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @param options - Optional generation options\n * @returns Result containing the best hint, or failure if no hints available\n */\n public getBestHint(puzzle: Puzzle, state: PuzzleState, options?: IHintGenerationOptions): Result<IHint> {\n return this.generateAllHints(puzzle, state, options).onSuccess((hints) => {\n if (hints.length === 0) {\n return fail('No hints available for the current puzzle state');\n }\n\n // Return the first hint (already sorted by priority and confidence)\n return succeed(hints[0]);\n });\n }\n\n /**\n * Gets the number of registered providers.\n * @returns The number of registered providers\n */\n public get providerCount(): number {\n return this._providers.size;\n }\n\n /**\n * Gets all registered technique IDs.\n * @returns Array of technique IDs\n */\n public getRegisteredTechniques(): readonly TechniqueId[] {\n return Array.from(this._providers.keys());\n }\n\n /**\n * Checks if a specific technique is registered.\n * @param techniqueId - The technique ID to check\n * @returns true if the technique is registered\n */\n public hasProvider(techniqueId: TechniqueId): boolean {\n return this._providers.has(techniqueId);\n }\n\n /**\n * Clears all registered providers.\n * @returns Result indicating success\n */\n public clear(): Result<void> {\n this._providers.clear();\n return succeed(undefined);\n }\n\n /**\n * Gets providers grouped by difficulty level.\n * @returns Map of difficulty levels to providers\n */\n public getProvidersByDifficulty(): Map<DifficultyLevel, readonly IHintProvider[]> {\n const providersByDifficulty = new Map<DifficultyLevel, IHintProvider[]>();\n\n for (const provider of this._providers.values()) {\n const existing = providersByDifficulty.get(provider.difficulty) ?? [];\n existing.push(provider);\n providersByDifficulty.set(provider.difficulty, existing);\n }\n\n // Convert to readonly arrays\n const result = new Map<DifficultyLevel, readonly IHintProvider[]>();\n for (const [difficulty, providers] of providersByDifficulty.entries()) {\n result.set(difficulty, providers);\n }\n\n return result;\n }\n\n /**\n * Applies global filtering options to the collected hints.\n * @param hints - The hints to filter\n * @param options - The filtering options\n * @returns Result containing the filtered hints\n */\n private _applyGlobalFiltering(\n hints: readonly IHint[],\n options?: IHintGenerationOptions\n ): Result<readonly IHint[]> {\n if (!options) {\n return succeed(hints);\n }\n\n let filtered = [...hints];\n\n // Apply minimum confidence filter\n if (options.minConfidence) {\n filtered = filtered.filter((hint) => hint.confidence >= options.minConfidence!);\n }\n\n // Apply maximum hints limit\n if (options.maxHints && options.maxHints > 0 && filtered.length > options.maxHints) {\n filtered = filtered.slice(0, options.maxHints);\n }\n\n return succeed(filtered);\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hints.js","sourceRoot":"","sources":["../../../src/packlets/hints/hints.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, fail, succeed, Logging, mapResults } from '@fgv/ts-utils';\nimport { ICellState, Puzzle, PuzzleState, DefaultSudokuLogger } from '../common';\nimport { ExplanationFormatter } from './explanations';\nimport { HintRegistry } from './hintRegistry';\nimport { HiddenSinglesProvider } from './hiddenSingles';\nimport { IHintApplicator, IHintRegistry } from './interfaces';\nimport { NakedSinglesProvider } from './nakedSingles';\nimport { ExplanationLevel, IHint, IHintGenerationOptions, TechniqueId } from './types';\n\n/**\n * Configuration options for the hint system.\n * @public\n */\nexport interface IHintSystemConfig {\n readonly enableNakedSingles?: boolean;\n readonly enableHiddenSingles?: boolean;\n readonly defaultExplanationLevel?: ExplanationLevel;\n readonly logger?: Logging.LogReporter<unknown>;\n}\n\n/**\n * Default hint applicator that converts hints to cell state updates.\n * @public\n */\nexport class DefaultHintApplicator implements IHintApplicator {\n private readonly _log: Logging.LogReporter<unknown>;\n\n /**\n * Creates a new {@link Hints.DefaultHintApplicator | DefaultHintApplicator}.\n * @param logger - Optional logger for diagnostic output\n */\n public constructor(logger?: Logging.LogReporter<unknown>) {\n /* c8 ignore next 1 - defense in depth */\n this._log = logger ?? DefaultSudokuLogger;\n }\n\n /**\n * Validates that a hint can be safely applied to the given state.\n * @param hint - The hint to validate\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns Result indicating validation success or failure with details\n */\n public canApplyHint(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<void> {\n const maxValue = Math.sqrt(puzzle.numRows * puzzle.numColumns);\n\n // Validate all cell actions\n const validations = hint.cellActions.map((action) => {\n // Only support set-value actions for now\n if (action.action !== 'set-value') {\n return fail(`Unsupported action type: ${action.action}`);\n }\n\n // Validate the value to be set\n if (action.value === undefined) {\n return fail(`No value specified for set-value action on cell ${action.cellId}`);\n }\n\n if (action.value < 1 || action.value > maxValue) {\n return fail(`Invalid value ${action.value} for cell ${action.cellId} (must be 1-${maxValue})`);\n }\n\n // Check that the cell exists and is empty\n return state\n .getCellContents(action.cellId)\n .withErrorFormat((e) => `Invalid cell ${action.cellId}: ${e}`)\n .onSuccess((cellContents) => {\n if (cellContents.value !== undefined) {\n return fail(`Cell ${action.cellId} already has value ${cellContents.value}`);\n }\n return succeed(undefined);\n });\n });\n\n return mapResults(validations).onSuccess(() => succeed(undefined));\n }\n\n /**\n * Applies a hint to the puzzle state, generating the necessary cell updates.\n * @param hint - The hint to apply\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns Result containing the cell state updates needed to apply the hint\n */\n public applyHint(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<readonly ICellState[]> {\n this._log.detail(`Applying hint: ${hint.techniqueName} affecting ${hint.cellActions.length} cell(s)`);\n\n return this.canApplyHint(hint, puzzle, state).onSuccess(() => {\n const updates: ICellState[] = [];\n\n for (const action of hint.cellActions) {\n if (action.action === 'set-value' && action.value !== undefined) {\n // Get current cell contents to preserve notes\n const cellResult = state.getCellContents(action.cellId);\n /* c8 ignore next 3 - internal error should never happen in practice */\n if (cellResult.isFailure()) {\n return fail(`Failed to get cell contents: ${cellResult.message}`);\n }\n\n updates.push({\n id: action.cellId,\n value: action.value,\n notes: cellResult.value.notes // Preserve existing notes\n });\n }\n }\n\n this._log.info(`Successfully applied hint: ${hint.techniqueName}, updated ${updates.length} cell(s)`);\n return succeed(updates);\n });\n }\n}\n\n/**\n * Main hint system that provides comprehensive hint generation and management.\n * @public\n */\nexport class HintSystem {\n private readonly _registry: IHintRegistry;\n private readonly _applicator: IHintApplicator;\n private readonly _config: IHintSystemConfig;\n private readonly _log: Logging.LogReporter<unknown>;\n private readonly _logHints: Logging.LogReporter<ReadonlyArray<IHint>>;\n\n /**\n * Creates a new HintSystem instance.\n * @param registry - The hint registry to use\n * @param applicator - The hint applicator to use\n * @param config - Configuration options\n */\n public constructor(registry: IHintRegistry, applicator: IHintApplicator, config: IHintSystemConfig) {\n this._registry = registry;\n this._applicator = applicator;\n this._config = config;\n this._log = config.logger ?? DefaultSudokuLogger;\n this._logHints = this._log.withValueFormatter(\n (hints: readonly IHint[]) =>\n `Generated ${hints.length} hint(s) using ${\n new Set(hints.map((h) => h.techniqueId)).size\n } technique(s)`\n );\n }\n\n /**\n * Creates a new HintSystem with default providers and configuration.\n * @param config - Optional configuration options\n * @returns Result containing the new hint system\n */\n public static create(config?: IHintSystemConfig): Result<HintSystem> {\n const finalConfig: IHintSystemConfig = {\n enableNakedSingles: true,\n enableHiddenSingles: true,\n defaultExplanationLevel: 'detailed',\n ...config\n };\n\n return HintRegistry.create()\n .withErrorFormat((e) => `Failed to create hint registry: ${e}`)\n .onSuccess((registry) => {\n // Register naked singles provider if enabled\n if (finalConfig.enableNakedSingles) {\n const nakedResult = NakedSinglesProvider.create()\n .withErrorFormat((e) => `Failed to create naked singles provider: ${e}`)\n .onSuccess((provider) => registry.registerProvider(provider))\n .withErrorFormat((e) => `Failed to register naked singles provider: ${e}`);\n\n /* c8 ignore next 3 - defensive coding: provider creation/registration should not fail */\n if (nakedResult.isFailure()) {\n return fail<HintSystem>(nakedResult.message);\n }\n }\n\n // Register hidden singles provider if enabled\n if (finalConfig.enableHiddenSingles) {\n const hiddenResult = HiddenSinglesProvider.create()\n .withErrorFormat((e) => `Failed to create hidden singles provider: ${e}`)\n .onSuccess((provider) => registry.registerProvider(provider))\n .withErrorFormat((e) => `Failed to register hidden singles provider: ${e}`);\n\n /* c8 ignore next 3 - defensive coding: provider creation/registration should not fail */\n if (hiddenResult.isFailure()) {\n return fail<HintSystem>(hiddenResult.message);\n }\n }\n\n // Create applicator and return complete system\n const applicator = new DefaultHintApplicator(finalConfig.logger);\n return succeed(new HintSystem(registry, applicator, finalConfig));\n });\n }\n\n /**\n * Gets the hint registry.\n * @returns The hint registry\n */\n public get registry(): IHintRegistry {\n return this._registry;\n }\n\n /**\n * Gets the hint applicator.\n * @returns The hint applicator\n */\n public get applicator(): IHintApplicator {\n return this._applicator;\n }\n\n /**\n * Gets the system configuration.\n * @returns The configuration\n */\n public get config(): IHintSystemConfig {\n return this._config;\n }\n\n /**\n * Generates all available hints for the current puzzle state.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @param options - Optional generation options\n * @returns Result containing array of hints\n */\n public generateHints(\n puzzle: Puzzle,\n state: PuzzleState,\n options?: IHintGenerationOptions\n ): Result<readonly IHint[]> {\n this._log.detail('Generating hints for puzzle state');\n\n return this._registry.generateAllHints(puzzle, state, options).report(this._logHints, {\n success: { level: 'info' },\n failure: {\n level: 'warning',\n message: (msg: string) => `Failed to generate hints: ${msg}`\n }\n });\n }\n\n /**\n * Gets the best available hint for the current puzzle state.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @param options - Optional generation options\n * @returns Result containing the best hint\n */\n public getBestHint(puzzle: Puzzle, state: PuzzleState, options?: IHintGenerationOptions): Result<IHint> {\n return this._registry.getBestHint(puzzle, state, options);\n }\n\n /**\n * Applies a hint to generate cell state updates.\n * @param hint - The hint to apply\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns Result containing the cell updates\n */\n public applyHint(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<readonly ICellState[]> {\n return this._applicator.applyHint(hint, puzzle, state);\n }\n\n /**\n * Validates that a hint can be applied to the current state.\n * @param hint - The hint to validate\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns Result indicating validation success or failure\n */\n public validateHint(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<void> {\n return this._applicator.canApplyHint(hint, puzzle, state);\n }\n\n /**\n * Formats a hint explanation for display.\n * @param hint - The hint to format\n * @param level - The explanation level to use (defaults to config default)\n * @returns Formatted explanation string\n */\n public formatHintExplanation(hint: IHint, level?: ExplanationLevel): string {\n /* c8 ignore next 1 - ?? is defense in depth */\n const explanationLevel = level ?? this._config.defaultExplanationLevel ?? 'detailed';\n const explanation = hint.explanations.find((exp) => exp.level === explanationLevel);\n\n if (!explanation) {\n return `No explanation available at level ${explanationLevel} for ${hint.techniqueName}`;\n }\n\n return ExplanationFormatter.formatExplanation(explanation);\n }\n\n /**\n * Gets a summary of the hint system capabilities.\n * @returns System capabilities summary\n */\n public getSystemSummary(): string {\n const techniques = this._registry.getRegisteredTechniques();\n const techniqueNames = techniques.map((id: TechniqueId) => {\n const provider = this._registry.getProvider(id);\n /* c8 ignore next 1 - fallback to id should not happen in practice */\n return provider.isSuccess() ? provider.value.techniqueName : id;\n });\n\n /* c8 ignore next 1 - ?? is defense in depth */\n const defaultExplanationLevel = this._config.defaultExplanationLevel ?? 'detailed';\n return [\n 'Sudoku Hint System',\n '='.repeat(20),\n `Registered Techniques: ${techniques.length}`,\n ...techniqueNames.map((name: string) => `• ${name}`),\n '',\n `Default Explanation Level: ${defaultExplanationLevel}`\n ].join('\\n');\n }\n\n /**\n * Checks if the puzzle state has any available hints.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns Result containing boolean indicating if hints are available\n */\n public hasHints(puzzle: Puzzle, state: PuzzleState): Result<boolean> {\n return this.generateHints(puzzle, state, { maxHints: 1 }).onSuccess((hints) => {\n return succeed(hints.length > 0);\n });\n }\n\n /**\n * Gets statistics about available hints for the current state.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns Result containing hint statistics\n */\n public getHintStatistics(\n puzzle: Puzzle,\n state: PuzzleState\n ): Result<{\n totalHints: number;\n hintsByTechnique: Map<string, number>;\n hintsByDifficulty: Map<string, number>;\n }> {\n return this.generateHints(puzzle, state).onSuccess((hints) => {\n const hintsByTechnique = new Map<string, number>();\n const hintsByDifficulty = new Map<string, number>();\n\n for (const hint of hints) {\n // Count by technique\n /* c8 ignore next 1 - ?? is defense in depth */\n const techniqueCount = hintsByTechnique.get(hint.techniqueName) ?? 0;\n hintsByTechnique.set(hint.techniqueName, techniqueCount + 1);\n\n // Count by difficulty\n const difficultyCount = hintsByDifficulty.get(hint.difficulty) ?? 0;\n hintsByDifficulty.set(hint.difficulty, difficultyCount + 1);\n }\n\n return succeed({\n totalHints: hints.length,\n hintsByTechnique,\n hintsByDifficulty\n });\n });\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/hints/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,4BAA4B;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAE7B,6BAA6B;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAE/B,4BAA4B;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAEhC,wBAAwB;AACxB,cAAc,gBAAgB,CAAC;AAE/B,mBAAmB;AACnB,cAAc,SAAS,CAAC;AAExB,4BAA4B;AAC5B,cAAc,sBAAsB,CAAC","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n// Core types and interfaces\nexport * from './types';\nexport * from './interfaces';\n\n// Base classes and utilities\nexport * from './baseHintProvider';\nexport * from './hintRegistry';\n\n// Technique implementations\nexport * from './nakedSingles';\nexport * from './hiddenSingles';\n\n// Educational framework\nexport * from './explanations';\n\n// Main hint system\nexport * from './hints';\n\n// PuzzleSession integration\nexport * from './puzzleSessionHints';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../../src/packlets/hints/interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result } from '@fgv/ts-utils';\nimport { ICellState, Puzzle, PuzzleState } from '../common';\nimport { DifficultyLevel, IHint, IHintExplanation, IHintGenerationOptions, TechniqueId } from './types';\n\n/**\n * Interface for classes that can provide hints using a specific solving technique.\n * @public\n */\nexport interface IHintProvider {\n readonly techniqueId: TechniqueId;\n readonly techniqueName: string;\n readonly difficulty: DifficultyLevel;\n readonly priority: number;\n\n /**\n * Determines if this provider can potentially generate hints for the given puzzle.\n * This should be a fast check to avoid expensive hint generation when not applicable.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns true if this provider might be able to generate hints\n */\n canProvideHints(puzzle: Puzzle, state: PuzzleState): boolean;\n\n /**\n * Generates all possible hints using this technique for the given puzzle.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @param options - Optional generation options\n * @returns Result containing array of hints, or failure if generation fails\n */\n generateHints(\n puzzle: Puzzle,\n state: PuzzleState,\n options?: IHintGenerationOptions\n ): Result<readonly IHint[]>;\n}\n\n/**\n * Interface for managing and coordinating multiple hint providers.\n * @public\n */\nexport interface IHintRegistry {\n /**\n * Registers a new hint provider.\n * @param provider - The provider to register\n * @returns Result indicating success or failure of registration\n */\n registerProvider(provider: IHintProvider): Result<void>;\n\n /**\n * Unregisters a hint provider.\n * @param techniqueId - The ID of the technique to unregister\n * @returns Result indicating success or failure of unregistration\n */\n unregisterProvider(techniqueId: TechniqueId): Result<void>;\n\n /**\n * Gets a specific provider by technique ID.\n * @param techniqueId - The ID of the technique\n * @returns Result containing the provider, or failure if not found\n */\n getProvider(techniqueId: TechniqueId): Result<IHintProvider>;\n\n /**\n * Gets all registered providers, optionally filtered by criteria.\n * @param options - Optional filtering options\n * @returns Array of providers matching the criteria\n */\n getProviders(options?: IHintGenerationOptions): readonly IHintProvider[];\n\n /**\n * Generates hints using all applicable providers.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @param options - Optional generation options\n * @returns Result containing array of hints from all providers\n */\n generateAllHints(\n puzzle: Puzzle,\n state: PuzzleState,\n options?: IHintGenerationOptions\n ): Result<readonly IHint[]>;\n\n /**\n * Gets the best available hint based on difficulty and confidence.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @param options - Optional generation options\n * @returns Result containing the best hint, or failure if no hints available\n */\n getBestHint(puzzle: Puzzle, state: PuzzleState, options?: IHintGenerationOptions): Result<IHint>;\n\n /**\n * Gets all registered technique IDs.\n * @returns Array of technique IDs\n */\n getRegisteredTechniques(): readonly TechniqueId[];\n}\n\n/**\n * Interface for hint explanation generation and formatting.\n * @public\n */\nexport interface IHintExplanationProvider {\n /**\n * Gets the technique ID this explanation provider supports.\n */\n readonly techniqueId: TechniqueId;\n\n /**\n * Generates explanations for a specific hint.\n * @param hint - The hint to explain\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The puzzle state context\n * @returns Result containing array of explanations at different levels\n */\n generateExplanations(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<readonly IHintExplanation[]>;\n}\n\n/**\n * Interface for hint application and validation.\n * @public\n */\nexport interface IHintApplicator {\n /**\n * Validates that a hint can be safely applied to the given state.\n * @param hint - The hint to validate\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns Result indicating validation success or failure with details\n */\n canApplyHint(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<void>;\n\n /**\n * Applies a hint to the puzzle state, generating the necessary cell updates.\n * @param hint - The hint to apply\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns Result containing the cell state updates needed to apply the hint\n */\n applyHint(hint: IHint, puzzle: Puzzle, state: PuzzleState): Result<readonly ICellState[]>;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nakedSingles.js","sourceRoot":"","sources":["../../../src/packlets/hints/nakedSingles.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport { CellId, Ids, Puzzle, PuzzleState, parseCellId } from '../common';\nimport { BaseHintProvider } from './baseHintProvider';\nimport { ConfidenceLevels, IHint, IHintGenerationOptions, TechniqueIds } from './types';\n\n/**\n * Hint provider for the Naked Singles technique.\n *\n * A Naked Single occurs when a cell has only one possible candidate value\n * based on the constraints of its row, column, and 3x3 box.\n *\n * @public\n */\nexport class NakedSinglesProvider extends BaseHintProvider {\n /**\n * Creates a new NakedSinglesProvider instance.\n */\n public constructor() {\n super({\n techniqueId: TechniqueIds.NAKED_SINGLES,\n techniqueName: 'Naked Singles',\n difficulty: 'beginner',\n priority: 1, // Highest priority - should be checked first\n defaultConfidence: ConfidenceLevels.HIGH\n });\n }\n\n /**\n * Determines if this provider can potentially generate hints for the given puzzle.\n * Always returns true since naked singles can potentially exist in any incomplete puzzle.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @returns true if there are empty cells that might have naked singles\n */\n public canProvideHints(puzzle: Puzzle, state: PuzzleState): boolean {\n // Quick check: if there are empty cells, there might be naked singles\n const emptyCells = this.getEmptyCells(puzzle, state);\n return emptyCells.length > 0;\n }\n\n /**\n * Generates all naked single hints for the given puzzle.\n * @param puzzle - The puzzle structure containing constraints\n * @param state - The current puzzle state\n * @param options - Optional generation options\n * @returns Result containing array of naked single hints\n */\n public generateHints(\n puzzle: Puzzle,\n state: PuzzleState,\n options?: IHintGenerationOptions\n ): Result<readonly IHint[]> {\n return this.validateOptions(options).onSuccess(() => {\n const hints: IHint[] = [];\n const emptyCells = this.getEmptyCells(puzzle, state);\n\n for (const cellId of emptyCells) {\n const candidates = this.getCandidates(cellId, puzzle, state);\n\n // A naked single has exactly one candidate\n if (candidates.length === 1) {\n const value = candidates[0];\n const hint = this._createNakedSingleHint(cellId, value, state, puzzle);\n hints.push(hint);\n }\n }\n\n return succeed(this.filterHints(hints, options));\n });\n }\n\n /**\n * Creates a hint for a specific naked single.\n * @param cellId - The cell containing the naked single\n * @param value - The single possible value for the cell\n * @param state - The current puzzle state\n * @returns A complete hint for this naked single\n */\n private _createNakedSingleHint(cellId: CellId, value: number, state: PuzzleState, puzzle: Puzzle): IHint {\n // Create cell action to set the value\n const cellAction = this.createCellAction(cellId, 'set-value', value, `Only possible value for this cell`);\n\n // Find related cells that eliminate other candidates\n const relatedCells = this._findRelatedCells(cellId, value, state, puzzle);\n\n // Create relevant cells grouping\n const relevantCells = this.createRelevantCells(\n [cellId], // Primary: the cell with the naked single\n relatedCells, // Secondary: cells that constrain this cell\n [] // Affected: none for naked singles\n );\n\n // Create explanations at different levels\n const explanations = [\n this.createExplanation(\n 'brief',\n 'Naked Single',\n `Cell ${cellId} can only contain the value ${value}.`,\n [`Set ${cellId} = ${value}`],\n ['Look for cells with only one possible value']\n ),\n\n this.createExplanation(\n 'detailed',\n 'Naked Single Analysis',\n `Cell ${cellId} has only one possible candidate: ${value}. All other values (1-9) are ` +\n `eliminated by existing numbers in the same row, column, or 3x3 box.`,\n [\n `Examine cell ${cellId}`,\n `Check which values 1-9 are already used in the same row, column, and 3x3 box`,\n `Identify that only ${value} is not eliminated`,\n `Set ${cellId} = ${value}`\n ],\n [\n 'Naked singles are the most basic solving technique',\n 'Always check for naked singles first',\n 'A cell with one candidate must contain that value'\n ]\n ),\n\n this.createExplanation(\n 'educational',\n 'Understanding Naked Singles',\n `A naked single occurs when a cell has only one possible value due to Sudoku's fundamental rules. ` +\n `In cell ${cellId}, the value ${value} is the only number from 1-${puzzle.dimensions.maxValue} that doesn't already appear in the ` +\n `same row, column, or section. This makes it a \"naked\" single because the solution is immediately visible.`,\n [\n `Locate cell ${cellId} in the grid`,\n `Scan the row containing ${cellId} and note which numbers 1-${puzzle.dimensions.maxValue} are already placed`,\n `Scan the column containing ${cellId} and note which numbers 1-${puzzle.dimensions.maxValue} are already placed`,\n `Scan the section containing ${cellId} and note which numbers 1-${puzzle.dimensions.maxValue} are already placed`,\n `Combine all the \"used\" numbers from row, column, and section`,\n `The remaining unused number is ${value}, which must go in ${cellId}`,\n `Place ${value} in cell ${cellId}`\n ],\n [\n 'Naked singles are always correct - there is no risk in placing them',\n 'Start every solving session by finding all naked singles',\n 'Placing naked singles often reveals new naked singles in related cells',\n 'This technique forms the foundation for all other Sudoku solving methods'\n ]\n )\n ];\n\n return this.createHint([cellAction], relevantCells, explanations, ConfidenceLevels.HIGH);\n }\n\n /**\n * Finds cells in the same row, column, and box that constrain the given cell.\n * @param cellId - The cell to analyze\n * @param value - The value that will be placed\n * @param state - The current puzzle state\n * @param puzzle - The puzzle for dimensions\n * @returns Array of cell IDs that are related to this naked single\n */\n private _findRelatedCells(cellId: CellId, value: number, state: PuzzleState, puzzle: Puzzle): CellId[] {\n const relatedCells: CellId[] = [];\n\n // Parse the cell ID to get row and column\n const coords = parseCellId(cellId.toString());\n /* c8 ignore next 3 - defensive coding: handles corrupted cell ID, impractical to test via public API */\n if (!coords) {\n return relatedCells;\n }\n\n const { row, col } = coords;\n const totalRows = puzzle.dimensions.totalRows;\n const totalColumns = puzzle.dimensions.totalColumns;\n const cageHeight = puzzle.dimensions.cageHeightInCells;\n const cageWidth = puzzle.dimensions.cageWidthInCells;\n\n // Find cells in the same row, column, and section that contain any value\n // These are the cells that constrain our naked single\n\n // Same row\n for (let c = 0; c < totalColumns; c++) {\n if (c !== col) {\n const checkIdResult = Ids.cellId({ row, col: c });\n if (checkIdResult.isSuccess() && state.hasValue(checkIdResult.value)) {\n relatedCells.push(checkIdResult.value);\n }\n }\n }\n\n // Same column\n for (let r = 0; r < totalRows; r++) {\n if (r !== row) {\n const checkIdResult = Ids.cellId({ row: r, col });\n if (checkIdResult.isSuccess() && state.hasValue(checkIdResult.value)) {\n relatedCells.push(checkIdResult.value);\n }\n }\n }\n\n // Same section/cage\n const boxStartRow = Math.floor(row / cageHeight) * cageHeight;\n const boxStartCol = Math.floor(col / cageWidth) * cageWidth;\n\n for (let r = boxStartRow; r < boxStartRow + cageHeight; r++) {\n for (let c = boxStartCol; c < boxStartCol + cageWidth; c++) {\n if (r !== row || c !== col) {\n const checkIdResult = Ids.cellId({ row: r, col: c });\n if (checkIdResult.isSuccess() && state.hasValue(checkIdResult.value)) {\n relatedCells.push(checkIdResult.value);\n }\n }\n }\n }\n\n return relatedCells;\n }\n\n /**\n * Static factory method to create a new NakedSinglesProvider.\n * @returns Result containing the new provider\n */\n public static create(): Result<NakedSinglesProvider> {\n return succeed(new NakedSinglesProvider());\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"puzzleSessionHints.js","sourceRoot":"","sources":["../../../src/packlets/hints/puzzleSessionHints.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport { ICellState, IRowColumn, Ids, ICell, PuzzleSession } from '../common';\nimport { HintSystem, IHintSystemConfig } from './hints';\nimport { ExplanationLevel, IHint, IHintGenerationOptions } from './types';\n\n/**\n * Cache key for hint generation based on puzzle state.\n * @internal\n */\ninterface IHintCacheKey {\n readonly stateHash: string;\n readonly options: string;\n}\n\n/**\n * Cached hint data with generation timestamp.\n * @internal\n */\ninterface ICachedHints {\n readonly hints: readonly IHint[];\n readonly generatedAt: number;\n readonly cacheKey: IHintCacheKey;\n}\n\n/**\n * Configuration options for the PuzzleSessionHints integration.\n * @public\n */\nexport interface IPuzzleSessionHintsConfig extends IHintSystemConfig {\n readonly cacheTimeoutMs?: number;\n readonly maxCacheEntries?: number;\n}\n\n/**\n * Wrapper class that integrates hint functionality with PuzzleSession.\n * Provides hint generation, application, and explanation capabilities while\n * maintaining integration with existing state management and undo/redo functionality.\n * @public\n */\nexport class PuzzleSessionHints {\n private readonly _session: PuzzleSession;\n private readonly _hintSystem: HintSystem;\n private readonly _config: IPuzzleSessionHintsConfig;\n private _hintCache: ICachedHints | undefined;\n\n /**\n * @internal\n */\n private constructor(session: PuzzleSession, hintSystem: HintSystem, config: IPuzzleSessionHintsConfig) {\n this._session = session;\n this._hintSystem = hintSystem;\n this._config = config;\n this._hintCache = undefined;\n }\n\n /**\n * Creates a new PuzzleSessionHints wrapper for an existing PuzzleSession.\n * @param session - The PuzzleSession to wrap\n * @param config - Optional configuration for the hint system\n * @returns Result containing the new PuzzleSessionHints wrapper\n */\n public static create(\n session: PuzzleSession,\n config?: IPuzzleSessionHintsConfig\n ): Result<PuzzleSessionHints> {\n const finalConfig: IPuzzleSessionHintsConfig = {\n enableNakedSingles: true,\n enableHiddenSingles: true,\n defaultExplanationLevel: 'detailed' as ExplanationLevel,\n cacheTimeoutMs: 5000, // 5 second cache timeout\n maxCacheEntries: 1, // Simple single-entry cache for now\n ...config\n };\n\n return HintSystem.create(finalConfig).onSuccess((hintSystem) => {\n return succeed(new PuzzleSessionHints(session, hintSystem, finalConfig));\n });\n }\n\n /**\n * Gets the wrapped PuzzleSession instance.\n * @returns The underlying PuzzleSession\n */\n public get session(): PuzzleSession {\n return this._session;\n }\n\n /**\n * Gets the HintSystem instance.\n * @returns The hint system\n */\n public get hintSystem(): HintSystem {\n return this._hintSystem;\n }\n\n /**\n * Gets the configuration.\n * @returns The configuration\n */\n public get config(): IPuzzleSessionHintsConfig {\n return this._config;\n }\n\n // Delegate all PuzzleSession methods for transparent access\n\n /**\n * Gets the puzzle ID.\n * @returns The puzzle ID\n */\n public get id(): string | undefined {\n return this._session.id;\n }\n\n /**\n * Gets the puzzle description.\n * @returns The puzzle description\n */\n public get description(): string {\n return this._session.description;\n }\n\n /**\n * Gets the number of rows in the puzzle.\n * @returns The number of rows\n */\n public get numRows(): number {\n return this._session.numRows;\n }\n\n /**\n * Gets the number of columns in the puzzle.\n * @returns The number of columns\n */\n public get numColumns(): number {\n return this._session.numColumns;\n }\n\n /**\n * Gets the current puzzle state.\n * @returns The current state\n */\n public get state(): import('../common').PuzzleState {\n return this._session.state;\n }\n\n /**\n * Gets whether undo is possible.\n * @returns true if undo is possible\n */\n public get canUndo(): boolean {\n return this._session.canUndo;\n }\n\n /**\n * Gets whether redo is possible.\n * @returns true if redo is possible\n */\n public get canRedo(): boolean {\n return this._session.canRedo;\n }\n\n /**\n * Checks if the puzzle is solved.\n * @returns true if the puzzle is solved\n */\n public checkIsSolved(): boolean {\n return this._session.checkIsSolved();\n }\n\n /**\n * Checks if the puzzle is valid.\n * @returns true if the puzzle is valid\n */\n public checkIsValid(): boolean {\n return this._session.checkIsValid();\n }\n\n /**\n * Updates a cell value.\n * @param spec - Cell specification\n * @param value - New value\n * @returns Result with this instance\n */\n public updateCellValue(spec: string | IRowColumn | ICell, value: number | undefined): Result<this> {\n return this._session.updateCellValue(spec, value).onSuccess(() => {\n this._invalidateCache();\n return succeed(this);\n });\n }\n\n /**\n * Updates cell notes.\n * @param spec - Cell specification\n * @param notes - New notes\n * @returns Result with this instance\n */\n public updateCellNotes(spec: string | IRowColumn | ICell, notes: number[]): Result<this> {\n return this._session.updateCellNotes(spec, notes).onSuccess(() => {\n this._invalidateCache();\n return succeed(this);\n });\n }\n\n /**\n * Updates multiple cells.\n * @param updates - Array of cell updates\n * @returns Result with this instance\n */\n public updateCells(updates: ICellState[]): Result<this> {\n return this._session.updateCells(updates).onSuccess(() => {\n this._invalidateCache();\n return succeed(this);\n });\n }\n\n /**\n * Performs an undo operation.\n * @returns Result with this instance\n */\n public undo(): Result<this> {\n return this._session.undo().onSuccess(() => {\n this._invalidateCache();\n return succeed(this);\n });\n }\n\n /**\n * Performs a redo operation.\n * @returns Result with this instance\n */\n public redo(): Result<this> {\n return this._session.redo().onSuccess(() => {\n this._invalidateCache();\n return succeed(this);\n });\n }\n\n // Hint-specific functionality\n\n /**\n * Gets the best available hint for the current puzzle state.\n * @param options - Optional hint generation options\n * @returns Result containing the best hint\n */\n public getHint(options?: IHintGenerationOptions): Result<IHint> {\n return this._hintSystem.getBestHint(this._session.puzzle, this._session.state, options);\n }\n\n /**\n * Gets all available hints for the current puzzle state.\n * @param options - Optional hint generation options\n * @returns Result containing array of hints\n */\n public getAllHints(options?: IHintGenerationOptions): Result<readonly IHint[]> {\n // Check cache first\n const cachedHints = this._getCachedHints(options);\n if (cachedHints) {\n return succeed(cachedHints);\n }\n\n // Generate new hints\n return this._hintSystem\n .generateHints(this._session.puzzle, this._session.state, options)\n .onSuccess((hints) => {\n this._updateCache(hints, options);\n return succeed(hints);\n });\n }\n\n /**\n * Applies a hint to the puzzle, updating the state and adding to undo history.\n * @param hint - The hint to apply\n * @returns Result with this instance if successful\n */\n public applyHint(hint: IHint): Result<this> {\n return this._hintSystem\n .applyHint(hint, this._session.puzzle, this._session.state)\n .onSuccess((updates) => {\n // Convert readonly array to mutable array for PuzzleSession.updateCells\n const mutableUpdates: ICellState[] = [...updates];\n return this._session.updateCells(mutableUpdates).onSuccess(() => {\n this._invalidateCache();\n return succeed(this);\n });\n });\n }\n\n /**\n * Gets hints that specifically affect a given cell.\n * @param spec - Cell specification (ID, row/column, or cell object)\n * @param options - Optional hint generation options\n * @returns Result containing hints affecting the specified cell\n */\n public getHintsForCell(\n spec: string | IRowColumn | ICell,\n options?: IHintGenerationOptions\n ): Result<readonly IHint[]> {\n const cellIdResult = Ids.cellId(spec);\n return cellIdResult.onSuccess((cellId) => {\n return this.getAllHints(options).onSuccess((allHints) => {\n const relevantHints = allHints.filter((hint) => {\n // Check if the hint affects this specific cell\n return (\n hint.cellActions.some((action) => action.cellId === cellId) ||\n hint.relevantCells.primary.includes(cellId) ||\n hint.relevantCells.secondary.includes(cellId) ||\n hint.relevantCells.affected.includes(cellId)\n );\n });\n return succeed(relevantHints);\n });\n });\n }\n\n /**\n * Gets a formatted explanation for a hint.\n * @param hint - The hint to explain\n * @param level - The explanation level (defaults to configured default)\n * @returns Formatted explanation string\n */\n public getExplanation(hint: IHint, level?: ExplanationLevel): string {\n return this._hintSystem.formatHintExplanation(hint, level);\n }\n\n /**\n * Validates that a hint can be applied to the current state.\n * @param hint - The hint to validate\n * @returns Result indicating validation success or failure\n */\n public validateHint(hint: IHint): Result<void> {\n return this._hintSystem.validateHint(hint, this._session.puzzle, this._session.state);\n }\n\n /**\n * Checks if hints are available for the current state.\n * @param options - Optional hint generation options\n * @returns Result containing boolean indicating availability\n */\n public hasHints(options?: IHintGenerationOptions): Result<boolean> {\n return this._hintSystem.hasHints(this._session.puzzle, this._session.state);\n }\n\n /**\n * Gets statistics about available hints.\n * @param options - Optional hint generation options\n * @returns Result containing hint statistics\n */\n public getHintStatistics(options?: IHintGenerationOptions): Result<{\n totalHints: number;\n hintsByTechnique: Map<string, number>;\n hintsByDifficulty: Map<string, number>;\n }> {\n return this._hintSystem.getHintStatistics(this._session.puzzle, this._session.state);\n }\n\n /**\n * Gets a summary of the hint system capabilities.\n * @returns System capabilities summary\n */\n public getSystemSummary(): string {\n return this._hintSystem.getSystemSummary();\n }\n\n // Delegate remaining PuzzleSession methods for complete transparency\n\n /**\n * Gets the rows.\n * @returns Array of row cages\n */\n public get rows(): import('../common').ICage[] {\n return this._session.rows;\n }\n\n /**\n * Gets the columns.\n * @returns Array of column cages\n */\n public get cols(): import('../common').ICage[] {\n return this._session.cols;\n }\n\n /**\n * Gets the sections.\n * @returns Array of section cages\n */\n public get sections(): import('../common').ICage[] {\n return this._session.sections;\n }\n\n /**\n * Gets all cages.\n * @returns Array of all cages\n */\n public get cages(): import('../common').ICage[] {\n return this._session.cages;\n }\n\n /**\n * Gets all cells.\n * @returns Array of all cells\n */\n public get cells(): ICell[] {\n return this._session.cells;\n }\n\n /**\n * Gets the next step index.\n * @returns Next step index\n */\n public get nextStep(): number {\n return this._session.nextStep;\n }\n\n /**\n * Gets the number of steps.\n * @returns Number of steps\n */\n public get numSteps(): number {\n return this._session.numSteps;\n }\n\n /**\n * Gets empty cells.\n * @returns Array of empty cells\n */\n public getEmptyCells(): ICell[] {\n return this._session.getEmptyCells();\n }\n\n /**\n * Gets invalid cells.\n * @returns Array of invalid cells\n */\n public getInvalidCells(): ICell[] {\n return this._session.getInvalidCells();\n }\n\n /**\n * Checks if a cell is valid.\n * @param spec - Cell specification\n * @returns true if valid\n */\n public cellIsValid(spec: string | IRowColumn | ICell): boolean {\n return this._session.cellIsValid(spec);\n }\n\n /**\n * Checks if a cell has a value.\n * @param spec - Cell specification\n * @returns true if cell has value\n */\n public cellHasValue(spec: string | IRowColumn | ICell): boolean {\n return this._session.cellHasValue(spec);\n }\n\n /**\n * Checks if a value is valid for a cell.\n * @param spec - Cell specification\n * @param value - Value to check\n * @returns true if valid\n */\n public isValidForCell(spec: string | IRowColumn | ICell, value: number): boolean {\n return this._session.isValidForCell(spec, value);\n }\n\n /**\n * Gets a cell neighbor.\n * @param spec - Cell specification\n * @param direction - Navigation direction\n * @param wrap - Wrap behavior\n * @returns Result containing neighbor cell\n */\n public getCellNeighbor(\n spec: string | IRowColumn | ICell,\n direction: import('../common').NavigationDirection,\n wrap: import('../common').NavigationWrap\n ): Result<ICell> {\n return this._session.getCellNeighbor(spec, direction, wrap);\n }\n\n /**\n * Gets cell contents.\n * @param spec - Cell specification\n * @returns Result containing cell and contents\n */\n public getCellContents(\n spec: string | IRowColumn\n ): Result<{ cell: ICell; contents: import('../common').ICellContents }> {\n return this._session.getCellContents(spec);\n }\n\n /**\n * Checks if a cage contains a value.\n * @param spec - Cage specification\n * @param value - Value to check\n * @returns true if cage contains value\n */\n public cageContainsValue(spec: string | import('../common').ICage, value: number): boolean {\n return this._session.cageContainsValue(spec, value);\n }\n\n /**\n * Gets contained values in a cage.\n * @param spec - Cage specification\n * @returns Set of contained values\n */\n public cageContainedValues(spec: string | import('../common').ICage): Set<number> {\n return this._session.cageContainedValues(spec);\n }\n\n /**\n * Gets string representation of the puzzle.\n * @returns Array of strings representing puzzle rows\n */\n public toStrings(): string[] {\n return this._session.toStrings();\n }\n\n // Private cache management methods\n\n /**\n * Generates a cache key for the current state and options.\n * @param options - Hint generation options\n * @returns Cache key\n */\n private _generateCacheKey(options?: IHintGenerationOptions): IHintCacheKey {\n // Simple state hash based on cell values and notes\n const stateString = this._session.toStrings().join('|');\n const notesString = this._session.cells\n .map((cell) => {\n const contents = this._session\n .getCellContents(cell.id)\n .orDefault({ cell, contents: { value: undefined, notes: [] } });\n return `${cell.id}:${contents.contents.notes.join(',')}`;\n })\n .join('|');\n\n const stateHash = `${stateString}#${notesString}`;\n const optionsString = options ? JSON.stringify(options) : '';\n\n return { stateHash, options: optionsString };\n }\n\n /**\n * Gets cached hints if valid.\n * @param options - Hint generation options\n * @returns Cached hints or undefined\n */\n private _getCachedHints(options?: IHintGenerationOptions): readonly IHint[] | undefined {\n if (!this._hintCache) {\n return undefined;\n }\n\n const cacheKey = this._generateCacheKey(options);\n /* c8 ignore next 1 - ?? is defense in depth */\n const timeoutMs = this._config.cacheTimeoutMs ?? 5000;\n const now = Date.now();\n\n // Check if cache is still valid\n if (\n this._hintCache.cacheKey.stateHash === cacheKey.stateHash &&\n this._hintCache.cacheKey.options === cacheKey.options &&\n now - this._hintCache.generatedAt < timeoutMs\n ) {\n return this._hintCache.hints;\n }\n\n return undefined;\n }\n\n /**\n * Updates the cache with new hints.\n * @param hints - Generated hints\n * @param options - Generation options\n */\n private _updateCache(hints: readonly IHint[], options?: IHintGenerationOptions): void {\n const cacheKey = this._generateCacheKey(options);\n this._hintCache = {\n hints,\n generatedAt: Date.now(),\n cacheKey\n };\n }\n\n /**\n * Invalidates the current hint cache.\n */\n private _invalidateCache(): void {\n this._hintCache = undefined;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/packlets/hints/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA+FH;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,aAAa,EAAE,eAA8B;IAC7C,cAAc,EAAE,gBAA+B;CACvC,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,GAAG,EAAE,CAAoB;IACzB,UAAU,EAAE,CAAoB;IAChC,MAAM,EAAE,CAAoB;IAC5B,WAAW,EAAE,CAAoB;IACjC,IAAI,EAAE,CAAoB;CAClB,CAAC","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Brand } from '@fgv/ts-utils';\nimport { CellId } from '../common';\n\n/**\n * Nominal identifier for a hint generation technique.\n * @public\n */\nexport type TechniqueId = Brand<string, 'TechniqueId'>;\n\n/**\n * Confidence level for a generated hint, ranging from 1 (low) to 5 (high).\n * @public\n */\nexport type ConfidenceLevel = Brand<1 | 2 | 3 | 4 | 5, 'ConfidenceLevel'>;\n\n/**\n * The type of action that should be taken on a cell as part of a hint.\n * @public\n */\nexport type CellAction = 'set-value' | 'eliminate-candidate' | 'add-candidate' | 'highlight';\n\n/**\n * The level of detail for hint explanations.\n * @public\n */\nexport type ExplanationLevel = 'brief' | 'detailed' | 'educational';\n\n/**\n * Standard difficulty classifications for Sudoku techniques.\n * @public\n */\nexport type DifficultyLevel = 'beginner' | 'intermediate' | 'advanced' | 'expert';\n\n/**\n * Describes an action to be taken on a specific cell as part of a hint.\n * @public\n */\nexport interface ICellAction {\n readonly cellId: CellId;\n readonly action: CellAction;\n readonly value?: number;\n readonly reason?: string;\n}\n\n/**\n * Information about the cells that are relevant to understanding a hint.\n * @public\n */\nexport interface IRelevantCells {\n readonly primary: readonly CellId[];\n readonly secondary: readonly CellId[];\n readonly affected: readonly CellId[];\n}\n\n/**\n * A hint explanation at a specific level of detail.\n * @public\n */\nexport interface IHintExplanation {\n readonly level: ExplanationLevel;\n readonly title: string;\n readonly description: string;\n readonly steps?: readonly string[];\n readonly tips?: readonly string[];\n}\n\n/**\n * A complete hint with all necessary information for display and application.\n * @public\n */\nexport interface IHint {\n readonly techniqueId: TechniqueId;\n readonly techniqueName: string;\n readonly difficulty: DifficultyLevel;\n readonly confidence: ConfidenceLevel;\n readonly cellActions: readonly ICellAction[];\n readonly relevantCells: IRelevantCells;\n readonly explanations: readonly IHintExplanation[];\n readonly priority: number;\n}\n\n/**\n * Configuration options for hint generation.\n * @public\n */\nexport interface IHintGenerationOptions {\n readonly maxHints?: number;\n readonly minConfidence?: ConfidenceLevel;\n readonly preferredDifficulty?: DifficultyLevel;\n readonly enabledTechniques?: readonly TechniqueId[];\n readonly explanationLevel?: ExplanationLevel;\n}\n\n/**\n * Standard technique identifiers for built-in solving techniques.\n * @public\n */\nexport const TechniqueIds = {\n NAKED_SINGLES: 'naked-singles' as TechniqueId,\n HIDDEN_SINGLES: 'hidden-singles' as TechniqueId\n} as const;\n\n/**\n * Standard confidence levels as branded types.\n * @public\n */\nexport const ConfidenceLevels = {\n LOW: 1 as ConfidenceLevel,\n MEDIUM_LOW: 2 as ConfidenceLevel,\n MEDIUM: 3 as ConfidenceLevel,\n MEDIUM_HIGH: 4 as ConfidenceLevel,\n HIGH: 5 as ConfidenceLevel\n} as const;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anyPuzzle.js","sourceRoot":"","sources":["../../../src/packlets/puzzles/anyPuzzle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAU,IAAI,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAO,SAAS;IACb,MAAM,CAAC,MAAM,CAAC,MAAyB;QAC5C,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,QAAQ;gBACX,OAAO,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACrC,KAAK,UAAU;gBACb,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACtC,KAAK,eAAe;gBAClB,OAAO,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3C,sBAAsB;YACtB;gBACE,OAAO,IAAI,CAAC,WAAW,MAAM,CAAC,WAAW,sBAAsB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;CACF","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, fail } from '@fgv/ts-utils';\n\nimport { IPuzzleDefinition, Puzzle } from '../common';\nimport { KillerSudokuPuzzle } from './killerSudokuPuzzle';\nimport { SudokuPuzzle } from './sudokuPuzzle';\nimport { SudokuXPuzzle } from './sudokuXPuzzle';\n\n/**\n * Static class to instantiate any puzzle from a {@link IPuzzleDefinition | puzzle definition}.\n * @internal\n */\nexport class AnyPuzzle {\n public static create(puzzle: IPuzzleDefinition): Result<Puzzle> {\n switch (puzzle.type) {\n case 'sudoku':\n return SudokuPuzzle.create(puzzle);\n case 'sudoku-x':\n return SudokuXPuzzle.create(puzzle);\n case 'killer-sudoku':\n return KillerSudokuPuzzle.create(puzzle);\n /* c8 ignore next 2 */\n default:\n return fail(`Puzzle '${puzzle.description}' unsupported type ${puzzle.type}`);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/puzzles/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,kBAAkB,IAAI,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,YAAY,IAAI,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,SAAS,IAAI,GAAG,EAAE,MAAM,aAAa,CAAC;AAC/C,cAAc,2BAA2B,CAAC;AAE1C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { KillerSudokuPuzzle as Killer } from './killerSudokuPuzzle';\nimport { SudokuPuzzle as Sudoku } from './sudokuPuzzle';\nimport { SudokuXPuzzle as SudokuX } from './sudokuXPuzzle';\nimport { KillerCombinations } from './killerCombinations';\nimport { AnyPuzzle as Any } from './anyPuzzle';\nexport * from './killerCombinationsTypes';\n\nexport { Any, Killer, KillerCombinations, Sudoku, SudokuX };\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combinationCache.js","sourceRoot":"","sources":["../../../../src/packlets/puzzles/internal/combinationCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH;;GAEG;AACH,MAAM,OAAO,gBAAgB;IAI3B;;OAEG;IACI,MAAM,CAAC,WAAW,CAAC,QAAgB,EAAE,KAAa,EAAE,WAAgC;;QACzF,MAAM,QAAQ,GAAG,MAAA,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,eAAe,0CAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,mCAAI,EAAE,CAAC;QAC9E,MAAM,QAAQ,GAAG,MAAA,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,eAAe,0CAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,mCAAI,EAAE,CAAC;QAC9E,OAAO,GAAG,QAAQ,IAAI,KAAK,IAAI,QAAQ,IAAI,QAAQ,EAAE,CAAC;IACxD,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,GAAG,CAAC,GAAW;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,MAAM,EAAE,CAAC;YACX,gDAAgD;YAChD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,GAAG,CAAC,GAAW,EAAE,YAAwB;QACrD,gEAAgE;QAChE,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;QAED,gDAAgD;QAChD,MAAM,kBAAkB,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,KAAK;QACjB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,IAAI;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;;AAlDuB,uBAAM,GAA4B,IAAI,GAAG,EAAsB,CAAC;AAChE,8BAAa,GAAW,IAAI,CAAC","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { IKillerConstraints } from '../killerCombinationsTypes';\n\n/**\n * @internal\n */\nexport class CombinationCache {\n private static readonly _cache: Map<string, number[][]> = new Map<string, number[][]>();\n private static readonly _maxCacheSize: number = 1000;\n\n /**\n * Generate cache key for combination parameters.\n */\n public static generateKey(cageSize: number, total: number, constraints?: IKillerConstraints): string {\n const excluded = constraints?.excludedNumbers?.slice().sort().join(',') ?? '';\n const required = constraints?.requiredNumbers?.slice().sort().join(',') ?? '';\n return `${cageSize}:${total}:${excluded}:${required}`;\n }\n\n /**\n * Get cached combination result.\n */\n public static get(key: string): number[][] | undefined {\n const result = this._cache.get(key);\n if (result) {\n // Deep clone to prevent mutation of cached data\n return result.map((combination) => [...combination]);\n }\n return undefined;\n }\n\n /**\n * Store combination result in cache.\n */\n public static set(key: string, combinations: number[][]): void {\n // Implement simple LRU by clearing cache when it gets too large\n if (this._cache.size >= this._maxCacheSize) {\n this.clear();\n }\n\n // Deep clone to prevent mutation of cached data\n const clonedCombinations = combinations.map((combination) => [...combination]);\n this._cache.set(key, clonedCombinations);\n }\n\n /**\n * Clear the entire cache.\n */\n public static clear(): void {\n this._cache.clear();\n }\n\n /**\n * Get current cache size (for testing/debugging).\n */\n public static size(): number {\n return this._cache.size;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combinationGenerator.js","sourceRoot":"","sources":["../../../../src/packlets/puzzles/internal/combinationGenerator.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { IKillerConstraints } from '../killerCombinationsTypes';\n\n/**\n * @internal\n */\nexport class CombinationGenerator {\n /**\n * Generates all possible combinations of unique numbers that sum to the target total.\n * @param cageSize - Number of cells in the cage\n * @param total - Target sum\n * @param constraints - Optional constraints on included/excluded numbers\n * @param maxValue - Maximum value allowed in cells (defaults to 9)\n * @returns Array of combinations, each sorted in ascending order\n */\n public static generate(\n cageSize: number,\n total: number,\n constraints?: IKillerConstraints,\n maxValue: number = 9\n ): number[][] {\n const results: number[][] = [];\n const excludedSet = new Set(constraints?.excludedNumbers ?? []);\n const requiredNumbers = constraints?.requiredNumbers ?? [];\n const requiredSet = new Set(requiredNumbers);\n\n // Validate that we can satisfy required numbers constraint\n if (requiredNumbers.length > cageSize) {\n return [];\n }\n\n // Check if required numbers sum is already too high\n const requiredSum = requiredNumbers.reduce((sum, num) => sum + num, 0);\n if (requiredSum > total) {\n return [];\n }\n\n // Calculate remaining constraints after accounting for required numbers\n const remainingSize = cageSize - requiredNumbers.length;\n const remainingTotal = total - requiredSum;\n\n if (remainingSize === 0) {\n // Only required numbers should be present\n /* c8 ignore next 3 - edge case: required numbers fill cage but sum doesn't match (tested via KillerCombinations) */\n if (remainingTotal === 0) {\n return [Array.from(requiredNumbers).sort()];\n }\n return [];\n }\n\n // Generate combinations for the remaining slots\n const availableNumbers: number[] = [];\n for (let i = 1; i <= maxValue; i++) {\n if (!excludedSet.has(i) && !requiredSet.has(i)) {\n availableNumbers.push(i);\n }\n }\n\n this._generateCombinations(availableNumbers, remainingSize, remainingTotal, 0, [], results);\n\n // Add required numbers to each result and sort\n return results.map((combination) => [...combination, ...requiredNumbers].sort((a, b) => a - b));\n }\n\n /**\n * Recursive helper to generate combinations.\n * @param availableNumbers - Numbers that can be used\n * @param targetSize - Number of numbers needed\n * @param targetSum - Target sum for the combination\n * @param startIndex - Starting index in available numbers\n * @param currentCombination - Current partial combination\n * @param results - Array to collect results\n */\n private static _generateCombinations(\n availableNumbers: number[],\n targetSize: number,\n targetSum: number,\n startIndex: number,\n currentCombination: number[],\n results: number[][]\n ): void {\n // Base case: we have the right number of elements\n if (currentCombination.length === targetSize) {\n const sum = currentCombination.reduce((total, num) => total + num, 0);\n if (sum === targetSum) {\n results.push([...currentCombination]);\n }\n return;\n }\n\n // Early termination: impossible to reach target\n const remainingSlots = targetSize - currentCombination.length;\n const currentSum = currentCombination.reduce((total, num) => total + num, 0);\n const remainingSum = targetSum - currentSum;\n\n // Check minimum possible sum with remaining slots\n const minPossible = this._calculateMinSum(availableNumbers, startIndex, remainingSlots);\n if (remainingSum < minPossible) {\n return;\n }\n\n // Check maximum possible sum with remaining slots\n const maxPossible = this._calculateMaxSum(availableNumbers, startIndex, remainingSlots);\n if (remainingSum > maxPossible) {\n return;\n }\n\n // Try each available number\n for (let i = startIndex; i < availableNumbers.length; i++) {\n const number = availableNumbers[i];\n\n // Skip if adding this number would exceed our target\n if (currentSum + number > targetSum) {\n continue;\n }\n\n currentCombination.push(number);\n this._generateCombinations(availableNumbers, targetSize, targetSum, i + 1, currentCombination, results);\n currentCombination.pop();\n }\n }\n\n /**\n * Calculate minimum possible sum for remaining slots.\n */\n private static _calculateMinSum(\n availableNumbers: number[],\n startIndex: number,\n remainingSlots: number\n ): number {\n let sum = 0;\n let count = 0;\n\n for (let i = startIndex; i < availableNumbers.length && count < remainingSlots; i++) {\n sum += availableNumbers[i];\n count++;\n }\n\n return sum;\n }\n\n /**\n * Calculate maximum possible sum for remaining slots.\n */\n private static _calculateMaxSum(\n availableNumbers: number[],\n startIndex: number,\n remainingSlots: number\n ): number {\n let sum = 0;\n let count = 0;\n\n for (let i = availableNumbers.length - 1; i >= startIndex && count < remainingSlots; i--) {\n sum += availableNumbers[i];\n count++;\n }\n\n return sum;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"possibilityAnalyzer.js","sourceRoot":"","sources":["../../../../src/packlets/puzzles/internal/possibilityAnalyzer.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport { CellId, Cage, ICage, ICell, Puzzle, PuzzleState } from '../../common';\n\n/**\n * @internal\n */\nexport class PossibilityAnalyzer {\n /**\n * Analyze possible values for each cell in a killer cage based on current puzzle state.\n * @param puzzle - The puzzle instance\n * @param state - Current puzzle state\n * @param cage - The killer cage to analyze\n * @param validCombinations - Pre-computed valid combinations for the cage\n * @returns Map of CellId to possible number arrays\n */\n public static analyze(\n puzzle: Puzzle,\n state: PuzzleState,\n cage: ICage,\n validCombinations: number[][]\n ): Result<Map<CellId, number[]>> {\n const possibilities = new Map<CellId, number[]>();\n\n // Get current values in the cage\n // We need the concrete Cage class for containedValues method\n const concreteCage = cage as Cage;\n const currentValues = concreteCage.containedValues(state);\n const emptyCells: CellId[] = [];\n\n // Identify empty cells and initialize possibilities\n for (const cellId of cage.cellIds) {\n const cellContents = state.getCellContents(cellId).orDefault();\n /* c8 ignore next 1 - ? is defense in depth */\n if (cellContents?.value !== undefined) {\n // Cell already has a value - set empty possibilities array\n possibilities.set(cellId, []);\n } else {\n // Empty cell\n emptyCells.push(cellId);\n possibilities.set(cellId, []);\n }\n }\n\n // Filter combinations that are compatible with current state\n const compatibleCombinations = validCombinations.filter((combination) => {\n // Check if combination contains all current values\n for (const currentValue of currentValues) {\n if (!combination.includes(currentValue)) {\n return false;\n }\n }\n\n // Check if combination has the right number of remaining values\n const remainingValues = combination.filter((value) => !currentValues.has(value));\n return remainingValues.length === emptyCells.length;\n });\n\n // If no compatible combinations exist, return empty possibilities for all cells\n if (compatibleCombinations.length === 0) {\n return succeed(possibilities);\n }\n\n // For each empty cell, find which values are possible\n for (const cellId of emptyCells) {\n const cellPossibilities = new Set<number>();\n\n // Get the cell to check sudoku constraints\n const cellResult = puzzle.getCell(cellId);\n /* c8 ignore next 3 - defensive coding: protects against internal cage/puzzle state corruption */\n if (cellResult.isFailure()) {\n continue;\n }\n const cell = cellResult.value;\n\n // Check each compatible combination\n for (const combination of compatibleCombinations) {\n const remainingValues = combination.filter((value) => !currentValues.has(value));\n\n // Check if each remaining value could go in this cell\n for (const value of remainingValues) {\n if (this._canPlaceValueInCell(cell, value, state)) {\n cellPossibilities.add(value);\n }\n }\n }\n\n possibilities.set(cellId, Array.from(cellPossibilities).sort());\n }\n\n return succeed(possibilities);\n }\n\n /**\n * Check if a value can be placed in a cell considering sudoku constraints.\n * @param cell - The cell to check\n * @param value - The value to place\n * @param state - Current puzzle state\n * @returns True if the value can be placed\n */\n private static _canPlaceValueInCell(cell: ICell, value: number, state: PuzzleState): boolean {\n // Check all cages this cell belongs to for sudoku constraints\n for (const cage of cell.cages) {\n if (cage.cageType === 'killer') {\n // Skip killer cage check - we're already analyzing within killer constraints\n continue;\n }\n\n // Check if value already exists in this constraint cage (row, column, section, etc.)\n const concreteCage = cage as Cage;\n if (concreteCage.containsValue(value, state, [cell.id])) {\n return false;\n }\n }\n\n return true;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"killerCombinations.js","sourceRoot":"","sources":["../../../src/packlets/puzzles/killerCombinations.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, fail, succeed } from '@fgv/ts-utils';\nimport { CellId, getCageTotalBounds, Cage, ICage, Puzzle, PuzzleState } from '../common';\nimport { IKillerConstraints } from './killerCombinationsTypes';\nimport { CombinationCache } from './internal/combinationCache';\nimport { CombinationGenerator } from './internal/combinationGenerator';\nimport { PossibilityAnalyzer } from './internal/possibilityAnalyzer';\n\n/**\n * Helper class providing UI assistance functions for killer sudoku puzzle solving.\n * Generates possible totals, number combinations with constraints, and cell-specific\n * possibilities based on current puzzle state.\n * @public\n */\nexport class KillerCombinations {\n /**\n * Gets all mathematically possible totals for a given cage size.\n *\n * Uses the existing totalsByCageSize constant to determine the valid range\n * of totals for the specified cage size and returns all integers in that range.\n *\n * @param cageSize - The number of cells in the cage (must be 1-9)\n * @returns Result containing array of possible totals in ascending order\n *\n * @example\n * ```typescript\n * // Get possible totals for a 3-cell cage\n * const result = KillerCombinations.getPossibleTotals(3);\n * if (result.isSuccess()) {\n * console.log(result.value); // [6, 7, 8, 9, ..., 24]\n * }\n * ```\n */\n public static getPossibleTotals(cageSize: number, maxValue: number = 9): Result<number[]> {\n // Validate cage size\n if (!Number.isInteger(cageSize) || cageSize < 1 || cageSize > maxValue) {\n return fail(`Cage size must be an integer between 1 and ${maxValue}, got ${cageSize}`);\n }\n\n const bounds = getCageTotalBounds(cageSize, maxValue);\n\n // Generate all totals in the valid range\n const totals: number[] = [];\n for (let total = bounds.min; total <= bounds.max; total++) {\n totals.push(total);\n }\n\n return succeed(totals);\n }\n\n /**\n * Generates all possible number combinations that sum to the target total.\n *\n * Each combination contains unique numbers from 1-9 that sum exactly to the\n * specified total. Combinations respect both excluded and required number\n * constraints if provided.\n *\n * @param cageSize - The number of cells in the cage (must be 1-9)\n * @param total - The target sum (must be valid for the cage size)\n * @param constraints - Optional constraints on included/excluded numbers\n * @returns Result containing array of combinations, each sorted in ascending order\n *\n * @example\n * ```typescript\n * // Get all combinations for a 3-cell cage with total 15\n * const result = KillerCombinations.getCombinations(3, 15);\n * if (result.isSuccess()) {\n * console.log(result.value); // [[1,5,9], [1,6,8], [2,4,9], ...]\n * }\n *\n * // With constraints - exclude 1 and 2, require 9\n * const constrained = KillerCombinations.getCombinations(3, 15, {\n * excludedNumbers: [1, 2],\n * requiredNumbers: [9]\n * });\n * ```\n */\n public static getCombinations(\n cageSize: number,\n total: number,\n constraints?: IKillerConstraints,\n maxValue: number = 9\n ): Result<number[][]> {\n // Validate cage size\n if (!Number.isInteger(cageSize) || cageSize < 1 || cageSize > maxValue) {\n return fail(`Cage size must be an integer between 1 and ${maxValue}, got ${cageSize}`);\n }\n\n // Validate total against cage size bounds\n const bounds = getCageTotalBounds(cageSize, maxValue);\n\n if (!Number.isInteger(total) || total < bounds.min || total > bounds.max) {\n return fail(\n `Total ${total} is invalid for cage size ${cageSize} - valid range: ${bounds.min}-${bounds.max}`\n );\n }\n\n // Validate constraints\n const constraintValidation = this._validateConstraints(constraints, maxValue);\n if (constraintValidation.isFailure()) {\n return fail(constraintValidation.message);\n }\n\n // Check cache first\n const cacheKey = CombinationCache.generateKey(cageSize, total, constraints);\n const cached = CombinationCache.get(cacheKey);\n if (cached) {\n return succeed(cached);\n }\n\n // Generate combinations\n const combinations = CombinationGenerator.generate(cageSize, total, constraints, maxValue);\n\n // Cache the result\n CombinationCache.set(cacheKey, combinations);\n\n return succeed(combinations);\n }\n\n /**\n * Determines possible values for each cell in a killer cage based on current puzzle state.\n *\n * Analyzes the current state of the puzzle and cage to determine which values\n * are possible for each empty cell, considering both killer cage constraints\n * and standard sudoku constraints (row, column, section uniqueness).\n *\n * @param puzzle - The puzzle instance\n * @param state - Current puzzle state\n * @param cage - The killer cage to analyze (must be of type 'killer')\n * @returns Result containing map of CellId to possible number arrays\n *\n * @example\n * ```typescript\n * const cage = puzzle.getCage(cageId).orThrow();\n * const possibilities = KillerCombinations.getCellPossibilities(puzzle, state, cage);\n * if (possibilities.isSuccess()) {\n * for (const [cellId, values] of possibilities.value) {\n * console.log(`Cell ${cellId} can have values: ${values.join(', ')}`);\n * }\n * }\n * ```\n */\n public static getCellPossibilities(\n puzzle: Puzzle,\n state: PuzzleState,\n cage: ICage\n ): Result<Map<CellId, number[]>> {\n // Validate cage type\n if (cage.cageType !== 'killer') {\n return fail(`Expected killer cage, got ${cage.cageType}`);\n }\n\n // Get current values in the cage to build constraints\n // We need the concrete Cage class for containedValues method\n const concreteCage = cage as Cage;\n const currentValues = concreteCage.containedValues(state);\n const constraints: IKillerConstraints = {\n excludedNumbers: Array.from(currentValues)\n };\n\n // Get valid combinations for this cage\n const combinationsResult = this.getCombinations(cage.numCells, cage.total, constraints);\n /* c8 ignore next 3 - error propagation from getCombinations already tested, integration error hard to trigger */\n if (combinationsResult.isFailure()) {\n return fail(`Failed to get combinations for cage ${cage.id}: ${combinationsResult.message}`);\n }\n\n // Analyze possibilities for each cell\n return PossibilityAnalyzer.analyze(puzzle, state, cage, combinationsResult.value);\n }\n\n /**\n * Validate constraint parameters.\n * @param constraints - Constraints to validate\n * @param maxValue - Maximum value allowed in cells\n * @returns Result indicating success or failure with error message\n */\n private static _validateConstraints(constraints?: IKillerConstraints, maxValue: number = 9): Result<true> {\n if (!constraints) {\n return succeed(true);\n }\n\n // Validate excluded numbers\n if (constraints.excludedNumbers) {\n const excluded = constraints.excludedNumbers;\n\n if (!Array.isArray(excluded)) {\n return fail('excludedNumbers must be an array');\n }\n\n for (const num of excluded) {\n if (!Number.isInteger(num) || num < 1 || num > maxValue) {\n return fail(`excludedNumbers must contain integers between 1-${maxValue}, got ${num}`);\n }\n }\n\n // Check for duplicates\n const uniqueExcluded = new Set(excluded);\n if (uniqueExcluded.size !== excluded.length) {\n return fail('excludedNumbers must not contain duplicates');\n }\n }\n\n // Validate required numbers\n if (constraints.requiredNumbers) {\n const required = constraints.requiredNumbers;\n\n if (!Array.isArray(required)) {\n return fail('requiredNumbers must be an array');\n }\n\n for (const num of required) {\n if (!Number.isInteger(num) || num < 1 || num > maxValue) {\n return fail(`requiredNumbers must contain integers between 1-${maxValue}, got ${num}`);\n }\n }\n\n // Check for duplicates\n const uniqueRequired = new Set(required);\n if (uniqueRequired.size !== required.length) {\n return fail('requiredNumbers must not contain duplicates');\n }\n }\n\n // Check for overlap between excluded and required\n if (constraints.excludedNumbers && constraints.requiredNumbers) {\n const excludedSet = new Set(constraints.excludedNumbers);\n const requiredSet = new Set(constraints.requiredNumbers);\n\n for (const required of requiredSet) {\n if (excludedSet.has(required)) {\n return fail(`Number ${required} cannot be both required and excluded`);\n }\n }\n }\n\n return succeed(true);\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"killerCombinationsTypes.js","sourceRoot":"","sources":["../../../src/packlets/puzzles/killerCombinationsTypes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/* c8 ignore file */\n\n/**\n * Constraints that can be applied when generating killer cage combinations.\n * @public\n */\nexport interface IKillerConstraints {\n /**\n * Numbers that cannot be present in the combination.\n * Must be unique values between 1-9.\n */\n readonly excludedNumbers?: readonly number[];\n\n /**\n * Numbers that must be present in the combination.\n * Must be unique values between 1-9.\n */\n readonly requiredNumbers?: readonly number[];\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"killerSudokuPuzzle.js","sourceRoot":"","sources":["../../../src/packlets/puzzles/killerSudokuPuzzle.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, captureResult, fail, succeed } from '@fgv/ts-utils';\nimport { Cage, CageId, CellId, IPuzzleDefinition, Ids, Puzzle, getCageTotalBounds } from '../common';\n\nconst cageDefFormat: RegExp = /^[A-Za-z][0-9][0-9]$/;\n\n/**\n * @public\n */\nexport class KillerSudokuPuzzle extends Puzzle {\n private constructor(puzzle: IPuzzleDefinition, cages: [CageId, Cage][]) {\n super(puzzle, cages);\n }\n\n public static create(desc: IPuzzleDefinition): Result<Puzzle> {\n /* c8 ignore next 3 */\n if (desc.type !== 'killer-sudoku') {\n return fail(`Puzzle '${desc.description}' unsupported type ${desc.type}`);\n }\n\n return captureResult(() => {\n const { cages, givens } = KillerSudokuPuzzle._getKillerCages(desc);\n const cells = KillerSudokuPuzzle._getKillerCells(desc, givens);\n return new KillerSudokuPuzzle({ ...desc, cells }, cages);\n })\n .onSuccess((puzzle) => {\n return succeed(puzzle);\n })\n .onFailure((message) => {\n return fail(`Failed to load killer puzzle \"${desc.description}\" - ${message}`);\n });\n }\n\n private static _getKillerCages(puzzle: IPuzzleDefinition): { cages: [CageId, Cage][]; givens: Cage[] } {\n const decl = puzzle.cells.split('|');\n /* c8 ignore next 3 - tested but coverage has intermittent issues */\n if (decl.length !== 2) {\n throw new Error(`malformed cells|cages \"${puzzle.cells}\"`);\n }\n\n const cageCells = KillerSudokuPuzzle._getCageCells(puzzle, decl[0]);\n const allCages = KillerSudokuPuzzle._getCages(puzzle, cageCells, decl[1]);\n const cages = allCages.filter(([__id, cage]) => cage.numCells > 1);\n const givens = allCages.filter(([__id, cage]) => cage.numCells === 1).map(([__id, cage]) => cage);\n return { cages, givens };\n }\n\n private static _getCageCells(puzzle: IPuzzleDefinition, mappingDecl: string): Map<string, CellId[]> {\n const cages: Map<string, CellId[]> = new Map();\n const cageMapping = Array.from(mappingDecl);\n\n /* c8 ignore next 5 - defensive coding: protected by validation in puzzleDefinitions.ts */\n if (cageMapping.length !== puzzle.totalRows * puzzle.totalColumns) {\n const expected = puzzle.totalRows * puzzle.totalColumns;\n const got = cageMapping.length;\n throw new Error(`expected ${expected} cell mappings, found ${got}`);\n }\n\n for (let row = 0; row < puzzle.totalRows; row++) {\n for (let col = 0; col < puzzle.totalColumns; col++) {\n const cage = cageMapping.shift()!;\n const cell = Ids.cellId({ row, col }).orThrow();\n const cells = cages.get(cage) ?? [];\n cells.push(cell);\n cages.set(cage, cells);\n }\n }\n\n return cages;\n }\n\n private static _getCages(\n puzzle: IPuzzleDefinition,\n cageCells: Map<string, CellId[]>,\n cagePart: string\n ): [CageId, Cage][] {\n const cages = new Map<CageId, Cage>();\n\n const cageDefs = cagePart.split(',');\n if (cageDefs.length !== cageCells.size) {\n throw new Error(`expected ${cageCells.size} cage sizes, found ${cageDefs.length}`);\n }\n\n for (const def of cageDefs) {\n if (!cageDefFormat.test(def)) {\n throw new Error(`malformed cage spec ${def}`);\n }\n const cage = def.slice(0, 1);\n const cageId = Ids.cageId(`K${cage}`).orThrow();\n\n const total = Number.parseInt(def.slice(1));\n const cells = cageCells.get(cage);\n /* c8 ignore next 3 - should never happen */\n if (!cells) {\n throw new Error(`cage ${cageId} has no cells`);\n }\n // Use the puzzle's maxValue for dynamic validation\n const maxValue = puzzle.maxValue;\n if (cells.length < 1 || cells.length > maxValue) {\n throw new Error(`invalid cell count ${cells.length} for cage ${cageId} (max ${maxValue})`);\n }\n const { min, max } = getCageTotalBounds(cells.length, maxValue);\n if (total < min || total > max) {\n throw new Error(`invalid total ${total} for cage ${cageId} (expected ${min}..${max})`);\n }\n\n cages.set(cageId, Cage.create(cageId, 'killer', total, cells).orThrow());\n }\n\n return Array.from(cages.entries());\n }\n\n private static _getKillerCells(puzzle: IPuzzleDefinition, givens: Cage[]): string {\n const cells: string[] = [];\n for (let row = 0; row < puzzle.totalRows; row++) {\n for (let col = 0; col < puzzle.totalColumns; col++) {\n const cellId = Ids.cellId({ row, col }).orThrow();\n const cage = givens.find((g) => g.cellIds[0] === cellId);\n if (cage) {\n /* c8 ignore next 3 - defense in depth should never happen */\n if (cage.total < 1 || cage.total > puzzle.maxValue) {\n throw new Error(`invalid total ${cage.total} for cell ${cellId}`);\n }\n cells.push(String(cage.total));\n } else {\n cells.push('.');\n }\n }\n }\n return cells.join('');\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sudokuPuzzle.js","sourceRoot":"","sources":["../../../src/packlets/puzzles/sudokuPuzzle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAU,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAqB,MAAM,EAAE,MAAM,WAAW,CAAC;AAEtD;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,MAAM;IACtC,YAAoB,MAAyB;QAC3C,KAAK,CAAC,MAAM,CAAC,CAAC;IAChB,CAAC;IAEM,MAAM,CAAC,MAAM,CAAC,MAAyB;QAC5C,sBAAsB;QACtB,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,WAAW,MAAM,CAAC,WAAW,sBAAsB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,OAAO,aAAa,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE;YACxE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;CACF","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, captureResult, fail, succeed } from '@fgv/ts-utils';\nimport { IPuzzleDefinition, Puzzle } from '../common';\n\n/**\n * @public\n */\nexport class SudokuPuzzle extends Puzzle {\n private constructor(puzzle: IPuzzleDefinition) {\n super(puzzle);\n }\n\n public static create(puzzle: IPuzzleDefinition): Result<Puzzle> {\n /* c8 ignore next 3 */\n if (puzzle.type !== 'sudoku') {\n return fail(`Puzzle '${puzzle.description}' unsupported type ${puzzle.type}`);\n }\n\n return captureResult(() => new SudokuPuzzle(puzzle)).onSuccess((puzzle) => {\n return succeed(puzzle);\n });\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sudokuXPuzzle.js","sourceRoot":"","sources":["../../../src/packlets/puzzles/sudokuXPuzzle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAU,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,IAAI,EAAqC,GAAG,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEjF;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,MAAM;IACvC,YAAoB,MAAyB,EAAE,UAA4B;QACzE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC5B,CAAC;IAEM,MAAM,CAAC,MAAM,CAAC,MAAyB;QAC5C,sBAAsB;QACtB,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,WAAW,MAAM,CAAC,WAAW,sBAAsB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAChF,CAAC;QACD,6EAA6E;QAC7E,IAAI,MAAM,CAAC,SAAS,KAAK,MAAM,CAAC,YAAY,EAAE,CAAC;YAC7C,OAAO,IAAI,CAAC,uCAAuC,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QAChG,CAAC;QACD,OAAO,aAAa,CAAC,GAAG,EAAE;YACxB,OAAO,IAAI,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE;YACtB,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,MAAM,CAAC,UAAU,CAAC,MAAyB;QACjD,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;QAE7B,KACE,IAAI,GAAG,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,YAAY,GAAG,CAAC,EACrD,GAAG,GAAG,MAAM,CAAC,SAAS,EACtB,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EACrB,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QAExC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;QAC5E,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;QAC5E,OAAO;YACL,CAAC,IAAI,EAAE,EAAE,CAAC;YACV,CAAC,IAAI,EAAE,EAAE,CAAC;SACX,CAAC;IACJ,CAAC;CACF","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Result, captureResult, succeed, fail } from '@fgv/ts-utils';\nimport { Cage, CageId, CellId, IPuzzleDefinition, Ids, Puzzle } from '../common';\n\n/**\n * @public\n */\nexport class SudokuXPuzzle extends Puzzle {\n private constructor(puzzle: IPuzzleDefinition, extraCages: [CageId, Cage][]) {\n super(puzzle, extraCages);\n }\n\n public static create(puzzle: IPuzzleDefinition): Result<Puzzle> {\n /* c8 ignore next 3 */\n if (puzzle.type !== 'sudoku-x') {\n return fail(`Puzzle '${puzzle.description}' unsupported type ${puzzle.type}`);\n }\n // Sudoku X diagonals work for any square puzzle (totalRows === totalColumns)\n if (puzzle.totalRows !== puzzle.totalColumns) {\n return fail(`Sudoku X puzzle must be square, got ${puzzle.totalRows}x${puzzle.totalColumns}`);\n }\n return captureResult(() => {\n return new SudokuXPuzzle(puzzle, SudokuXPuzzle._getXCages(puzzle));\n }).onSuccess((puzzle) => {\n return succeed(puzzle);\n });\n }\n\n private static _getXCages(puzzle: IPuzzleDefinition): [CageId, Cage][] {\n const x1Cells: CellId[] = [];\n const x2Cells: CellId[] = [];\n\n for (\n let row = 0, col1 = 0, col2 = puzzle.totalColumns - 1;\n row < puzzle.totalRows;\n row++, col1++, col2--\n ) {\n x1Cells.push(Ids.cellId({ row, col: col1 }).orThrow());\n x2Cells.push(Ids.cellId({ row, col: col2 }).orThrow());\n }\n\n const x1Id = Ids.cageId('X1').orThrow();\n const x2Id = Ids.cageId('X2').orThrow();\n\n const x1 = Cage.create(x1Id, 'x', puzzle.basicCageTotal, x1Cells).orThrow();\n const x2 = Cage.create(x2Id, 'x', puzzle.basicCageTotal, x2Cells).orThrow();\n return [\n [x1Id, x1],\n [x2Id, x2]\n ];\n }\n}\n"]}
|
package/dist/ts-sudoku-lib.d.ts
CHANGED
|
@@ -126,7 +126,7 @@ export declare class Cage implements ICage {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
|
-
* Nominal identifier for a single {@
|
|
129
|
+
* Nominal identifier for a single {@link ICage | cage} in a {@link PuzzleSession | puzzle}.
|
|
130
130
|
* @public
|
|
131
131
|
*/
|
|
132
132
|
export declare type CageId = Brand<string, 'CageId'>;
|
|
@@ -138,7 +138,7 @@ export declare type CageId = Brand<string, 'CageId'>;
|
|
|
138
138
|
declare const cageId: Converter<CageId>;
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
|
-
* Identifies the type of a {@
|
|
141
|
+
* Identifies the type of a {@link ICage | cage}.
|
|
142
142
|
* @public
|
|
143
143
|
*/
|
|
144
144
|
export declare type CageType = 'row' | 'column' | 'section' | 'x' | 'killer';
|
|
@@ -170,7 +170,7 @@ export declare class Cell implements ICellInit, ICell {
|
|
|
170
170
|
declare type CellAction = 'set-value' | 'eliminate-candidate' | 'add-candidate' | 'highlight';
|
|
171
171
|
|
|
172
172
|
/**
|
|
173
|
-
* Nominal identifier for a single {@
|
|
173
|
+
* Nominal identifier for a single {@link ICell | cell} in a {@link PuzzleSession | puzzle}.
|
|
174
174
|
* @public
|
|
175
175
|
*/
|
|
176
176
|
export declare type CellId = Brand<string, 'CellId'>;
|
|
@@ -763,7 +763,7 @@ export declare interface ICell {
|
|
|
763
763
|
*/
|
|
764
764
|
readonly col: number;
|
|
765
765
|
/**
|
|
766
|
-
* All of the {@
|
|
766
|
+
* All of the {@link ICage | cages} to which this cell belongs.
|
|
767
767
|
*/
|
|
768
768
|
readonly cages: readonly ICage[];
|
|
769
769
|
/**
|
|
@@ -788,7 +788,7 @@ declare interface ICellAction {
|
|
|
788
788
|
}
|
|
789
789
|
|
|
790
790
|
/**
|
|
791
|
-
* The contents of a single {@
|
|
791
|
+
* The contents of a single {@link ICell | cell} in a {@link PuzzleSession | puzzle}.
|
|
792
792
|
* @public
|
|
793
793
|
*/
|
|
794
794
|
export declare interface ICellContents {
|
|
@@ -1141,7 +1141,7 @@ declare interface IRelevantCells {
|
|
|
1141
1141
|
}
|
|
1142
1142
|
|
|
1143
1143
|
/**
|
|
1144
|
-
* The row/column coordinate of a single {@
|
|
1144
|
+
* The row/column coordinate of a single {@link ICell | cell} in a {@link PuzzleSession | puzzle}.
|
|
1145
1145
|
* @public
|
|
1146
1146
|
*/
|
|
1147
1147
|
export declare interface IRowColumn {
|
|
@@ -1391,7 +1391,7 @@ export declare class Puzzle {
|
|
|
1391
1391
|
protected readonly _cells: Map<CellId, Cell>;
|
|
1392
1392
|
/**
|
|
1393
1393
|
* Constructs a new puzzle state.
|
|
1394
|
-
* @param puzzle - {@
|
|
1394
|
+
* @param puzzle - {@link IPuzzleDefinition | Puzzle definition} from which this puzzle state
|
|
1395
1395
|
* is to be initialized.
|
|
1396
1396
|
*/
|
|
1397
1397
|
protected constructor(puzzle: IPuzzleDefinition, extraCages?: [CageId, Cage][]);
|
|
@@ -1464,7 +1464,7 @@ export declare class PuzzleCollection {
|
|
|
1464
1464
|
static create(from: Files.Model.IPuzzlesFile): Result<PuzzleCollection>;
|
|
1465
1465
|
/**
|
|
1466
1466
|
* Creates a new puzzle from a JSON file.
|
|
1467
|
-
* @param
|
|
1467
|
+
* @param file - file item to be loaded.
|
|
1468
1468
|
* @returns `Success` with the resulting {@link PuzzleCollection | PuzzleCollection}
|
|
1469
1469
|
* or `Failure` with details if an error occurs.
|
|
1470
1470
|
*/
|
package/dist/tsdoc-metadata.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,KAAK,MAAM,gCAAgC,CAAC;AACxD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAE9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.browser.js","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,iEAAiE;AAEjE,0HAA0H;AAC1H,sEAAwD;AAM/C,sBAAK;AALd,wDAA0C;AAK1B,sBAAK;AAJrB,4DAA8C;AAIvB,0BAAO;AAF9B,yDAAuC;AACvC,oDAAkC;AAGlC,yBAAyB;AACzB,yFAAyF;AACzF,EAAE;AACF,sCAAsC;AACtC,oEAAoE","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2025 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n// Browser entry point - excludes Node.js filesystem dependencies\n\n// eslint-disable-next-line @rushstack/packlets/mechanics -- browser-specific entry point excludes Node.js fs dependencies\nimport * as Files from './packlets/files/index.browser';\nimport * as Hints from './packlets/hints';\nimport * as Puzzles from './packlets/puzzles';\n\nexport * from './packlets/collections';\nexport * from './packlets/common';\nexport { Files, Hints, Puzzles };\n\n// Excluded from browser:\n// - Files.loadJsonPuzzlesFileSync (requires Node.js fs via JsonFile.convertJsonFileSync)\n//\n// Included in browser (via FileTree):\n// - Files.loadJsonPuzzlesFromTree (browser-compatible via FileTree)\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAE9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC"}
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,wDAA0C;AAMjC,sBAAK;AALd,wDAA0C;AAK1B,sBAAK;AAJrB,4DAA8C;AAIvB,0BAAO;AAF9B,yDAAuC;AACvC,oDAAkC","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport * as Files from './packlets/files';\nimport * as Hints from './packlets/hints';\nimport * as Puzzles from './packlets/puzzles';\n\nexport * from './packlets/collections';\nexport * from './packlets/common';\nexport { Files, Hints, Puzzles };\n"]}
|
|
@@ -22,7 +22,7 @@ export declare class PuzzleCollection {
|
|
|
22
22
|
static create(from: Files.Model.IPuzzlesFile): Result<PuzzleCollection>;
|
|
23
23
|
/**
|
|
24
24
|
* Creates a new puzzle from a JSON file.
|
|
25
|
-
* @param
|
|
25
|
+
* @param file - file item to be loaded.
|
|
26
26
|
* @returns `Success` with the resulting {@link PuzzleCollection | PuzzleCollection}
|
|
27
27
|
* or `Failure` with details if an error occurs.
|
|
28
28
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections.d.ts","sourceRoot":"","sources":["../../../src/packlets/collections/collections.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,KAAK,MAAM,UAAU,CAAC;AAGlC,OAAO,EAAE,MAAM,EAAgC,MAAM,eAAe,CAAC;AAErE,OAAO,EAA8C,aAAa,EAAE,MAAM,WAAW,CAAC;AAEtF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B;;OAEG;IACH,SAAgB,OAAO,EAAE,SAAS,KAAK,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;IAEhE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2C;IAEjE,OAAO;IASP;;;;;OAKG;WACW,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAI9E;;;;;OAKG;WACW,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,iBAAiB,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAI9E;;;;;OAKG;IACI,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;IAanD;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IA4B5B;;;;;OAKG;IACI,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC;CAQvE;AAED;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA2C;IAElE;;OAEG;IACH,WAAkB,OAAO,IAAI,gBAAgB,CAQ5C;CACF"}
|
|
@@ -87,7 +87,7 @@ class PuzzleCollection {
|
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
89
|
* Creates a new puzzle from a JSON file.
|
|
90
|
-
* @param
|
|
90
|
+
* @param file - file item to be loaded.
|
|
91
91
|
* @returns `Success` with the resulting {@link PuzzleCollection | PuzzleCollection}
|
|
92
92
|
* or `Failure` with details if an error occurs.
|
|
93
93
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections.js","sourceRoot":"","sources":["../../../src/packlets/collections/collections.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport * as Files from '../files';\nimport * as Puzzles from '../puzzles';\n\nimport { Result, captureResult, fail, succeed } from '@fgv/ts-utils';\n\nimport { IPuzzleDefinition, PuzzleDefinitionFactory, PuzzleSession } from '../common';\nimport DefaultPuzzles from './data/puzzles.json';\nimport { FileTree } from '@fgv/ts-json-base';\n\n/**\n * A collection of puzzles of various types.\n * @public\n */\nexport class PuzzleCollection {\n /**\n * All puzzles in the collection.\n */\n public readonly puzzles: readonly Files.Model.IPuzzleFileData[];\n\n private readonly _byId: Map<string, Files.Model.IPuzzleFileData>;\n\n private constructor(puzzles: Files.Model.IPuzzlesFile) {\n this.puzzles = puzzles.puzzles;\n this._byId = new Map(\n this.puzzles\n .map((p): [string | undefined, Files.Model.IPuzzleFileData] => [p.id, p])\n .filter((p): p is [string, Files.Model.IPuzzleFileData] => p[0] !== undefined)\n );\n }\n\n /**\n * Creates a new puzzle from a loaded {@link Files.Model.IPuzzlesFile | PuzzlesFile}\n * @param from - The {@link Files.Model.IPuzzlesFile | puzzles file} to be loaded.\n * @returns `Success` with the resulting {@link PuzzleCollection | PuzzleCollection}\n * or `Failure` with details if an error occurs.\n */\n public static create(from: Files.Model.IPuzzlesFile): Result<PuzzleCollection> {\n return captureResult(() => new PuzzleCollection(from));\n }\n\n /**\n * Creates a new puzzle from a JSON file.\n * @param file - file item to be loaded.\n * @returns `Success` with the resulting {@link PuzzleCollection | PuzzleCollection}\n * or `Failure` with details if an error occurs.\n */\n public static load(file: FileTree.IFileTreeFileItem): Result<PuzzleCollection> {\n return Files.Converters.loadPuzzlesFile(file).onSuccess(PuzzleCollection.create);\n }\n\n /**\n * Gets a puzzle by id from this collection.\n * @param id - The string ID of the puzzle to be returned.\n * @returns `Success` with the requested {@link PuzzleSession | puzzle}, or\n * `Failure` with details if an error occurs.\n */\n public getPuzzle(id: string): Result<PuzzleSession> {\n return this.getDescription(id)\n .onSuccess((desc) => {\n return this._convertToDefinition(desc);\n })\n .onSuccess((def) => {\n return Puzzles.Any.create(def);\n })\n .onSuccess((puzzle) => {\n return PuzzleSession.create(puzzle);\n });\n }\n\n /**\n * Converts IPuzzleFileData to IPuzzleDefinition format.\n * @param fileData - The puzzle file data to convert\n * @returns Result containing the puzzle definition\n */\n private _convertToDefinition(fileData: Files.Model.IPuzzleFileData): Result<IPuzzleDefinition> {\n // Validate that dimensions match cell count\n const config = fileData.dimensions;\n const expectedCells =\n config.cageWidthInCells *\n config.cageHeightInCells *\n config.boardWidthInCages *\n config.boardHeightInCages;\n\n // For killer sudoku, cells includes cage definitions after '|' separator\n const gridCells = fileData.type === 'killer-sudoku' ? fileData.cells.split('|')[0] : fileData.cells;\n const actualCells = gridCells.length;\n\n if (actualCells !== expectedCells) {\n return fail(\n `Dimensions mismatch: dimensions specify ${expectedCells} cells (${config.cageWidthInCells}x${config.cageHeightInCells} cages in ${config.boardWidthInCages}x${config.boardHeightInCages} layout) but cells string contains ${actualCells} cells`\n );\n }\n\n return PuzzleDefinitionFactory.create(config, {\n id: fileData.id,\n description: fileData.description,\n type: fileData.type,\n level: fileData.level,\n cells: fileData.cells\n });\n }\n\n /**\n * Gets a puzzle by id from this collection.\n * @param id - The string ID of the puzzle to be returned.\n * @returns `Success` with the requested {@link PuzzleSession | puzzle}, or\n * `Failure` with details if an error occurs.\n */\n public getDescription(id: string): Result<Files.Model.IPuzzleFileData> {\n const fileData = this._byId.get(id);\n /* c8 ignore next 3 - tested but coverage fails intermittently */\n if (!fileData) {\n return fail(`Puzzle \"${id}\" not found`);\n }\n return succeed(fileData);\n }\n}\n\n/**\n * Get well-known {@link PuzzleCollection | puzzle collections}.\n * @public\n */\nexport class PuzzleCollections {\n private static _default: PuzzleCollection | undefined = undefined;\n\n /**\n * The default {@link PuzzleCollection | puzzle collection}.\n */\n public static get default(): PuzzleCollection {\n if (!PuzzleCollections._default) {\n PuzzleCollections._default = Files.Converters.puzzlesFile\n .convert(DefaultPuzzles)\n .onSuccess(PuzzleCollection.create)\n .orThrow();\n }\n return PuzzleCollections._default;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/packlets/collections/index.ts"],"names":[],"mappings":"AAwBA,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/collections/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;;;;;;;;;;;;;;AAEH,gDAA8B","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nexport * from './collections';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cage.d.ts","sourceRoot":"","sources":["../../../src/packlets/common/cage.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,MAAM,EAAiB,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C;;GAEG;AACH,qBAAa,IAAK,YAAW,KAAK;IAChC,SAAgB,EAAE,EAAE,MAAM,CAAC;IAC3B,SAAgB,QAAQ,EAAE,QAAQ,CAAC;IACnC,SAAgB,KAAK,EAAE,MAAM,CAAC;IAE9B,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,OAAO,CAAc;IAE7B,OAAO;IAQP,IAAW,OAAO,IAAI,MAAM,EAAE,CAE7B;IAED,IAAW,QAAQ,IAAI,MAAM,CAE5B;WAEa,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IAIvF,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIjC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO;IAY5E,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC;IAYhD,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM;CAS7C"}
|