@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,278 @@
1
+ /**
2
+ * Center3 (the center part of the phase-3 search): track the center
3
+ * arrangement through the phase-3 moves (which disturb the solved centers
4
+ * with the wide doubles) and reach the fully-solved centers.
5
+ *
6
+ * Ported from the reference's `Center3` class, adapted to this repo's
7
+ * layout. The state is three 8-entry arrays of "half-class" bits
8
+ * ((color / 3) ^ 1) per face-pair, plus a parity bit:
9
+ * - ud: the U, D face positions (this repo: U = 0-3, D = 12-15)
10
+ * - fb: the F, B face positions (8-11, 20-23)
11
+ * - rl: the R, L face positions (4-7, 16-19)
12
+ *
13
+ * Coordinate (reference `$getct_0`): C(7,4) ranks of ud and fb vs their 8th
14
+ * entry (35 each), the rl C(8,4) rank reduced to one of 12 standard values,
15
+ * then ×2 + parity = 29,400 × 2. The rank-0 pattern is "the first four
16
+ * entries differ from the last four" — i.e. each face-pair uniform in half
17
+ * — which is exactly the solved-cube arrangement (verified: the solved cube
18
+ * encodes to coordinate 0). The parity bit absorbs the wing-pairing parity
19
+ * XOR the corner parity at the phase-2 → phase-3 bridge and flips on the
20
+ * wide doubles (`pmove`).
21
+ */
22
+ import { FACE_MOVE_EFFECTS, parseMove } from "../moves.js";
23
+ // ---------------------------------------------------------------------------
24
+ // Constants
25
+ // ---------------------------------------------------------------------------
26
+ export const CENTER3_MOVES = [
27
+ "U", "U2", "U'", "R2", "F", "F2", "F'", "D", "D2", "D'",
28
+ "L2", "B", "B2", "B'", "Uw2", "Rw2", "Fw2", "Dw2", "Lw2", "Bw2"
29
+ ];
30
+ /** The 17-move BFS subset (the reference's pruning loop uses m < 17). */
31
+ export const CENTER3_BFS_MOVES = CENTER3_MOVES.slice(0, 17);
32
+ /** Wide doubles flip the parity bit. */
33
+ const PARITY_FLIP = new Set(["Uw2", "Rw2", "Fw2", "Dw2", "Lw2", "Bw2"]);
34
+ /** Face-pair positions in this repo's numbering. */
35
+ const UD_POS = [0, 1, 2, 3, 12, 13, 14, 15];
36
+ const FB_POS = [8, 9, 10, 11, 20, 21, 22, 23];
37
+ const RL_POS = [4, 5, 6, 7, 16, 17, 18, 19];
38
+ /** The 12 standard rl ranks (reference rl2std). */
39
+ const RL2STD = [0, 9, 14, 23, 27, 28, 41, 42, 46, 55, 60, 69];
40
+ export const CENTER3_STATES = 29400;
41
+ // Cnk table up to n=24.
42
+ const CNK = (() => {
43
+ const table = [];
44
+ for (let n = 0; n <= 24; n++) {
45
+ table[n] = [];
46
+ for (let k = 0; k <= 24; k++) {
47
+ if (k === 0 || k === n)
48
+ table[n][k] = 1;
49
+ else if (k > n)
50
+ table[n][k] = 0;
51
+ else
52
+ table[n][k] = table[n - 1][k - 1] + table[n - 1][k];
53
+ }
54
+ }
55
+ return table;
56
+ })();
57
+ // ---------------------------------------------------------------------------
58
+ // Half-class helpers
59
+ // ---------------------------------------------------------------------------
60
+ function halfClass(color) {
61
+ return (Math.floor(color / 3) ^ 1) & 1;
62
+ }
63
+ /** C(7,4) rank of the entries differing from the last one. */
64
+ function rank7(values) {
65
+ const last = values[7];
66
+ let idx = 0;
67
+ let r = 4;
68
+ for (let i = 6; i >= 0; i--) {
69
+ if (values[i] !== last)
70
+ idx += CNK[i][r--];
71
+ }
72
+ return idx;
73
+ }
74
+ /** C(8,4) rank of the entries differing from `check`. */
75
+ function rank8(values, check) {
76
+ let idx = 0;
77
+ let r = 4;
78
+ for (let i = 7; i >= 0; i--) {
79
+ if (values[i] !== check)
80
+ idx += CNK[i][r--];
81
+ }
82
+ return idx;
83
+ }
84
+ /**
85
+ * Encode a full cube state to the Center3 coordinate.
86
+ *
87
+ * `edgeXorCornerParity` = the $set_6 pairing parity XOR the corner
88
+ * permutation parity, computed at the phase-2 → phase-3 bridge.
89
+ */
90
+ export function encodeCenter3(state, edgeXorCornerParity) {
91
+ const colors = state.centers;
92
+ // reference: parity = (ct[0]%3 > ct[8]%3) ^ (ct[8]%3 > ct[16]%3) ^ (ct[0]%3 > ct[16]%3) ? 0 : 1
93
+ const a = colors[0] % 3;
94
+ const b = colors[8] % 3;
95
+ const c = colors[16] % 3;
96
+ const xored = (a > b ? 1 : 0) ^ (b > c ? 1 : 0) ^ (a > c ? 1 : 0);
97
+ const parity = xored === 0 ? 1 : 0;
98
+ const ud = new Uint8Array(8);
99
+ const fb = new Uint8Array(8);
100
+ const rl = new Uint8Array(8);
101
+ for (let i = 0; i < 8; i++) {
102
+ ud[i] = halfClass(colors[UD_POS[i]]);
103
+ fb[i] = halfClass(colors[FB_POS[i]]);
104
+ rl[i] = halfClass(colors[RL_POS[i]]);
105
+ }
106
+ let idx = rank7(ud);
107
+ idx = idx * 35 + rank7(fb);
108
+ idx *= 12;
109
+ const check = fb[7] ^ ud[7];
110
+ const rlRank = rank8(rl, check);
111
+ const rlIndex = RL2STD.indexOf(rlRank);
112
+ const finalParity = (parity ^ edgeXorCornerParity);
113
+ return {
114
+ coordinate: finalParity + 2 * (idx + rlIndex),
115
+ parity: finalParity
116
+ };
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // Move tables + pruning
120
+ // ---------------------------------------------------------------------------
121
+ function buildMovePositionMaps() {
122
+ const maps = new Int32Array(CENTER3_MOVES.length * 24);
123
+ for (let m = 0; m < CENTER3_MOVES.length; m++) {
124
+ const { face, turns } = parseMove(CENTER3_MOVES[m]);
125
+ const effect = FACE_MOVE_EFFECTS[face];
126
+ let pos = Array.from({ length: 24 }, (_, i) => i);
127
+ for (let t = 0; t < turns; t++) {
128
+ const next = new Array(24);
129
+ for (let i = 0; i < 24; i++)
130
+ next[i] = pos[effect.ctperm[i]];
131
+ pos = next;
132
+ }
133
+ for (let i = 0; i < 24; i++)
134
+ maps[m * 24 + i] = pos[i];
135
+ }
136
+ return maps;
137
+ }
138
+ /**
139
+ * Decode a coordinate to canonical half-class arrays (for the move tables):
140
+ * the rank-0 pattern is "first four differ from the last four", so the
141
+ * canonical state is ud = [1,1,1,1,0,0,0,0], fb = [1,1,1,1,0,0,0,0],
142
+ * rl = [1,1,1,1,0,0,0,0] for coordinate 0.
143
+ */
144
+ function decodeCenter3(coordinate) {
145
+ const parity = coordinate & 1;
146
+ let value = coordinate >> 1;
147
+ const rlIndex = value % 12;
148
+ value = Math.floor(value / 12);
149
+ const fbRank = value % 35;
150
+ const udRank = Math.floor(value / 35);
151
+ const ud = new Uint8Array(8);
152
+ const fb = new Uint8Array(8);
153
+ const rl = new Uint8Array(8);
154
+ // unrank C(7,4) patterns
155
+ let idx = udRank;
156
+ let r = 4;
157
+ for (let i = 6; i >= 0; i--) {
158
+ if (idx >= CNK[i][r]) {
159
+ idx -= CNK[i][r];
160
+ r--;
161
+ ud[i] = 1;
162
+ }
163
+ }
164
+ idx = fbRank;
165
+ r = 4;
166
+ for (let i = 6; i >= 0; i--) {
167
+ if (idx >= CNK[i][r]) {
168
+ idx -= CNK[i][r];
169
+ r--;
170
+ fb[i] = 1;
171
+ }
172
+ }
173
+ // rl: the standard rank from the index
174
+ const rlRank = RL2STD[rlIndex];
175
+ idx = rlRank;
176
+ r = 4;
177
+ for (let i = 7; i >= 0; i--) {
178
+ if (idx >= CNK[i][r]) {
179
+ idx -= CNK[i][r];
180
+ r--;
181
+ rl[i] = 1;
182
+ }
183
+ }
184
+ return { ud, fb, rl, parity };
185
+ }
186
+ export function generateCenter3Tables() {
187
+ const moveMaps = buildMovePositionMaps();
188
+ const moveTable = new Int32Array(CENTER3_STATES * CENTER3_MOVES.length).fill(-1);
189
+ // canonical full arrangements: half-class bits at the face-pair positions.
190
+ // The rl array stores the parity-XORed halves, so the decode undoes the XOR
191
+ // to recover the colors; the move evolution re-extracts the RAW halves and
192
+ // evolves the parity bit via pmove only (it is NOT re-derived from the
193
+ // centers, matching the reference's doMoveCenter3).
194
+ const full = new Uint8Array(24);
195
+ const moved = new Uint8Array(24);
196
+ const ud2 = new Uint8Array(8);
197
+ const fb2 = new Uint8Array(8);
198
+ const rl2 = new Uint8Array(8);
199
+ const coordinateOfMoved = (coordinate, m, parity) => {
200
+ const { ud, fb, rl } = decodeCenter3(coordinate);
201
+ // the half-class arrays live at the 24 face-pair positions; a move is a
202
+ // position permutation, so build the 24-array, permute, and re-extract.
203
+ for (let i = 0; i < 24; i++)
204
+ full[i] = 0;
205
+ for (let i = 0; i < 8; i++) {
206
+ full[UD_POS[i]] = ud[i];
207
+ full[FB_POS[i]] = fb[i];
208
+ full[RL_POS[i]] = rl[i];
209
+ }
210
+ const map = moveMaps;
211
+ for (let i = 0; i < 24; i++)
212
+ moved[i] = full[map[m * 24 + i]];
213
+ for (let i = 0; i < 8; i++) {
214
+ ud2[i] = moved[UD_POS[i]];
215
+ fb2[i] = moved[FB_POS[i]];
216
+ rl2[i] = moved[RL_POS[i]];
217
+ }
218
+ let idx = rank7(ud2);
219
+ idx = idx * 35 + rank7(fb2);
220
+ idx *= 12;
221
+ const check = fb2[7] ^ ud2[7];
222
+ const rlRank = rank8(rl2, check);
223
+ const rlIndex = RL2STD.indexOf(rlRank);
224
+ if (rlIndex < 0)
225
+ return -1;
226
+ const nextParity = (parity ^ (PARITY_FLIP.has(CENTER3_MOVES[m]) ? 1 : 0));
227
+ return nextParity + 2 * (idx + rlIndex);
228
+ };
229
+ // BFS from coordinate 0 with the 17 BFS moves, computing the move-table
230
+ // entries on demand (only the reachable states' transitions are needed).
231
+ const pruning = new Uint8Array(CENTER3_STATES).fill(0xff);
232
+ pruning[0] = 0;
233
+ const queue = [0];
234
+ let head = 0;
235
+ while (head < queue.length) {
236
+ const coord = queue[head++];
237
+ const d = pruning[coord];
238
+ for (let m = 0; m < CENTER3_MOVES.length; m++) {
239
+ const next = coordinateOfMoved(coord, m, coord & 1);
240
+ if (next < 0)
241
+ continue;
242
+ moveTable[coord * CENTER3_MOVES.length + m] = next;
243
+ if (m < CENTER3_BFS_MOVES.length && pruning[next] === 0xff) {
244
+ pruning[next] = d + 1;
245
+ queue.push(next);
246
+ }
247
+ }
248
+ }
249
+ return { moveTable, pruning };
250
+ }
251
+ // ---------------------------------------------------------------------------
252
+ // Verification gates
253
+ // ---------------------------------------------------------------------------
254
+ export function verifyCenter3(tables) {
255
+ const errors = [];
256
+ if (tables.pruning[0] !== 0) {
257
+ errors.push("Goal coordinate must have pruning 0.");
258
+ }
259
+ let maxDepth = 0;
260
+ for (let coord = 0; coord < CENTER3_STATES; coord++) {
261
+ const d = tables.pruning[coord];
262
+ if (d === 0xff)
263
+ continue; // unreachable coordinates have no entries
264
+ if (d > maxDepth)
265
+ maxDepth = d;
266
+ for (let m = 0; m < CENTER3_MOVES.length; m++) {
267
+ const next = tables.moveTable[coord * CENTER3_MOVES.length + m];
268
+ if (next < 0)
269
+ continue;
270
+ const nd = tables.pruning[next];
271
+ if (nd === 0xff || nd < d - 1 || nd > d + 1) {
272
+ errors.push(`Pruning inconsistency at ${coord} move ${CENTER3_MOVES[m]}: ${d} -> ${nd}.`);
273
+ break;
274
+ }
275
+ }
276
+ }
277
+ return errors;
278
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Edge3 (the pairing pruning table for the phase-3 search).
3
+ *
4
+ * The BFS from the solved pairing state over the 17 BFS moves, canonicalized
5
+ * by the 8 rotation symmetries of the reference's Edge3 phase (`$rot_1` /
6
+ * `$rotate_0` decomposition with the `$circlex` / `$swapx` coordinate
7
+ * effects). The pairing state is the relative 12-permutation between the
8
+ * two halves of the edge slots (see `pairing-coordinate.ts`), stored in the
9
+ * reference's `(edge, edgeo)` form with shared labels and `$std`-normalized
10
+ * (second halves hold pairs 0..11 in order).
11
+ *
12
+ * Canonicalization and indexing follow the reference exactly:
13
+ * - the state space is the 12!/2 even edge permutations (parity is
14
+ * invariant under the phase-3 moves), grouped into 1538 first-4
15
+ * "sym" classes under the 8 rotations (the reference's
16
+ * `initEdge3Sym2Raw`);
17
+ * - the canonical sym-index of a state is `symcord1 * 20160 + cord2`,
18
+ * computed by `getMvSym` (the reference's move-conjugated
19
+ * canonicalization with the `mvrot`/`mvroto` tables);
20
+ * - the table stores the exact BFS distance per sym-index (4-bit packed
21
+ * nibbles): 31,006,080 * 4 bits = 15.5 MB, no class-key file needed.
22
+ */
23
+ /** The sym-index space: 1538 first-4 classes × 20160 remaining. */
24
+ export declare const EDGE3_TOTAL = 31006080;
25
+ export declare const EDGE3_CLASSES = 1538;
26
+ export declare const EDGE3_CORD2 = 20160;
27
+ /** The maximum BFS depth (the reference's prunValues length - 1). */
28
+ export declare const EDGE3_MAX_DEPTH = 13;
29
+ /** The reference's `move3std`: $move_4 case → std3 move id (the % 3 class). */
30
+ export declare const MOVE3_STD: readonly number[];
31
+ export declare function bitCount(v: number): number;
32
+ /**
33
+ * The reference's `$move_4` (cases 0-19) as coordinate effects: the same
34
+ * slot permutations applied to `edge` and `edgeo`. The `circle` direction
35
+ * (the cubing.js `mathlib.circle`: the value at a moves to b) is verified
36
+ * against the physical move tables in `verifyEdge3`.
37
+ */
38
+ declare function applyMoveCase(edge: Uint8Array, edgeo: Uint8Array, m: number): void;
39
+ /** The reference's `$rotate_0`: rotation r (0..7) via the rot1 decomposition. */
40
+ declare function rotateByIndex(edge: Uint8Array, edgeo: Uint8Array, r: number): void;
41
+ /**
42
+ * The mixed-radix Lehmer encode of the first `end` values (the reference's
43
+ * `get12Perm`): idx = Σ digit_i * (12-i)!-radix, with the digits reduced by
44
+ * the values already seen.
45
+ */
46
+ declare function get12Perm(arr: Uint8Array, end: number, returnMask?: boolean): number;
47
+ /**
48
+ * Decode a 12-permutation from the mixed-radix index (the reference's
49
+ * `$set_4`): the 11 digits with the parity-corrected last two positions.
50
+ */
51
+ declare function set4(out: Uint8Array, idx: number): void;
52
+ export interface SymTables {
53
+ mvrot: Uint8Array;
54
+ mvroto: Uint8Array;
55
+ sym2Raw: Uint32Array;
56
+ /** class → the used-values mask of its representative's first 4. */
57
+ sym2Mask: Uint32Array;
58
+ /** first-4 Lehmer → (class << 3 | rotation). */
59
+ raw2Sym: Uint32Array;
60
+ /** class → the bitmask of the rotations mapping the representative to itself. */
61
+ symState: Uint32Array;
62
+ /** (move, class) → the moved state's (class << 3 | rotation) for the %3==1 moves. */
63
+ symMove: Uint16Array;
64
+ }
65
+ export declare function getSymTables(): SymTables;
66
+ export declare function buildSymTables(): SymTables;
67
+ /**
68
+ * The reference's `getMvSym`: the canonical sym-index of the state (edge,
69
+ * edgeo) under the move-conjugated rotation, as `(sym-index << 3 | r)`.
70
+ */
71
+ export declare function getMvSymFrom(edge: Uint8Array, _edgeo: Uint8Array, mv: number, sym2Raw: Uint32Array, sym2Mask: Uint32Array, raw2Sym: Uint32Array, mvrot: Uint8Array, mvroto: Uint8Array, assumeIdx?: number): number;
72
+ export interface Edge3Tables {
73
+ /** sym-index → BFS distance (packed 4-bit nibbles). */
74
+ distances: Uint8Array;
75
+ /** the distance of a std-form edge (a 12-permutation). */
76
+ distanceOfEdge(edge: Uint8Array): number;
77
+ }
78
+ /**
79
+ * Generate the exact BFS distances over the 31,006,080 sym-index space.
80
+ * `maxDepthCap` stops the BFS early (validation only).
81
+ */
82
+ export declare function generateEdge3Tables(maxDepthCap?: number): Edge3Tables;
83
+ /**
84
+ * The distance lookup over a loaded distances buffer: builds the sym
85
+ * machinery once (cached) and returns a closure from the std-form edge
86
+ * (a 12-permutation) to its BFS distance.
87
+ */
88
+ export declare function edge3DistanceLookup(distances: Uint8Array): (edge: Uint8Array) => number;
89
+ /** Test-only hooks. */
90
+ export declare function __edge3TestHooks(): {
91
+ buildSymTables: typeof buildSymTables;
92
+ applyMoveCase: typeof applyMoveCase;
93
+ get12Perm: typeof get12Perm;
94
+ set4: typeof set4;
95
+ rotateByIndex: typeof rotateByIndex;
96
+ getMvSymFrom: typeof getMvSymFrom;
97
+ };
98
+ export declare function verifyEdge3(tables: Edge3Tables): string[];
99
+ export {};
100
+ //# sourceMappingURL=edge3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge3.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/edge3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH,mEAAmE;AACnE,eAAO,MAAM,WAAW,WAAa,CAAC;AACtC,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,WAAW,QAAS,CAAC;AAGlC,qEAAqE;AACrE,eAAO,MAAM,eAAe,KAAK,CAAC;AAUlC,+EAA+E;AAC/E,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAEtC,CAAC;AAaF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1C;AAkCD;;;;;GAKG;AACH,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CA2G3E;AAkCD,iFAAiF;AACjF,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAO3E;AAMD;;;;GAIG;AACH,iBAAS,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,CAS3E;AAED;;;GAGG;AACH,iBAAS,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAmBhD;AAOD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,WAAW,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,WAAW,CAAC;IACtB,gDAAgD;IAChD,OAAO,EAAE,WAAW,CAAC;IACrB,iFAAiF;IACjF,QAAQ,EAAE,WAAW,CAAC;IACtB,qFAAqF;IACrF,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,wBAAgB,YAAY,IAAI,SAAS,CAKxC;AAED,wBAAgB,cAAc,IAAI,SAAS,CAoE1C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,EAClB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,UAAU,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA6BR;AAMD,MAAM,WAAW,WAAW;IAC1B,uDAAuD;IACvD,SAAS,EAAE,UAAU,CAAC;IACtB,0DAA0D;IAC1D,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,SAAK,GAAG,WAAW,CAkEjE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,UAAU,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CASvF;AAMD,uBAAuB;AACvB,wBAAgB,gBAAgB;;;;;;;EAE/B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAgBzD"}