@cubingcenter/scrambler 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +15 -4
  2. package/dist/core/api.d.ts.map +1 -1
  3. package/dist/core/api.js +10 -2
  4. package/dist/puzzles/cube2/solver/browser-loader.d.ts +4 -7
  5. package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
  6. package/dist/puzzles/cube2/solver/browser-loader.js +42 -14
  7. package/dist/puzzles/cube2/solver/generated-assets.d.ts.map +1 -1
  8. package/dist/puzzles/cube2/solver/generated-assets.js +8 -3
  9. package/dist/puzzles/cube3/solver/browser-loader.d.ts.map +1 -1
  10. package/dist/puzzles/cube3/solver/browser-loader.js +42 -20
  11. package/dist/puzzles/cube3/solver/generated-assets.d.ts.map +1 -1
  12. package/dist/puzzles/cube3/solver/generated-assets.js +9 -3
  13. package/dist/puzzles/cube3/solver/phase1.d.ts +19 -0
  14. package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
  15. package/dist/puzzles/cube3/solver/phase1.js +29 -6
  16. package/dist/puzzles/cube3/solver/phase2.d.ts +28 -0
  17. package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
  18. package/dist/puzzles/cube3/solver/phase2.js +56 -15
  19. package/dist/puzzles/cube3/solver/tables.d.ts.map +1 -1
  20. package/dist/puzzles/cube3/solver/tables.js +14 -12
  21. package/dist/puzzles/cube4/browser-worker-pool.d.ts +82 -0
  22. package/dist/puzzles/cube4/browser-worker-pool.d.ts.map +1 -0
  23. package/dist/puzzles/cube4/browser-worker-pool.js +205 -0
  24. package/dist/puzzles/cube4/browser-worker.d.ts +2 -0
  25. package/dist/puzzles/cube4/browser-worker.d.ts.map +1 -0
  26. package/dist/puzzles/cube4/browser-worker.js +31 -0
  27. package/dist/puzzles/cube4/index.d.ts +7 -0
  28. package/dist/puzzles/cube4/index.d.ts.map +1 -1
  29. package/dist/puzzles/cube4/index.js +4 -0
  30. package/dist/puzzles/cube4/moves.d.ts.map +1 -1
  31. package/dist/puzzles/cube4/moves.js +122 -30
  32. package/dist/puzzles/cube4/reduction/browser-loader.d.ts +26 -1
  33. package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
  34. package/dist/puzzles/cube4/reduction/browser-loader.js +212 -48
  35. package/dist/puzzles/cube4/reduction/center2.d.ts +24 -0
  36. package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
  37. package/dist/puzzles/cube4/reduction/center2.js +99 -1
  38. package/dist/puzzles/cube4/reduction/finish3.d.ts +53 -0
  39. package/dist/puzzles/cube4/reduction/finish3.d.ts.map +1 -0
  40. package/dist/puzzles/cube4/reduction/finish3.js +393 -0
  41. package/dist/puzzles/cube4/reduction/generated/hc-manifest.json +6 -0
  42. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +5 -3
  43. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -1
  44. package/dist/puzzles/cube4/reduction/hc-pdb.js +30 -12
  45. package/dist/puzzles/cube4/reduction/node-loader.d.ts +10 -1
  46. package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
  47. package/dist/puzzles/cube4/reduction/node-loader.js +170 -36
  48. package/dist/puzzles/cube4/reduction/pair-transversal.d.ts +28 -0
  49. package/dist/puzzles/cube4/reduction/pair-transversal.d.ts.map +1 -0
  50. package/dist/puzzles/cube4/reduction/pair-transversal.js +107 -0
  51. package/dist/puzzles/cube4/reduction/phase3-search.d.ts +33 -30
  52. package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
  53. package/dist/puzzles/cube4/reduction/phase3-search.js +211 -104
  54. package/dist/puzzles/cube4/reduction/rotation-fix.d.ts +28 -0
  55. package/dist/puzzles/cube4/reduction/rotation-fix.d.ts.map +1 -0
  56. package/dist/puzzles/cube4/reduction/rotation-fix.js +212 -0
  57. package/dist/puzzles/cube4/reduction/solve.d.ts +72 -13
  58. package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
  59. package/dist/puzzles/cube4/reduction/solve.js +374 -92
  60. package/dist/puzzles/cube4/scramble-cache.d.ts +93 -0
  61. package/dist/puzzles/cube4/scramble-cache.d.ts.map +1 -0
  62. package/dist/puzzles/cube4/scramble-cache.js +150 -0
  63. package/dist/puzzles/cube4/scrambler.d.ts +112 -18
  64. package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
  65. package/dist/puzzles/cube4/scrambler.js +271 -83
  66. package/dist/puzzles/cube4/worker-pool.d.ts +139 -0
  67. package/dist/puzzles/cube4/worker-pool.d.ts.map +1 -0
  68. package/dist/puzzles/cube4/worker-pool.js +405 -0
  69. package/dist/puzzles/cube4/worker-protocol.d.ts +58 -0
  70. package/dist/puzzles/cube4/worker-protocol.d.ts.map +1 -0
  71. package/dist/puzzles/cube4/worker-protocol.js +58 -0
  72. package/dist/puzzles/cube4/worker.d.ts +26 -0
  73. package/dist/puzzles/cube4/worker.d.ts.map +1 -0
  74. package/dist/puzzles/cube4/worker.js +49 -0
  75. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts +4 -8
  76. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts.map +1 -1
  77. package/dist/puzzles/pyraminx/solver/browser-loader.js +44 -15
  78. package/dist/puzzles/pyraminx/solver/generated-assets.d.ts.map +1 -1
  79. package/dist/puzzles/pyraminx/solver/generated-assets.js +8 -3
  80. package/dist/puzzles/shared/compressed-fetch.d.ts +37 -1
  81. package/dist/puzzles/shared/compressed-fetch.d.ts.map +1 -1
  82. package/dist/puzzles/shared/compressed-fetch.js +226 -31
  83. package/dist/puzzles/shared/load-once.d.ts +16 -0
  84. package/dist/puzzles/shared/load-once.d.ts.map +1 -0
  85. package/dist/puzzles/shared/load-once.js +11 -0
  86. package/dist/puzzles/skewb/solver/browser-loader.d.ts +4 -11
  87. package/dist/puzzles/skewb/solver/browser-loader.d.ts.map +1 -1
  88. package/dist/puzzles/skewb/solver/browser-loader.js +44 -16
  89. package/dist/puzzles/skewb/solver/generated-assets.d.ts.map +1 -1
  90. package/dist/puzzles/skewb/solver/generated-assets.js +8 -3
  91. package/dist/puzzles/sq1/solver/browser-loader.d.ts.map +1 -1
  92. package/dist/puzzles/sq1/solver/browser-loader.js +58 -26
  93. package/dist-cjs/core/api.js +10 -2
  94. package/dist-cjs/puzzles/cube2/solver/browser-loader.js +42 -14
  95. package/dist-cjs/puzzles/cube2/solver/generated-assets.js +8 -3
  96. package/dist-cjs/puzzles/cube3/solver/browser-loader.js +42 -20
  97. package/dist-cjs/puzzles/cube3/solver/generated-assets.js +9 -3
  98. package/dist-cjs/puzzles/cube3/solver/phase1.js +29 -6
  99. package/dist-cjs/puzzles/cube3/solver/phase2.js +56 -15
  100. package/dist-cjs/puzzles/cube3/solver/tables.js +14 -12
  101. package/dist-cjs/puzzles/cube4/browser-worker-pool.js +211 -0
  102. package/dist-cjs/puzzles/cube4/index.js +21 -1
  103. package/dist-cjs/puzzles/cube4/moves.js +122 -30
  104. package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +212 -48
  105. package/dist-cjs/puzzles/cube4/reduction/center2.js +102 -2
  106. package/dist-cjs/puzzles/cube4/reduction/finish3.js +398 -0
  107. package/dist-cjs/puzzles/cube4/reduction/generated/hc-manifest.json +6 -0
  108. package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +31 -12
  109. package/dist-cjs/puzzles/cube4/reduction/node-loader.js +171 -36
  110. package/dist-cjs/puzzles/cube4/reduction/pair-transversal.js +112 -0
  111. package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +211 -104
  112. package/dist-cjs/puzzles/cube4/reduction/rotation-fix.js +216 -0
  113. package/dist-cjs/puzzles/cube4/reduction/solve.js +374 -88
  114. package/dist-cjs/puzzles/cube4/scramble-cache.js +156 -0
  115. package/dist-cjs/puzzles/cube4/scrambler.js +271 -80
  116. package/dist-cjs/puzzles/cube4/worker-pool.js +447 -0
  117. package/dist-cjs/puzzles/cube4/worker-protocol.js +62 -0
  118. package/dist-cjs/puzzles/pyraminx/solver/browser-loader.js +44 -15
  119. package/dist-cjs/puzzles/pyraminx/solver/generated-assets.js +8 -3
  120. package/dist-cjs/puzzles/shared/compressed-fetch.js +229 -31
  121. package/dist-cjs/puzzles/shared/load-once.js +14 -0
  122. package/dist-cjs/puzzles/skewb/solver/browser-loader.js +44 -16
  123. package/dist-cjs/puzzles/skewb/solver/generated-assets.js +8 -3
  124. package/dist-cjs/puzzles/sq1/solver/browser-loader.js +58 -26
  125. package/docs/development.md +19 -7
  126. package/package.json +4 -3
