@cubingcenter/scrambler 0.5.3 → 0.6.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 (244) hide show
  1. package/README.md +24 -9
  2. package/dist/cli/index.js +0 -0
  3. package/dist/core/api.d.ts.map +1 -1
  4. package/dist/core/api.js +11 -1
  5. package/dist/core/types.d.ts +1 -1
  6. package/dist/core/types.d.ts.map +1 -1
  7. package/dist/puzzles/cube4/index.d.ts +4 -2
  8. package/dist/puzzles/cube4/index.d.ts.map +1 -1
  9. package/dist/puzzles/cube4/index.js +3 -1
  10. package/dist/puzzles/cube4/random-state.d.ts +16 -0
  11. package/dist/puzzles/cube4/random-state.d.ts.map +1 -0
  12. package/dist/puzzles/cube4/random-state.js +76 -0
  13. package/dist/puzzles/cube4/reduction/browser-loader.d.ts +18 -0
  14. package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -0
  15. package/dist/puzzles/cube4/reduction/browser-loader.js +95 -0
  16. package/dist/puzzles/cube4/reduction/center1.d.ts +65 -0
  17. package/dist/puzzles/cube4/reduction/center1.d.ts.map +1 -0
  18. package/dist/puzzles/cube4/reduction/center1.js +245 -0
  19. package/dist/puzzles/cube4/reduction/center2.d.ts +91 -0
  20. package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -0
  21. package/dist/puzzles/cube4/reduction/center2.js +449 -0
  22. package/dist/puzzles/cube4/reduction/center3.d.ts +47 -0
  23. package/dist/puzzles/cube4/reduction/center3.d.ts.map +1 -0
  24. package/dist/puzzles/cube4/reduction/center3.js +278 -0
  25. package/dist/puzzles/cube4/reduction/edge3.d.ts +100 -0
  26. package/dist/puzzles/cube4/reduction/edge3.d.ts.map +1 -0
  27. package/dist/puzzles/cube4/reduction/edge3.js +511 -0
  28. package/dist/puzzles/cube4/reduction/generated/center1-distance.bin +1 -0
  29. package/dist/puzzles/cube4/reduction/generated/center1-manifest.json +46 -0
  30. package/dist/puzzles/cube4/reduction/generated/center1-move-maps.bin +0 -0
  31. package/dist/puzzles/cube4/reduction/generated/center2-ct-move.bin +0 -0
  32. package/dist/puzzles/cube4/reduction/generated/center2-manifest.json +87 -0
  33. package/dist/puzzles/cube4/reduction/generated/center2-pruning.bin +1 -0
  34. package/dist/puzzles/cube4/reduction/generated/center2-rl-move.bin +0 -0
  35. package/dist/puzzles/cube4/reduction/generated/center3-manifest.json +40 -0
  36. package/dist/puzzles/cube4/reduction/generated/center3-move.bin +0 -0
  37. package/dist/puzzles/cube4/reduction/generated/center3-pruning.bin +0 -0
  38. package/dist/puzzles/cube4/reduction/generated/edge3-distances.bin +1 -0
  39. package/dist/puzzles/cube4/reduction/generated/edge3-manifest.json +12 -0
  40. package/dist/puzzles/cube4/reduction/geometry.d.ts +33 -0
  41. package/dist/puzzles/cube4/reduction/geometry.d.ts.map +1 -0
  42. package/dist/puzzles/cube4/reduction/geometry.js +302 -0
  43. package/dist/puzzles/cube4/reduction/node-loader.d.ts +17 -0
  44. package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -0
  45. package/dist/puzzles/cube4/reduction/node-loader.js +98 -0
  46. package/dist/puzzles/cube4/reduction/pairing-coordinate.d.ts +130 -0
  47. package/dist/puzzles/cube4/reduction/pairing-coordinate.d.ts.map +1 -0
  48. package/dist/puzzles/cube4/reduction/pairing-coordinate.js +281 -0
  49. package/dist/puzzles/cube4/reduction/phase3-search.d.ts +42 -0
  50. package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -0
  51. package/dist/puzzles/cube4/reduction/phase3-search.js +133 -0
  52. package/dist/puzzles/cube4/reduction/phase3.d.ts +34 -0
  53. package/dist/puzzles/cube4/reduction/phase3.d.ts.map +1 -0
  54. package/dist/puzzles/cube4/reduction/phase3.js +83 -0
  55. package/dist/puzzles/cube4/reduction/phase4.d.ts +35 -0
  56. package/dist/puzzles/cube4/reduction/phase4.d.ts.map +1 -0
  57. package/dist/puzzles/cube4/reduction/phase4.js +82 -0
  58. package/dist/puzzles/cube4/reduction/solve.d.ts +31 -0
  59. package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -0
  60. package/dist/puzzles/cube4/reduction/solve.js +311 -0
  61. package/dist/puzzles/cube4/scrambler.d.ts +9 -1
  62. package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
  63. package/dist/puzzles/cube4/scrambler.js +123 -81
  64. package/dist/puzzles/cube4/types.d.ts +6 -0
  65. package/dist/puzzles/cube4/types.d.ts.map +1 -1
  66. package/dist/puzzles/cube4/validation.d.ts +6 -0
  67. package/dist/puzzles/cube4/validation.d.ts.map +1 -0
  68. package/dist/puzzles/cube4/validation.js +91 -0
  69. package/dist/puzzles/fto/coordinates.d.ts +53 -0
  70. package/dist/puzzles/fto/coordinates.d.ts.map +1 -0
  71. package/dist/puzzles/fto/coordinates.js +228 -0
  72. package/dist/puzzles/fto/facelets.d.ts +6 -0
  73. package/dist/puzzles/fto/facelets.d.ts.map +1 -0
  74. package/dist/puzzles/fto/facelets.js +99 -0
  75. package/dist/puzzles/fto/index.d.ts +12 -0
  76. package/dist/puzzles/fto/index.d.ts.map +1 -0
  77. package/dist/puzzles/fto/index.js +13 -0
  78. package/dist/puzzles/fto/moves.d.ts +12 -0
  79. package/dist/puzzles/fto/moves.d.ts.map +1 -0
  80. package/dist/puzzles/fto/moves.js +115 -0
  81. package/dist/puzzles/fto/random-state.d.ts +17 -0
  82. package/dist/puzzles/fto/random-state.d.ts.map +1 -0
  83. package/dist/puzzles/fto/random-state.js +40 -0
  84. package/dist/puzzles/fto/scrambler.d.ts +6 -0
  85. package/dist/puzzles/fto/scrambler.d.ts.map +1 -0
  86. package/dist/puzzles/fto/scrambler.js +49 -0
  87. package/dist/puzzles/fto/solver/engine.d.ts +18 -0
  88. package/dist/puzzles/fto/solver/engine.d.ts.map +1 -0
  89. package/dist/puzzles/fto/solver/engine.js +1168 -0
  90. package/dist/puzzles/fto/solver/index.d.ts +2 -0
  91. package/dist/puzzles/fto/solver/index.d.ts.map +1 -0
  92. package/dist/puzzles/fto/solver/index.js +1 -0
  93. package/dist/puzzles/fto/state.d.ts +16 -0
  94. package/dist/puzzles/fto/state.d.ts.map +1 -0
  95. package/dist/puzzles/fto/state.js +61 -0
  96. package/dist/puzzles/fto/types.d.ts +32 -0
  97. package/dist/puzzles/fto/types.d.ts.map +1 -0
  98. package/dist/puzzles/fto/types.js +46 -0
  99. package/dist/puzzles/fto/validation.d.ts +19 -0
  100. package/dist/puzzles/fto/validation.d.ts.map +1 -0
  101. package/dist/puzzles/fto/validation.js +143 -0
  102. package/dist/puzzles/index.d.ts +2 -0
  103. package/dist/puzzles/index.d.ts.map +1 -1
  104. package/dist/puzzles/index.js +6 -0
  105. package/dist/puzzles/skewb/index.d.ts +1 -1
  106. package/dist/puzzles/skewb/index.d.ts.map +1 -1
  107. package/dist/puzzles/skewb/index.js +1 -1
  108. package/dist/puzzles/skewb/random-state.d.ts.map +1 -1
  109. package/dist/puzzles/skewb/random-state.js +4 -1
  110. package/dist/puzzles/sq1/facelets.d.ts +12 -0
  111. package/dist/puzzles/sq1/facelets.d.ts.map +1 -0
  112. package/dist/puzzles/sq1/facelets.js +26 -0
  113. package/dist/puzzles/sq1/index.d.ts +10 -0
  114. package/dist/puzzles/sq1/index.d.ts.map +1 -0
  115. package/dist/puzzles/sq1/index.js +11 -0
  116. package/dist/puzzles/sq1/moves.d.ts +55 -0
  117. package/dist/puzzles/sq1/moves.d.ts.map +1 -0
  118. package/dist/puzzles/sq1/moves.js +220 -0
  119. package/dist/puzzles/sq1/random-state.d.ts +6 -0
  120. package/dist/puzzles/sq1/random-state.d.ts.map +1 -0
  121. package/dist/puzzles/sq1/random-state.js +49 -0
  122. package/dist/puzzles/sq1/scrambler.d.ts +10 -0
  123. package/dist/puzzles/sq1/scrambler.d.ts.map +1 -0
  124. package/dist/puzzles/sq1/scrambler.js +90 -0
  125. package/dist/puzzles/sq1/solver/browser-loader.d.ts +5 -0
  126. package/dist/puzzles/sq1/solver/browser-loader.d.ts.map +1 -0
  127. package/dist/puzzles/sq1/solver/browser-loader.js +92 -0
  128. package/dist/puzzles/sq1/solver/full-cube.d.ts +53 -0
  129. package/dist/puzzles/sq1/solver/full-cube.d.ts.map +1 -0
  130. package/dist/puzzles/sq1/solver/full-cube.js +281 -0
  131. package/dist/puzzles/sq1/solver/generated/manifest.json +50 -0
  132. package/dist/puzzles/sq1/solver/generated/shape-bottom-move.bin +0 -0
  133. package/dist/puzzles/sq1/solver/generated/shape-idx.bin +0 -0
  134. package/dist/puzzles/sq1/solver/generated/shape-prun.bin +0 -0
  135. package/dist/puzzles/sq1/solver/generated/shape-top-move.bin +0 -0
  136. package/dist/puzzles/sq1/solver/generated/shape-twist-move.bin +0 -0
  137. package/dist/puzzles/sq1/solver/generated/square-bottom-move.bin +0 -0
  138. package/dist/puzzles/sq1/solver/generated/square-prun.bin +0 -0
  139. package/dist/puzzles/sq1/solver/generated/square-top-move.bin +0 -0
  140. package/dist/puzzles/sq1/solver/generated/square-twist-move.bin +0 -0
  141. package/dist/puzzles/sq1/solver/index.d.ts +5 -0
  142. package/dist/puzzles/sq1/solver/index.d.ts.map +1 -0
  143. package/dist/puzzles/sq1/solver/index.js +4 -0
  144. package/dist/puzzles/sq1/solver/node-loader.d.ts +4 -0
  145. package/dist/puzzles/sq1/solver/node-loader.d.ts.map +1 -0
  146. package/dist/puzzles/sq1/solver/node-loader.js +113 -0
  147. package/dist/puzzles/sq1/solver/search.d.ts +27 -0
  148. package/dist/puzzles/sq1/solver/search.d.ts.map +1 -0
  149. package/dist/puzzles/sq1/solver/search.js +225 -0
  150. package/dist/puzzles/sq1/solver/shape.d.ts +22 -0
  151. package/dist/puzzles/sq1/solver/shape.d.ts.map +1 -0
  152. package/dist/puzzles/sq1/solver/shape.js +185 -0
  153. package/dist/puzzles/sq1/solver/square.d.ts +10 -0
  154. package/dist/puzzles/sq1/solver/square.d.ts.map +1 -0
  155. package/dist/puzzles/sq1/solver/square.js +90 -0
  156. package/dist/puzzles/sq1/solver/table-metadata.d.ts +48 -0
  157. package/dist/puzzles/sq1/solver/table-metadata.d.ts.map +1 -0
  158. package/dist/puzzles/sq1/solver/table-metadata.js +47 -0
  159. package/dist/puzzles/sq1/solver/tables.d.ts +17 -0
  160. package/dist/puzzles/sq1/solver/tables.d.ts.map +1 -0
  161. package/dist/puzzles/sq1/solver/tables.js +66 -0
  162. package/dist/puzzles/sq1/state.d.ts +10 -0
  163. package/dist/puzzles/sq1/state.d.ts.map +1 -0
  164. package/dist/puzzles/sq1/state.js +74 -0
  165. package/dist/puzzles/sq1/types.d.ts +32 -0
  166. package/dist/puzzles/sq1/types.d.ts.map +1 -0
  167. package/dist/puzzles/sq1/types.js +1 -0
  168. package/dist/puzzles/sq1/validation.d.ts +7 -0
  169. package/dist/puzzles/sq1/validation.d.ts.map +1 -0
  170. package/dist/puzzles/sq1/validation.js +64 -0
  171. package/dist/random/prng.d.ts.map +1 -1
  172. package/dist/random/prng.js +9 -4
  173. package/dist-cjs/core/api.js +11 -1
  174. package/dist-cjs/puzzles/cube4/index.js +11 -4
  175. package/dist-cjs/puzzles/cube4/random-state.js +79 -0
  176. package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +100 -0
  177. package/dist-cjs/puzzles/cube4/reduction/center1.js +258 -0
  178. package/dist-cjs/puzzles/cube4/reduction/center2.js +462 -0
  179. package/dist-cjs/puzzles/cube4/reduction/center3.js +284 -0
  180. package/dist-cjs/puzzles/cube4/reduction/edge3.js +522 -0
  181. package/dist-cjs/puzzles/cube4/reduction/generated/center1-distance.bin +1 -0
  182. package/dist-cjs/puzzles/cube4/reduction/generated/center1-manifest.json +46 -0
  183. package/dist-cjs/puzzles/cube4/reduction/generated/center1-move-maps.bin +0 -0
  184. package/dist-cjs/puzzles/cube4/reduction/generated/center2-ct-move.bin +0 -0
  185. package/dist-cjs/puzzles/cube4/reduction/generated/center2-manifest.json +87 -0
  186. package/dist-cjs/puzzles/cube4/reduction/generated/center2-pruning.bin +1 -0
  187. package/dist-cjs/puzzles/cube4/reduction/generated/center2-rl-move.bin +0 -0
  188. package/dist-cjs/puzzles/cube4/reduction/generated/center3-manifest.json +40 -0
  189. package/dist-cjs/puzzles/cube4/reduction/generated/center3-move.bin +0 -0
  190. package/dist-cjs/puzzles/cube4/reduction/generated/center3-pruning.bin +0 -0
  191. package/dist-cjs/puzzles/cube4/reduction/generated/edge3-distances.bin +1 -0
  192. package/dist-cjs/puzzles/cube4/reduction/generated/edge3-manifest.json +12 -0
  193. package/dist-cjs/puzzles/cube4/reduction/geometry.js +308 -0
  194. package/dist-cjs/puzzles/cube4/reduction/node-loader.js +104 -0
  195. package/dist-cjs/puzzles/cube4/reduction/pairing-coordinate.js +294 -0
  196. package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +138 -0
  197. package/dist-cjs/puzzles/cube4/reduction/phase3.js +86 -0
  198. package/dist-cjs/puzzles/cube4/reduction/phase4.js +87 -0
  199. package/dist-cjs/puzzles/cube4/reduction/solve.js +314 -0
  200. package/dist-cjs/puzzles/cube4/scrambler.js +122 -79
  201. package/dist-cjs/puzzles/cube4/validation.js +97 -0
  202. package/dist-cjs/puzzles/fto/coordinates.js +240 -0
  203. package/dist-cjs/puzzles/fto/facelets.js +104 -0
  204. package/dist-cjs/puzzles/fto/index.js +68 -0
  205. package/dist-cjs/puzzles/fto/moves.js +126 -0
  206. package/dist-cjs/puzzles/fto/random-state.js +43 -0
  207. package/dist-cjs/puzzles/fto/scrambler.js +53 -0
  208. package/dist-cjs/puzzles/fto/solver/engine.js +1175 -0
  209. package/dist-cjs/puzzles/fto/solver/index.js +9 -0
  210. package/dist-cjs/puzzles/fto/state.js +69 -0
  211. package/dist-cjs/puzzles/fto/types.js +49 -0
  212. package/dist-cjs/puzzles/fto/validation.js +147 -0
  213. package/dist-cjs/puzzles/index.js +7 -1
  214. package/dist-cjs/puzzles/skewb/index.js +2 -1
  215. package/dist-cjs/puzzles/skewb/random-state.js +4 -1
  216. package/dist-cjs/puzzles/sq1/facelets.js +30 -0
  217. package/dist-cjs/puzzles/sq1/index.js +52 -0
  218. package/dist-cjs/puzzles/sq1/moves.js +234 -0
  219. package/dist-cjs/puzzles/sq1/random-state.js +52 -0
  220. package/dist-cjs/puzzles/sq1/scrambler.js +95 -0
  221. package/dist-cjs/puzzles/sq1/solver/browser-loader.js +97 -0
  222. package/dist-cjs/puzzles/sq1/solver/full-cube.js +289 -0
  223. package/dist-cjs/puzzles/sq1/solver/generated/manifest.json +50 -0
  224. package/dist-cjs/puzzles/sq1/solver/generated/shape-bottom-move.bin +0 -0
  225. package/dist-cjs/puzzles/sq1/solver/generated/shape-idx.bin +0 -0
  226. package/dist-cjs/puzzles/sq1/solver/generated/shape-prun.bin +0 -0
  227. package/dist-cjs/puzzles/sq1/solver/generated/shape-top-move.bin +0 -0
  228. package/dist-cjs/puzzles/sq1/solver/generated/shape-twist-move.bin +0 -0
  229. package/dist-cjs/puzzles/sq1/solver/generated/square-bottom-move.bin +0 -0
  230. package/dist-cjs/puzzles/sq1/solver/generated/square-prun.bin +0 -0
  231. package/dist-cjs/puzzles/sq1/solver/generated/square-top-move.bin +0 -0
  232. package/dist-cjs/puzzles/sq1/solver/generated/square-twist-move.bin +0 -0
  233. package/dist-cjs/puzzles/sq1/solver/index.js +18 -0
  234. package/dist-cjs/puzzles/sq1/solver/node-loader.js +120 -0
  235. package/dist-cjs/puzzles/sq1/solver/search.js +229 -0
  236. package/dist-cjs/puzzles/sq1/solver/shape.js +191 -0
  237. package/dist-cjs/puzzles/sq1/solver/square.js +93 -0
  238. package/dist-cjs/puzzles/sq1/solver/table-metadata.js +50 -0
  239. package/dist-cjs/puzzles/sq1/solver/tables.js +106 -0
  240. package/dist-cjs/puzzles/sq1/state.js +81 -0
  241. package/dist-cjs/puzzles/sq1/types.js +2 -0
  242. package/dist-cjs/puzzles/sq1/validation.js +68 -0
  243. package/dist-cjs/random/prng.js +9 -4
  244. package/package.json +43 -14
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Center1 (phase 1 of the 4x4 reduction solver): reduce the two colors of a
3
+ * chosen axis (U/D, R/L or F/B) onto a single face-pair.
4
+ *
5
+ * Design (simpler than the reference's symmetry-reduced tables, which use a
6
+ * fragile 48-element non-group of "block-turn symmetries"):
7
+ * - coordinate: the raw C(24,8) = 735,471 mask of the positions holding the
8
+ * two axis colors — NO symmetry reduction;
9
+ * - pruning: the exact BFS distance table from the three face-pair goal
10
+ * masks (God's number 9; the 27-move set reaches the whole space);
11
+ * - search: greedy descent — any move that decreases the exact distance by
12
+ * exactly 1 is on an optimal path, so repeatedly applying the first such
13
+ * move reaches the goal in exactly `distance` moves, with zero
14
+ * backtracking and no iterative deepening.
15
+ *
16
+ * One table serves all three axes (the face-pairs are equivalent goals).
17
+ * The phase-1 move set: 9 axes [U,R,F,D,L,B,Uw,Rw,Fw] × 3 powers = 27.
18
+ */
19
+ import { FACE_MOVE_EFFECTS, parseMove } from "../moves.js";
20
+ // ---------------------------------------------------------------------------
21
+ // Constants
22
+ // ---------------------------------------------------------------------------
23
+ export const CENTER1_AXES = [
24
+ "U", "R", "F", "D", "L", "B", "Uw", "Rw", "Fw"
25
+ ];
26
+ export const CENTER1_MOVES = CENTER1_AXES.flatMap((face) => [face, `${face}2`, `${face}'`]);
27
+ export const CENTER1_RAW_STATES = 735471;
28
+ // Cnk table up to n=24.
29
+ const CNK = (() => {
30
+ const table = [];
31
+ for (let n = 0; n <= 24; n++) {
32
+ table[n] = [];
33
+ for (let k = 0; k <= 24; k++) {
34
+ if (k === 0 || k === n)
35
+ table[n][k] = 1;
36
+ else if (k > n)
37
+ table[n][k] = 0;
38
+ else
39
+ table[n][k] = table[n - 1][k - 1] + table[n - 1][k];
40
+ }
41
+ }
42
+ return table;
43
+ })();
44
+ // ---------------------------------------------------------------------------
45
+ // Mask utilities (24-bit integers; bit i = center position i)
46
+ // ---------------------------------------------------------------------------
47
+ /** The mask of the positions holding the two axis colors (colors % 3 == urf). */
48
+ export function center1Mask(centers, urf) {
49
+ let mask = 0;
50
+ for (let i = 0; i < 24; i++) {
51
+ if (centers[i] % 3 === urf)
52
+ mask |= 1 << i;
53
+ }
54
+ return mask;
55
+ }
56
+ /** C(24,8) combination rank of a mask. */
57
+ export function rankCenter1Mask(mask) {
58
+ let idx = 0;
59
+ let r = 8;
60
+ for (let i = 23; i >= 0; i--) {
61
+ if ((mask >> i) & 1)
62
+ idx += CNK[i][r--];
63
+ }
64
+ return idx;
65
+ }
66
+ export function unrankCenter1Mask(idx) {
67
+ let mask = 0;
68
+ let r = 8;
69
+ for (let i = 23; i >= 0; i--) {
70
+ if (idx >= CNK[i][r]) {
71
+ idx -= CNK[i][r];
72
+ r--;
73
+ mask |= 1 << i;
74
+ }
75
+ }
76
+ return mask;
77
+ }
78
+ /** The three face-pair goal masks (bit i = center position i, face-major). */
79
+ export const CENTER1_GOAL_MASKS = {
80
+ ud: 0xf00f,
81
+ rl: 0xf00f0,
82
+ fb: 0xf00f00
83
+ };
84
+ // ---------------------------------------------------------------------------
85
+ // Move bit-permutations
86
+ // ---------------------------------------------------------------------------
87
+ /** The 27 phase-1 moves as bit-permutations of the 24-bit mask. */
88
+ export function buildCenter1MoveMaps() {
89
+ const maps = new Int32Array(CENTER1_MOVES.length * 24);
90
+ for (let m = 0; m < CENTER1_MOVES.length; m++) {
91
+ const { face, turns } = parseMove(CENTER1_MOVES[m]);
92
+ const effect = FACE_MOVE_EFFECTS[face];
93
+ let pos = Array.from({ length: 24 }, (_, i) => i);
94
+ for (let t = 0; t < turns; t++) {
95
+ const next = new Array(24);
96
+ for (let i = 0; i < 24; i++)
97
+ next[i] = pos[effect.ctperm[i]];
98
+ pos = next;
99
+ }
100
+ // apply a move: dest bit i takes source bit pos[i]
101
+ for (let i = 0; i < 24; i++)
102
+ maps[m * 24 + i] = pos[i];
103
+ }
104
+ return maps;
105
+ }
106
+ export function applyCenter1Move(mask, moveMap, move) {
107
+ let out = 0;
108
+ for (let i = 0; i < 24; i++) {
109
+ if ((mask >> moveMap[move * 24 + i]) & 1)
110
+ out |= 1 << i;
111
+ }
112
+ return out;
113
+ }
114
+ export function generateCenter1Tables() {
115
+ const moveMaps = buildCenter1MoveMaps();
116
+ const distance = new Uint8Array(Math.ceil(CENTER1_RAW_STATES / 2)).fill(0xff);
117
+ const setDistance = (rank, d) => {
118
+ const byte = rank >> 1;
119
+ if ((rank & 1) === 0)
120
+ distance[byte] = (distance[byte] & 0xf0) | (d & 0x0f);
121
+ else
122
+ distance[byte] = (distance[byte] & 0x0f) | ((d & 0x0f) << 4);
123
+ };
124
+ const getDistance = (rank) => {
125
+ const byte = distance[rank >> 1];
126
+ return (rank & 1) === 0 ? byte & 0x0f : byte >> 4;
127
+ };
128
+ const isUnset = (rank) => {
129
+ const byte = distance[rank >> 1];
130
+ return (rank & 1) === 0 ? (byte & 0x0f) === 0x0f : (byte >> 4) === 0x0f;
131
+ };
132
+ // BFS from the three goal masks.
133
+ for (const goal of [CENTER1_GOAL_MASKS.ud, CENTER1_GOAL_MASKS.rl, CENTER1_GOAL_MASKS.fb]) {
134
+ setDistance(rankCenter1Mask(goal), 0);
135
+ }
136
+ const queue = [
137
+ rankCenter1Mask(CENTER1_GOAL_MASKS.ud),
138
+ rankCenter1Mask(CENTER1_GOAL_MASKS.rl),
139
+ rankCenter1Mask(CENTER1_GOAL_MASKS.fb)
140
+ ];
141
+ let head = 0;
142
+ while (head < queue.length) {
143
+ const rank = queue[head++];
144
+ const mask = unrankCenter1Mask(rank);
145
+ const d = getDistance(rank);
146
+ for (let m = 0; m < CENTER1_MOVES.length; m++) {
147
+ const next = applyCenter1Move(mask, moveMaps, m);
148
+ const nextRank = rankCenter1Mask(next);
149
+ if (isUnset(nextRank)) {
150
+ setDistance(nextRank, d + 1);
151
+ queue.push(nextRank);
152
+ }
153
+ }
154
+ }
155
+ // verify completeness: every state reached
156
+ let reached = 0;
157
+ for (let rank = 0; rank < CENTER1_RAW_STATES; rank++) {
158
+ if (!isUnset(rank))
159
+ reached++;
160
+ }
161
+ if (reached !== CENTER1_RAW_STATES) {
162
+ throw new Error(`Center1 BFS incomplete: ${reached}/${CENTER1_RAW_STATES} states reached.`);
163
+ }
164
+ return { distance, moveMaps };
165
+ }
166
+ export function getCenter1Distance(mask, tables) {
167
+ const rank = rankCenter1Mask(mask);
168
+ const byte = tables.distance[rank >> 1];
169
+ return (rank & 1) === 0 ? byte & 0x0f : byte >> 4;
170
+ }
171
+ /**
172
+ * Phase-1 greedy descent: bring the axis colors onto some face-pair in
173
+ * exactly `distance` moves (optimal). Every move that decreases the exact
174
+ * distance by 1 is on an optimal path, so no backtracking is needed.
175
+ * Returns null if the state is unreachable (should not happen).
176
+ */
177
+ export function solveCenter1(centers, urf, tables) {
178
+ const solutions = solveCenter1All(centers, urf, tables, 1);
179
+ return solutions.length > 0 ? solutions[0] : null;
180
+ }
181
+ /**
182
+ * Enumerate phase-1 solutions (the optimal descending paths, then longer
183
+ * alternatives up to `cap`). The phase-1 → phase-3 pipeline needs variety:
184
+ * the phase-2 gate rate depends strongly on the phase-1 output's wings.
185
+ */
186
+ export function solveCenter1All(centers, urf, tables, cap = 50) {
187
+ const moveMaps = tables.moveMaps;
188
+ const solutions = [];
189
+ const path = [];
190
+ const maxLength = 30;
191
+ const dfs = (mask, depth) => {
192
+ if (solutions.length >= cap)
193
+ return true;
194
+ const h = getCenter1Distance(mask, tables);
195
+ if (h === 0) {
196
+ solutions.push({ moves: path.slice(0, depth).map((m) => CENTER1_MOVES[m]), length: depth });
197
+ return false;
198
+ }
199
+ if (depth >= maxLength)
200
+ return false;
201
+ for (let m = 0; m < CENTER1_MOVES.length; m++) {
202
+ const next = applyCenter1Move(mask, moveMaps, m);
203
+ if (getCenter1Distance(next, tables) !== h - 1)
204
+ continue;
205
+ path[depth] = m;
206
+ if (dfs(next, depth + 1))
207
+ return true;
208
+ }
209
+ return false;
210
+ };
211
+ dfs(center1Mask(centers, urf), 0);
212
+ return solutions;
213
+ }
214
+ // ---------------------------------------------------------------------------
215
+ // Verification gates
216
+ // ---------------------------------------------------------------------------
217
+ export function verifyCenter1(tables) {
218
+ const errors = [];
219
+ if (getCenter1Distance(CENTER1_GOAL_MASKS.ud, tables) !== 0) {
220
+ errors.push("Goal mask ud must have distance 0.");
221
+ }
222
+ if (getCenter1Distance(CENTER1_GOAL_MASKS.rl, tables) !== 0) {
223
+ errors.push("Goal mask rl must have distance 0.");
224
+ }
225
+ if (getCenter1Distance(CENTER1_GOAL_MASKS.fb, tables) !== 0) {
226
+ errors.push("Goal mask fb must have distance 0.");
227
+ }
228
+ // distances must be consistent with the moves
229
+ let maxDepth = 0;
230
+ for (let rank = 0; rank < CENTER1_RAW_STATES; rank++) {
231
+ const mask = unrankCenter1Mask(rank);
232
+ const d = getCenter1Distance(mask, tables);
233
+ if (d > maxDepth)
234
+ maxDepth = d;
235
+ for (let m = 0; m < CENTER1_MOVES.length; m++) {
236
+ const next = applyCenter1Move(mask, tables.moveMaps, m);
237
+ const nd = getCenter1Distance(next, tables);
238
+ if (nd < d - 1 || nd > d + 1) {
239
+ errors.push(`Distance inconsistency at rank ${rank} move ${CENTER1_MOVES[m]}: ${d} -> ${nd}.`);
240
+ break;
241
+ }
242
+ }
243
+ }
244
+ return errors;
245
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Center2 (phase 2 of the 4x4 reduction solver): from the phase-1 output
3
+ * (axis colors on a face-pair), reach the phase-2 goal: the U/D/F/B faces
4
+ * uniform in color-class and the R/L faces in one of the 6 goal patterns.
5
+ *
6
+ * Ported from the reference's `Center2` class, adapted to this repo's layout:
7
+ * - ct part: the 16 positions of the U, D, F, B faces, storing color % 3
8
+ * (the color-class), ranked as C(15,8) vs ct[15] = 6,435 states;
9
+ * - rl part: the 8 positions of the R, L faces, storing the actual colors,
10
+ * ranked as C(7,4) vs rl[7] × 2 + wing-parity = 70 states;
11
+ * - full coordinate: ct × 70 = 450,450;
12
+ * - goal: ct == 0 and rl in the 6 reference goal patterns (even parity);
13
+ * - the 28-move set keeps the ct and rl position sets separate, so the move
14
+ * tables factor: ctmv (6,435×28) and rlmv (70×28);
15
+ * - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw').
16
+ *
17
+ * The phase-1 → phase-2 bridge (alignment + encode) is in `reduction.ts`.
18
+ */
19
+ import type { Cube4State, Cube4Move } from "../types.js";
20
+ /** The phase-2 move set (28 real moves + the reference's trailing no-op). */
21
+ export declare const CENTER2_MOVES: readonly Cube4Move[];
22
+ /** The search's exploration set (the first 23 of CENTER2_MOVES). */
23
+ export declare const CENTER2_SEARCH_MOVES: readonly Cube4Move[];
24
+ /** The 6 reference goal rl patterns (full 70-space values, even parity). */
25
+ export declare const CENTER2_GOAL_RL: readonly number[];
26
+ export declare const CENTER2_CT_STATES = 6435;
27
+ export declare const CENTER2_RL_STATES = 70;
28
+ export declare const CENTER2_STATES: number;
29
+ export interface Center2Encoded {
30
+ ct: number;
31
+ rl: number;
32
+ }
33
+ export declare function wingPermutationParity(wp: Uint8Array): 0 | 1;
34
+ /** Encode a full 4x4 state to the (ct, rl) coordinate (0..450,449). */
35
+ export declare function encodeCenter2(state: Cube4State): Center2Encoded;
36
+ export declare function isCenter2Goal(ct: number, rl: number): boolean;
37
+ export interface Center2BridgeResult {
38
+ /** Alignment moves (0-2). */
39
+ alignment: Cube4Move[];
40
+ /** The aligned state (invariant). */
41
+ state: Cube4State;
42
+ /** The (ct, rl) coordinate of the aligned state. */
43
+ encoded: Center2Encoded;
44
+ }
45
+ /**
46
+ * Phase-1 → phase-2 bridge: apply the shortest alignment candidate that
47
+ * makes the state coordinate-invariant, then encode.
48
+ */
49
+ export declare function center2Bridge(state: Cube4State): Center2BridgeResult | null;
50
+ /**
51
+ * Generate the ct/rl move tables + the pruning BFS. The move tables are
52
+ * computed from canonical decodes (the class partition is all the rank
53
+ * needs — the actual class values are irrelevant).
54
+ */
55
+ export interface Center2Tables {
56
+ /** 6435 × 28 — ct rank after each move. */
57
+ ctMove: Int32Array;
58
+ /** 70 × 28 — rl rank (parity included) after each move. */
59
+ rlMove: Int32Array;
60
+ /** 450,450 exact BFS distances from the goal. */
61
+ pruning: Uint8Array;
62
+ }
63
+ export declare function generateCenter2Tables(): Center2Tables;
64
+ export interface Center2Phase2Result {
65
+ /** Phase-2 solution moves (standard 4x4 moves). */
66
+ moves: Cube4Move[];
67
+ /** Final coordinate (satisfies isCenter2Goal). */
68
+ ct: number;
69
+ rl: number;
70
+ /** Final wing permutation (present for gated solutions). */
71
+ wp?: Uint8Array;
72
+ }
73
+ export declare function solveCenter2(startCt: number, startRl: number, tables: Center2Tables, maxLength?: number): Center2Phase2Result | null;
74
+ /**
75
+ * Enumerate phase-2 solutions (shortest first). The phase-2 → phase-3 bridge
76
+ * needs multiple candidates: the reference filters them by the pairing gate
77
+ * (transversal + even wing permutation), which most states fail.
78
+ */
79
+ export declare function solveCenter2All(startCt: number, startRl: number, tables: Center2Tables, maxLength?: number, cap?: number): Center2Phase2Result[];
80
+ /**
81
+ * Solve Center 2 gated for an exact target length.
82
+ */
83
+ export declare function solveCenter2GatedLength(startCt: number, startRl: number, startWp: Uint8Array, tables: Center2Tables, isGateEligible: (wp: Uint8Array) => boolean, targetLength: number, moveLog?: number[]): Center2Phase2Result | null;
84
+ /**
85
+ * Phase-2 search that tracks the wing permutation alongside the (ct, rl)
86
+ * coordinate and only accepts goal states whose wings satisfy the pairing
87
+ * transversal (the phase-3 entry gate).
88
+ */
89
+ export declare function solveCenter2Gated(startCt: number, startRl: number, startWp: Uint8Array, tables: Center2Tables, isGateEligible: (wp: Uint8Array) => boolean, maxLength?: number): Center2Phase2Result | null;
90
+ export declare function verifyCenter2(tables: Center2Tables): string[];
91
+ //# sourceMappingURL=center2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"center2.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/center2.ts"],"names":[],"mappings":"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"}