@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,462 @@
1
+ "use strict";
2
+ /**
3
+ * Center2 (phase 2 of the 4x4 reduction solver): from the phase-1 output
4
+ * (axis colors on a face-pair), reach the phase-2 goal: the U/D/F/B faces
5
+ * uniform in color-class and the R/L faces in one of the 6 goal patterns.
6
+ *
7
+ * Ported from the reference's `Center2` class, adapted to this repo's layout:
8
+ * - ct part: the 16 positions of the U, D, F, B faces, storing color % 3
9
+ * (the color-class), ranked as C(15,8) vs ct[15] = 6,435 states;
10
+ * - rl part: the 8 positions of the R, L faces, storing the actual colors,
11
+ * ranked as C(7,4) vs rl[7] × 2 + wing-parity = 70 states;
12
+ * - full coordinate: ct × 70 = 450,450;
13
+ * - goal: ct == 0 and rl in the 6 reference goal patterns (even parity);
14
+ * - the 28-move set keeps the ct and rl position sets separate, so the move
15
+ * tables factor: ctmv (6,435×28) and rlmv (70×28);
16
+ * - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw').
17
+ *
18
+ * The phase-1 → phase-2 bridge (alignment + encode) is in `reduction.ts`.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.CENTER2_STATES = exports.CENTER2_RL_STATES = exports.CENTER2_CT_STATES = exports.CENTER2_GOAL_RL = exports.CENTER2_SEARCH_MOVES = exports.CENTER2_MOVES = void 0;
22
+ exports.wingPermutationParity = wingPermutationParity;
23
+ exports.encodeCenter2 = encodeCenter2;
24
+ exports.isCenter2Goal = isCenter2Goal;
25
+ exports.center2Bridge = center2Bridge;
26
+ exports.generateCenter2Tables = generateCenter2Tables;
27
+ exports.solveCenter2 = solveCenter2;
28
+ exports.solveCenter2All = solveCenter2All;
29
+ exports.solveCenter2GatedLength = solveCenter2GatedLength;
30
+ exports.solveCenter2Gated = solveCenter2Gated;
31
+ exports.verifyCenter2 = verifyCenter2;
32
+ const moves_js_1 = require("../moves.js");
33
+ // ---------------------------------------------------------------------------
34
+ // Constants
35
+ // ---------------------------------------------------------------------------
36
+ /** The phase-2 move set (28 real moves + the reference's trailing no-op). */
37
+ exports.CENTER2_MOVES = [
38
+ "U", "U2", "U'", "R", "R2", "R'", "F", "F2", "F'", "D", "D2", "D'",
39
+ "L", "L2", "L'", "B", "B2", "B'",
40
+ "Uw2", "Rw", "Rw2", "Rw'", "Fw2", "Dw2", "Lw", "Lw2", "Lw'", "Bw2"
41
+ ];
42
+ /** The search's exploration set (the first 23 of CENTER2_MOVES). */
43
+ exports.CENTER2_SEARCH_MOVES = exports.CENTER2_MOVES.slice(0, 23);
44
+ /** The 6 reference goal rl patterns (full 70-space values, even parity). */
45
+ exports.CENTER2_GOAL_RL = [0, 18, 28, 46, 54, 56];
46
+ exports.CENTER2_CT_STATES = 6435;
47
+ exports.CENTER2_RL_STATES = 70;
48
+ exports.CENTER2_STATES = exports.CENTER2_CT_STATES * exports.CENTER2_RL_STATES;
49
+ // ct positions: U(0-3), D(12-15), F(8-11), B(20-23) — the reference's face
50
+ // order {U, D, F, B}, in this repo's face-major numbering.
51
+ const CT_POS = [0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11, 20, 21, 22, 23];
52
+ // rl positions: R(4-7), L(16-19).
53
+ const RL_POS = [4, 5, 6, 7, 16, 17, 18, 19];
54
+ /** Wide quarter turns flip the wing permutation parity. */
55
+ const PARITY_FLIP = new Set(["Rw", "Rw'", "Lw", "Lw'"]);
56
+ // Cnk table up to n=24.
57
+ const CNK = (() => {
58
+ const table = [];
59
+ for (let n = 0; n <= 24; n++) {
60
+ table[n] = [];
61
+ for (let k = 0; k <= 24; k++) {
62
+ if (k === 0 || k === n)
63
+ table[n][k] = 1;
64
+ else if (k > n)
65
+ table[n][k] = 0;
66
+ else
67
+ table[n][k] = table[n - 1][k - 1] + table[n - 1][k];
68
+ }
69
+ }
70
+ return table;
71
+ })();
72
+ function rankVsLast(values, size, ones) {
73
+ const last = values[size - 1];
74
+ let idx = 0;
75
+ let r = ones;
76
+ for (let i = size - 2; i >= 0; i--) {
77
+ if (values[i] !== last)
78
+ idx += CNK[i][r--];
79
+ }
80
+ return idx;
81
+ }
82
+ function wingPermutationParity(wp) {
83
+ let parity = 0;
84
+ const seen = new Array(24).fill(false);
85
+ for (let start = 0; start < 24; start++) {
86
+ if (seen[start] || wp[start] === start)
87
+ continue;
88
+ let length = 0;
89
+ let current = start;
90
+ while (!seen[current]) {
91
+ seen[current] = true;
92
+ current = wp[current];
93
+ length++;
94
+ }
95
+ parity ^= (length - 1) & 1;
96
+ }
97
+ return parity;
98
+ }
99
+ /** Encode a full 4x4 state to the (ct, rl) coordinate (0..450,449). */
100
+ function encodeCenter2(state) {
101
+ const ct = new Uint8Array(16);
102
+ const rl = new Uint8Array(8);
103
+ for (let i = 0; i < 16; i++)
104
+ ct[i] = state.centers[CT_POS[i]] % 3;
105
+ for (let i = 0; i < 8; i++)
106
+ rl[i] = state.centers[RL_POS[i]];
107
+ const ctRank = rankVsLast(ct, 16, 8);
108
+ const rlRank = rankVsLast(rl, 8, 4) * 2 + wingPermutationParity(state.wp);
109
+ return { ct: ctRank, rl: rlRank };
110
+ }
111
+ function isCenter2Goal(ct, rl) {
112
+ return ct === 0 && exports.CENTER2_GOAL_RL.includes(rl);
113
+ }
114
+ /**
115
+ * The phase-2 coordinate is only valid for states where exactly 8 of the
116
+ * first 15 ct entries differ from ct[15] and exactly 4 of the first 7 rl
117
+ * entries differ from rl[7]. Phase-1 outputs don't always satisfy this; the
118
+ * phase-1 → phase-2 bridge applies a short alignment sequence (0-2 moves)
119
+ * that re-categorizes the axis colors onto the R/L face-pair, which is
120
+ * always invariant (verified over random states).
121
+ */
122
+ const ALIGNMENT_CANDIDATES = [
123
+ [],
124
+ ["Fw", "Bw'"],
125
+ ["Uw", "Dw'"]
126
+ ];
127
+ function center2InvariantDiffs(centers) {
128
+ let ctDiff = 0;
129
+ let rlDiff = 0;
130
+ for (let i = 0; i < 15; i++) {
131
+ if (centers[CT_POS[i]] % 3 !== centers[CT_POS[15]] % 3)
132
+ ctDiff++;
133
+ }
134
+ for (let i = 0; i < 7; i++) {
135
+ if (centers[RL_POS[i]] !== centers[RL_POS[7]])
136
+ rlDiff++;
137
+ }
138
+ return { ctDiff, rlDiff };
139
+ }
140
+ /**
141
+ * Phase-1 → phase-2 bridge: apply the shortest alignment candidate that
142
+ * makes the state coordinate-invariant, then encode.
143
+ */
144
+ function center2Bridge(state) {
145
+ for (const alignment of ALIGNMENT_CANDIDATES) {
146
+ const aligned = alignment.length === 0
147
+ ? state
148
+ : (0, moves_js_1.applyCube4Algorithm)(state, alignment);
149
+ const { ctDiff, rlDiff } = center2InvariantDiffs(aligned.centers);
150
+ if (ctDiff === 8 && rlDiff === 4) {
151
+ return {
152
+ alignment: [...alignment],
153
+ state: aligned,
154
+ encoded: encodeCenter2(aligned)
155
+ };
156
+ }
157
+ }
158
+ return null;
159
+ }
160
+ // ---------------------------------------------------------------------------
161
+ // Move tables
162
+ // ---------------------------------------------------------------------------
163
+ function buildMovePositionMaps() {
164
+ const maps = new Int32Array(exports.CENTER2_MOVES.length * 24);
165
+ for (let m = 0; m < exports.CENTER2_MOVES.length; m++) {
166
+ const { face, turns } = (0, moves_js_1.parseMove)(exports.CENTER2_MOVES[m]);
167
+ const effect = moves_js_1.FACE_MOVE_EFFECTS[face];
168
+ let pos = Array.from({ length: 24 }, (_, i) => i);
169
+ for (let t = 0; t < turns; t++) {
170
+ const next = new Array(24);
171
+ for (let i = 0; i < 24; i++)
172
+ next[i] = pos[effect.ctperm[i]];
173
+ pos = next;
174
+ }
175
+ for (let i = 0; i < 24; i++)
176
+ maps[m * 24 + i] = pos[i];
177
+ }
178
+ return maps;
179
+ }
180
+ function unrankVsLast(idx, size, ones) {
181
+ const values = new Uint8Array(size);
182
+ let r = ones;
183
+ for (let i = size - 2; i >= 0; i--) {
184
+ if (idx >= CNK[i][r]) {
185
+ idx -= CNK[i][r];
186
+ r--;
187
+ values[i] = 1;
188
+ }
189
+ }
190
+ return values;
191
+ }
192
+ function generateCenter2Tables() {
193
+ const moveMaps = buildMovePositionMaps();
194
+ const ctMove = new Int32Array(exports.CENTER2_CT_STATES * exports.CENTER2_MOVES.length);
195
+ const rlMove = new Int32Array(exports.CENTER2_RL_STATES * exports.CENTER2_MOVES.length);
196
+ // canonical full arrangements: ct classes {0,1} (ct[15]=0), rl colors {0,1} (rl[7]=0)
197
+ const full = new Uint8Array(24);
198
+ const moved = new Uint8Array(24);
199
+ for (let ctRank = 0; ctRank < exports.CENTER2_CT_STATES; ctRank++) {
200
+ const pattern = unrankVsLast(ctRank, 16, 8);
201
+ for (let i = 0; i < 24; i++)
202
+ full[i] = 0;
203
+ for (let i = 0; i < 16; i++) {
204
+ if (pattern[i] === 1)
205
+ full[CT_POS[i]] = 1;
206
+ }
207
+ for (let m = 0; m < exports.CENTER2_MOVES.length; m++) {
208
+ const map = moveMaps;
209
+ for (let i = 0; i < 24; i++)
210
+ moved[i] = full[map[m * 24 + i]];
211
+ const ct = new Uint8Array(16);
212
+ for (let i = 0; i < 16; i++)
213
+ ct[i] = moved[CT_POS[i]] % 3;
214
+ ctMove[ctRank * exports.CENTER2_MOVES.length + m] = rankVsLast(ct, 16, 8);
215
+ }
216
+ }
217
+ for (let rlRankBase = 0; rlRankBase < 35; rlRankBase++) {
218
+ for (const parity of [0, 1]) {
219
+ const rlRank = rlRankBase * 2 + parity;
220
+ const pattern = unrankVsLast(rlRankBase, 8, 4);
221
+ for (let i = 0; i < 24; i++)
222
+ full[i] = 0;
223
+ for (let i = 0; i < 8; i++) {
224
+ if (pattern[i] === 1)
225
+ full[RL_POS[i]] = 1;
226
+ }
227
+ for (let m = 0; m < exports.CENTER2_MOVES.length; m++) {
228
+ const map = moveMaps;
229
+ for (let i = 0; i < 24; i++)
230
+ moved[i] = full[map[m * 24 + i]];
231
+ const rl = new Uint8Array(8);
232
+ for (let i = 0; i < 8; i++)
233
+ rl[i] = moved[RL_POS[i]];
234
+ const nextParity = (parity ^ (PARITY_FLIP.has(exports.CENTER2_MOVES[m]) ? 1 : 0));
235
+ rlMove[rlRank * exports.CENTER2_MOVES.length + m] = rankVsLast(rl, 8, 4) * 2 + nextParity;
236
+ }
237
+ }
238
+ }
239
+ // pruning BFS from the 6 goal states, with the 23 exploration moves
240
+ const pruning = new Uint8Array(exports.CENTER2_STATES).fill(0xff);
241
+ const queue = [];
242
+ for (const rl of exports.CENTER2_GOAL_RL) {
243
+ pruning[rl] = 0; // ct == 0 → index == rl
244
+ queue.push(rl);
245
+ }
246
+ let head = 0;
247
+ while (head < queue.length) {
248
+ const idx = queue[head++];
249
+ const ct = Math.floor(idx / exports.CENTER2_RL_STATES);
250
+ const rl = idx % exports.CENTER2_RL_STATES;
251
+ const d = pruning[idx];
252
+ for (let m = 0; m < exports.CENTER2_SEARCH_MOVES.length; m++) {
253
+ const nextCt = ctMove[ct * exports.CENTER2_MOVES.length + m];
254
+ const nextRl = rlMove[rl * exports.CENTER2_MOVES.length + m];
255
+ const next = nextCt * exports.CENTER2_RL_STATES + nextRl;
256
+ if (pruning[next] === 0xff) {
257
+ pruning[next] = d + 1;
258
+ queue.push(next);
259
+ }
260
+ }
261
+ }
262
+ return { ctMove, rlMove, pruning };
263
+ }
264
+ // The reference's cancellation rule (ckmv over move2std): skip the same base
265
+ // move, or the same axis-class when the previous move's standard index is
266
+ // larger than the current's.
267
+ const STANDARD_ORDER = [
268
+ "U", "U2", "U'", "R", "R2", "R'", "F", "F2", "F'", "D", "D2", "D'",
269
+ "L", "L2", "L'", "B", "B2", "B'",
270
+ "Uw", "Uw2", "Uw'", "Rw", "Rw2", "Rw'", "Fw", "Fw2", "Fw'",
271
+ "Dw", "Dw2", "Dw'", "Lw", "Lw2", "Lw'", "Bw", "Bw2", "Bw'"
272
+ ];
273
+ const STD_INDEX = new Map(STANDARD_ORDER.map((m, i) => [m, i]));
274
+ function baseOf(move) {
275
+ return move.replace(/[2']$/, "");
276
+ }
277
+ function axisClass(base) {
278
+ return { U: 0, D: 0, R: 1, L: 1, F: 2, B: 2 }[base];
279
+ }
280
+ function isCancelled(previous, current) {
281
+ const pb = baseOf(previous);
282
+ const cb = baseOf(current);
283
+ if (pb === cb)
284
+ return true;
285
+ if (axisClass(pb) === axisClass(cb)) {
286
+ return STD_INDEX.get(previous) > STD_INDEX.get(current);
287
+ }
288
+ return false;
289
+ }
290
+ function solveCenter2(startCt, startRl, tables, maxLength = 40) {
291
+ const solutions = solveCenter2All(startCt, startRl, tables, maxLength, 1);
292
+ return solutions.length > 0 ? solutions[0] : null;
293
+ }
294
+ /**
295
+ * Enumerate phase-2 solutions (shortest first). The phase-2 → phase-3 bridge
296
+ * needs multiple candidates: the reference filters them by the pairing gate
297
+ * (transversal + even wing permutation), which most states fail.
298
+ */
299
+ function solveCenter2All(startCt, startRl, tables, maxLength = 40, cap = 200) {
300
+ const solutions = [];
301
+ const moveLog = new Array(maxLength);
302
+ for (let length = tables.pruning[startCt * exports.CENTER2_RL_STATES + startRl]; length <= maxLength; length++) {
303
+ if (search(startCt, startRl, length, -1, 0))
304
+ break;
305
+ }
306
+ return solutions;
307
+ function search(c, r, maxl, lm, depth) {
308
+ if (isCenter2Goal(c, r)) {
309
+ if (maxl === 0) {
310
+ solutions.push({
311
+ moves: moveLog.slice(0, depth).map((m) => exports.CENTER2_MOVES[m]),
312
+ ct: c,
313
+ rl: r
314
+ });
315
+ return solutions.length >= cap;
316
+ }
317
+ return false;
318
+ }
319
+ for (let m = 0; m < exports.CENTER2_SEARCH_MOVES.length; m++) {
320
+ if (lm >= 0 && isCancelled(exports.CENTER2_SEARCH_MOVES[lm], exports.CENTER2_SEARCH_MOVES[m])) {
321
+ continue;
322
+ }
323
+ const nc = tables.ctMove[c * exports.CENTER2_MOVES.length + m];
324
+ const nr = tables.rlMove[r * exports.CENTER2_MOVES.length + m];
325
+ const prun = tables.pruning[nc * exports.CENTER2_RL_STATES + nr];
326
+ if (prun >= maxl)
327
+ continue;
328
+ moveLog[depth] = m;
329
+ if (search(nc, nr, maxl - 1, m, depth + 1))
330
+ return true;
331
+ }
332
+ return false;
333
+ }
334
+ }
335
+ let cachedWingMaps = null;
336
+ function getWingMoveMaps() {
337
+ if (!cachedWingMaps) {
338
+ cachedWingMaps = buildWingMoveMaps();
339
+ }
340
+ return cachedWingMaps;
341
+ }
342
+ const tempWpPool = Array.from({ length: 5 }, () => new Uint8Array(24));
343
+ function replayWp(startWp, moves, depth, wingMaps, outWp) {
344
+ outWp.set(startWp);
345
+ const temp = tempWpPool[0];
346
+ for (let d = 0; d < depth; d++) {
347
+ const m = moves[d];
348
+ temp.set(outWp);
349
+ for (let i = 0; i < 24; i++) {
350
+ outWp[i] = temp[wingMaps[m * 24 + i]];
351
+ }
352
+ }
353
+ }
354
+ /**
355
+ * Solve Center 2 gated for an exact target length.
356
+ */
357
+ function solveCenter2GatedLength(startCt, startRl, startWp, tables, isGateEligible, targetLength, moveLog = new Array(targetLength)) {
358
+ const wingMaps = getWingMoveMaps();
359
+ let finalWp = null;
360
+ let finalCt = -1;
361
+ let finalRl = -1;
362
+ if (search(startCt, startRl, targetLength, -1, 0)) {
363
+ return {
364
+ moves: moveLog.slice(0, targetLength).map((m) => exports.CENTER2_MOVES[m]),
365
+ ct: finalCt,
366
+ rl: finalRl,
367
+ wp: finalWp
368
+ };
369
+ }
370
+ return null;
371
+ function search(c, r, maxl, lm, depth) {
372
+ if (isCenter2Goal(c, r)) {
373
+ if (maxl === 0) {
374
+ replayWp(startWp, moveLog, depth, wingMaps, tempWpPool[1]);
375
+ if (isGateEligible(tempWpPool[1])) {
376
+ finalWp = tempWpPool[1].slice();
377
+ finalCt = c;
378
+ finalRl = r;
379
+ return true;
380
+ }
381
+ }
382
+ return false;
383
+ }
384
+ if (maxl === 0)
385
+ return false;
386
+ for (let m = 0; m < exports.CENTER2_SEARCH_MOVES.length; m++) {
387
+ if (lm >= 0 && isCancelled(exports.CENTER2_SEARCH_MOVES[lm], exports.CENTER2_SEARCH_MOVES[m])) {
388
+ continue;
389
+ }
390
+ const nc = tables.ctMove[c * exports.CENTER2_MOVES.length + m];
391
+ const nr = tables.rlMove[r * exports.CENTER2_MOVES.length + m];
392
+ const prun = tables.pruning[nc * exports.CENTER2_RL_STATES + nr];
393
+ if (prun >= maxl)
394
+ continue;
395
+ moveLog[depth] = m;
396
+ if (search(nc, nr, maxl - 1, m, depth + 1))
397
+ return true;
398
+ }
399
+ return false;
400
+ }
401
+ }
402
+ /**
403
+ * Phase-2 search that tracks the wing permutation alongside the (ct, rl)
404
+ * coordinate and only accepts goal states whose wings satisfy the pairing
405
+ * transversal (the phase-3 entry gate).
406
+ */
407
+ function solveCenter2Gated(startCt, startRl, startWp, tables, isGateEligible, maxLength = 40) {
408
+ const moveLog = new Array(maxLength);
409
+ const minLength = tables.pruning[startCt * exports.CENTER2_RL_STATES + startRl];
410
+ for (let length = minLength; length <= maxLength; length++) {
411
+ const result = solveCenter2GatedLength(startCt, startRl, startWp, tables, isGateEligible, length, moveLog);
412
+ if (result)
413
+ return result;
414
+ }
415
+ return null;
416
+ }
417
+ function buildWingMoveMaps() {
418
+ const maps = new Int32Array(exports.CENTER2_MOVES.length * 24);
419
+ for (let m = 0; m < exports.CENTER2_MOVES.length; m++) {
420
+ const { face, turns } = (0, moves_js_1.parseMove)(exports.CENTER2_MOVES[m]);
421
+ const effect = moves_js_1.FACE_MOVE_EFFECTS[face];
422
+ let pos = Array.from({ length: 24 }, (_, i) => i);
423
+ for (let t = 0; t < turns; t++) {
424
+ const next = new Array(24);
425
+ for (let i = 0; i < 24; i++)
426
+ next[i] = pos[effect.wperm[i]];
427
+ pos = next;
428
+ }
429
+ for (let i = 0; i < 24; i++)
430
+ maps[m * 24 + i] = pos[i];
431
+ }
432
+ return maps;
433
+ }
434
+ // ---------------------------------------------------------------------------
435
+ // Verification gates
436
+ // ---------------------------------------------------------------------------
437
+ function verifyCenter2(tables) {
438
+ const errors = [];
439
+ for (const rl of exports.CENTER2_GOAL_RL) {
440
+ if (tables.pruning[rl] !== 0) {
441
+ errors.push(`Goal rl ${rl} must have pruning 0.`);
442
+ }
443
+ }
444
+ let maxDepth = 0;
445
+ for (let idx = 0; idx < exports.CENTER2_STATES; idx++) {
446
+ const d = tables.pruning[idx];
447
+ if (d > maxDepth)
448
+ maxDepth = d;
449
+ const ct = Math.floor(idx / exports.CENTER2_RL_STATES);
450
+ const rl = idx % exports.CENTER2_RL_STATES;
451
+ for (let m = 0; m < exports.CENTER2_SEARCH_MOVES.length; m++) {
452
+ const nc = tables.ctMove[ct * exports.CENTER2_MOVES.length + m];
453
+ const nr = tables.rlMove[rl * exports.CENTER2_MOVES.length + m];
454
+ const nd = tables.pruning[nc * exports.CENTER2_RL_STATES + nr];
455
+ if (nd < d - 1 || nd > d + 1) {
456
+ errors.push(`Pruning inconsistency at ${idx} move ${exports.CENTER2_MOVES[m]}: ${d} -> ${nd}.`);
457
+ break;
458
+ }
459
+ }
460
+ }
461
+ return errors;
462
+ }