@fgv/ts-sudoku-ui 5.1.0-2 → 5.1.0-21
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/eslint.config.js +18 -0
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +22 -21
- package/.test-failures-catalog.md +0 -470
- package/CHANGELOG.md +0 -11
- package/CSS_MODULES_MIGRATION_SUMMARY.md +0 -189
- package/dist/ts-sudoku-ui.d.ts +0 -668
- package/etc/ts-sudoku-ui.api.md +0 -481
- package/lib/test/fixtures/killerSudokuPuzzles.d.ts +0 -36
- package/lib/test/fixtures/killerSudokuPuzzles.d.ts.map +0 -1
- package/lib/test/fixtures/killerSudokuPuzzles.js +0 -57
- package/lib/test/fixtures/killerSudokuPuzzles.js.map +0 -1
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.d.ts +0 -22
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.d.ts.map +0 -1
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.js +0 -116
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.js.map +0 -1
- package/lib-commonjs/components/CageOverlay.js +0 -138
- package/lib-commonjs/components/CageOverlay.js.map +0 -1
- package/lib-commonjs/components/CageSumIndicator.js +0 -71
- package/lib-commonjs/components/CageSumIndicator.js.map +0 -1
- package/lib-commonjs/components/CombinationCard.js +0 -68
- package/lib-commonjs/components/CombinationCard.js.map +0 -1
- package/lib-commonjs/components/CombinationGrid.js +0 -56
- package/lib-commonjs/components/CombinationGrid.js.map +0 -1
- package/lib-commonjs/components/CompactControlRibbon.js +0 -146
- package/lib-commonjs/components/CompactControlRibbon.js.map +0 -1
- package/lib-commonjs/components/DualKeypad.js +0 -142
- package/lib-commonjs/components/DualKeypad.js.map +0 -1
- package/lib-commonjs/components/KillerCombinationsExplorer.js +0 -74
- package/lib-commonjs/components/KillerCombinationsExplorer.js.map +0 -1
- package/lib-commonjs/components/KillerCombinationsModal.js +0 -140
- package/lib-commonjs/components/KillerCombinationsModal.js.map +0 -1
- package/lib-commonjs/components/KillerCombinationsPanel.js +0 -84
- package/lib-commonjs/components/KillerCombinationsPanel.js.map +0 -1
- package/lib-commonjs/components/NumberKeypad.js +0 -186
- package/lib-commonjs/components/NumberKeypad.js.map +0 -1
- package/lib-commonjs/components/SudokuCell.js +0 -245
- package/lib-commonjs/components/SudokuCell.js.map +0 -1
- package/lib-commonjs/components/SudokuControls.js +0 -145
- package/lib-commonjs/components/SudokuControls.js.map +0 -1
- package/lib-commonjs/components/SudokuGrid.js +0 -182
- package/lib-commonjs/components/SudokuGrid.js.map +0 -1
- package/lib-commonjs/components/SudokuGridEntry.js +0 -378
- package/lib-commonjs/components/SudokuGridEntry.js.map +0 -1
- package/lib-commonjs/components/ValidationDisplay.js +0 -100
- package/lib-commonjs/components/ValidationDisplay.js.map +0 -1
- package/lib-commonjs/contexts/DiagnosticLoggerContext.js +0 -104
- package/lib-commonjs/contexts/DiagnosticLoggerContext.js.map +0 -1
- package/lib-commonjs/contexts/index.js +0 -32
- package/lib-commonjs/contexts/index.js.map +0 -1
- package/lib-commonjs/hooks/useCombinationElimination.js +0 -126
- package/lib-commonjs/hooks/useCombinationElimination.js.map +0 -1
- package/lib-commonjs/hooks/useKeyboardShortcut.js +0 -58
- package/lib-commonjs/hooks/useKeyboardShortcut.js.map +0 -1
- package/lib-commonjs/hooks/useKillerCombinations.js +0 -68
- package/lib-commonjs/hooks/useKillerCombinations.js.map +0 -1
- package/lib-commonjs/hooks/useLongPress.js +0 -72
- package/lib-commonjs/hooks/useLongPress.js.map +0 -1
- package/lib-commonjs/hooks/usePuzzleSession.js +0 -401
- package/lib-commonjs/hooks/usePuzzleSession.js.map +0 -1
- package/lib-commonjs/hooks/useResponsiveLayout.js +0 -142
- package/lib-commonjs/hooks/useResponsiveLayout.js.map +0 -1
- package/lib-commonjs/index.browser.js +0 -29
- package/lib-commonjs/index.browser.js.map +0 -1
- package/lib-commonjs/index.js +0 -73
- package/lib-commonjs/index.js.map +0 -1
- package/lib-commonjs/test/fixtures/killerSudokuPuzzles.js +0 -60
- package/lib-commonjs/test/fixtures/killerSudokuPuzzles.js.map +0 -1
- package/lib-commonjs/test/unit/components/SudokuGridEntry.testHelpers.js +0 -125
- package/lib-commonjs/test/unit/components/SudokuGridEntry.testHelpers.js.map +0 -1
- package/lib-commonjs/types/index.js +0 -26
- package/lib-commonjs/types/index.js.map +0 -1
- package/lib-commonjs/utils/CageLookupManager.js +0 -287
- package/lib-commonjs/utils/CageLookupManager.js.map +0 -1
- package/lib-commonjs/utils/CagePatternManager.js +0 -143
- package/lib-commonjs/utils/CagePatternManager.js.map +0 -1
- package/lib-commonjs/utils/puzzleDefinitionHelper.js +0 -103
- package/lib-commonjs/utils/puzzleDefinitionHelper.js.map +0 -1
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* MIT License
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) 2023 Erik Fortune
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
* copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
* SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.CageLookupManager = exports.SegmentBit = void 0;
|
|
27
|
-
/**
|
|
28
|
-
* Segment bit positions for the 12-bit mask
|
|
29
|
-
*/
|
|
30
|
-
var SegmentBit;
|
|
31
|
-
(function (SegmentBit) {
|
|
32
|
-
// Edges (bits 0-3)
|
|
33
|
-
SegmentBit[SegmentBit["EDGE_TOP"] = 1] = "EDGE_TOP";
|
|
34
|
-
SegmentBit[SegmentBit["EDGE_RIGHT"] = 2] = "EDGE_RIGHT";
|
|
35
|
-
SegmentBit[SegmentBit["EDGE_BOTTOM"] = 4] = "EDGE_BOTTOM";
|
|
36
|
-
SegmentBit[SegmentBit["EDGE_LEFT"] = 8] = "EDGE_LEFT";
|
|
37
|
-
// Corner connectors (bits 4-7)
|
|
38
|
-
SegmentBit[SegmentBit["CORNER_TOP_RIGHT"] = 16] = "CORNER_TOP_RIGHT";
|
|
39
|
-
SegmentBit[SegmentBit["CORNER_BOTTOM_RIGHT"] = 32] = "CORNER_BOTTOM_RIGHT";
|
|
40
|
-
SegmentBit[SegmentBit["CORNER_BOTTOM_LEFT"] = 64] = "CORNER_BOTTOM_LEFT";
|
|
41
|
-
SegmentBit[SegmentBit["CORNER_TOP_LEFT"] = 128] = "CORNER_TOP_LEFT";
|
|
42
|
-
// Internal segments (bits 8-11)
|
|
43
|
-
SegmentBit[SegmentBit["INTERNAL_TOP"] = 256] = "INTERNAL_TOP";
|
|
44
|
-
SegmentBit[SegmentBit["INTERNAL_RIGHT"] = 512] = "INTERNAL_RIGHT";
|
|
45
|
-
SegmentBit[SegmentBit["INTERNAL_BOTTOM"] = 1024] = "INTERNAL_BOTTOM";
|
|
46
|
-
SegmentBit[SegmentBit["INTERNAL_LEFT"] = 2048] = "INTERNAL_LEFT";
|
|
47
|
-
})(SegmentBit || (exports.SegmentBit = SegmentBit = {}));
|
|
48
|
-
/**
|
|
49
|
-
* Two-stage lookup manager for cage border rendering
|
|
50
|
-
* Stage 1: 8-neighbor pattern → segment pattern ID (256 → 47)
|
|
51
|
-
* Stage 2: segment pattern ID → SVG path data (47 unique patterns)
|
|
52
|
-
*/
|
|
53
|
-
class CageLookupManager {
|
|
54
|
-
constructor() {
|
|
55
|
-
// Stage 1: Neighbor pattern (8-bit) to segment pattern ID mapping
|
|
56
|
-
this._neighborToSegmentLookup = new Array(256);
|
|
57
|
-
// Stage 2: Segment pattern ID to SVG path data mapping
|
|
58
|
-
this._segmentPatterns = [];
|
|
59
|
-
// Reverse lookup for debugging: segment mask to pattern ID
|
|
60
|
-
this._segmentMaskToId = new Map();
|
|
61
|
-
this._generateLookupTables();
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Get singleton instance
|
|
65
|
-
*/
|
|
66
|
-
static getInstance() {
|
|
67
|
-
if (!CageLookupManager._instance) {
|
|
68
|
-
CageLookupManager._instance = new CageLookupManager();
|
|
69
|
-
}
|
|
70
|
-
return CageLookupManager._instance;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Convert neighbor analysis to 8-bit pattern
|
|
74
|
-
*/
|
|
75
|
-
neighborsToPattern(neighbors) {
|
|
76
|
-
// should be bitwise or but that makes eslint cranky
|
|
77
|
-
return ((neighbors.north ? 1 : 0) +
|
|
78
|
-
(neighbors.northEast ? 2 : 0) +
|
|
79
|
-
(neighbors.east ? 4 : 0) +
|
|
80
|
-
(neighbors.southEast ? 8 : 0) +
|
|
81
|
-
(neighbors.south ? 16 : 0) +
|
|
82
|
-
(neighbors.southWest ? 32 : 0) +
|
|
83
|
-
(neighbors.west ? 64 : 0) +
|
|
84
|
-
(neighbors.northWest ? 128 : 0));
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Get SVG path data for a given neighbor pattern
|
|
88
|
-
*/
|
|
89
|
-
getPatternSVG(neighborPattern) {
|
|
90
|
-
if (neighborPattern < 0 || neighborPattern >= 256) {
|
|
91
|
-
throw new Error(`Invalid neighbor pattern: ${neighborPattern}`);
|
|
92
|
-
}
|
|
93
|
-
const segmentPatternId = this._neighborToSegmentLookup[neighborPattern];
|
|
94
|
-
return this._segmentPatterns[segmentPatternId];
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Get segment pattern ID for debugging
|
|
98
|
-
*/
|
|
99
|
-
getSegmentPatternId(neighborPattern) {
|
|
100
|
-
return this._neighborToSegmentLookup[neighborPattern];
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Get total number of unique segment patterns
|
|
104
|
-
*/
|
|
105
|
-
get uniquePatternCount() {
|
|
106
|
-
return this._segmentPatterns.length;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Generate both lookup tables
|
|
110
|
-
*/
|
|
111
|
-
_generateLookupTables() {
|
|
112
|
-
// First pass: generate all unique segment masks and assign IDs
|
|
113
|
-
const uniqueSegmentMasks = new Set();
|
|
114
|
-
for (let neighborPattern = 0; neighborPattern < 256; neighborPattern++) {
|
|
115
|
-
const segmentMask = this._calculateSegmentMask(neighborPattern);
|
|
116
|
-
uniqueSegmentMasks.add(segmentMask);
|
|
117
|
-
}
|
|
118
|
-
// Convert to sorted array for consistent ordering
|
|
119
|
-
const sortedSegmentMasks = Array.from(uniqueSegmentMasks).sort((a, b) => a - b);
|
|
120
|
-
// Generate SVG patterns for each unique segment mask
|
|
121
|
-
for (let i = 0; i < sortedSegmentMasks.length; i++) {
|
|
122
|
-
const segmentMask = sortedSegmentMasks[i];
|
|
123
|
-
this._segmentMaskToId.set(segmentMask, i);
|
|
124
|
-
this._segmentPatterns[i] = this._generateSVGPattern(segmentMask);
|
|
125
|
-
}
|
|
126
|
-
// Second pass: populate neighbor-to-segment lookup
|
|
127
|
-
for (let neighborPattern = 0; neighborPattern < 256; neighborPattern++) {
|
|
128
|
-
const segmentMask = this._calculateSegmentMask(neighborPattern);
|
|
129
|
-
const segmentPatternId = this._segmentMaskToId.get(segmentMask);
|
|
130
|
-
this._neighborToSegmentLookup[neighborPattern] = segmentPatternId;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Calculate segment mask from 8-bit neighbor pattern
|
|
135
|
-
*/
|
|
136
|
-
_calculateSegmentMask(neighborPattern) {
|
|
137
|
-
// Extract individual neighbor bits
|
|
138
|
-
// eslint-disable-next-line no-bitwise
|
|
139
|
-
const n = Boolean(neighborPattern & 1); // North
|
|
140
|
-
// eslint-disable-next-line no-bitwise
|
|
141
|
-
const ne = Boolean(neighborPattern & 2); // NorthEast
|
|
142
|
-
// eslint-disable-next-line no-bitwise
|
|
143
|
-
const e = Boolean(neighborPattern & 4); // East
|
|
144
|
-
// eslint-disable-next-line no-bitwise
|
|
145
|
-
const se = Boolean(neighborPattern & 8); // SouthEast
|
|
146
|
-
// eslint-disable-next-line no-bitwise
|
|
147
|
-
const s = Boolean(neighborPattern & 16); // South
|
|
148
|
-
// eslint-disable-next-line no-bitwise
|
|
149
|
-
const sw = Boolean(neighborPattern & 32); // SouthWest
|
|
150
|
-
// eslint-disable-next-line no-bitwise
|
|
151
|
-
const w = Boolean(neighborPattern & 64); // West
|
|
152
|
-
// eslint-disable-next-line no-bitwise
|
|
153
|
-
const nw = Boolean(neighborPattern & 128); // NorthWest
|
|
154
|
-
let segmentMask = 0;
|
|
155
|
-
// Edges: Draw when corresponding cardinal neighbor is NOT present
|
|
156
|
-
// eslint-disable-next-line no-bitwise
|
|
157
|
-
if (!n)
|
|
158
|
-
segmentMask |= SegmentBit.EDGE_TOP;
|
|
159
|
-
// eslint-disable-next-line no-bitwise
|
|
160
|
-
if (!e)
|
|
161
|
-
segmentMask |= SegmentBit.EDGE_RIGHT;
|
|
162
|
-
// eslint-disable-next-line no-bitwise
|
|
163
|
-
if (!s)
|
|
164
|
-
segmentMask |= SegmentBit.EDGE_BOTTOM;
|
|
165
|
-
// eslint-disable-next-line no-bitwise
|
|
166
|
-
if (!w)
|
|
167
|
-
segmentMask |= SegmentBit.EDGE_LEFT;
|
|
168
|
-
// Internal segments: Draw when corresponding cardinal neighbor IS present
|
|
169
|
-
// eslint-disable-next-line no-bitwise
|
|
170
|
-
if (n)
|
|
171
|
-
segmentMask |= SegmentBit.INTERNAL_TOP;
|
|
172
|
-
// eslint-disable-next-line no-bitwise
|
|
173
|
-
if (e)
|
|
174
|
-
segmentMask |= SegmentBit.INTERNAL_RIGHT;
|
|
175
|
-
// eslint-disable-next-line no-bitwise
|
|
176
|
-
if (s)
|
|
177
|
-
segmentMask |= SegmentBit.INTERNAL_BOTTOM;
|
|
178
|
-
// eslint-disable-next-line no-bitwise
|
|
179
|
-
if (w)
|
|
180
|
-
segmentMask |= SegmentBit.INTERNAL_LEFT;
|
|
181
|
-
// Corner connectors: Draw when diagonal is missing but both adjacent cardinals are present
|
|
182
|
-
// eslint-disable-next-line no-bitwise
|
|
183
|
-
if (!ne && n && e)
|
|
184
|
-
segmentMask |= SegmentBit.CORNER_TOP_RIGHT;
|
|
185
|
-
// eslint-disable-next-line no-bitwise
|
|
186
|
-
if (!se && s && e)
|
|
187
|
-
segmentMask |= SegmentBit.CORNER_BOTTOM_RIGHT;
|
|
188
|
-
// eslint-disable-next-line no-bitwise
|
|
189
|
-
if (!sw && s && w)
|
|
190
|
-
segmentMask |= SegmentBit.CORNER_BOTTOM_LEFT;
|
|
191
|
-
// eslint-disable-next-line no-bitwise
|
|
192
|
-
if (!nw && n && w)
|
|
193
|
-
segmentMask |= SegmentBit.CORNER_TOP_LEFT;
|
|
194
|
-
return segmentMask;
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Generate SVG path data for a segment mask
|
|
198
|
-
*/
|
|
199
|
-
_generateSVGPattern(segmentMask) {
|
|
200
|
-
const paths = [];
|
|
201
|
-
const inset = 0.1; // Standard inset for cage boundaries
|
|
202
|
-
const edgeExtension = 0.0; // Extend to cell edge for connections
|
|
203
|
-
// Determine which segments have internal connections (need to extend to edge)
|
|
204
|
-
// eslint-disable-next-line no-bitwise
|
|
205
|
-
const hasInternalTop = Boolean(segmentMask & SegmentBit.INTERNAL_TOP);
|
|
206
|
-
// eslint-disable-next-line no-bitwise
|
|
207
|
-
const hasInternalRight = Boolean(segmentMask & SegmentBit.INTERNAL_RIGHT);
|
|
208
|
-
// eslint-disable-next-line no-bitwise
|
|
209
|
-
const hasInternalBottom = Boolean(segmentMask & SegmentBit.INTERNAL_BOTTOM);
|
|
210
|
-
// eslint-disable-next-line no-bitwise
|
|
211
|
-
const hasInternalLeft = Boolean(segmentMask & SegmentBit.INTERNAL_LEFT);
|
|
212
|
-
// Edge segments - draw main segment with standard inset, then extensions to edge where needed
|
|
213
|
-
// eslint-disable-next-line no-bitwise
|
|
214
|
-
if (segmentMask & SegmentBit.EDGE_TOP) {
|
|
215
|
-
// Main horizontal segment
|
|
216
|
-
paths.push(`M${inset},${inset} L${1 - inset},${inset}`);
|
|
217
|
-
// Extensions to cell edges where there are internal connections
|
|
218
|
-
if (hasInternalLeft) {
|
|
219
|
-
paths.push(`M${edgeExtension},${inset} L${inset},${inset}`);
|
|
220
|
-
}
|
|
221
|
-
if (hasInternalRight) {
|
|
222
|
-
paths.push(`M${1 - inset},${inset} L${1 - edgeExtension},${inset}`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// eslint-disable-next-line no-bitwise
|
|
226
|
-
if (segmentMask & SegmentBit.EDGE_RIGHT) {
|
|
227
|
-
// Main vertical segment
|
|
228
|
-
paths.push(`M${1 - inset},${inset} L${1 - inset},${1 - inset}`);
|
|
229
|
-
// Extensions to cell edges where there are internal connections
|
|
230
|
-
if (hasInternalTop) {
|
|
231
|
-
paths.push(`M${1 - inset},${edgeExtension} L${1 - inset},${inset}`);
|
|
232
|
-
}
|
|
233
|
-
if (hasInternalBottom) {
|
|
234
|
-
paths.push(`M${1 - inset},${1 - inset} L${1 - inset},${1 - edgeExtension}`);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
// eslint-disable-next-line no-bitwise
|
|
238
|
-
if (segmentMask & SegmentBit.EDGE_BOTTOM) {
|
|
239
|
-
// Main horizontal segment
|
|
240
|
-
paths.push(`M${1 - inset},${1 - inset} L${inset},${1 - inset}`);
|
|
241
|
-
// Extensions to cell edges where there are internal connections
|
|
242
|
-
if (hasInternalRight) {
|
|
243
|
-
paths.push(`M${1 - edgeExtension},${1 - inset} L${1 - inset},${1 - inset}`);
|
|
244
|
-
}
|
|
245
|
-
if (hasInternalLeft) {
|
|
246
|
-
paths.push(`M${inset},${1 - inset} L${edgeExtension},${1 - inset}`);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
// eslint-disable-next-line no-bitwise
|
|
250
|
-
if (segmentMask & SegmentBit.EDGE_LEFT) {
|
|
251
|
-
// Main vertical segment
|
|
252
|
-
paths.push(`M${inset},${1 - inset} L${inset},${inset}`);
|
|
253
|
-
// Extensions to cell edges where there are internal connections
|
|
254
|
-
if (hasInternalBottom) {
|
|
255
|
-
paths.push(`M${inset},${1 - edgeExtension} L${inset},${1 - inset}`);
|
|
256
|
-
}
|
|
257
|
-
if (hasInternalTop) {
|
|
258
|
-
paths.push(`M${inset},${inset} L${inset},${edgeExtension}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
// Corner connectors - extend outward from cage corners
|
|
262
|
-
const cornerRadius = 0.08;
|
|
263
|
-
// eslint-disable-next-line no-bitwise
|
|
264
|
-
if (segmentMask & SegmentBit.CORNER_TOP_RIGHT) {
|
|
265
|
-
// Extend outward from top-right corner
|
|
266
|
-
paths.push(`M${1 - inset},${inset} L${1 - inset + cornerRadius},${inset} M${1 - inset},${inset} L${1 - inset},${inset - cornerRadius}`);
|
|
267
|
-
}
|
|
268
|
-
// eslint-disable-next-line no-bitwise
|
|
269
|
-
if (segmentMask & SegmentBit.CORNER_BOTTOM_RIGHT) {
|
|
270
|
-
// Extend outward from bottom-right corner
|
|
271
|
-
paths.push(`M${1 - inset},${1 - inset} L${1 - inset + cornerRadius},${1 - inset} M${1 - inset},${1 - inset} L${1 - inset},${1 - inset + cornerRadius}`);
|
|
272
|
-
}
|
|
273
|
-
// eslint-disable-next-line no-bitwise
|
|
274
|
-
if (segmentMask & SegmentBit.CORNER_BOTTOM_LEFT) {
|
|
275
|
-
// Extend outward from bottom-left corner
|
|
276
|
-
paths.push(`M${inset},${1 - inset} L${inset - cornerRadius},${1 - inset} M${inset},${1 - inset} L${inset},${1 - inset + cornerRadius}`);
|
|
277
|
-
}
|
|
278
|
-
// eslint-disable-next-line no-bitwise
|
|
279
|
-
if (segmentMask & SegmentBit.CORNER_TOP_LEFT) {
|
|
280
|
-
// Extend outward from top-left corner
|
|
281
|
-
paths.push(`M${inset},${inset} L${inset - cornerRadius},${inset} M${inset},${inset} L${inset},${inset - cornerRadius}`);
|
|
282
|
-
}
|
|
283
|
-
return paths.join(' ');
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
exports.CageLookupManager = CageLookupManager;
|
|
287
|
-
//# sourceMappingURL=CageLookupManager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CageLookupManager.js","sourceRoot":"","sources":["../../src/utils/CageLookupManager.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;AAgBH;;GAEG;AACH,IAAY,UAkBX;AAlBD,WAAY,UAAU;IACpB,mBAAmB;IACnB,mDAAiB,CAAA;IACjB,uDAAmB,CAAA;IACnB,yDAAoB,CAAA;IACpB,qDAAkB,CAAA;IAElB,+BAA+B;IAC/B,oEAAyB,CAAA;IACzB,0EAA4B,CAAA;IAC5B,wEAA2B,CAAA;IAC3B,mEAAwB,CAAA;IAExB,gCAAgC;IAChC,6DAAqB,CAAA;IACrB,iEAAuB,CAAA;IACvB,oEAAyB,CAAA;IACzB,gEAAuB,CAAA;AACzB,CAAC,EAlBW,UAAU,0BAAV,UAAU,QAkBrB;AAED;;;;GAIG;AACH,MAAa,iBAAiB;IAY5B;QATA,kEAAkE;QACjD,6BAAwB,GAAa,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;QAErE,uDAAuD;QACtC,qBAAgB,GAAa,EAAE,CAAC;QAEjD,2DAA2D;QAC1C,qBAAgB,GAAwB,IAAI,GAAG,EAAkB,CAAC;QAGjF,IAAI,CAAC,qBAAqB,EAAE,CAAC;IAC/B,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,WAAW;QACvB,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,CAAC;YACjC,iBAAiB,CAAC,SAAS,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACxD,CAAC;QACD,OAAO,iBAAiB,CAAC,SAAS,CAAC;IACrC,CAAC;IAED;;OAEG;IACI,kBAAkB,CAAC,SAA4B;QACpD,oDAAoD;QACpD,OAAO,CACL,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACzB,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACxB,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9B,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACzB,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAChC,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,aAAa,CAAC,eAAuB;QAC1C,IAAI,eAAe,GAAG,CAAC,IAAI,eAAe,IAAI,GAAG,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,6BAA6B,eAAe,EAAE,CAAC,CAAC;QAClE,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,wBAAwB,CAAC,eAAe,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IACjD,CAAC;IAED;;OAEG;IACI,mBAAmB,CAAC,eAAuB;QAChD,OAAO,IAAI,CAAC,wBAAwB,CAAC,eAAe,CAAC,CAAC;IACxD,CAAC;IAED;;OAEG;IACH,IAAW,kBAAkB;QAC3B,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;IACtC,CAAC;IAED;;OAEG;IACK,qBAAqB;QAC3B,+DAA+D;QAC/D,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;QAE7C,KAAK,IAAI,eAAe,GAAG,CAAC,EAAE,eAAe,GAAG,GAAG,EAAE,eAAe,EAAE,EAAE,CAAC;YACvE,MAAM,WAAW,GAAG,IAAI,CAAC,qBAAqB,CAAC,eAAe,CAAC,CAAC;YAChE,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACtC,CAAC;QAED,kDAAkD;QAClD,MAAM,kBAAkB,GAAG,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAEhF,qDAAqD;QACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,kBAAkB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACnD,MAAM,WAAW,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC;YAC1C,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;YAC1C,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;QACnE,CAAC;QAED,mDAAmD;QACnD,KAAK,IAAI,eAAe,GAAG,CAAC,EAAE,eAAe,GAAG,GAAG,EAAE,eAAe,EAAE,EAAE,CAAC;YACvE,MAAM,WAAW,GAAG,IAAI,CAAC,qBAAqB,CAAC,eAAe,CAAC,CAAC;YAChE,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC;YACjE,IAAI,CAAC,wBAAwB,CAAC,eAAe,CAAC,GAAG,gBAAgB,CAAC;QACpE,CAAC;IACH,CAAC;IAED;;OAEG;IACK,qBAAqB,CAAC,eAAuB;QACnD,mCAAmC;QACnC,sCAAsC;QACtC,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ;QAChD,sCAAsC;QACtC,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY;QACrD,sCAAsC;QACtC,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO;QAC/C,sCAAsC;QACtC,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY;QACrD,sCAAsC;QACtC,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ;QACjD,sCAAsC;QACtC,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,GAAG,EAAE,CAAC,CAAC,CAAC,YAAY;QACtD,sCAAsC;QACtC,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO;QAChD,sCAAsC;QACtC,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,GAAG,GAAG,CAAC,CAAC,CAAC,YAAY;QAEvD,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,kEAAkE;QAClE,sCAAsC;QACtC,IAAI,CAAC,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,QAAQ,CAAC;QAC3C,sCAAsC;QACtC,IAAI,CAAC,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,UAAU,CAAC;QAC7C,sCAAsC;QACtC,IAAI,CAAC,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,WAAW,CAAC;QAC9C,sCAAsC;QACtC,IAAI,CAAC,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,SAAS,CAAC;QAE5C,0EAA0E;QAC1E,sCAAsC;QACtC,IAAI,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,YAAY,CAAC;QAC9C,sCAAsC;QACtC,IAAI,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,cAAc,CAAC;QAChD,sCAAsC;QACtC,IAAI,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,eAAe,CAAC;QACjD,sCAAsC;QACtC,IAAI,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,aAAa,CAAC;QAE/C,2FAA2F;QAC3F,sCAAsC;QACtC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,gBAAgB,CAAC;QAC9D,sCAAsC;QACtC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,mBAAmB,CAAC;QACjE,sCAAsC;QACtC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,kBAAkB,CAAC;QAChE,sCAAsC;QACtC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;YAAE,WAAW,IAAI,UAAU,CAAC,eAAe,CAAC;QAE7D,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,WAAmB;QAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,qCAAqC;QACxD,MAAM,aAAa,GAAG,GAAG,CAAC,CAAC,sCAAsC;QAEjE,8EAA8E;QAC9E,sCAAsC;QACtC,MAAM,cAAc,GAAG,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;QACtE,sCAAsC;QACtC,MAAM,gBAAgB,GAAG,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC;QAC1E,sCAAsC;QACtC,MAAM,iBAAiB,GAAG,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;QAC5E,sCAAsC;QACtC,MAAM,eAAe,GAAG,OAAO,CAAC,WAAW,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;QAExE,8FAA8F;QAC9F,sCAAsC;QACtC,IAAI,WAAW,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;YACtC,0BAA0B;YAC1B,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC,GAAG,KAAK,IAAI,KAAK,EAAE,CAAC,CAAC;YAExD,gEAAgE;YAChE,IAAI,eAAe,EAAE,CAAC;gBACpB,KAAK,CAAC,IAAI,CAAC,IAAI,aAAa,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,EAAE,CAAC,CAAC;YAC9D,CAAC;YACD,IAAI,gBAAgB,EAAE,CAAC;gBACrB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,IAAI,WAAW,GAAG,UAAU,CAAC,UAAU,EAAE,CAAC;YACxC,wBAAwB;YACxB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;YAEhE,gEAAgE;YAChE,IAAI,cAAc,EAAE,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,aAAa,KAAK,CAAC,GAAG,KAAK,IAAI,KAAK,EAAE,CAAC,CAAC;YACtE,CAAC;YACD,IAAI,iBAAiB,EAAE,CAAC;gBACtB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,aAAa,EAAE,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,IAAI,WAAW,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;YACzC,0BAA0B;YAC1B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,KAAK,KAAK,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;YAEhE,gEAAgE;YAChE,IAAI,gBAAgB,EAAE,CAAC;gBACrB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,aAAa,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;YAC9E,CAAC;YACD,IAAI,eAAe,EAAE,CAAC;gBACpB,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,KAAK,KAAK,aAAa,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,IAAI,WAAW,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC;YACvC,wBAAwB;YACxB,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,KAAK,KAAK,KAAK,IAAI,KAAK,EAAE,CAAC,CAAC;YAExD,gEAAgE;YAChE,IAAI,iBAAiB,EAAE,CAAC;gBACtB,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,aAAa,KAAK,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;YACtE,CAAC;YACD,IAAI,cAAc,EAAE,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,KAAK,KAAK,IAAI,aAAa,EAAE,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;QAED,uDAAuD;QACvD,MAAM,YAAY,GAAG,IAAI,CAAC;QAC1B,sCAAsC;QACtC,IAAI,WAAW,GAAG,UAAU,CAAC,gBAAgB,EAAE,CAAC;YAC9C,uCAAuC;YACvC,KAAK,CAAC,IAAI,CACR,IAAI,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,GAAG,KAAK,GAAG,YAAY,IAAI,KAAK,KAAK,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,GAAG,KAAK,IAC/F,KAAK,GAAG,YACV,EAAE,CACH,CAAC;QACJ,CAAC;QACD,sCAAsC;QACtC,IAAI,WAAW,GAAG,UAAU,CAAC,mBAAmB,EAAE,CAAC;YACjD,0CAA0C;YAC1C,KAAK,CAAC,IAAI,CACR,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,GAAG,YAAY,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,KAAK,KAC7F,CAAC,GAAG,KACN,IAAI,CAAC,GAAG,KAAK,GAAG,YAAY,EAAE,CAC/B,CAAC;QACJ,CAAC;QACD,sCAAsC;QACtC,IAAI,WAAW,GAAG,UAAU,CAAC,kBAAkB,EAAE,CAAC;YAChD,yCAAyC;YACzC,KAAK,CAAC,IAAI,CACR,IAAI,KAAK,IAAI,CAAC,GAAG,KAAK,KAAK,KAAK,GAAG,YAAY,IAAI,CAAC,GAAG,KAAK,KAAK,KAAK,IAAI,CAAC,GAAG,KAAK,KAAK,KAAK,IAC3F,CAAC,GAAG,KAAK,GAAG,YACd,EAAE,CACH,CAAC;QACJ,CAAC;QACD,sCAAsC;QACtC,IAAI,WAAW,GAAG,UAAU,CAAC,eAAe,EAAE,CAAC;YAC7C,sCAAsC;YACtC,KAAK,CAAC,IAAI,CACR,IAAI,KAAK,IAAI,KAAK,KAAK,KAAK,GAAG,YAAY,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,IAC/E,KAAK,GAAG,YACV,EAAE,CACH,CAAC;QACJ,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;CACF;AAhRD,8CAgRC","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 { INeighborAnalysis } from './CagePatternManager';\n\n/**\n * Represents a cage cell's segment requirements as a 12-bit mask\n */\nexport interface ISegmentMask {\n /** 4 bits: Edges (Top, Right, Bottom, Left) */\n edges: number;\n /** 4 bits: Corner connectors (TR, BR, BL, TL) */\n corners: number;\n /** 4 bits: Internal segments (Top, Right, Bottom, Left) */\n internals: number;\n}\n\n/**\n * Segment bit positions for the 12-bit mask\n */\nexport enum SegmentBit {\n // Edges (bits 0-3)\n EDGE_TOP = 1 << 0,\n EDGE_RIGHT = 1 << 1,\n EDGE_BOTTOM = 1 << 2,\n EDGE_LEFT = 1 << 3,\n\n // Corner connectors (bits 4-7)\n CORNER_TOP_RIGHT = 1 << 4,\n CORNER_BOTTOM_RIGHT = 1 << 5,\n CORNER_BOTTOM_LEFT = 1 << 6,\n CORNER_TOP_LEFT = 1 << 7,\n\n // Internal segments (bits 8-11)\n INTERNAL_TOP = 1 << 8,\n INTERNAL_RIGHT = 1 << 9,\n INTERNAL_BOTTOM = 1 << 10,\n INTERNAL_LEFT = 1 << 11\n}\n\n/**\n * Two-stage lookup manager for cage border rendering\n * Stage 1: 8-neighbor pattern → segment pattern ID (256 → 47)\n * Stage 2: segment pattern ID → SVG path data (47 unique patterns)\n */\nexport class CageLookupManager {\n private static _instance: CageLookupManager | undefined;\n\n // Stage 1: Neighbor pattern (8-bit) to segment pattern ID mapping\n private readonly _neighborToSegmentLookup: number[] = new Array(256);\n\n // Stage 2: Segment pattern ID to SVG path data mapping\n private readonly _segmentPatterns: string[] = [];\n\n // Reverse lookup for debugging: segment mask to pattern ID\n private readonly _segmentMaskToId: Map<number, number> = new Map<number, number>();\n\n private constructor() {\n this._generateLookupTables();\n }\n\n /**\n * Get singleton instance\n */\n public static getInstance(): CageLookupManager {\n if (!CageLookupManager._instance) {\n CageLookupManager._instance = new CageLookupManager();\n }\n return CageLookupManager._instance;\n }\n\n /**\n * Convert neighbor analysis to 8-bit pattern\n */\n public neighborsToPattern(neighbors: INeighborAnalysis): number {\n // should be bitwise or but that makes eslint cranky\n return (\n (neighbors.north ? 1 : 0) +\n (neighbors.northEast ? 2 : 0) +\n (neighbors.east ? 4 : 0) +\n (neighbors.southEast ? 8 : 0) +\n (neighbors.south ? 16 : 0) +\n (neighbors.southWest ? 32 : 0) +\n (neighbors.west ? 64 : 0) +\n (neighbors.northWest ? 128 : 0)\n );\n }\n\n /**\n * Get SVG path data for a given neighbor pattern\n */\n public getPatternSVG(neighborPattern: number): string {\n if (neighborPattern < 0 || neighborPattern >= 256) {\n throw new Error(`Invalid neighbor pattern: ${neighborPattern}`);\n }\n\n const segmentPatternId = this._neighborToSegmentLookup[neighborPattern];\n return this._segmentPatterns[segmentPatternId];\n }\n\n /**\n * Get segment pattern ID for debugging\n */\n public getSegmentPatternId(neighborPattern: number): number {\n return this._neighborToSegmentLookup[neighborPattern];\n }\n\n /**\n * Get total number of unique segment patterns\n */\n public get uniquePatternCount(): number {\n return this._segmentPatterns.length;\n }\n\n /**\n * Generate both lookup tables\n */\n private _generateLookupTables(): void {\n // First pass: generate all unique segment masks and assign IDs\n const uniqueSegmentMasks = new Set<number>();\n\n for (let neighborPattern = 0; neighborPattern < 256; neighborPattern++) {\n const segmentMask = this._calculateSegmentMask(neighborPattern);\n uniqueSegmentMasks.add(segmentMask);\n }\n\n // Convert to sorted array for consistent ordering\n const sortedSegmentMasks = Array.from(uniqueSegmentMasks).sort((a, b) => a - b);\n\n // Generate SVG patterns for each unique segment mask\n for (let i = 0; i < sortedSegmentMasks.length; i++) {\n const segmentMask = sortedSegmentMasks[i];\n this._segmentMaskToId.set(segmentMask, i);\n this._segmentPatterns[i] = this._generateSVGPattern(segmentMask);\n }\n\n // Second pass: populate neighbor-to-segment lookup\n for (let neighborPattern = 0; neighborPattern < 256; neighborPattern++) {\n const segmentMask = this._calculateSegmentMask(neighborPattern);\n const segmentPatternId = this._segmentMaskToId.get(segmentMask)!;\n this._neighborToSegmentLookup[neighborPattern] = segmentPatternId;\n }\n }\n\n /**\n * Calculate segment mask from 8-bit neighbor pattern\n */\n private _calculateSegmentMask(neighborPattern: number): number {\n // Extract individual neighbor bits\n // eslint-disable-next-line no-bitwise\n const n = Boolean(neighborPattern & 1); // North\n // eslint-disable-next-line no-bitwise\n const ne = Boolean(neighborPattern & 2); // NorthEast\n // eslint-disable-next-line no-bitwise\n const e = Boolean(neighborPattern & 4); // East\n // eslint-disable-next-line no-bitwise\n const se = Boolean(neighborPattern & 8); // SouthEast\n // eslint-disable-next-line no-bitwise\n const s = Boolean(neighborPattern & 16); // South\n // eslint-disable-next-line no-bitwise\n const sw = Boolean(neighborPattern & 32); // SouthWest\n // eslint-disable-next-line no-bitwise\n const w = Boolean(neighborPattern & 64); // West\n // eslint-disable-next-line no-bitwise\n const nw = Boolean(neighborPattern & 128); // NorthWest\n\n let segmentMask = 0;\n\n // Edges: Draw when corresponding cardinal neighbor is NOT present\n // eslint-disable-next-line no-bitwise\n if (!n) segmentMask |= SegmentBit.EDGE_TOP;\n // eslint-disable-next-line no-bitwise\n if (!e) segmentMask |= SegmentBit.EDGE_RIGHT;\n // eslint-disable-next-line no-bitwise\n if (!s) segmentMask |= SegmentBit.EDGE_BOTTOM;\n // eslint-disable-next-line no-bitwise\n if (!w) segmentMask |= SegmentBit.EDGE_LEFT;\n\n // Internal segments: Draw when corresponding cardinal neighbor IS present\n // eslint-disable-next-line no-bitwise\n if (n) segmentMask |= SegmentBit.INTERNAL_TOP;\n // eslint-disable-next-line no-bitwise\n if (e) segmentMask |= SegmentBit.INTERNAL_RIGHT;\n // eslint-disable-next-line no-bitwise\n if (s) segmentMask |= SegmentBit.INTERNAL_BOTTOM;\n // eslint-disable-next-line no-bitwise\n if (w) segmentMask |= SegmentBit.INTERNAL_LEFT;\n\n // Corner connectors: Draw when diagonal is missing but both adjacent cardinals are present\n // eslint-disable-next-line no-bitwise\n if (!ne && n && e) segmentMask |= SegmentBit.CORNER_TOP_RIGHT;\n // eslint-disable-next-line no-bitwise\n if (!se && s && e) segmentMask |= SegmentBit.CORNER_BOTTOM_RIGHT;\n // eslint-disable-next-line no-bitwise\n if (!sw && s && w) segmentMask |= SegmentBit.CORNER_BOTTOM_LEFT;\n // eslint-disable-next-line no-bitwise\n if (!nw && n && w) segmentMask |= SegmentBit.CORNER_TOP_LEFT;\n\n return segmentMask;\n }\n\n /**\n * Generate SVG path data for a segment mask\n */\n private _generateSVGPattern(segmentMask: number): string {\n const paths: string[] = [];\n const inset = 0.1; // Standard inset for cage boundaries\n const edgeExtension = 0.0; // Extend to cell edge for connections\n\n // Determine which segments have internal connections (need to extend to edge)\n // eslint-disable-next-line no-bitwise\n const hasInternalTop = Boolean(segmentMask & SegmentBit.INTERNAL_TOP);\n // eslint-disable-next-line no-bitwise\n const hasInternalRight = Boolean(segmentMask & SegmentBit.INTERNAL_RIGHT);\n // eslint-disable-next-line no-bitwise\n const hasInternalBottom = Boolean(segmentMask & SegmentBit.INTERNAL_BOTTOM);\n // eslint-disable-next-line no-bitwise\n const hasInternalLeft = Boolean(segmentMask & SegmentBit.INTERNAL_LEFT);\n\n // Edge segments - draw main segment with standard inset, then extensions to edge where needed\n // eslint-disable-next-line no-bitwise\n if (segmentMask & SegmentBit.EDGE_TOP) {\n // Main horizontal segment\n paths.push(`M${inset},${inset} L${1 - inset},${inset}`);\n\n // Extensions to cell edges where there are internal connections\n if (hasInternalLeft) {\n paths.push(`M${edgeExtension},${inset} L${inset},${inset}`);\n }\n if (hasInternalRight) {\n paths.push(`M${1 - inset},${inset} L${1 - edgeExtension},${inset}`);\n }\n }\n\n // eslint-disable-next-line no-bitwise\n if (segmentMask & SegmentBit.EDGE_RIGHT) {\n // Main vertical segment\n paths.push(`M${1 - inset},${inset} L${1 - inset},${1 - inset}`);\n\n // Extensions to cell edges where there are internal connections\n if (hasInternalTop) {\n paths.push(`M${1 - inset},${edgeExtension} L${1 - inset},${inset}`);\n }\n if (hasInternalBottom) {\n paths.push(`M${1 - inset},${1 - inset} L${1 - inset},${1 - edgeExtension}`);\n }\n }\n\n // eslint-disable-next-line no-bitwise\n if (segmentMask & SegmentBit.EDGE_BOTTOM) {\n // Main horizontal segment\n paths.push(`M${1 - inset},${1 - inset} L${inset},${1 - inset}`);\n\n // Extensions to cell edges where there are internal connections\n if (hasInternalRight) {\n paths.push(`M${1 - edgeExtension},${1 - inset} L${1 - inset},${1 - inset}`);\n }\n if (hasInternalLeft) {\n paths.push(`M${inset},${1 - inset} L${edgeExtension},${1 - inset}`);\n }\n }\n\n // eslint-disable-next-line no-bitwise\n if (segmentMask & SegmentBit.EDGE_LEFT) {\n // Main vertical segment\n paths.push(`M${inset},${1 - inset} L${inset},${inset}`);\n\n // Extensions to cell edges where there are internal connections\n if (hasInternalBottom) {\n paths.push(`M${inset},${1 - edgeExtension} L${inset},${1 - inset}`);\n }\n if (hasInternalTop) {\n paths.push(`M${inset},${inset} L${inset},${edgeExtension}`);\n }\n }\n\n // Corner connectors - extend outward from cage corners\n const cornerRadius = 0.08;\n // eslint-disable-next-line no-bitwise\n if (segmentMask & SegmentBit.CORNER_TOP_RIGHT) {\n // Extend outward from top-right corner\n paths.push(\n `M${1 - inset},${inset} L${1 - inset + cornerRadius},${inset} M${1 - inset},${inset} L${1 - inset},${\n inset - cornerRadius\n }`\n );\n }\n // eslint-disable-next-line no-bitwise\n if (segmentMask & SegmentBit.CORNER_BOTTOM_RIGHT) {\n // Extend outward from bottom-right corner\n paths.push(\n `M${1 - inset},${1 - inset} L${1 - inset + cornerRadius},${1 - inset} M${1 - inset},${1 - inset} L${\n 1 - inset\n },${1 - inset + cornerRadius}`\n );\n }\n // eslint-disable-next-line no-bitwise\n if (segmentMask & SegmentBit.CORNER_BOTTOM_LEFT) {\n // Extend outward from bottom-left corner\n paths.push(\n `M${inset},${1 - inset} L${inset - cornerRadius},${1 - inset} M${inset},${1 - inset} L${inset},${\n 1 - inset + cornerRadius\n }`\n );\n }\n // eslint-disable-next-line no-bitwise\n if (segmentMask & SegmentBit.CORNER_TOP_LEFT) {\n // Extend outward from top-left corner\n paths.push(\n `M${inset},${inset} L${inset - cornerRadius},${inset} M${inset},${inset} L${inset},${\n inset - cornerRadius\n }`\n );\n }\n\n return paths.join(' ');\n }\n}\n"]}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* MIT License
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) 2023 Erik Fortune
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
* copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
* SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.CagePatternManager = void 0;
|
|
27
|
-
const ts_sudoku_lib_1 = require("@fgv/ts-sudoku-lib");
|
|
28
|
-
/**
|
|
29
|
-
* Direction vectors for 8-directional neighbor analysis
|
|
30
|
-
*/
|
|
31
|
-
const NEIGHBOR_DIRECTIONS = [
|
|
32
|
-
{ dx: 0, dy: -1, name: 'north' }, // N
|
|
33
|
-
{ dx: 1, dy: -1, name: 'northEast' }, // NE
|
|
34
|
-
{ dx: 1, dy: 0, name: 'east' }, // E
|
|
35
|
-
{ dx: 1, dy: 1, name: 'southEast' }, // SE
|
|
36
|
-
{ dx: 0, dy: 1, name: 'south' }, // S
|
|
37
|
-
{ dx: -1, dy: 1, name: 'southWest' }, // SW
|
|
38
|
-
{ dx: -1, dy: 0, name: 'west' }, // W
|
|
39
|
-
{ dx: -1, dy: -1, name: 'northWest' } // NW
|
|
40
|
-
];
|
|
41
|
-
/**
|
|
42
|
-
* Manages cage border patterns using compositional approach
|
|
43
|
-
* Analyzes 8-directional neighbors and generates appropriate border patterns
|
|
44
|
-
*/
|
|
45
|
-
class CagePatternManager {
|
|
46
|
-
/**
|
|
47
|
-
* Convert CellId to grid position
|
|
48
|
-
*/
|
|
49
|
-
_getCellPosition(cellId) {
|
|
50
|
-
const parsed = (0, ts_sudoku_lib_1.parseCellId)(cellId);
|
|
51
|
-
if (parsed) {
|
|
52
|
-
return parsed;
|
|
53
|
-
}
|
|
54
|
-
console.warn(`Invalid CellId format: ${cellId}`);
|
|
55
|
-
return { row: 0, col: 0 };
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Analyze neighbors in 8 directions to determine which cells are part of the cage
|
|
59
|
-
*/
|
|
60
|
-
analyzeNeighbors(cellId, occupiedCells) {
|
|
61
|
-
const { row, col } = this._getCellPosition(cellId);
|
|
62
|
-
const analysis = {};
|
|
63
|
-
for (const direction of NEIGHBOR_DIRECTIONS) {
|
|
64
|
-
const neighborRow = row + direction.dy;
|
|
65
|
-
const neighborCol = col + direction.dx;
|
|
66
|
-
const neighborKey = `${neighborRow},${neighborCol}`;
|
|
67
|
-
analysis[direction.name] = occupiedCells.has(neighborKey);
|
|
68
|
-
}
|
|
69
|
-
return analysis;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Generate edge pattern based on cardinal direction neighbors
|
|
73
|
-
* Edges are drawn where there is NO neighbor (external boundary)
|
|
74
|
-
*/
|
|
75
|
-
generateEdgePattern(neighbors) {
|
|
76
|
-
return {
|
|
77
|
-
top: !neighbors.north,
|
|
78
|
-
right: !neighbors.east,
|
|
79
|
-
bottom: !neighbors.south,
|
|
80
|
-
left: !neighbors.west
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Generate corner pattern based on diagonal neighbors and adjacent edges
|
|
85
|
-
* Corners are drawn where diagonal neighbor is missing AND both adjacent edges exist
|
|
86
|
-
*/
|
|
87
|
-
generateCornerPattern(neighbors, edges) {
|
|
88
|
-
return {
|
|
89
|
-
// Top-right corner: draw if NE neighbor is missing AND both top and right edges exist
|
|
90
|
-
topRight: !neighbors.northEast && edges.top && edges.right,
|
|
91
|
-
// Bottom-right corner: draw if SE neighbor is missing AND both bottom and right edges exist
|
|
92
|
-
bottomRight: !neighbors.southEast && edges.bottom && edges.right,
|
|
93
|
-
// Bottom-left corner: draw if SW neighbor is missing AND both bottom and left edges exist
|
|
94
|
-
bottomLeft: !neighbors.southWest && edges.bottom && edges.left,
|
|
95
|
-
// Top-left corner: draw if NW neighbor is missing AND both top and left edges exist
|
|
96
|
-
topLeft: !neighbors.northWest && edges.top && edges.left
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Convert edge pattern to numeric ID (0-15)
|
|
101
|
-
*/
|
|
102
|
-
getEdgeId(edges) {
|
|
103
|
-
// should be bitwise or but that makes eslint cranky
|
|
104
|
-
return (edges.top ? 1 : 0) + (edges.right ? 2 : 0) + (edges.bottom ? 4 : 0) + (edges.left ? 8 : 0);
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Convert corner pattern to numeric ID (0-15)
|
|
108
|
-
*/
|
|
109
|
-
getCornerId(corners) {
|
|
110
|
-
// should be bitwise or but that makes eslint cranky
|
|
111
|
-
return ((corners.topRight ? 1 : 0) +
|
|
112
|
-
(corners.bottomRight ? 2 : 0) +
|
|
113
|
-
(corners.bottomLeft ? 4 : 0) +
|
|
114
|
-
(corners.topLeft ? 8 : 0));
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Generate complete pattern specification for a cell
|
|
118
|
-
*/
|
|
119
|
-
getPatternForCell(cellId, occupiedCells) {
|
|
120
|
-
const neighbors = this.analyzeNeighbors(cellId, occupiedCells);
|
|
121
|
-
const edges = this.generateEdgePattern(neighbors);
|
|
122
|
-
const corners = this.generateCornerPattern(neighbors, edges);
|
|
123
|
-
return {
|
|
124
|
-
edges,
|
|
125
|
-
corners,
|
|
126
|
-
edgeId: this.getEdgeId(edges),
|
|
127
|
-
cornerId: this.getCornerId(corners)
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Build occupied cells set from cell IDs
|
|
132
|
-
*/
|
|
133
|
-
buildOccupiedCellsSet(cellIds) {
|
|
134
|
-
const occupiedCells = new Set();
|
|
135
|
-
for (const cellId of cellIds) {
|
|
136
|
-
const position = this._getCellPosition(cellId);
|
|
137
|
-
occupiedCells.add(`${position.row},${position.col}`);
|
|
138
|
-
}
|
|
139
|
-
return occupiedCells;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
exports.CagePatternManager = CagePatternManager;
|
|
143
|
-
//# sourceMappingURL=CagePatternManager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CagePatternManager.js","sourceRoot":"","sources":["../../src/utils/CagePatternManager.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 { CellId, parseCellId } from '@fgv/ts-sudoku-lib';\n\n/**\n * Represents the neighbor analysis for a cell in 8 directions\n */\nexport interface INeighborAnalysis {\n // Cardinal directions\n north: boolean;\n east: boolean;\n south: boolean;\n west: boolean;\n // Diagonal directions\n northEast: boolean;\n southEast: boolean;\n southWest: boolean;\n northWest: boolean;\n}\n\n/**\n * Represents edge pattern requirements\n */\nexport interface IEdgePattern {\n top: boolean;\n right: boolean;\n bottom: boolean;\n left: boolean;\n}\n\n/**\n * Represents corner pattern requirements\n */\nexport interface ICornerPattern {\n topRight: boolean;\n bottomRight: boolean;\n bottomLeft: boolean;\n topLeft: boolean;\n}\n\n/**\n * Complete pattern specification for a cell\n */\nexport interface ICellPattern {\n edges: IEdgePattern;\n corners: ICornerPattern;\n edgeId: number;\n cornerId: number;\n}\n\n/**\n * Direction vectors for 8-directional neighbor analysis\n */\nconst NEIGHBOR_DIRECTIONS = [\n { dx: 0, dy: -1, name: 'north' }, // N\n { dx: 1, dy: -1, name: 'northEast' }, // NE\n { dx: 1, dy: 0, name: 'east' }, // E\n { dx: 1, dy: 1, name: 'southEast' }, // SE\n { dx: 0, dy: 1, name: 'south' }, // S\n { dx: -1, dy: 1, name: 'southWest' }, // SW\n { dx: -1, dy: 0, name: 'west' }, // W\n { dx: -1, dy: -1, name: 'northWest' } // NW\n] as const;\n\n/**\n * Manages cage border patterns using compositional approach\n * Analyzes 8-directional neighbors and generates appropriate border patterns\n */\nexport class CagePatternManager {\n /**\n * Convert CellId to grid position\n */\n private _getCellPosition(cellId: CellId): { row: number; col: number } {\n const parsed = parseCellId(cellId);\n if (parsed) {\n return parsed;\n }\n\n console.warn(`Invalid CellId format: ${cellId}`);\n return { row: 0, col: 0 };\n }\n\n /**\n * Analyze neighbors in 8 directions to determine which cells are part of the cage\n */\n public analyzeNeighbors(cellId: CellId, occupiedCells: Set<string>): INeighborAnalysis {\n const { row, col } = this._getCellPosition(cellId);\n const analysis: Partial<INeighborAnalysis> = {};\n\n for (const direction of NEIGHBOR_DIRECTIONS) {\n const neighborRow = row + direction.dy;\n const neighborCol = col + direction.dx;\n const neighborKey = `${neighborRow},${neighborCol}`;\n\n analysis[direction.name as keyof INeighborAnalysis] = occupiedCells.has(neighborKey);\n }\n\n return analysis as INeighborAnalysis;\n }\n\n /**\n * Generate edge pattern based on cardinal direction neighbors\n * Edges are drawn where there is NO neighbor (external boundary)\n */\n public generateEdgePattern(neighbors: INeighborAnalysis): IEdgePattern {\n return {\n top: !neighbors.north,\n right: !neighbors.east,\n bottom: !neighbors.south,\n left: !neighbors.west\n };\n }\n\n /**\n * Generate corner pattern based on diagonal neighbors and adjacent edges\n * Corners are drawn where diagonal neighbor is missing AND both adjacent edges exist\n */\n public generateCornerPattern(neighbors: INeighborAnalysis, edges: IEdgePattern): ICornerPattern {\n return {\n // Top-right corner: draw if NE neighbor is missing AND both top and right edges exist\n topRight: !neighbors.northEast && edges.top && edges.right,\n\n // Bottom-right corner: draw if SE neighbor is missing AND both bottom and right edges exist\n bottomRight: !neighbors.southEast && edges.bottom && edges.right,\n\n // Bottom-left corner: draw if SW neighbor is missing AND both bottom and left edges exist\n bottomLeft: !neighbors.southWest && edges.bottom && edges.left,\n\n // Top-left corner: draw if NW neighbor is missing AND both top and left edges exist\n topLeft: !neighbors.northWest && edges.top && edges.left\n };\n }\n\n /**\n * Convert edge pattern to numeric ID (0-15)\n */\n public getEdgeId(edges: IEdgePattern): number {\n // should be bitwise or but that makes eslint cranky\n return (edges.top ? 1 : 0) + (edges.right ? 2 : 0) + (edges.bottom ? 4 : 0) + (edges.left ? 8 : 0);\n }\n\n /**\n * Convert corner pattern to numeric ID (0-15)\n */\n public getCornerId(corners: ICornerPattern): number {\n // should be bitwise or but that makes eslint cranky\n return (\n (corners.topRight ? 1 : 0) +\n (corners.bottomRight ? 2 : 0) +\n (corners.bottomLeft ? 4 : 0) +\n (corners.topLeft ? 8 : 0)\n );\n }\n\n /**\n * Generate complete pattern specification for a cell\n */\n public getPatternForCell(cellId: CellId, occupiedCells: Set<string>): ICellPattern {\n const neighbors = this.analyzeNeighbors(cellId, occupiedCells);\n const edges = this.generateEdgePattern(neighbors);\n const corners = this.generateCornerPattern(neighbors, edges);\n\n return {\n edges,\n corners,\n edgeId: this.getEdgeId(edges),\n cornerId: this.getCornerId(corners)\n };\n }\n\n /**\n * Build occupied cells set from cell IDs\n */\n public buildOccupiedCellsSet(cellIds: CellId[]): Set<string> {\n const occupiedCells = new Set<string>();\n\n for (const cellId of cellIds) {\n const position = this._getCellPosition(cellId);\n occupiedCells.add(`${position.row},${position.col}`);\n }\n\n return occupiedCells;\n }\n}\n"]}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* MIT License
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) 2023 Erik Fortune
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
* copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
* SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.createPuzzleDefinition = createPuzzleDefinition;
|
|
27
|
-
exports.createEmptyPuzzleDefinition = createEmptyPuzzleDefinition;
|
|
28
|
-
/**
|
|
29
|
-
* Helper to create a complete IPuzzleDefinition from simple parameters
|
|
30
|
-
* @public
|
|
31
|
-
*/
|
|
32
|
-
function createPuzzleDefinition(simple) {
|
|
33
|
-
const { totalRows, totalColumns } = simple;
|
|
34
|
-
// Determine cage dimensions based on grid size
|
|
35
|
-
let cageWidth, cageHeight;
|
|
36
|
-
if (totalRows === 4 && totalColumns === 4) {
|
|
37
|
-
cageWidth = 2;
|
|
38
|
-
cageHeight = 2;
|
|
39
|
-
}
|
|
40
|
-
else if (totalRows === 6 && totalColumns === 6) {
|
|
41
|
-
cageWidth = 3;
|
|
42
|
-
cageHeight = 2;
|
|
43
|
-
}
|
|
44
|
-
else if (totalRows === 9 && totalColumns === 9) {
|
|
45
|
-
cageWidth = 3;
|
|
46
|
-
cageHeight = 3;
|
|
47
|
-
}
|
|
48
|
-
else if (totalRows === 12 && totalColumns === 12) {
|
|
49
|
-
cageWidth = 4;
|
|
50
|
-
cageHeight = 3;
|
|
51
|
-
}
|
|
52
|
-
else if (totalRows === 16 && totalColumns === 16) {
|
|
53
|
-
cageWidth = 4;
|
|
54
|
-
cageHeight = 4;
|
|
55
|
-
}
|
|
56
|
-
else if (totalRows === 25 && totalColumns === 25) {
|
|
57
|
-
cageWidth = 5;
|
|
58
|
-
cageHeight = 5;
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
// Default to square cages
|
|
62
|
-
const size = Math.sqrt(totalRows);
|
|
63
|
-
cageWidth = Math.floor(size);
|
|
64
|
-
cageHeight = Math.floor(size);
|
|
65
|
-
}
|
|
66
|
-
const boardWidthInCages = totalColumns / cageWidth;
|
|
67
|
-
const boardHeightInCages = totalRows / cageHeight;
|
|
68
|
-
const maxValue = cageWidth * cageHeight;
|
|
69
|
-
const totalCages = boardWidthInCages * boardHeightInCages;
|
|
70
|
-
const basicCageTotal = (maxValue * (maxValue + 1)) / 2;
|
|
71
|
-
return {
|
|
72
|
-
id: simple.id,
|
|
73
|
-
description: simple.description,
|
|
74
|
-
type: simple.type,
|
|
75
|
-
level: simple.level,
|
|
76
|
-
totalRows,
|
|
77
|
-
totalColumns,
|
|
78
|
-
cells: simple.cells,
|
|
79
|
-
cageWidthInCells: cageWidth,
|
|
80
|
-
cageHeightInCells: cageHeight,
|
|
81
|
-
boardWidthInCages,
|
|
82
|
-
boardHeightInCages,
|
|
83
|
-
maxValue,
|
|
84
|
-
totalCages,
|
|
85
|
-
basicCageTotal
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Helper to create an empty puzzle definition
|
|
90
|
-
* @public
|
|
91
|
-
*/
|
|
92
|
-
function createEmptyPuzzleDefinition(rows = 9, columns = 9, type = 'sudoku') {
|
|
93
|
-
return createPuzzleDefinition({
|
|
94
|
-
id: 'manual-entry',
|
|
95
|
-
description: 'Manual Entry Puzzle',
|
|
96
|
-
type,
|
|
97
|
-
level: 1,
|
|
98
|
-
totalRows: rows,
|
|
99
|
-
totalColumns: columns,
|
|
100
|
-
cells: '.'.repeat(rows * columns)
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
//# sourceMappingURL=puzzleDefinitionHelper.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"puzzleDefinitionHelper.js","sourceRoot":"","sources":["../../src/utils/puzzleDefinitionHelper.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;AAsBH,wDAqDC;AAMD,kEAcC;AA7ED;;;GAGG;AACH,SAAgB,sBAAsB,CAAC,MAAgC;IACrE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAE3C,+CAA+C;IAC/C,IAAI,SAAiB,EAAE,UAAkB,CAAC;IAE1C,IAAI,SAAS,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QAC1C,SAAS,GAAG,CAAC,CAAC;QACd,UAAU,GAAG,CAAC,CAAC;IACjB,CAAC;SAAM,IAAI,SAAS,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACjD,SAAS,GAAG,CAAC,CAAC;QACd,UAAU,GAAG,CAAC,CAAC;IACjB,CAAC;SAAM,IAAI,SAAS,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACjD,SAAS,GAAG,CAAC,CAAC;QACd,UAAU,GAAG,CAAC,CAAC;IACjB,CAAC;SAAM,IAAI,SAAS,KAAK,EAAE,IAAI,YAAY,KAAK,EAAE,EAAE,CAAC;QACnD,SAAS,GAAG,CAAC,CAAC;QACd,UAAU,GAAG,CAAC,CAAC;IACjB,CAAC;SAAM,IAAI,SAAS,KAAK,EAAE,IAAI,YAAY,KAAK,EAAE,EAAE,CAAC;QACnD,SAAS,GAAG,CAAC,CAAC;QACd,UAAU,GAAG,CAAC,CAAC;IACjB,CAAC;SAAM,IAAI,SAAS,KAAK,EAAE,IAAI,YAAY,KAAK,EAAE,EAAE,CAAC;QACnD,SAAS,GAAG,CAAC,CAAC;QACd,UAAU,GAAG,CAAC,CAAC;IACjB,CAAC;SAAM,CAAC;QACN,0BAA0B;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAClC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,iBAAiB,GAAG,YAAY,GAAG,SAAS,CAAC;IACnD,MAAM,kBAAkB,GAAG,SAAS,GAAG,UAAU,CAAC;IAClD,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;IACxC,MAAM,UAAU,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;IAC1D,MAAM,cAAc,GAAG,CAAC,QAAQ,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEvD,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,SAAS;QACT,YAAY;QACZ,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,gBAAgB,EAAE,SAAS;QAC3B,iBAAiB,EAAE,UAAU;QAC7B,iBAAiB;QACjB,kBAAkB;QAClB,QAAQ;QACR,UAAU;QACV,cAAc;KACf,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,2BAA2B,CACzC,OAAe,CAAC,EAChB,UAAkB,CAAC,EACnB,OAAmB,QAAQ;IAE3B,OAAO,sBAAsB,CAAC;QAC5B,EAAE,EAAE,cAAc;QAClB,WAAW,EAAE,qBAAqB;QAClC,IAAI;QACJ,KAAK,EAAE,CAAC;QACR,SAAS,EAAE,IAAI;QACf,YAAY,EAAE,OAAO;QACrB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;KAClC,CAAC,CAAC;AACL,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 { IPuzzleDefinition, PuzzleType } from '@fgv/ts-sudoku-lib';\n\n/**\n * Simple puzzle definition for UI usage\n * @public\n */\nexport interface ISimplePuzzleDescription {\n readonly id?: string;\n readonly description: string;\n readonly type: PuzzleType;\n readonly level: number;\n readonly totalRows: number;\n readonly totalColumns: number;\n readonly cells: string;\n}\n\n/**\n * Helper to create a complete IPuzzleDefinition from simple parameters\n * @public\n */\nexport function createPuzzleDefinition(simple: ISimplePuzzleDescription): IPuzzleDefinition {\n const { totalRows, totalColumns } = simple;\n\n // Determine cage dimensions based on grid size\n let cageWidth: number, cageHeight: number;\n\n if (totalRows === 4 && totalColumns === 4) {\n cageWidth = 2;\n cageHeight = 2;\n } else if (totalRows === 6 && totalColumns === 6) {\n cageWidth = 3;\n cageHeight = 2;\n } else if (totalRows === 9 && totalColumns === 9) {\n cageWidth = 3;\n cageHeight = 3;\n } else if (totalRows === 12 && totalColumns === 12) {\n cageWidth = 4;\n cageHeight = 3;\n } else if (totalRows === 16 && totalColumns === 16) {\n cageWidth = 4;\n cageHeight = 4;\n } else if (totalRows === 25 && totalColumns === 25) {\n cageWidth = 5;\n cageHeight = 5;\n } else {\n // Default to square cages\n const size = Math.sqrt(totalRows);\n cageWidth = Math.floor(size);\n cageHeight = Math.floor(size);\n }\n\n const boardWidthInCages = totalColumns / cageWidth;\n const boardHeightInCages = totalRows / cageHeight;\n const maxValue = cageWidth * cageHeight;\n const totalCages = boardWidthInCages * boardHeightInCages;\n const basicCageTotal = (maxValue * (maxValue + 1)) / 2;\n\n return {\n id: simple.id,\n description: simple.description,\n type: simple.type,\n level: simple.level,\n totalRows,\n totalColumns,\n cells: simple.cells,\n cageWidthInCells: cageWidth,\n cageHeightInCells: cageHeight,\n boardWidthInCages,\n boardHeightInCages,\n maxValue,\n totalCages,\n basicCageTotal\n };\n}\n\n/**\n * Helper to create an empty puzzle definition\n * @public\n */\nexport function createEmptyPuzzleDefinition(\n rows: number = 9,\n columns: number = 9,\n type: PuzzleType = 'sudoku'\n): IPuzzleDefinition {\n return createPuzzleDefinition({\n id: 'manual-entry',\n description: 'Manual Entry Puzzle',\n type,\n level: 1,\n totalRows: rows,\n totalColumns: columns,\n cells: '.'.repeat(rows * columns)\n });\n}\n"]}
|