@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,12 @@
1
+ {
2
+ "version": 1,
3
+ "puzzle": "cube4",
4
+ "phase": "edge3",
5
+ "classCount": 31006080,
6
+ "maxDepth": 13,
7
+ "distances": {
8
+ "file": "edge3-distances.bin",
9
+ "range": 15503040,
10
+ "checksum": "b441c57a052dc5650958426c7fddbdf5cab195f615449bc2235364f2ceb6c939"
11
+ }
12
+ }
@@ -0,0 +1,308 @@
1
+ "use strict";
2
+ /**
3
+ * Reduction geometry for the 4x4 solver.
4
+ *
5
+ * Provides:
6
+ * - the 24-rotation symmetry group as permutations on the 24 center
7
+ * positions and the 24 wing positions (built from a 3D sticker model),
8
+ * - the phase move sets (F1: all moves, F2: UD-preserving, F3: pairing),
9
+ * - per-move corner/wing permutation parities,
10
+ * - the wing orbit structure preserved by the phase-3 move set,
11
+ * - verification gates (group order, move-set invariants, orbit structure).
12
+ *
13
+ * Everything here is verified programmatically by the gates so a hand error
14
+ * in any table fails loudly instead of silently corrupting solutions.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.PHASE_3_MOVES = exports.PHASE_2_MOVES = exports.PHASE_1_MOVES = void 0;
18
+ exports.buildReductionGeometry = buildReductionGeometry;
19
+ exports.permutationParity = permutationParity;
20
+ exports.verifyReductionGeometry = verifyReductionGeometry;
21
+ const types_js_1 = require("../types.js");
22
+ const moves_js_1 = require("../moves.js");
23
+ // ---------------------------------------------------------------------------
24
+ // Phase move sets
25
+ // ---------------------------------------------------------------------------
26
+ exports.PHASE_1_MOVES = types_js_1.CUBE4_ALL_MOVES;
27
+ // Moves that keep U/D-colored centers on U/D faces: all face turns plus
28
+ // Uw/Dw (the u/d slices cycle centers among the R/F/L/B faces only).
29
+ exports.PHASE_2_MOVES = [
30
+ "U", "U2", "U'", "R", "R2", "R'", "F", "F2", "F'",
31
+ "D", "D2", "D'", "L", "L2", "L'", "B", "B2", "B'",
32
+ "Uw", "Uw2", "Uw'", "Dw", "Dw2", "Dw'"
33
+ ];
34
+ // Wing-pairing moves (cstimer-style): all face turns plus quarter wide
35
+ // turns. The wide turns move centers mid-search (guided back by the center
36
+ // coordinate) and flip wing permutation parity (tracked as the phase-2 bit).
37
+ exports.PHASE_3_MOVES = [
38
+ "U", "U2", "U'", "R", "F", "F2", "F'",
39
+ "D", "D2", "D'", "L", "B", "B2", "B'",
40
+ "Uw", "Rw", "Fw", "Dw", "Lw", "Bw"
41
+ ];
42
+ const FACE_BASES = [
43
+ { n: [0, 0, 1], u: [1, 0, 0], v: [0, 1, 0] }, // U
44
+ { n: [1, 0, 0], u: [0, -1, 0], v: [0, 0, 1] }, // R
45
+ { n: [0, 1, 0], u: [1, 0, 0], v: [0, 0, 1] }, // F
46
+ { n: [0, 0, -1], u: [1, 0, 0], v: [0, 1, 0] }, // D
47
+ { n: [-1, 0, 0], u: [0, 1, 0], v: [0, 0, 1] }, // L
48
+ { n: [0, -1, 0], u: [-1, 0, 0], v: [0, 0, 1] } // B
49
+ ];
50
+ function stickerCoord(face, row, col) {
51
+ const { n, u, v } = FACE_BASES[face];
52
+ return [
53
+ n[0] * 4 + (col - 1.5) * 2 * u[0] + (1.5 - row) * 2 * v[0],
54
+ n[1] * 4 + (col - 1.5) * 2 * u[1] + (1.5 - row) * 2 * v[1],
55
+ n[2] * 4 + (col - 1.5) * 2 * u[2] + (1.5 - row) * 2 * v[2]
56
+ ];
57
+ }
58
+ function faceAtCoord(c) {
59
+ let best = 0;
60
+ let bestDot = -Infinity;
61
+ for (let face = 0; face < 6; face += 1) {
62
+ const { n } = FACE_BASES[face];
63
+ const dot = c[0] * n[0] + c[1] * n[1] + c[2] * n[2];
64
+ if (dot > bestDot) {
65
+ bestDot = dot;
66
+ best = face;
67
+ }
68
+ }
69
+ return best;
70
+ }
71
+ function rowColAtCoord(face, c) {
72
+ const { u, v } = FACE_BASES[face];
73
+ const uu = c[0] * u[0] + c[1] * u[1] + c[2] * u[2];
74
+ const vv = c[0] * v[0] + c[1] * v[1] + c[2] * v[2];
75
+ const col = Math.round(uu / 2 + 1.5);
76
+ const row = Math.round(1.5 - vv / 2);
77
+ return [row, col];
78
+ }
79
+ // Rotation generators (as 3x3 integer matrices):
80
+ // Rz90: 90° about the UD axis (x,y,z) → (-y,x,z)
81
+ // Rdiag: 120° about (1,1,1) (x,y,z) → (z,x,y)
82
+ const RZ90 = [
83
+ [0, -1, 0],
84
+ [1, 0, 0],
85
+ [0, 0, 1]
86
+ ];
87
+ const RDIAG = [
88
+ [0, 0, 1],
89
+ [1, 0, 0],
90
+ [0, 1, 0]
91
+ ];
92
+ function applyMatrix(m, v) {
93
+ return [
94
+ m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
95
+ m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
96
+ m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2]
97
+ ];
98
+ }
99
+ function composeMatrix(a, b) {
100
+ return b.map((col) => applyMatrix(a, col));
101
+ }
102
+ function matrixKey(m) {
103
+ return m.map((col) => col.join(",")).join(";");
104
+ }
105
+ function buildRotationGroup() {
106
+ const seen = new Map();
107
+ const group = [];
108
+ const queue = [RZ90, RDIAG];
109
+ while (queue.length > 0) {
110
+ const current = queue.pop();
111
+ const key = matrixKey(current);
112
+ if (seen.has(key))
113
+ continue;
114
+ seen.set(key, current);
115
+ group.push(current);
116
+ for (const generator of [RZ90, RDIAG]) {
117
+ const composed = composeMatrix(current, generator);
118
+ if (!seen.has(matrixKey(composed))) {
119
+ queue.push(composed);
120
+ }
121
+ }
122
+ }
123
+ return group;
124
+ }
125
+ const ROTATIONS = buildRotationGroup();
126
+ // ---------------------------------------------------------------------------
127
+ // Position mappings under a rotation
128
+ // ---------------------------------------------------------------------------
129
+ function centerCoord(index) {
130
+ const face = Math.floor(index / 4);
131
+ const slot = index % 4;
132
+ // slot → (row, col) for the inner 2×2 at face positions 5,6,9,10
133
+ const rowCol = [
134
+ [1, 1], [1, 2], [2, 1], [2, 2]
135
+ ];
136
+ const [row, col] = rowCol[slot];
137
+ return stickerCoord(face, row, col);
138
+ }
139
+ function centerIndexAtCoord(c) {
140
+ const face = faceAtCoord(c);
141
+ const [row, col] = rowColAtCoord(face, c);
142
+ const slot = row === 1 && col === 1 ? 0 : row === 1 && col === 2 ? 1 : row === 2 && col === 1 ? 2 : 3;
143
+ return face * 4 + slot;
144
+ }
145
+ function buildReductionGeometry() {
146
+ return {
147
+ rotations: ROTATIONS,
148
+ centerRotations: ROTATIONS.map((m) => buildPermutation(m, centerCoord, centerIndexAtCoord)),
149
+ wingRotations: ROTATIONS.map((m) => buildWingRotation(m)),
150
+ moveCornerParity: types_js_1.CUBE4_ALL_MOVES.map((move) => permutationParityOf(moves_js_1.FACE_MOVE_EFFECTS[(0, moves_js_1.parseMove)(move).face].cperm)),
151
+ moveWingParity: types_js_1.CUBE4_ALL_MOVES.map((move) => permutationParityOf(moves_js_1.FACE_MOVE_EFFECTS[(0, moves_js_1.parseMove)(move).face].wperm)),
152
+ phase1Moves: exports.PHASE_1_MOVES,
153
+ phase2Moves: exports.PHASE_2_MOVES,
154
+ phase3Moves: exports.PHASE_3_MOVES
155
+ };
156
+ }
157
+ function buildPermutation(matrix, positionCoord, positionAtCoord) {
158
+ const perm = new Array(24);
159
+ for (let index = 0; index < 24; index += 1) {
160
+ perm[positionAtCoord(applyMatrix(matrix, positionCoord(index)))] = index;
161
+ }
162
+ return perm;
163
+ }
164
+ function buildWingRotation(matrix) {
165
+ const stickers = wingStickers();
166
+ const perm = new Array(24);
167
+ for (let index = 0; index < 24; index += 1) {
168
+ const [s1, s2] = stickers[index];
169
+ const r1 = applyMatrix(matrix, s1);
170
+ const r2 = applyMatrix(matrix, s2);
171
+ perm[wingIndexAtSticker(r1)] = index;
172
+ }
173
+ return perm;
174
+ }
175
+ // Wing position → its two stickers as (face, sticker 0-15), from facelets.ts.
176
+ const WING_STICKER_POSITIONS = [
177
+ [0, 11], [1, 1], [0, 7], [1, 2], [0, 14], [2, 2], [0, 13], [2, 1],
178
+ [0, 8], [4, 2], [0, 4], [4, 1], [0, 1], [5, 2], [0, 2], [5, 1],
179
+ [3, 7], [1, 13], [3, 11], [1, 14], [3, 2], [2, 14], [3, 1], [2, 13],
180
+ [3, 4], [4, 14], [3, 8], [4, 13], [3, 13], [5, 14], [3, 14], [5, 13],
181
+ [2, 7], [1, 4], [2, 11], [1, 8], [2, 4], [4, 7], [2, 8], [4, 11],
182
+ [5, 7], [4, 4], [5, 11], [4, 8], [5, 4], [1, 7], [5, 8], [1, 11]
183
+ ];
184
+ const WING_INDEX_BY_STICKER = (() => {
185
+ const map = new Map();
186
+ for (let position = 0; position < 24; position += 1) {
187
+ for (let k = 0; k < 2; k += 1) {
188
+ const [face, sticker] = WING_STICKER_POSITIONS[position * 2 + k];
189
+ map.set(`${face},${Math.floor(sticker / 4)},${sticker % 4}`, position);
190
+ }
191
+ }
192
+ return map;
193
+ })();
194
+ function wingStickers() {
195
+ const stickers = WING_STICKER_POSITIONS.map(([face, sticker]) => stickerCoord(face, Math.floor(sticker / 4), sticker % 4));
196
+ const result = [];
197
+ for (let position = 0; position < 24; position += 1) {
198
+ result.push([stickers[position * 2], stickers[position * 2 + 1]]);
199
+ }
200
+ return result;
201
+ }
202
+ function wingIndexAtSticker(c) {
203
+ const face = faceAtCoord(c);
204
+ const [row, col] = rowColAtCoord(face, c);
205
+ const index = WING_INDEX_BY_STICKER.get(`${face},${row},${col}`);
206
+ if (index === undefined) {
207
+ throw new Error(`Not a wing sticker position: face=${face} row=${row} col=${col}`);
208
+ }
209
+ return index;
210
+ }
211
+ // ---------------------------------------------------------------------------
212
+ // Move helpers
213
+ // ---------------------------------------------------------------------------
214
+ // Position map of a move effect: piece at position p moves to position
215
+ // inversePerm[p] where newState[i] = oldState[perm[i]].
216
+ function positionMapOf(perm) {
217
+ const map = new Array(perm.length);
218
+ for (let destination = 0; destination < perm.length; destination += 1) {
219
+ map[perm[destination]] = destination;
220
+ }
221
+ return map;
222
+ }
223
+ function permutationParityOf(perm) {
224
+ let parity = 0;
225
+ const seen = new Array(perm.length).fill(false);
226
+ for (let start = 0; start < perm.length; start += 1) {
227
+ if (seen[start] || perm[start] === start)
228
+ continue;
229
+ let length = 0;
230
+ let current = start;
231
+ while (!seen[current]) {
232
+ seen[current] = true;
233
+ current = perm[current];
234
+ length += 1;
235
+ }
236
+ parity ^= (length - 1) & 1;
237
+ }
238
+ return parity;
239
+ }
240
+ function permutationParity(values) {
241
+ let inversions = 0;
242
+ for (let left = 0; left < values.length; left += 1) {
243
+ for (let right = left + 1; right < values.length; right += 1) {
244
+ if (values[left] > values[right]) {
245
+ inversions += 1;
246
+ }
247
+ }
248
+ }
249
+ return (inversions % 2);
250
+ }
251
+ // ---------------------------------------------------------------------------
252
+ // Verification gates
253
+ // ---------------------------------------------------------------------------
254
+ function verifyReductionGeometry() {
255
+ const errors = [];
256
+ if (ROTATIONS.length !== 24) {
257
+ errors.push(`Rotation group has ${ROTATIONS.length} elements, expected 24.`);
258
+ }
259
+ const geometry = buildReductionGeometry();
260
+ // Rotations must be bijections.
261
+ for (let r = 0; r < geometry.centerRotations.length; r += 1) {
262
+ const perm = geometry.centerRotations[r];
263
+ if (new Set(perm).size !== 24) {
264
+ errors.push(`Center rotation ${r} is not a bijection.`);
265
+ break;
266
+ }
267
+ const wperm = geometry.wingRotations[r];
268
+ if (new Set(wperm).size !== 24) {
269
+ errors.push(`Wing rotation ${r} is not a bijection.`);
270
+ break;
271
+ }
272
+ }
273
+ // Phase 2 moves must keep U/D colors on U/D center positions.
274
+ const udPositions = new Set([0, 1, 2, 3, 12, 13, 14, 15]);
275
+ for (const move of exports.PHASE_2_MOVES) {
276
+ const map = positionMapOf(moves_js_1.FACE_MOVE_EFFECTS[(0, moves_js_1.parseMove)(move).face].ctperm);
277
+ for (const position of udPositions) {
278
+ if (!udPositions.has(map[position])) {
279
+ errors.push(`Phase 2 move ${move} moves a U/D center position out of U/D faces.`);
280
+ break;
281
+ }
282
+ }
283
+ }
284
+ // Wing permutation parity flips exactly on the wide quarter turns (the
285
+ // bit the phase-2 search absorbs). Parity of the n-th power is
286
+ // base-parity × (turns mod 2).
287
+ for (const move of exports.PHASE_3_MOVES) {
288
+ const { face, turns } = (0, moves_js_1.parseMove)(move);
289
+ const effect = moves_js_1.FACE_MOVE_EFFECTS[face];
290
+ const wingParity = (permutationParityOf(effect.wperm) * (turns & 1)) & 1;
291
+ const expectedWing = move.includes("w") && !move.endsWith("2") ? 1 : 0;
292
+ if (wingParity !== expectedWing) {
293
+ errors.push(`Phase 3 move ${move} has unexpected wing permutation parity.`);
294
+ }
295
+ }
296
+ // Phase 2 moves keep U/D colors on U/D faces; wing-parity flips only on
297
+ // the Uw/Dw quarter turns (the tracked bit).
298
+ for (const move of exports.PHASE_2_MOVES) {
299
+ const { face, turns } = (0, moves_js_1.parseMove)(move);
300
+ const effect = moves_js_1.FACE_MOVE_EFFECTS[face];
301
+ const wingParity = (permutationParityOf(effect.wperm) * (turns & 1)) & 1;
302
+ const expectedWing = move.includes("w") && !move.endsWith("2") ? 1 : 0;
303
+ if (wingParity !== expectedWing) {
304
+ errors.push(`Phase 2 move ${move} has unexpected wing permutation parity.`);
305
+ }
306
+ }
307
+ return errors;
308
+ }
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * Node loader for the cube4 reduction tables (the committed .bin files).
4
+ * The manifests are per-phase (`center1-manifest.json`, ...).
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.loadCube4ReductionTables = loadCube4ReductionTables;
11
+ const node_fs_1 = require("node:fs");
12
+ const node_module_1 = require("node:module");
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ const node_crypto_1 = require("node:crypto");
15
+ const edge3_js_1 = require("./edge3.js");
16
+ const GENERATED_DIRECTORY = "puzzles/cube4/reduction/generated";
17
+ function resolveGeneratedAssetDirectory() {
18
+ const envDirectory = process.env.CUBINGCENTER_SCRAMBLER_TABLES_DIR;
19
+ if (envDirectory && (0, node_fs_1.existsSync)(node_path_1.default.join(envDirectory, "center1-manifest.json"))) {
20
+ return envDirectory;
21
+ }
22
+ for (const candidate of getAssetDirectoryCandidates()) {
23
+ if ((0, node_fs_1.existsSync)(node_path_1.default.join(candidate, "center1-manifest.json"))) {
24
+ return candidate;
25
+ }
26
+ }
27
+ return node_path_1.default.join(process.cwd(), "src", GENERATED_DIRECTORY);
28
+ }
29
+ function getAssetDirectoryCandidates() {
30
+ const cwd = process.cwd();
31
+ const candidates = [
32
+ node_path_1.default.join(cwd, "src", GENERATED_DIRECTORY),
33
+ node_path_1.default.join(cwd, "dist", GENERATED_DIRECTORY),
34
+ node_path_1.default.join(cwd, "dist-cjs", GENERATED_DIRECTORY)
35
+ ];
36
+ try {
37
+ const requireFromCwd = (0, node_module_1.createRequire)(node_path_1.default.join(cwd, "package.json"));
38
+ const packageEntry = requireFromCwd.resolve("@cubingcenter/scrambler");
39
+ candidates.push(node_path_1.default.join(node_path_1.default.dirname(packageEntry), GENERATED_DIRECTORY));
40
+ }
41
+ catch {
42
+ // Source-tree usage is covered by the cwd candidates.
43
+ }
44
+ return candidates;
45
+ }
46
+ function sha256(buffer) {
47
+ return (0, node_crypto_1.createHash)("sha256").update(buffer).digest("hex");
48
+ }
49
+ function readChecked(file, expectedChecksum) {
50
+ const buffer = (0, node_fs_1.readFileSync)(file);
51
+ if (sha256(buffer) !== expectedChecksum) {
52
+ throw new Error(`Table checksum mismatch: ${file}`);
53
+ }
54
+ return buffer;
55
+ }
56
+ function readInt32(buffer) {
57
+ return new Int32Array(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));
58
+ }
59
+ function loadCenter1(assetDirectory) {
60
+ const manifest = JSON.parse((0, node_fs_1.readFileSync)(node_path_1.default.join(assetDirectory, "center1-manifest.json"), "utf8"));
61
+ return {
62
+ distance: new Uint8Array(readChecked(node_path_1.default.join(assetDirectory, manifest.distanceTable.file), manifest.distanceTable.checksum)),
63
+ moveMaps: readInt32(readChecked(node_path_1.default.join(assetDirectory, manifest.moveMaps.file), manifest.moveMaps.checksum))
64
+ };
65
+ }
66
+ function loadCenter2(assetDirectory) {
67
+ const manifest = JSON.parse((0, node_fs_1.readFileSync)(node_path_1.default.join(assetDirectory, "center2-manifest.json"), "utf8"));
68
+ const pruningBuffer = readChecked(node_path_1.default.join(assetDirectory, manifest.pruningTable.file), manifest.pruningTable.checksum);
69
+ const pruning = new Uint8Array(450450);
70
+ for (let i = 0; i < 450450; i++) {
71
+ const byte = pruningBuffer[i >> 1];
72
+ pruning[i] = (i & 1) === 0 ? byte & 0x0f : byte >> 4;
73
+ }
74
+ return {
75
+ ctMove: readInt32(readChecked(node_path_1.default.join(assetDirectory, manifest.ctMove.file), manifest.ctMove.checksum)),
76
+ rlMove: readInt32(readChecked(node_path_1.default.join(assetDirectory, manifest.rlMove.file), manifest.rlMove.checksum)),
77
+ pruning
78
+ };
79
+ }
80
+ function loadCenter3(assetDirectory) {
81
+ const manifest = JSON.parse((0, node_fs_1.readFileSync)(node_path_1.default.join(assetDirectory, "center3-manifest.json"), "utf8"));
82
+ return {
83
+ moveTable: readInt32(readChecked(node_path_1.default.join(assetDirectory, manifest.moveTable.file), manifest.moveTable.checksum)),
84
+ pruning: new Uint8Array(readChecked(node_path_1.default.join(assetDirectory, manifest.pruningTable.file), manifest.pruningTable.checksum))
85
+ };
86
+ }
87
+ function loadEdge3(assetDirectory) {
88
+ const manifest = JSON.parse((0, node_fs_1.readFileSync)(node_path_1.default.join(assetDirectory, "edge3-manifest.json"), "utf8"));
89
+ const distances = new Uint8Array(readChecked(node_path_1.default.join(assetDirectory, manifest.distances.file), manifest.distances.checksum));
90
+ return { distances, distanceOfEdge: (0, edge3_js_1.edge3DistanceLookup)(distances) };
91
+ }
92
+ /** Load all cube4 reduction tables (throws if the generated assets are missing). */
93
+ function loadCube4ReductionTables() {
94
+ const assetDirectory = resolveGeneratedAssetDirectory();
95
+ if (!(0, node_fs_1.existsSync)(node_path_1.default.join(assetDirectory, "center1-manifest.json"))) {
96
+ throw new Error("Cube4 reduction tables not generated — run pnpm cube4:center1:tables etc.");
97
+ }
98
+ return {
99
+ center1: loadCenter1(assetDirectory),
100
+ center2: loadCenter2(assetDirectory),
101
+ center3: loadCenter3(assetDirectory),
102
+ edge3: loadEdge3(assetDirectory)
103
+ };
104
+ }