@@ -163,30 +163,48 @@ export function generateHcPdb(onProgress) {
163
163
  }
164
164
  /** Number of reachable states (BFS orbit size). Fixed by the move set. */
165
165
  export const HC_REACHABLE = 58800;
166
- /** Build the dense index + unpacked distances from nibble-packed BFS output. */
166
+ /** Build the compact coordinate list + unpacked distances from the packed PDB. */
167
167
  export function buildHcDense(packed) {
168
- const index = new Int32Array(HC_PDB_STATES).fill(-1);
169
168
  let count = 0;
170
169
  for (let c = 0; c < HC_PDB_STATES; c++) {
171
170
  if (hcPdbDistance(packed, c) !== 0x0f)
172
- index[c] = count++;
171
+ count++;
173
172
  }
173
+ const coordinates = new Uint32Array(count);
174
174
  const distances = new Uint8Array(count).fill(0x0f);
175
+ let denseIndex = 0;
175
176
  for (let c = 0; c < HC_PDB_STATES; c++) {
176
- const di = index[c];
177
- if (di >= 0)
178
- distances[di] = hcPdbDistance(packed, c);
177
+ const distance = hcPdbDistance(packed, c);
178
+ if (distance === 0x0f)
179
+ continue;
180
+ coordinates[denseIndex] = c;
181
+ distances[denseIndex] = distance;
182
+ denseIndex++;
183
+ }
184
+ return { coordinates, moves: new Int32Array(0), distances, reachableCount: count };
185
+ }
186
+ /** Full HC coordinate -> dense index, or -1 when it is unreachable. */
187
+ export function hcDenseIndex(dense, coordinate) {
188
+ let low = 0;
189
+ let high = dense.coordinates.length - 1;
190
+ while (low <= high) {
191
+ const middle = (low + high) >>> 1;
192
+ const value = dense.coordinates[middle];
193
+ if (value === coordinate)
194
+ return middle;
195
+ if (value < coordinate)
196
+ low = middle + 1;
197
+ else
198
+ high = middle - 1;
179
199
  }
180
- return { index, moves: new Int32Array(0), distances, reachableCount: count };
200
+ return -1;
181
201
  }
