@cubingcenter/scrambler 0.6.3 → 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 (191) hide show
  1. package/README.md +17 -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/cube2/solver/search.d.ts.map +1 -1
  10. package/dist/puzzles/cube2/solver/search.js +3 -3
  11. package/dist/puzzles/cube2/solver/tables.d.ts +6 -0
  12. package/dist/puzzles/cube2/solver/tables.d.ts.map +1 -1
  13. package/dist/puzzles/cube2/solver/tables.js +16 -2
  14. package/dist/puzzles/cube3/index.d.ts +1 -0
  15. package/dist/puzzles/cube3/index.d.ts.map +1 -1
  16. package/dist/puzzles/cube3/moves.d.ts.map +1 -1
  17. package/dist/puzzles/cube3/moves.js +104 -18
  18. package/dist/puzzles/cube3/scrambler.d.ts +16 -1
  19. package/dist/puzzles/cube3/scrambler.d.ts.map +1 -1
  20. package/dist/puzzles/cube3/scrambler.js +1 -1
  21. package/dist/puzzles/cube3/solver/browser-loader.d.ts.map +1 -1
  22. package/dist/puzzles/cube3/solver/browser-loader.js +48 -22
  23. package/dist/puzzles/cube3/solver/generated-assets.d.ts.map +1 -1
  24. package/dist/puzzles/cube3/solver/generated-assets.js +9 -3
  25. package/dist/puzzles/cube3/solver/index.d.ts +5 -3
  26. package/dist/puzzles/cube3/solver/index.d.ts.map +1 -1
  27. package/dist/puzzles/cube3/solver/index.js +2 -1
  28. package/dist/puzzles/cube3/solver/node-loader.js +9 -2
  29. package/dist/puzzles/cube3/solver/phase1.d.ts +37 -0
  30. package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
  31. package/dist/puzzles/cube3/solver/phase1.js +156 -15
  32. package/dist/puzzles/cube3/solver/phase2.d.ts +75 -0
  33. package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
  34. package/dist/puzzles/cube3/solver/phase2.js +279 -68
  35. package/dist/puzzles/cube3/solver/search-utils.d.ts +1 -1
  36. package/dist/puzzles/cube3/solver/search-utils.d.ts.map +1 -1
  37. package/dist/puzzles/cube3/solver/symmetry.d.ts +24 -0
  38. package/dist/puzzles/cube3/solver/symmetry.d.ts.map +1 -0
  39. package/dist/puzzles/cube3/solver/symmetry.js +74 -0
  40. package/dist/puzzles/cube3/solver/tables.d.ts +6 -1
  41. package/dist/puzzles/cube3/solver/tables.d.ts.map +1 -1
  42. package/dist/puzzles/cube3/solver/tables.js +14 -12
  43. package/dist/puzzles/cube3/validation.d.ts.map +1 -1
  44. package/dist/puzzles/cube3/validation.js +15 -7
  45. package/dist/puzzles/cube4/browser-worker-pool.d.ts +82 -0
  46. package/dist/puzzles/cube4/browser-worker-pool.d.ts.map +1 -0
  47. package/dist/puzzles/cube4/browser-worker-pool.js +205 -0
  48. package/dist/puzzles/cube4/browser-worker.d.ts +2 -0
  49. package/dist/puzzles/cube4/browser-worker.d.ts.map +1 -0
  50. package/dist/puzzles/cube4/browser-worker.js +31 -0
  51. package/dist/puzzles/cube4/index.d.ts +10 -2
  52. package/dist/puzzles/cube4/index.d.ts.map +1 -1
  53. package/dist/puzzles/cube4/index.js +6 -2
  54. package/dist/puzzles/cube4/moves.d.ts +5 -0
  55. package/dist/puzzles/cube4/moves.d.ts.map +1 -1
  56. package/dist/puzzles/cube4/moves.js +212 -11
  57. package/dist/puzzles/cube4/reduction/browser-loader.d.ts +29 -1
  58. package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
  59. package/dist/puzzles/cube4/reduction/browser-loader.js +227 -32
  60. package/dist/puzzles/cube4/reduction/center2.d.ts +44 -3
  61. package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
  62. package/dist/puzzles/cube4/reduction/center2.js +152 -10
  63. package/dist/puzzles/cube4/reduction/edge3.d.ts +4 -0
  64. package/dist/puzzles/cube4/reduction/edge3.d.ts.map +1 -1
  65. package/dist/puzzles/cube4/reduction/edge3.js +23 -3
  66. package/dist/puzzles/cube4/reduction/finish3.d.ts +53 -0
  67. package/dist/puzzles/cube4/reduction/finish3.d.ts.map +1 -0
  68. package/dist/puzzles/cube4/reduction/finish3.js +393 -0
  69. package/dist/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
  70. package/dist/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
  71. package/dist/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
  72. package/dist/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
  73. package/dist/puzzles/cube4/reduction/generated/hc-manifest.json +44 -0
  74. package/dist/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
  75. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +52 -0
  76. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -0
  77. package/dist/puzzles/cube4/reduction/hc-pdb.js +220 -0
  78. package/dist/puzzles/cube4/reduction/node-loader.d.ts +15 -1
  79. package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
  80. package/dist/puzzles/cube4/reduction/node-loader.js +199 -25
  81. package/dist/puzzles/cube4/reduction/pair-transversal.d.ts +28 -0
  82. package/dist/puzzles/cube4/reduction/pair-transversal.d.ts.map +1 -0
  83. package/dist/puzzles/cube4/reduction/pair-transversal.js +107 -0
  84. package/dist/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
  85. package/dist/puzzles/cube4/reduction/phase3-search.d.ts +116 -7
  86. package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
  87. package/dist/puzzles/cube4/reduction/phase3-search.js +458 -32
  88. package/dist/puzzles/cube4/reduction/rotation-fix.d.ts +28 -0
  89. package/dist/puzzles/cube4/reduction/rotation-fix.d.ts.map +1 -0
  90. package/dist/puzzles/cube4/reduction/rotation-fix.js +212 -0
  91. package/dist/puzzles/cube4/reduction/solve.d.ts +172 -2
  92. package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
  93. package/dist/puzzles/cube4/reduction/solve.js +1201 -180
  94. package/dist/puzzles/cube4/scramble-cache.d.ts +93 -0
  95. package/dist/puzzles/cube4/scramble-cache.d.ts.map +1 -0
  96. package/dist/puzzles/cube4/scramble-cache.js +150 -0
  97. package/dist/puzzles/cube4/scrambler.d.ts +201 -6
  98. package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
  99. package/dist/puzzles/cube4/scrambler.js +414 -176
  100. package/dist/puzzles/cube4/state.d.ts.map +1 -1
  101. package/dist/puzzles/cube4/state.js +4 -1
  102. package/dist/puzzles/cube4/worker-pool.d.ts +139 -0
  103. package/dist/puzzles/cube4/worker-pool.d.ts.map +1 -0
  104. package/dist/puzzles/cube4/worker-pool.js +405 -0
  105. package/dist/puzzles/cube4/worker-protocol.d.ts +58 -0
  106. package/dist/puzzles/cube4/worker-protocol.d.ts.map +1 -0
  107. package/dist/puzzles/cube4/worker-protocol.js +58 -0
  108. package/dist/puzzles/cube4/worker.d.ts +26 -0
  109. package/dist/puzzles/cube4/worker.d.ts.map +1 -0
  110. package/dist/puzzles/cube4/worker.js +49 -0
  111. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts +4 -8
  112. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts.map +1 -1
  113. package/dist/puzzles/pyraminx/solver/browser-loader.js +44 -15
  114. package/dist/puzzles/pyraminx/solver/generated-assets.d.ts.map +1 -1
  115. package/dist/puzzles/pyraminx/solver/generated-assets.js +8 -3
  116. package/dist/puzzles/pyraminx/solver/search.d.ts.map +1 -1
  117. package/dist/puzzles/pyraminx/solver/search.js +3 -6
  118. package/dist/puzzles/pyraminx/solver/tables.d.ts +6 -0
  119. package/dist/puzzles/pyraminx/solver/tables.d.ts.map +1 -1
  120. package/dist/puzzles/pyraminx/solver/tables.js +17 -0
  121. package/dist/puzzles/shared/compressed-fetch.d.ts +37 -1
  122. package/dist/puzzles/shared/compressed-fetch.d.ts.map +1 -1
  123. package/dist/puzzles/shared/compressed-fetch.js +226 -31
  124. package/dist/puzzles/shared/load-once.d.ts +16 -0
  125. package/dist/puzzles/shared/load-once.d.ts.map +1 -0
  126. package/dist/puzzles/shared/load-once.js +11 -0
  127. package/dist/puzzles/skewb/solver/browser-loader.d.ts +4 -11
  128. package/dist/puzzles/skewb/solver/browser-loader.d.ts.map +1 -1
  129. package/dist/puzzles/skewb/solver/browser-loader.js +44 -16
  130. package/dist/puzzles/skewb/solver/generated-assets.d.ts.map +1 -1
  131. package/dist/puzzles/skewb/solver/generated-assets.js +8 -3
  132. package/dist/puzzles/skewb/solver/search.js +3 -3
  133. package/dist/puzzles/skewb/solver/tables.d.ts +6 -0
  134. package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -1
  135. package/dist/puzzles/skewb/solver/tables.js +17 -0
  136. package/dist/puzzles/sq1/solver/browser-loader.d.ts.map +1 -1
  137. package/dist/puzzles/sq1/solver/browser-loader.js +58 -26
  138. package/dist-cjs/core/api.js +10 -2
  139. package/dist-cjs/puzzles/cube2/solver/browser-loader.js +42 -14
  140. package/dist-cjs/puzzles/cube2/solver/generated-assets.js +8 -3
  141. package/dist-cjs/puzzles/cube2/solver/search.js +2 -2
  142. package/dist-cjs/puzzles/cube2/solver/tables.js +17 -3
  143. package/dist-cjs/puzzles/cube3/moves.js +104 -18
  144. package/dist-cjs/puzzles/cube3/scrambler.js +1 -1
  145. package/dist-cjs/puzzles/cube3/solver/browser-loader.js +48 -22
  146. package/dist-cjs/puzzles/cube3/solver/generated-assets.js +9 -3
  147. package/dist-cjs/puzzles/cube3/solver/index.js +8 -1
  148. package/dist-cjs/puzzles/cube3/solver/node-loader.js +9 -2
  149. package/dist-cjs/puzzles/cube3/solver/phase1.js +157 -15
  150. package/dist-cjs/puzzles/cube3/solver/phase2.js +276 -65
  151. package/dist-cjs/puzzles/cube3/solver/symmetry.js +80 -0
  152. package/dist-cjs/puzzles/cube3/solver/tables.js +14 -12
  153. package/dist-cjs/puzzles/cube3/validation.js +15 -7
  154. package/dist-cjs/puzzles/cube4/browser-worker-pool.js +211 -0
  155. package/dist-cjs/puzzles/cube4/index.js +23 -1
  156. package/dist-cjs/puzzles/cube4/moves.js +213 -11
  157. package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +227 -32
  158. package/dist-cjs/puzzles/cube4/reduction/center2.js +155 -10
  159. package/dist-cjs/puzzles/cube4/reduction/edge3.js +24 -3
  160. package/dist-cjs/puzzles/cube4/reduction/finish3.js +398 -0
  161. package/dist-cjs/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
  162. package/dist-cjs/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
  163. package/dist-cjs/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
  164. package/dist-cjs/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
  165. package/dist-cjs/puzzles/cube4/reduction/generated/hc-manifest.json +44 -0
  166. package/dist-cjs/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
  167. package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +232 -0
  168. package/dist-cjs/puzzles/cube4/reduction/node-loader.js +200 -25
  169. package/dist-cjs/puzzles/cube4/reduction/pair-transversal.js +112 -0
  170. package/dist-cjs/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
  171. package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +460 -33
  172. package/dist-cjs/puzzles/cube4/reduction/rotation-fix.js +216 -0
  173. package/dist-cjs/puzzles/cube4/reduction/solve.js +1203 -172
  174. package/dist-cjs/puzzles/cube4/scramble-cache.js +156 -0
  175. package/dist-cjs/puzzles/cube4/scrambler.js +416 -174
  176. package/dist-cjs/puzzles/cube4/state.js +4 -1
  177. package/dist-cjs/puzzles/cube4/worker-pool.js +447 -0
  178. package/dist-cjs/puzzles/cube4/worker-protocol.js +62 -0
  179. package/dist-cjs/puzzles/pyraminx/solver/browser-loader.js +44 -15
  180. package/dist-cjs/puzzles/pyraminx/solver/generated-assets.js +8 -3
  181. package/dist-cjs/puzzles/pyraminx/solver/search.js +2 -5
  182. package/dist-cjs/puzzles/pyraminx/solver/tables.js +18 -1
  183. package/dist-cjs/puzzles/shared/compressed-fetch.js +229 -31
  184. package/dist-cjs/puzzles/shared/load-once.js +14 -0
  185. package/dist-cjs/puzzles/skewb/solver/browser-loader.js +44 -16
  186. package/dist-cjs/puzzles/skewb/solver/generated-assets.js +8 -3
  187. package/dist-cjs/puzzles/skewb/solver/search.js +2 -2
  188. package/dist-cjs/puzzles/skewb/solver/tables.js +18 -1
  189. package/dist-cjs/puzzles/sq1/solver/browser-loader.js +58 -26
  190. package/docs/development.md +19 -7
  191. package/package.json +17 -7
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Placement of the whole-cube rotation fix inside a 4x4 solution.
3
+ *
4
+ * The reduction ends with the cube solved in some orientation, so the solve
5
+ * is `P ++ W(R) ++ Q` where `W(R)` are the 2-4 wide moves of the rotation R
6
+ * (`x` = `Rw Lw'`, ...). A rotation commutes with any move sequence once the
7
+ * sequence's faces are re-mapped:
8
+ *
9
+ * X ++ W(R) ≡ W(R) ++ rotateAlgorithm(X, R)
10
+ *
11
+ * so the fix can sit at ANY point of the solution (with the moves after it
12
+ * re-mapped), and a two-rotation fix can be split across two points. The
13
+ * total is unchanged except at the insertion junctions, where the fix can
14
+ * cancel against same-axis wide moves (`Uw2 · y2` = `Uw2 · Uw2 Dw2` = `Dw2`).
15
+ * `placeRotationFix` picks the placement with the shortest simplified total.
16
+ */
17
+ import { applyCube4Algorithm, simplifyCube4Algorithm } from "../moves.js";
18
+ import { createSolvedCube4State } from "../state.js";
19
+ /** Wide-move words of the 9 single rotations. */
20
+ export const ROTATION_WORDS = {
21
+ "x": ["Rw", "Lw'"],
22
+ "y": ["Uw", "Dw'"],
23
+ "z": ["Fw", "Bw'"],
24
+ "x'": ["Rw'", "Lw"],
25
+ "y'": ["Uw'", "Dw"],
26
+ "z'": ["Fw'", "Bw"],
27
+ "x2": ["Rw2", "Lw2"],
28
+ "y2": ["Uw2", "Dw2"],
29
+ "z2": ["Fw2", "Bw2"]
30
+ };
31
+ const FACE_MAPS = {
32
+ "x": { U: "B", R: "R", F: "U", D: "F", L: "L", B: "D" },
33
+ "x'": { U: "F", R: "R", F: "D", D: "B", L: "L", B: "U" },
34
+ "y": { U: "U", R: "F", F: "L", D: "D", L: "B", B: "R" },
35
+ "y'": { U: "U", R: "B", F: "R", D: "D", L: "F", B: "L" },
36
+ "z": { U: "R", R: "D", F: "F", D: "L", L: "U", B: "B" },
37
+ "z'": { U: "L", R: "U", F: "F", D: "R", L: "D", B: "B" },
38
+ "x2": { U: "D", R: "R", F: "B", D: "U", L: "L", B: "F" },
39
+ "y2": { U: "U", R: "L", F: "B", D: "D", L: "R", B: "F" },
40
+ "z2": { U: "D", R: "L", F: "F", D: "U", L: "R", B: "B" }
41
+ };
42
+ const FACES = ["U", "R", "F", "D", "L", "B"];
43
+ /** Composite face map of a rotation sequence (applied left to right). */
44
+ function faceMapOf(rotations) {
45
+ const map = {};
46
+ for (const face of FACES) {
47
+ let current = face;
48
+ for (const rotation of rotations)
49
+ current = FACE_MAPS[rotation][current];
50
+ map[face] = current;
51
+ }
52
+ return map;
53
+ }
54
+ function remap(moves, map) {
55
+ const out = new Array(moves.length);
56
+ for (let i = 0; i < moves.length; i++) {
57
+ const move = moves[i];
58
+ out[i] = (map[move[0]] + move.slice(1));
59
+ }
60
+ return out;
61
+ }
62
+ const rotationKey = (rotations) => applyCube4Algorithm(createSolvedCube4State(), rotations.flatMap((rotation) => ROTATION_WORDS[rotation])).centers.join(",");
63
+ /**
64
+ * Every factorization of each rotation element into 1 or 2 single rotations
65
+ * (keyed by the rotated solved cube's centers). Non-minimal ones are kept on
66
+ * purpose: `x2` as `x · x` costs 4 moves but can cancel at two junctions.
67
+ */
68
+ const FACTORIZATIONS = (() => {
69
+ const names = Object.keys(ROTATION_WORDS);
70
+ const byKey = new Map();
71
+ const add = (sequence) => {
72
+ const key = rotationKey(sequence);
73
+ const list = byKey.get(key);
74
+ if (!list)
75
+ byKey.set(key, [sequence]);
76
+ else
77
+ list.push(sequence);
78
+ };
79
+ for (const a of names)
80
+ add([a]);
81
+ for (const a of names)
82
+ for (const b of names)
83
+ add([a, b]);
84
+ return byKey;
85
+ })();
86
+ const AXIS_OF_FACE = { U: 0, D: 0, R: 1, L: 1, F: 2, B: 2 };
87
+ /** Segment boundaries (maximal same-axis runs) of a sequence: 0, ..., n. */
88
+ function runBoundaries(moves) {
89
+ const bounds = [0];
90
+ for (let i = 1; i < moves.length; i++) {
91
+ if (AXIS_OF_FACE[moves[i][0]] !== AXIS_OF_FACE[moves[i - 1][0]])
92
+ bounds.push(i);
93
+ }
94
+ if (moves.length > 0)
95
+ bounds.push(moves.length);
96
+ return bounds;
97
+ }
98
+ /** Moves saved by simplifying across one inserted word, judged on a window of 2 runs per side. */
99
+ function junctionSaving(moves, bounds, b, word, leftMap, rightMap) {
100
+ const lo = bounds[Math.max(0, b - 2)];
101
+ const hi = bounds[Math.min(bounds.length - 1, b + 2)];
102
+ const at = bounds[b];
103
+ const left = moves.slice(lo, at);
104
+ const window = [...(leftMap ? remap(left, leftMap) : left), ...word, ...remap(moves.slice(at, hi), rightMap)];
105
+ return window.length - simplifyCube4Algorithm(window).length;
106
+ }
107
+ /**
108
+ * Shortest simplified solution equivalent to `prefix ++ W(rotation) ++
109
+ * suffix` (same effect on every state), over every run-boundary insertion
110
+ * point of the fix and every minimal factorization of the rotation. Only
111
+ * run boundaries matter: the fix can only merge with runs on its own axis,
112
+ * and re-mapping preserves the run structure of what follows it.
113
+ */
114
+ export function placeRotationFix(prefix, rotation, suffix) {
115
+ const fixed = simplifyCube4Algorithm([
116
+ ...prefix,
117
+ ...rotation.flatMap((r) => ROTATION_WORDS[r]),
118
+ ...suffix
119
+ ]);
120
+ if (rotation.length === 0)
121
+ return fixed;
122
+ let best = fixed;
123
+ const consider = (candidate) => {
124
+ const simplified = simplifyCube4Algorithm(candidate);
125
+ if (simplified.length < best.length)
126
+ best = simplified;
127
+ };
128
+ // A = prefix ++ Q' with W(R) ++ suffix ≡ Q' ++ W(R): Q' = remap(suffix, R⁻¹).
129
+ const forward = faceMapOf(rotation);
130
+ const inverseMap = {};
131
+ for (const face of FACES)
132
+ inverseMap[forward[face]] = face;
133
+ const all = simplifyCube4Algorithm([...prefix, ...remap(suffix, inverseMap)]);
134
+ const bounds = runBoundaries(all);
135
+ const m = bounds.length;
136
+ const identity = faceMapOf([]);
137
+ for (const factors of FACTORIZATIONS.get(rotationKey(rotation)) ?? [rotation]) {
138
+ if (factors.length === 1) {
139
+ const map = faceMapOf(factors);
140
+ const word = ROTATION_WORDS[factors[0]];
141
+ let bestB = -1;
142
+ let bestSaving = 0;
143
+ for (let b = 0; b < m; b++) {
144
+ const saving = junctionSaving(all, bounds, b, word, null, map);
145
+ if (saving > bestSaving) {
146
+ bestSaving = saving;
147
+ bestB = b;
148
+ }
149
+ }
150
+ if (bestB >= 0) {
151
+ const k = bounds[bestB];
152
+ consider([...all.slice(0, k), ...word, ...remap(all.slice(k), map)]);
153
+ }
154
+ continue;
155
+ }
156
+ const map1 = faceMapOf([factors[0]]);
157
+ const map12 = faceMapOf(factors);
158
+ const word1 = ROTATION_WORDS[factors[0]];
159
+ const word2 = ROTATION_WORDS[factors[1]];
160
+ const build = (b1, b2) => {
161
+ const k1 = bounds[b1];
162
+ const k2 = bounds[b2];
163
+ return [
164
+ ...all.slice(0, k1),
165
+ ...word1,
166
+ ...remap(all.slice(k1, k2), map1),
167
+ ...word2,
168
+ ...remap(all.slice(k2), map12)
169
+ ];
170
+ };
171
+ // Far-apart junctions save independently: s1 (unmapped | map1) at b1,
172
+ // s2 (map1 | map12) at b2 >= b1 + 3, combined with a suffix maximum.
173
+ // Both words at one point (b1 == b2) is a single junction (s12); nearby
174
+ // pairs interact, so the best few estimates are re-scored exactly.
175
+ const s1 = new Int32Array(m);
176
+ const s2 = new Int32Array(m);
177
+ const word12 = [...word1, ...word2];
178
+ const shortlist = [];
179
+ for (let b = 0; b < m; b++) {
180
+ s1[b] = junctionSaving(all, bounds, b, word1, identity, map1);
181
+ s2[b] = junctionSaving(all, bounds, b, word2, map1, map12);
182
+ const s12 = junctionSaving(all, bounds, b, word12, identity, map12);
183
+ if (s12 > 0)
184
+ shortlist.push([s12, b, b]);
185
+ }
186
+ let suffixBest = -1;
187
+ for (let b1 = m - 4; b1 >= 0; b1--) {
188
+ const b2Candidate = b1 + 3;
189
+ if (suffixBest < 0 || s2[b2Candidate] > s2[suffixBest])
190
+ suffixBest = b2Candidate;
191
+ const saving = s1[b1] + s2[suffixBest];
192
+ if (saving > 0)
193
+ shortlist.push([saving, b1, suffixBest]);
194
+ for (let b2 = b1 + 1; b2 < Math.min(m, b1 + 3); b2++) {
195
+ if (s1[b1] + s2[b2] > 0)
196
+ shortlist.push([s1[b1] + s2[b2], b1, b2]);
197
+ }
198
+ }
199
+ shortlist.sort((a, b) => b[0] - a[0] || a[1] - b[1] || a[2] - b[2]);
200
+ const seen = new Set();
201
+ for (const [, b1, b2] of shortlist) {
202
+ if (seen.size >= 4)
203
+ break;
204
+ const key = b1 * 256 + b2;
205
+ if (seen.has(key))
206
+ continue;
207
+ seen.add(key);
208
+ consider(build(b1, b2));
209
+ }
210
+ }
211
+ return best;
212
+ }
@@ -7,25 +7,195 @@ import type { Cube4State, Cube4Move } from "../types.js";
7
7
  import { type Center1Tables } from "./center1.js";
8
8
  import { type Center2Tables } from "./center2.js";
9
9
  import { type Center3Tables } from "./center3.js";
10
+ import { type HcDenseTables } from "./hc-pdb.js";
10
11
  import { type Edge3Tables } from "./edge3.js";
11
12
  import type { Cube3State, Cube3Move } from "../../cube3/types.js";
13
+ import type { Cube3Phase1Tables, Cube3Phase2Tables } from "../../cube3/solver/tables.js";
12
14
  export interface Cube4SolveTables {
13
15
  center1: Center1Tables;
14
16
  center2: Center2Tables;
15
17
  center3: Center3Tables;
16
18
  edge3: Edge3Tables;
19
+ cube3Phase1?: Cube3Phase1Tables;
20
+ /** Phase-2 tables for the optional continuous 3x3 finish. */
21
+ cube3Phase2?: Cube3Phase2Tables;
22
+ /** Half-class exact PDB (optional; tightens the phase-3 root bound and node-level pruning). */
23
+ hc?: HcDenseTables;
24
+ /** Joint Center3 x Single Pair PDB distances (optional). */
25
+ c3PairDist?: Uint8Array;
17
26
  }
18
27
  export interface Cube4SolveResult {
19
28
  /** The complete solve sequence (solves the input state). */
20
29
  moves: Cube4Move[];
21
30
  /** The scramble (the inverted solution). */
22
31
  scramble: Cube4Move[];
32
+ /** Winning reduction breakdown (unsimplified lengths). */
33
+ phases?: {
34
+ length12: number;
35
+ length1: number;
36
+ length2: number;
37
+ length3: number;
38
+ finish: number;
39
+ };
40
+ /** Search work: phase-2 DFS nodes + phase-3 IDA* nodes this attempt. */
41
+ nodes?: {
42
+ p2: number;
43
+ p3: number;
44
+ f3calls: number;
45
+ };
46
+ /** Wall-clock split of this solve (reduction search / finish loop / polish). */
47
+ timings?: {
48
+ reductionMs: number;
49
+ f1Ms: number;
50
+ f2Ms: number;
51
+ f3Ms: number;
52
+ finishMs: number;
53
+ polishMs: number;
54
+ };
23
55
  }