182
202
  /** Dense transition table: for each reachable state and move, dense index (or -1). */
183
203
  export function generateHcDenseMoves(dense) {
184
204
  const maps = getHcPdbMoveMaps();
185
205
  const moves = new Int32Array(dense.reachableCount * HC_PDB_MOVES.length).fill(-1);
186
- for (let c = 0; c < HC_PDB_STATES; c++) {
187
- const di = dense.index[c];
188
- if (di < 0)
189
- continue;
206
+ for (let di = 0; di < dense.coordinates.length; di++) {
207
+ const c = dense.coordinates[di];
190
208
  const mask = hcUnrank(c);
191
209
  for (let m = 0; m < HC_PDB_MOVES.length; m++) {
192
210
  const base = m * 24;
@@ -195,7 +213,7 @@ export function generateHcDenseMoves(dense) {
195
213
  if (mask & (1 << maps[base + i]))
196
214
  nmask |= 1 << i;
197
215
  }
198
- moves[di * HC_PDB_MOVES.length + m] = dense.index[hcRank(nmask)];
216
+ moves[di * HC_PDB_MOVES.length + m] = hcDenseIndex(dense, hcRank(nmask));
199
217
  }
200
218
  }
201
219
  return moves;
@@ -1,12 +1,21 @@
1
1
  /**
2
2
  * Node loader for the cube4 reduction tables (the committed .bin files).
3
- * The manifests are per-phase (`center1-manifest.json`, ...).
3
+ * The manifests are per-phase (`center1-manifest.json`, ...) plus the shared
4
+ * `hc-manifest.json`, which also seals the optional `c3_pair_dist.bin`.
5
+ *
6
+ * Every .bin read is verified: sha256 checksum AND expected byte length
7
+ * (derived from the manifest `range` + the known packing of each table).
8
+ * All integrity errors name the file and the expected/actual sizes so a
9
+ * corrupted download fails loudly instead of silently producing divergent
10
+ * scrambles. browser-loader.ts performs the identical checks.
4
11
  */
5
12
  import type { Center1Tables } from "./center1.js";
6
13
  import type { Center2Tables } from "./center2.js";
7
14
  import type { Center3Tables } from "./center3.js";
8
15
  import type { Edge3Tables } from "./edge3.js";
9
16
  import { type HcDenseTables } from "./hc-pdb.js";
17
+ /** Estimated committed table bytes per solver instance (excludes JS objects). */
18
+ export declare function estimateCube4ReductionTableBytes(tables: Cube4ReductionTables): number;
10
19
  export interface Cube4ReductionTables {
11
20
  center1: Center1Tables;
12
21
  center2: Center2Tables;
@@ -1 +1 @@
1
- {"version":3,"file":"node-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/node-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,0EAA0E;IAC1E,EAAE,CAAC,EAAE,aAAa,CAAC;IACnB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AA2ID,oFAAoF;AACpF,wBAAgB,wBAAwB,IAAI,oBAAoB,CAgB/D"}
1
+ {"version":3,"file":"node-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/node-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAI/D,iFAAiF;AACjF,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAgBrF;AAYD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,0EAA0E;IAC1E,EAAE,CAAC,EAAE,aAAa,CAAC;IACnB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAuPD,oFAAoF;AACpF,wBAAgB,wBAAwB,IAAI,oBAAoB,CAqB/D"}
@@ -1,14 +1,52 @@
1
1
  /**
2
2
  * Node loader for the cube4 reduction tables (the committed .bin files).
3
- * The manifests are per-phase (`center1-manifest.json`, ...).
3
+ * The manifests are per-phase (`center1-manifest.json`, ...) plus the shared
4
+ * `hc-manifest.json`, which also seals the optional `c3_pair_dist.bin`.
5
+ *
6
+ * Every .bin read is verified: sha256 checksum AND expected byte length
7
+ * (derived from the manifest `range` + the known packing of each table).
8
+ * All integrity errors name the file and the expected/actual sizes so a
9
+ * corrupted download fails loudly instead of silently producing divergent
10
+ * scrambles. browser-loader.ts performs the identical checks.
4
11
  */
5
12
  import { existsSync, readFileSync } from "node:fs";
6
13
  import { createRequire } from "node:module";
7
14
  import path from "node:path";
8
15
  import { createHash } from "node:crypto";
16
+ import { CENTER3_STATES } from "./center3.js";
9
17
  import { edge3DistanceLookup } from "./edge3.js";
10
18
  import { buildHcDense } from "./hc-pdb.js";
19
+ const TABLE_MEMORY_BUDGET_BYTES = 150 * 1024 * 1024;
20
+ /** Estimated committed table bytes per solver instance (excludes JS objects). */
21
+ export function estimateCube4ReductionTableBytes(tables) {
22
+ return (tables.center1.distance.byteLength +
23
+ tables.center1.moveMaps.byteLength +
24
+ tables.center2.ctMove.byteLength +
25
+ tables.center2.rlMove.byteLength +
26
+ tables.center2.pruning.byteLength +
27
+ (tables.center2.pruningWide?.byteLength ?? 0) +
28
+ tables.center3.moveTable.byteLength +
29
+ tables.center3.pruning.byteLength +
30
+ tables.edge3.distances.byteLength +
31
+ (tables.hc?.moves.byteLength ?? 0) +
32
+ (tables.hc?.distances.byteLength ?? 0) +
33
+ (tables.hc?.coordinates.byteLength ?? 0) +
34
+ (tables.c3PairDist?.byteLength ?? 0));
35
+ }
36
+ function assertCube4ReductionTableBudget(tables) {
37
+ const bytes = estimateCube4ReductionTableBytes(tables);
38
+ if (bytes > TABLE_MEMORY_BUDGET_BYTES) {
39
+ throw new Error(`Cube4 reduction tables use ${(bytes / (1024 * 1024)).toFixed(1)} MiB, above the ` +
40
+ `${(TABLE_MEMORY_BUDGET_BYTES / (1024 * 1024)).toFixed(0)} MiB table budget.`);
41
+ }
42
+ }
11
43
  const GENERATED_DIRECTORY = "puzzles/cube4/reduction/generated";
44
+ /**
45
+ * Byte length of `c3_pair_dist.bin`: CENTER3_STATES x 144 nibble entries
46
+ * (index = ct3 * 144 + f * 12 + h, see phase3-search.ts), nibble-packed.
47
+ * The same constant is enforced in browser-loader.ts.
48
+ */
49
+ const C3_PAIR_DIST_BYTES = (CENTER3_STATES * 144) / 2;
12
50
  function resolveGeneratedAssetDirectory() {
13
51
  const envDirectory = process.env.CUBINGCENTER_SCRAMBLER_TABLES_DIR;
14
52
  if (envDirectory && existsSync(path.join(envDirectory, "center1-manifest.json"))) {
@@ -41,25 +79,97 @@ function getAssetDirectoryCandidates() {
41
79
  function sha256(buffer) {
42
80
  return createHash("sha256").update(buffer).digest("hex");
43
81
  }
44
- function readChecked(file, expectedChecksum) {
45
- const buffer = readFileSync(file);
46
- if (sha256(buffer) !== expectedChecksum) {
47
- throw new Error(`Table checksum mismatch: ${file}`);
82
+ function readManifest(file) {
83
+ let text;
84
+ try {
85
+ text = readFileSync(file, "utf8");
86
+ }
87
+ catch (error) {
88
+ if (error.code === "ENOENT") {
89
+ throw new Error(`Table manifest missing: ${file} — run pnpm cube4:center1:tables etc. to regenerate the tables.`);
90
+ }
91
+ throw error;
92
+ }
93
+ try {
94
+ return JSON.parse(text);
95
+ }
96
+ catch (error) {
97
+ // Always name the file: a bare JSON.parse error is unactionable.
98
+ throw new Error(`Invalid table manifest JSON: ${file}: ${error instanceof Error ? error.message : String(error)}`);
99
+ }
100
+ }
101
+ function requireEntry(manifest, manifestPath, label) {
102
+ const entry = manifest[label];
103
+ if (!entry || typeof entry.file !== "string" || typeof entry.checksum !== "string") {
104
+ throw new Error(`Invalid table manifest ${manifestPath}: entry "${label}" must declare { file, checksum } — ` +
105
+ "run pnpm cube4:hc:tables etc. to regenerate the manifest.");
106
+ }
107
+ return entry;
108
+ }
109
+ /**
110
+ * Expected byte length of a table file from its manifest entry.
111
+ * `packing` matches how each table is serialized (see the load* functions).
112
+ */
113
+ function expectedBytes(entry, packing) {
114
+ if (typeof entry.bytes === "number")
115
+ return entry.bytes;
116
+ if (typeof entry.range !== "number")
117
+ return undefined;
118
+ if (packing === "byte")
119
+ return entry.range;
120
+ if (packing === "nibble")
121
+ return Math.ceil(entry.range / 2);
122
+ return entry.range * 4;
123
+ }
124
+ function readChecked(file, expectedChecksum, expectedLength) {
125
+ let buffer;
126
+ try {
127
+ buffer = readFileSync(file);
128
+ }
129
+ catch (error) {
130
+ if (error.code === "ENOENT") {
131
+ throw new Error(`Table file missing: ${file}` +
132
+ (expectedLength !== undefined ? ` (expected ${expectedLength} bytes)` : "") +
133
+ " — run pnpm cube4:center1:tables etc. to regenerate the tables.");
134
+ }
135
+ throw error;
136
+ }
137
+ if (expectedLength !== undefined && buffer.length !== expectedLength) {
138
+ throw new Error(`Table size mismatch: ${file} (expected ${expectedLength} bytes, got ${buffer.length})`);
139
+ }
140
+ const actual = sha256(buffer);
141
+ if (actual !== expectedChecksum) {
142
+ throw new Error(`Table checksum mismatch: ${file} (expected sha256 ${expectedChecksum}, got ${actual}, ${buffer.length} bytes)`);
48
143
  }
49
144
  return buffer;
50
145
  }
146
+ function readCheckedEntry(file, entry, packing) {
147
+ return readChecked(file, entry.checksum, expectedBytes(entry, packing));
148
+ }
51
149
  function readInt32(buffer) {
52
150
  return new Int32Array(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));
53
151
  }
54
152
  function loadCenter1(assetDirectory) {
55
- const manifest = JSON.parse(readFileSync(path.join(assetDirectory, "center1-manifest.json"), "utf8"));
153
+ const manifestPath = path.join(assetDirectory, "center1-manifest.json");
154
+ const manifest = readManifest(manifestPath);
155
+ const distanceTable = requireEntry(manifest, manifestPath, "distanceTable");
156
+ const moveMaps = requireEntry(manifest, manifestPath, "moveMaps");
56
157
  return {
57
- distance: new Uint8Array(readChecked(path.join(assetDirectory, manifest.distanceTable.file), manifest.distanceTable.checksum)),
58
- moveMaps: readInt32(readChecked(path.join(assetDirectory, manifest.moveMaps.file), manifest.moveMaps.checksum))
158
+ distance: new Uint8Array(readCheckedEntry(path.join(assetDirectory, distanceTable.file), distanceTable, "nibble")),
159
+ moveMaps: readInt32(readCheckedEntry(path.join(assetDirectory, moveMaps.file), moveMaps, "int32"))
59
160
  };
60
161
  }
61
162
  function loadCenter2(assetDirectory) {
62
- const manifest = JSON.parse(readFileSync(path.join(assetDirectory, "center2-manifest.json"), "utf8"));
163
+ const manifestPath = path.join(assetDirectory, "center2-manifest.json");
164
+ const manifest = readManifest(manifestPath);
165
+ const ctMove = requireEntry(manifest, manifestPath, "ctMove");
166
+ const rlMove = requireEntry(manifest, manifestPath, "rlMove");
167
+ const pruningTable = requireEntry(manifest, manifestPath, "pruningTable");
168
+ // Tolerates older assets without the wide table (wide searches throw
169
+ // a clear error only when actually requested).
170
+ const pruningWideEntry = manifest.pruningWideTable
171
+ ? requireEntry(manifest, manifestPath, "pruningWideTable")
172
+ : undefined;
63
173
  function unpackPruning(buffer) {
64
174
  const pruning = new Uint8Array(450450);
65
175
  for (let i = 0; i < 450450; i++) {
@@ -68,53 +178,73 @@ function loadCenter2(assetDirectory) {
68
178
  }
69
179
  return pruning;
70
180
  }
71
- const pruningBuffer = readChecked(path.join(assetDirectory, manifest.pruningTable.file), manifest.pruningTable.checksum);
181
+ const pruningBuffer = readCheckedEntry(path.join(assetDirectory, pruningTable.file), pruningTable, "nibble");
72
182
  const tables = {
73
- ctMove: readInt32(readChecked(path.join(assetDirectory, manifest.ctMove.file), manifest.ctMove.checksum)),
74
- rlMove: readInt32(readChecked(path.join(assetDirectory, manifest.rlMove.file), manifest.rlMove.checksum)),
183
+ ctMove: readInt32(readCheckedEntry(path.join(assetDirectory, ctMove.file), ctMove, "int32")),
184
+ rlMove: readInt32(readCheckedEntry(path.join(assetDirectory, rlMove.file), rlMove, "int32")),
75
185
  pruning: unpackPruning(pruningBuffer)
76
186
  };
77
- // Tolerates older assets without the wide table (wide searches throw
78
- // a clear error only when actually requested).
79
- if (manifest.pruningWideTable) {
80
- const wideBuffer = readChecked(path.join(assetDirectory, manifest.pruningWideTable.file), manifest.pruningWideTable.checksum);
187
+ if (pruningWideEntry) {
188
+ const wideBuffer = readCheckedEntry(path.join(assetDirectory, pruningWideEntry.file), pruningWideEntry, "nibble");
81
189
  tables.pruningWide = unpackPruning(wideBuffer);
82
190
  }
83
191
  return tables;
84
192
  }
85
193
  function loadCenter3(assetDirectory) {
86
- const manifest = JSON.parse(readFileSync(path.join(assetDirectory, "center3-manifest.json"), "utf8"));
194
+ const manifestPath = path.join(assetDirectory, "center3-manifest.json");
195
+ const manifest = readManifest(manifestPath);
196
+ const moveTable = requireEntry(manifest, manifestPath, "moveTable");
197
+ const pruningTable = requireEntry(manifest, manifestPath, "pruningTable");
87
198
  return {
88
- moveTable: readInt32(readChecked(path.join(assetDirectory, manifest.moveTable.file), manifest.moveTable.checksum)),
89
- pruning: new Uint8Array(readChecked(path.join(assetDirectory, manifest.pruningTable.file), manifest.pruningTable.checksum))
199
+ moveTable: readInt32(readCheckedEntry(path.join(assetDirectory, moveTable.file), moveTable, "int32")),
200
+ pruning: new Uint8Array(readCheckedEntry(path.join(assetDirectory, pruningTable.file), pruningTable, "byte"))
90
201
  };
91
202
  }
92
203
  function loadEdge3(assetDirectory) {
93
- const manifest = JSON.parse(readFileSync(path.join(assetDirectory, "edge3-manifest.json"), "utf8"));
204
+ const manifestPath = path.join(assetDirectory, "edge3-manifest.json");
205
+ const manifest = readManifest(manifestPath);
206
+ const distances = requireEntry(manifest, manifestPath, "distances");
207
+ // `range` here already counts packed nibble BYTES (classCount / 2), unlike
208
+ // the other manifests — derive the length from classCount instead.
209
+ const classCount = manifest.classCount;
210
+ const length = typeof classCount === "number" ? Math.ceil(classCount / 2) : expectedBytes(distances, "byte");
94
211
  // Keep the nibble-packed distances as-is (15.5MB instead of 31MB): every
95
212
  // reader (phase3 getPrun, solve.ts, edge3DistanceLookup) handles both
96
213
  // formats, and the smaller working set halves edge-table DRAM traffic.
97
- const distances = new Uint8Array(readChecked(path.join(assetDirectory, manifest.distances.file), manifest.distances.checksum));
98
- return { distances, distanceOfEdge: edge3DistanceLookup(distances) };
214
+ const buffer = readChecked(path.join(assetDirectory, distances.file), distances.checksum, length);
215
+ const table = new Uint8Array(buffer);
216
+ return { distances: table, distanceOfEdge: edge3DistanceLookup(table) };
99
217
  }
100
- function loadHc(assetDirectory) {
218
+ function loadHc(assetDirectory, manifest) {
101
219
  const manifestPath = path.join(assetDirectory, "hc-manifest.json");
102
- if (!existsSync(manifestPath))
103
- return undefined;
104
- const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
105
- const packed = new Uint8Array(readChecked(path.join(assetDirectory, manifest.distances.file), manifest.distances.checksum));
220
+ const distances = requireEntry(manifest, manifestPath, "distances");
221
+ const packed = new Uint8Array(readCheckedEntry(path.join(assetDirectory, distances.file), distances, "nibble"));
106
222
  const dense = buildHcDense(packed);
107
- if (manifest.moveTable) {
108
- const moveBuffer = readChecked(path.join(assetDirectory, manifest.moveTable.file), manifest.moveTable.checksum);
223
+ if (manifest.moveTable !== undefined) {
224
+ const moveTable = requireEntry(manifest, manifestPath, "moveTable");
225
+ const moveBuffer = readCheckedEntry(path.join(assetDirectory, moveTable.file), moveTable, "int32");
109
226
  dense.moves = new Int32Array(moveBuffer.buffer.slice(moveBuffer.byteOffset, moveBuffer.byteOffset + moveBuffer.byteLength));
110
227
  }
111
228
  return dense;
112
229
  }
113
- function loadC3Pair(assetDirectory) {
230
+ function loadC3Pair(assetDirectory, hcManifest) {
114
231
  const filePath = path.join(assetDirectory, "c3_pair_dist.bin");
115
- if (!existsSync(filePath))
232
+ const manifestPath = path.join(assetDirectory, "hc-manifest.json");
233
+ if (!hcManifest || hcManifest.c3PairDist === undefined) {
234
+ // Optional table: absent = not deployed (fine). Present but unsealed =
235
+ // the historical silent-corruption hole — refuse to load it unchecked.
236
+ if (existsSync(filePath)) {
237
+ throw new Error(`${filePath} exists but ${manifestPath} has no "c3PairDist" checksum entry — refusing to load it ` +
238
+ "unchecked. Regenerate the manifest: pnpm cube4:hc:tables.");
239
+ }
116
240
  return undefined;
117
- return new Uint8Array(readFileSync(filePath));
241
+ }
242
+ const checked = requireEntry(hcManifest, manifestPath, "c3PairDist");
243
+ if (typeof checked.bytes === "number" && checked.bytes !== C3_PAIR_DIST_BYTES) {
244
+ throw new Error(`Invalid table manifest ${manifestPath}: c3PairDist.bytes is ${checked.bytes}, expected ` +
245
+ `${C3_PAIR_DIST_BYTES} — run pnpm cube4:hc:tables to regenerate it.`);
246
+ }
247
+ return new Uint8Array(readChecked(path.join(assetDirectory, checked.file), checked.checksum, C3_PAIR_DIST_BYTES));
118
248
  }
119
249
  /** Load all cube4 reduction tables (throws if the generated assets are missing). */
120
250
  export function loadCube4ReductionTables() {
@@ -128,11 +258,15 @@ export function loadCube4ReductionTables() {
128
258
  center3: loadCenter3(assetDirectory),
129
259
  edge3: loadEdge3(assetDirectory)
130
260
  };
131
- const hc = loadHc(assetDirectory);
132
- if (hc)
133
- tables.hc = hc;
134
- const c3Pair = loadC3Pair(assetDirectory);
261
+ // Shared manifest for the two optional PDB tables; absent = not deployed.
262
+ const hcManifestPath = path.join(assetDirectory, "hc-manifest.json");
263
+ const hcManifest = existsSync(hcManifestPath) ? readManifest(hcManifestPath) : undefined;
264
+ if (hcManifest) {
265
+ tables.hc = loadHc(assetDirectory, hcManifest);
266
+ }
267
+ const c3Pair = loadC3Pair(assetDirectory, hcManifest);
135
268
  if (c3Pair)
136
269
  tables.c3PairDist = c3Pair;
270
+ assertCube4ReductionTableBudget(tables);
137
271
  return tables;
138
272
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Phase-2 wing-transversal pruning (two-pair PDB).
3
+ *
4
+ * A phase-2 goal is only usable when the wings are "transversal": every
5
+ * pair has exactly one wing in the second-half positions
6
+ * (`PAIRING_SECOND_HALVES`). Only ~0.1-0.25% of center goals pass, so the
7
+ * phase-2 search used to enumerate millions of center goals to find the
8
+ * transversal ones. This table bounds how many phase-2 moves two given pairs
9
+ * need to become transversal, so the search can prune branches that cannot
10
+ * end transversal within the remaining depth.
11
+ *
12
+ * State: the unordered positions of the two wings of pair X and of pair Y
13
+ * (276 × 276 index; overlapping positions are unreachable). The predicate
14
+ * only depends on positions, so ONE table serves every pair-of-pairs.
15
+ * Exact BFS distances under the given phase-2 move prefix (the search set),
16
+ * hence admissible; generated at runtime (76,176 bytes, a few ms).
17
+ */
18
+ export declare const PAIR_STATES = 276;
19
+ /** Unordered position pair (a != b) → 0..275, as a flat 24×24 table. */
20
+ export declare const PAIR_INDEX: Uint16Array;
21
+ /**
22
+ * Wing position maps: `posMove[m * 24 + p]` = where the wing at position p
23
+ * goes under CENTER2_MOVES[m] (inverse of the `wp` gather maps).
24
+ */
25
+ export declare function getWingPositionMoves(): Uint8Array;
26
+ /** Two-pair transversal distances under the first `moveCount` CENTER2_MOVES. */
27
+ export declare function getTransversalPairPdb(moveCount: number): Uint8Array;
28
+ //# sourceMappingURL=pair-transversal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair-transversal.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/pair-transversal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B,wEAAwE;AACxE,eAAO,MAAM,UAAU,EAAE,WAWrB,CAAC;AAeL;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,UAAU,CASjD;AAKD,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAsCnE"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Phase-2 wing-transversal pruning (two-pair PDB).
3
+ *
4
+ * A phase-2 goal is only usable when the wings are "transversal": every
5
+ * pair has exactly one wing in the second-half positions
6
+ * (`PAIRING_SECOND_HALVES`). Only ~0.1-0.25% of center goals pass, so the
7
+ * phase-2 search used to enumerate millions of center goals to find the
8
+ * transversal ones. This table bounds how many phase-2 moves two given pairs
9
+ * need to become transversal, so the search can prune branches that cannot
10
+ * end transversal within the remaining depth.
11
+ *
12
+ * State: the unordered positions of the two wings of pair X and of pair Y
13
+ * (276 × 276 index; overlapping positions are unreachable). The predicate
14
+ * only depends on positions, so ONE table serves every pair-of-pairs.
15
+ * Exact BFS distances under the given phase-2 move prefix (the search set),
16
+ * hence admissible; generated at runtime (76,176 bytes, a few ms).
17
+ */
18
+ import { CENTER2_MOVES, getWingMoveMaps } from "./center2.js";
19
+ import { PAIRING_SECOND_HALVES } from "./pairing-coordinate.js";
20
+ export const PAIR_STATES = 276;
21
+ /** Unordered position pair (a != b) → 0..275, as a flat 24×24 table. */
22
+ export const PAIR_INDEX = (() => {
23
+ const index = new Uint16Array(24 * 24).fill(0xffff);
24
+ let k = 0;
25
+ for (let a = 0; a < 24; a++) {
26
+ for (let b = a + 1; b < 24; b++) {
27
+ index[a * 24 + b] = k;
28
+ index[b * 24 + a] = k;
29
+ k++;
30
+ }
31
+ }
32
+ return index;
33
+ })();
34
+ const PAIR_A = new Uint8Array(PAIR_STATES);
35
+ const PAIR_B = new Uint8Array(PAIR_STATES);
36
+ for (let a = 0; a < 24; a++) {
37
+ for (let b = a + 1; b < 24; b++) {
38
+ const k = PAIR_INDEX[a * 24 + b];
39
+ PAIR_A[k] = a;
40
+ PAIR_B[k] = b;
41
+ }
42
+ }
43
+ const IS_SECOND = new Uint8Array(24);
44
+ for (const p of PAIRING_SECOND_HALVES)
45
+ IS_SECOND[p] = 1;
46
+ /**
47
+ * Wing position maps: `posMove[m * 24 + p]` = where the wing at position p
48
+ * goes under CENTER2_MOVES[m] (inverse of the `wp` gather maps).
49
+ */
50
+ export function getWingPositionMoves() {
51
+ if (cachedPosMove)
52
+ return cachedPosMove;
53
+ const gather = getWingMoveMaps();
54
+ const posMove = new Uint8Array(CENTER2_MOVES.length * 24);
55
+ for (let m = 0; m < CENTER2_MOVES.length; m++) {
56
+ for (let i = 0; i < 24; i++)
57
+ posMove[m * 24 + gather[m * 24 + i]] = i;
58
+ }
59
+ cachedPosMove = posMove;
60
+ return posMove;
61
+ }
62
+ let cachedPosMove;
63
+ const cache = new Map();
64
+ /** Two-pair transversal distances under the first `moveCount` CENTER2_MOVES. */
65
+ export function getTransversalPairPdb(moveCount) {
66
+ const hit = cache.get(moveCount);
67
+ if (hit)
68
+ return hit;
69
+ const posMove = getWingPositionMoves();
70
+ const n = PAIR_STATES * PAIR_STATES;
71
+ const dist = new Uint8Array(n).fill(0xff);
72
+ const queue = new Int32Array(n);
73
+ let tail = 0;
74
+ for (let x = 0; x < PAIR_STATES; x++) {
75
+ if (IS_SECOND[PAIR_A[x]] === IS_SECOND[PAIR_B[x]])
76
+ continue;
77
+ for (let y = 0; y < PAIR_STATES; y++) {
78
+ if (IS_SECOND[PAIR_A[y]] === IS_SECOND[PAIR_B[y]])
79
+ continue;
80
+ const ya = PAIR_A[y];
81
+ const yb = PAIR_B[y];
82
+ if (ya === PAIR_A[x] || ya === PAIR_B[x] || yb === PAIR_A[x] || yb === PAIR_B[x])
83
+ continue;
84
+ const s = x * PAIR_STATES + y;
85
+ dist[s] = 0;
86
+ queue[tail++] = s;
87
+ }
88
+ }
89
+ for (let head = 0; head < tail; head++) {
90
+ const s = queue[head];
91
+ const x = (s / PAIR_STATES) | 0;
92
+ const y = s - x * PAIR_STATES;
93
+ const d = dist[s] + 1;
94
+ for (let m = 0; m < moveCount; m++) {
95
+ const row = m * 24;
96
+ const nx = PAIR_INDEX[posMove[row + PAIR_A[x]] * 24 + posMove[row + PAIR_B[x]]];
97
+ const ny = PAIR_INDEX[posMove[row + PAIR_A[y]] * 24 + posMove[row + PAIR_B[y]]];
98
+ const t = nx * PAIR_STATES + ny;
99
+ if (dist[t] === 0xff) {
100
+ dist[t] = d;
101
+ queue[tail++] = t;
102
+ }
103
+ }
104
+ }
105
+ cache.set(moveCount, dist);
106
+ return dist;
107
+ }
@@ -41,30 +41,28 @@ export declare class FastPhase3Solver {
41
41
  private readonly tempep;
42
42
  private readonly moveLog;
43
43
  private readonly sym;
44
- /** Transposition Table size: 2^21 = 2,097,152 entries (24 MB in RAM).
45
- * NOTE: doubling to 4M was tried and reverted (-0.07% nodes on the
46
- * corpus — the 2M working set already captures the hit locality). */
44
+ /** Transposition Table size: 2^20 = 1,048,576 entries (12 MiB per solve).
45
+ * The previous 2M table used 24 MiB per process, plus a separate 8 MiB
46
+ * generation array. The compact 1M table leaves room for concurrent
47
+ * solvers under the 150 MiB reduction-table budget. */
47
48
  private static readonly TT_SIZE;
48
49
  private static readonly TT_MASK;
49
- private static readonly tt;
50
- private static epoch;
51
- /**
52
- * Per-slot exact-length-call tag (8 MB transient). TT pruning stays
53
- * epoch-scoped (unchanged traversal), but threshold proofs only trust
54
- * same-call hits: a hit from another call may have validated under a
55
- * different candidate, so it still prunes yet marks the proof dirty.
56
- */
57
- private static readonly ttGen;
58
- private static callSeq;
50
+ private readonly tt;
51
+ private static readonly CALL_ID_LIMIT;
52
+ private callSeq;
59
53
  private callId;
60
- /** Reset the per-attempt cumulative node counter. */
54
+ private contextCenter3;
55
+ private contextEdge3;
56
+ /** Reset this solver's cumulative node counter (legacy method name). */
61
57
  resetEpochNodes(): void;
62
- static nextEpoch(): void;
63
- /** Fresh per-call id for TT-generation tagging (epoch wrap resets it). */
64
- private static nextCallId;
58
+ clearTranspositionTable(): void;
59
+ private setTableContext;
60
+ /** Fresh per-call id for TT-generation tagging. */
61
+ private nextCallId;
65
62
  constructor();
63
+ private ensureDepth;
66
64
  private nodeCount;
67
- /** Cumulative nodes across calls (reset per attempt via resetEpochNodes). */
65
+ /** Cumulative nodes across calls (reset per solve generation). */
68
66
  epochNodes: number;
69
67
  /**
70
68
  * Proof bookkeeping for IDA* threshold propagation (reset per exact-length
@@ -76,9 +74,8 @@ export declare class FastPhase3Solver {
76
74
  * - `sawGoal`: a goal was reached (even if the validator rejected it —
77
75
  * a nearby length may still validate, so only step L+1).
78
76
  * - `ttTouched`: a transposition-table hit from ANOTHER exact-length call
79
- * skipped a subtree (same-call hits replay this call's own exhausted
80
- * subtree and stay clean; other calls may validate under a different
81
- * candidate, so their stored proof does not transfer).
77
+ * skipped a subtree, and the hit was not certified goal-free across
78
+ * candidate validators.
82
79
  * Bound-based axis skips need no flag: the skipped siblings are
83
80
  * same-face turns, so noteExcess records their triangle-inequality bound.
84
81
  */
@@ -87,22 +84,27 @@ export declare class FastPhase3Solver {
87
84
  private sawGoal;
88
85
  private ttTouched;
89
86
  private minExcess;
90
- private getPrun;
87
+ /**
88
+ * Root-length coverage of the tightest TT hit this call (depth +
89
+ * storedDepth): the pruned subtree proves no goal at or below that root
90
+ * length under the call's predicate. `Number.MAX_SAFE_INTEGER` when no hit
91
+ * pruned anything. Feeds `validatedThrough`.
92
+ */
93
+ private ttCoverage;
91
94
  /**
92
95
  * Recursive IDA* worker on table transitions with Transposition Table pruning.
93
96
  */
94
97
  search(eplast: Uint8Array, ct: number, prun: number, maxl: number, lm: number, depth: number, center3Tables: Center3Tables, edge3Distances: Uint8Array, maxNodes?: number, collect?: Phase3SolutionValidator, hc?: HcDenseTables, hcCoord?: number, c3PairDist?: Uint8Array): boolean;
95
98
  /**
96
- * Record an IDA* bound prune: the child at `depth + 1` needs at least
97
- * `childBound` more moves, so no solution exists through this branch
98
- * below root threshold `depth + 1 + childBound`. When a bound-based axis
99
- * skip also fires, the skipped siblings are same-face turns (verified:
100
- * every non-empty skipAxis3 run under m < 14 is a same-base triple),
101
- * hence at most 1 legal move apart — their bound is at least
99
+ * Record an IDA* bound prune (inlined at the four gate sites): the child at
100
+ * `depth + 1` needs at least `childBound` more moves, so no solution exists
101
+ * through this branch below root threshold `depth + 1 + childBound`. When a
102
+ * bound-based axis skip also fires, the skipped siblings are same-face turns
103
+ * (verified: every non-empty skipAxis3 run under m < 14 is a same-base
104
+ * triple), hence at most 1 legal move apart — their bound is at least
102
105
  * `childBound - 1` by the triangle inequality on the exact PDBs, so the
103
106
  * recorded excess `depth + childBound` stays a true lower bound.
104
107
  */
105
- private noteExcess;
106
108
  /**
107
109
  * Search for a phase-3 solution with exact target length (first found).
108
110
  */
@@ -127,6 +129,7 @@ export declare class FastPhase3Solver {
127
129
  solveLengthProven(edge: Uint8Array, ct3: number, edist: number, length3: number, center3Tables: Center3Tables, edge3Distances: Uint8Array, maxNodes: number | undefined, limit: number | undefined, validate: (moves: Cube4Move[]) => boolean, hc?: HcDenseTables, hcCoord?: number, c3PairDist?: Uint8Array): {
128
130
  moves: Cube4Move[] | null;
129
131
  provenThrough: number;
132
+ validatedThrough: number;
130
133
  nodesUsed: number;
131
134
  };
132
135
  }
@@ -144,5 +147,5 @@ export declare function resetProofStats(): void;
144
147
  /**
145
148
  * Solve Phase 3 with iterative deepening on a single Phase3Start.
146
149
  */
147
- export declare function solvePhase3(start: Phase3Start, center3Tables: Center3Tables, edge3Tables: Edge3Tables, maxLength?: number): Promise<Phase3Result | null>;
150
+ export declare function solvePhase3(start: Phase3Start, center3Tables: Center3Tables, edge3Tables: Edge3Tables, maxLength?: number, maxNodes?: number): Promise<Phase3Result | null>;
148
151
  //# sourceMappingURL=phase3-search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"phase3-search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/phase3-search.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"phase3-search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/phase3-search.ts"],"names":[],"mappings":"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"}