56
+ declare abstract class Cube4SolveError extends Error {
57
+ abstract readonly code: "cube4-phase2-cap" | "cube4-phase3-cap" | "cube4-finish-cap";
58
+ }
59
+ export declare class Cube4Phase2CapError extends Cube4SolveError {
60
+ readonly code: "cube4-phase2-cap";
61
+ constructor(message: string);
62
+ }
63
+ export declare class Cube4Phase3CapError extends Cube4SolveError {
64
+ readonly code: "cube4-phase3-cap";
65
+ constructor(message: string);
66
+ }
67
+ export declare class Cube4FinishCapError extends Cube4SolveError {
68
+ readonly code: "cube4-finish-cap";
69
+ constructor(message: string);
70
+ }
71
+ export declare function rotateAlgorithm(alg: readonly Cube4Move[], rotSeq: string[]): Cube4Move[];
72
+ /** Physical wide-move sequence implementing a whole-cube rotation. */
73
+ export declare function cubeRotationMoves(rotSeq: readonly string[]): Cube4Move[];
74
+ export declare function invertRotationSequence(rotSeq: readonly string[]): string[];
75
+ export declare function applyCubeRotation(state: Cube4State, rotSeq: string[]): Cube4State;
76
+ export declare const P2_CANCEL: readonly (readonly boolean[])[];
77
+ /** Temporary instrumentation for F2 goal statistics (CUBE4_F2_STATS=1). */
78
+ export declare const f2Stats: {
79
+ goals: number;
80
+ transversalPass: number;
81
+ axisRejects: number;
82
+ minTransversalL2: number;
83
+ minChiralL2: number;
84
+ };
85
+ export declare function resetF2Stats(): void;
86
+ /** Trigger rate for the stagnationCut early-stop (checks vs actual cuts). */
87
+ export declare const stagnationStats: {
88
+ checks: number;
89
+ cuts: number;
90
+ };
91
+ export declare function resetStagnationStats(): void;
92
+ /**
93
+ * Temporary instrumentation for the finish-skip bound (CUBE4_FINISH_STATS=1):
94
+ * `gap = totalEst - finalLen` per finished reduction. A skip
95
+ * `totalEst - SLACK >= minTotalMoves` is only sound while SLACK covers the
96
+ * observed gap maximum (junction cancellation + finish-vs-h3 slack).
97
+ */
98
+ export declare const finishStats: {
99
+ gapMin: number;
100
+ gapMax: number;
101
+ gapSum: number;
102
+ gapN: number;
103
+ finishes: number;
104
+ skipped: number;
105
+ };
106
+ /**
107
+ * Temporary instrumentation for the rotMoves cost (CUBE4_ROT_STATS=1):
108
+ * per built reduction records the prep frame rotation, the derived
109
+ * normalization rotation and the emitted fix length.
110
+ */
111
+ export declare const rotStats: Array<{
112
+ totalRot: string;
113
+ rotToStandard: string;
114
+ rotLen: number;
115
+ }>;
24
116
  /**
25
117
  * Solve a 4x4 state with the reduction solver + the cube3 two-phase finish.
26
118
  * `solveCube3` is injected (the cube3 solver loads its tables lazily).
119
+ *
120
+ * The search is bounded by deterministic work caps (candidate counts, node
121
+ * budgets and length caps); `timeoutMs` is retained as deprecated API and is
122
+ * not enforced. Attempt failures mean the
123
+ * state is outside the solver's horizon, never "ran out of time".
27
124
  */
28
- export declare function solveCube4State(state: Cube4State, tables: Cube4SolveTables, solveCube3: (state: Cube3State) => Promise<{
125
+ export interface Cube4SolveOptions {
126
+ /** @deprecated No wall-clock timeout is enforced; the search uses work caps. */
127
+ timeoutMs?: number;
128
+ /**
129
+ * Last-resort effort: widens every work cap (phase-2 depth/nodes,
130
+ * phase-3 budgets/lengths, pool caps). Only the fast path failing
131
+ * should trigger this — easy states never pay it.
132
+ */
133
+ deep?: boolean;
134
+ /** Phase-4 attempts among valid reductions (default 3, or centersBeam * edgesPerCenter). */
135
+ finishTries?: number;
136
+ /** Number of distinct center candidates from phase 2 to explore in phase 3 (default 1). */
137
+ centersBeam?: number | undefined;
138
+ /** Maximum number of distinct edge pairings to collect per center candidate in phase 3 (default 5). */
139
+ edgesPerCenter?: number | undefined;
140
+ /** Deep multi-candidate polish pass in Phase 3. */
141
+ deepPolish?: boolean | undefined;
142
+ /** Prune searches whose reduction exceeds this bound (branch-and-bound). */
143
+ maxReductionLen?: number | undefined;
144
+ /**
145
+ * Phase-2 wide move set: search all 28 center2 moves (adds
146
+ * Dw2/Lw/Lw2/Lw'/Bw2) with the `pruningWide` table instead of the
147
+ * default 23. Requires regenerated center2 tables; default false.
148
+ * (Measured: ~-0.2 moves for ~+15% time; keep opt-in.)
149
+ */
150
+ center2Wide?: boolean | undefined;
151
+ /**
152
+ * F1 enumeration depth beyond the per-axis optimum (default 3):
153
+ * collects up to `phase1Cap` F1 solutions of length <= minH + this,
154
+ * deliberately supra-optimal, to feed diverse F2/F3 candidates.
155
+ * (Measured: restricting to 0 shows no consistent benefit and
156
+ * interacts badly with center2Wide; keep the default.)
157
+ */
158
+ phase1ExtraDepths?: number | undefined;
159
+ /** F1 solution collection cap (default 60). */
160
+ phase1Cap?: number | undefined;
161
+ /** Phase-2 prep collection cap (default 100, or 160 in deep mode). */
162
+ prepCap?: number | undefined;
163
+ /** Max candidates to collect per (prep, length) DFS call (default 64, or 96 in deep mode). */
164
+ p1CandCap?: number | undefined;
165
+ /** Early stop when candidate pool has >= CAND_MIN and best value stagnates across 2 lengths. */
166
+ stagnationCut?: boolean | undefined;
167
+ /**
168
+ * Full-pipeline restarts over the SAME state (default 1): each restart
169
+ * re-runs phases 1-4 with a reshuffled search order (seeded by the state
170
+ * hash ^ restart index, so results stay deterministic) and the shortest
171
+ * simplified total wins. Same-state diversity preserves the sampled
172
+ * state's distribution — unlike bestOf, which samples new states.
173
+ * `1` keeps the legacy single deterministic pass bit-identical.
174
+ */
175
+ restarts?: number | undefined;
176
+ /**
177
+ * Maximum whole-cube rotation fix (rotMoves, before placement) accepted
178
+ * on a phase-3 goal end (default 4 = no filter). The phase-3 goal is
179
+ * scheme-agnostic (half-class centers): the end's color scheme lands
180
+ * 0/2/4 wide moves from standard depending on the goal path. Since
181
+ * `placeRotationFix` cancels most of the fix against the reduction's wide
182
+ * moves (and totalEst prices the placed fix), filtering no longer pays:
183
+ * measured 46.13 moves @ 1425 ms at 4 vs 46.31 @ 2009 ms at 2 (cc:0..15).
184
+ * `0` = only standard-scheme ends (fails on some states).
185
+ */
186
+ maxFixMoves?: number | undefined;
187
+ /**
188
+ * Two-stage finish (default 0 = off): every candidate reduction is first
189
+ * finished with the cheap "screen" effort, then the best N by screened
190
+ * total are re-finished with the "full" effort. Measured with the 1×5
191
+ * beam (cc:0..31): off 44.97 @ 662 ms vs top-2 45.19 @ 663 ms — with few
192
+ * finishes per solve, screening only loses quality. 0 = every finish at
193
+ * full effort.
194
+ */
195
+ finishScreenTop?: number | undefined;
196
+ }
197
+ export declare function solveCube4State(state: Cube4State, tables: Cube4SolveTables, solveCube3: (state: Cube3State, effort?: "screen" | "full") => Promise<{
29
198
  moves: Cube3Move[];
30
- }>): Promise<Cube4SolveResult>;
199
+ }>, options?: Cube4SolveOptions): Promise<Cube4SolveResult>;
200
+ export {};
31
201
  //# sourceMappingURL=solve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"solve.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/solve.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAKjE,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAIlE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,4CAA4C;IAC5C,QAAQ,EAAE,SAAS,EAAE,CAAC;CACvB;AAuKD;;;GAGG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC;IAAE,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC,GACjE,OAAO,CAAC,gBAAgB,CAAC,CAqL3B"}
1
+ {"version":3,"file":"solve.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/solve.ts"],"names":[],"mappings":"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"}