@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
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Reusable scramble generation toolkit for CubingCenter projects.
4
4
 
5
- The package currently ships random-state scramblers for `222` (2x2x2), `333` (3x3x3), and a first native reduced-state `444` (4x4x4) path. It does not depend on cstimer, cubing.js, or another external scramble engine at runtime.
5
+ The package currently ships random-state scramblers for `222` (2x2x2), `333` (3x3x3), `444` (4x4x4), `pymx` (Pyraminx) and `skwb` (Skewb). The 4x4 scrambler is a native reduction solver (centers + wing pairing + 3x3 finish), table-backed. It does not depend on cstimer, cubing.js, or another external scramble engine at runtime.
6
6
 
7
7
  ## Status
8
8
 
@@ -16,7 +16,7 @@ The package currently ships random-state scramblers for `222` (2x2x2), `333` (3x
16
16
  - 3x3 quality: random-state target + two-phase solver + verified scramble inversion
17
17
  - Pyraminx quality: random-state target + optimal policy-table solver (all 933,120 core states verified)
18
18
  - Skewb quality: random-state target + optimal policy-table solver (all 3,149,280 states; exact uniform sampling)
19
- - 4x4 quality: reduced 4x4 random-state target + existing 3x3 two-phase finish; full center/wing random-state reduction is isolated under `cube4.reduction`
19
+ - 4x4 quality: random-state target (uniform sampler) + reduction solver (4 phases) + verified scramble inversion
20
20
 
21
21
  This library is intended for practice, training, and internal CubingCenter use. Do not present it as an official WCA competition scrambler; sanctioned competitions must use the official program required by the delegate/organization.
22
22
 
@@ -45,16 +45,13 @@ import {
45
45
  const events = getSupportedEvents();
46
46
  const info222 = getEventInfo("222");
47
47
  const info333 = getEventInfo("333");
48
- const info444 = getEventInfo("444");
49
48
  const scramble2 = generateScramble("222", { seed: "demo" });
50
49
  const scramble3 = generateScramble("333", { seed: "demo" });
51
- const scramble4 = await generateScramble("444");
52
50
  const batch = generateScrambles("333", 5, { seed: "session-1" });
53
51
 
54
52
  console.log(events);
55
53
  console.log(scramble2.scramble);
56
54
  console.log(scramble3.scramble);
57
- console.log(scramble4.scramble);
58
55
  console.log(batch.map((item) => item.scramble));
59
56
  ```
60
57
 
@@ -72,7 +69,7 @@ puzzle you need — each subpath registers its own generator, so
72
69
 
73
70
  ```ts
74
71
  import { generateScramble, initializeScrambler } from "@cubingcenter/scrambler/puzzles/pyraminx";
75
- // "222" | "333" | "444" | "555" | "666" | "777" | "pymx" | "skwb" | "clck" | "mgmx"
72
+ // "222" | "333" | "555" | "666" | "777" | "pymx" | "skwb" | "clck" | "mgmx"
76
73
  await initializeScrambler("/scrambler-tables", "pymx");
77
74
  const result = await generateScramble("pymx", { seed: "demo" });
78
75
  ```
@@ -84,6 +81,25 @@ The demo repo ships precompressed `.br`/`.gz` variants of the tables (see
84
81
  `DecompressionStream` is available (Pyraminx ~470 KB → ~345 KB, cube3
85
82
  ~6.6 MB → ~2.2 MB).
86
83
 
84
+ ## 3x3 two-tier tables
85
+
86
+ The 3x3 solver ships in two tiers:
87
+
88
+ - **Default**: the standard tables (~7 MB) plus the phase-1 joint tables
89
+ (`phase1-udco.bin` / `phase1-udeo.bin`, ~1 MB), which tighten the phase-1
90
+ bound from `max(coeo, ud)` to `max(coeo, ud, udco, udeo)`. They are part
91
+ of the standard manifest and load automatically.
92
+ - **Turbo (optional)**: additionally, the combined (cp x uep) phase-2
93
+ pruning table (`generated/pair/`, ~55 MB, generated by the C++ tool
94
+ `tools/exp3-gen` in the companion `cubingcenter-scrambler-native` repo).
95
+ When present, the loaders pick it up automatically and phase-2 drops from
96
+ ~34k to ~40 nodes per solve. It is excluded from the npm package — drop
97
+ the files into `puzzles/cube3/solver/generated/pair/` (or serve them from
98
+ your tables base URL) to enable it.
99
+
100
+ Measured (i5-13420H, warm): default ~14-17 ms/scramble, turbo ~1.2 ms
101
+ (see the companion native repo for the full experiment series).
102
+
87
103
  ## 2x2 Technical Module
88
104
 
89
105
  ```ts
@@ -178,9 +194,9 @@ const paired = cube4.areWingsPaired(state);
178
194
 
179
195
  Supported moves: `U D R L F B` (outer) and `Uw Dw Rw Lw Fw Bw` (wide), each with `'` and `2` suffixes.
180
196
 
181
- The public `generateScramble("444")` path currently returns a reduced 4x4 random-state scramble: centers are solved, wings are paired, and the projected 3x3 state is random and solved with the existing two-phase solver. The full arbitrary center/wing random-state pipeline is being developed under `cube4.reduction`.
197
+ The public `generateScramble("444")` path is: sample a uniform random state (`generateRandomCube4State` corners `8! × 3^7`, wings `24!`, centers as a color multiset `24!/(4!)^6`), solve it with the reduction solver (phases: axis-color face-pairs → ct/rl phase-2 goal → joint center3+pairing → 3x3 finish), and invert the solution — so the scramble always reproduces the sampled target. The pairing pruning table is generated by a C++ tool (`tools/edge3-gen/`).
182
198
 
183
- The `cube4.reduction` namespace is exported for advanced tooling. Normal consumers should prefer `generateScramble("444")`.
199
+ `validateCube4State` checks the reachability constraints of a normal (non-super) 4x4: corner permutation + orientation sum divisible by 3, wing permutation (wings have no orientation), and 4-of-each-color centers. There is no visible corner/wing parity constraint on a normal 4x4 (all four parity combinations are reachable).
184
200
 
185
201
  ## CLI
186
202
 
@@ -193,7 +209,6 @@ cubingcenter-scrambler 222 --seed demo --json
193
209
  cubingcenter-scrambler 333 --count 5
194
210
  cubingcenter-scrambler 333 --seed demo
195
211
  cubingcenter-scrambler 333 --seed demo --json
196
- cubingcenter-scrambler 444 --count 3
197
212
  ```
198
213
 
199
214
  `list` prints registered events. `--json` prints structured output with `scramble`, `moves`, `facelets`, and `faceletString`.
package/dist/cli/index.js CHANGED
File without changes
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BrG;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,iBAAmC,GAC5C,IAAI,CAEN;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,GAAE,iBAAmC,GAAG,SAAS,EAAE,CAE7F;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,EAChB,QAAQ,GAAE,iBAAmC,GAC5C,SAAS,CAQX;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,eAAoB,EAC7B,QAAQ,GAAE,iBAAmC,GAC5C,OAAO,CAAC,cAAc,CAAC,CAiBzB;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,eAAoB,EAC7B,QAAQ,GAAE,iBAAmC,GAC5C,OAAO,CAAC,cAAc,EAAE,CAAC,CAU3B"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCrG;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,iBAAmC,GAC5C,IAAI,CAEN;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,GAAE,iBAAmC,GAAG,SAAS,EAAE,CAE7F;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,EAChB,QAAQ,GAAE,iBAAmC,GAC5C,SAAS,CAQX;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,eAAoB,EAC7B,QAAQ,GAAE,iBAAmC,GAC5C,OAAO,CAAC,cAAc,CAAC,CAiBzB;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,eAAoB,EAC7B,QAAQ,GAAE,iBAAmC,GAC5C,OAAO,CAAC,cAAc,EAAE,CAAC,CAU3B"}
package/dist/core/api.js CHANGED
@@ -34,12 +34,22 @@ export async function initializeScrambler(baseUrl, eventId) {
34
34
  const { loadBrowserTables } = await import("../puzzles/skewb/solver/browser-loader.js");
35
35
  await loadBrowserTables(baseUrl);
36
36
  }
37
+ else if (eventId === "sq1") {
38
+ const { loadBrowserTables } = await import("../puzzles/sq1/solver/browser-loader.js");
39
+ await loadBrowserTables(baseUrl);
40
+ }
41
+ else if (eventId === "444") {
42
+ const { loadCube4ReductionTablesBrowser } = await import("../puzzles/cube4/reduction/browser-loader.js");
43
+ await loadCube4ReductionTablesBrowser(baseUrl);
44
+ }
37
45
  else if (!eventId) {
38
46
  await Promise.all([
39
47
  import("../puzzles/cube2/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl)),
40
48
  import("../puzzles/cube3/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl)),
41
49
  import("../puzzles/pyraminx/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl)),
42
- import("../puzzles/skewb/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl))
50
+ import("../puzzles/skewb/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl)),
51
+ import("../puzzles/sq1/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl)),
52
+ import("../puzzles/cube4/reduction/browser-loader.js").then((m) => m.loadCube4ReductionTablesBrowser(baseUrl))
43
53
  ]);
44
54
  }
45
55
  }
@@ -1,5 +1,5 @@
1
1
  export type EventId = string;
2
- export type SupportedEventId = "222" | "333" | "444" | "555" | "666" | "777" | "pymx" | "skwb" | "clck" | "mgmx";
2
+ export type SupportedEventId = "222" | "333" | "444" | "555" | "666" | "777" | "pymx" | "skwb" | "clck" | "mgmx" | "fto" | "sq1";
3
3
  export type ScrambleQuality = "random-state" | "random-move" | "training" | "placeholder";
4
4
  export interface ScrambleOptions {
5
5
  seed?: string | number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAG7B,MAAM,MAAM,gBAAgB,GACxB,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,CAAC;AAEX,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,aAAa,GACb,UAAU,GACV,aAAa,CAAC;AAElB,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC7C,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS,CAAC;IACrD,IAAI,IAAI,SAAS,EAAE,CAAC;IACpB,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;CAChC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAG7B,MAAM,MAAM,gBAAgB,GACxB,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,GACN,KAAK,GACL,KAAK,CAAC;AAEV,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,aAAa,GACb,UAAU,GACV,aAAa,CAAC;AAElB,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC7C,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS,CAAC;IACrD,IAAI,IAAI,SAAS,EAAE,CAAC;IACpB,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;CAChC"}
@@ -1,8 +1,10 @@
1
1
  export { CORNER_COUNT, WING_COUNT, CENTER_COUNT, FACE_COUNT, CENTERS_PER_FACE, U, R, F, D, L, B, CUBE4_FACE_MOVES, CUBE4_ALL_MOVES, CUBE4_OUTER_MOVES } from "./types.js";
2
- export type { Cube4Face, Cube4FaceMove, Cube4Move, Cube4MoveSuffix, Cube4State, Cube4Sticker, Cube4Facelets, Cube4RandomStateOptions } from "./types.js";
2
+ export type { Cube4Face, Cube4FaceMove, Cube4Move, Cube4MoveSuffix, Cube4State, Cube4Sticker, Cube4Facelets, Cube4ValidationResult, Cube4RandomStateOptions } from "./types.js";
3
3
  export { createSolvedCube4State, cloneCube4State, cube4StatesEqual, isSolvedCube4State, areCentersReduced, areWingsPaired } from "./state.js";
4
- export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
5
4
  export { cube4ScrambleGenerator, generateCube4Scramble } from "./scrambler.js";
5
+ export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
6
+ export { generateRandomCube4State } from "./random-state.js";
7
+ export { validateCube4State, isOneMoveFromSolvedCube4State, isTrivialCube4State, isAllowedRandomCube4State } from "./validation.js";
6
8
  export { toCube4Facelets, formatCube4Facelets, getCube4FaceOrder } from "./facelets.js";
7
9
  export { generateScramble, generateScrambles, getEventInfo, getSupportedEvents, initializeScrambler } from "../../core/index.js";
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
@@ -6,7 +6,9 @@ import { cube4ScrambleGenerator } from "./scrambler.js";
6
6
  registerScrambleGenerator(cube4ScrambleGenerator);
7
7
  export { CORNER_COUNT, WING_COUNT, CENTER_COUNT, FACE_COUNT, CENTERS_PER_FACE, U, R, F, D, L, B, CUBE4_FACE_MOVES, CUBE4_ALL_MOVES, CUBE4_OUTER_MOVES } from "./types.js";
8
8
  export { createSolvedCube4State, cloneCube4State, cube4StatesEqual, isSolvedCube4State, areCentersReduced, areWingsPaired } from "./state.js";
9
- export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
10
9
  export { cube4ScrambleGenerator, generateCube4Scramble } from "./scrambler.js";
10
+ export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
11
+ export { generateRandomCube4State } from "./random-state.js";
12
+ export { validateCube4State, isOneMoveFromSolvedCube4State, isTrivialCube4State, isAllowedRandomCube4State } from "./validation.js";
11
13
  export { toCube4Facelets, formatCube4Facelets, getCube4FaceOrder } from "./facelets.js";
12
14
  export { generateScramble, generateScrambles, getEventInfo, getSupportedEvents, initializeScrambler } from "../../core/index.js";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Uniform random-state generation for 4x4x4.
3
+ *
4
+ * Samples uniformly from the reachable states of the normal (non-super) 4x4:
5
+ * - corner permutation 8! + orientation with sum ≡ 0 (mod 3)
6
+ * - wing permutation 24! (wings have no orientation)
7
+ * - centers: 24!/(4!)^6 (any arrangement with 4 centers of each color)
8
+ *
9
+ * On a normal 4x4 the corner and wing permutation parities are independent
10
+ * (a single face turn changes corner parity while leaving wing parity even),
11
+ * so no cross-parity constraint is applied here — the reduction solver must
12
+ * be able to reduce states with all four parity combinations.
13
+ */
14
+ import type { Cube4RandomStateOptions, Cube4State } from "./types.js";
15
+ export declare function generateRandomCube4State(options?: Cube4RandomStateOptions): Cube4State;
16
+ //# sourceMappingURL=random-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/random-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGtE,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAe1F"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Uniform random-state generation for 4x4x4.
3
+ *
4
+ * Samples uniformly from the reachable states of the normal (non-super) 4x4:
5
+ * - corner permutation 8! + orientation with sum ≡ 0 (mod 3)
6
+ * - wing permutation 24! (wings have no orientation)
7
+ * - centers: 24!/(4!)^6 (any arrangement with 4 centers of each color)
8
+ *
9
+ * On a normal 4x4 the corner and wing permutation parities are independent
10
+ * (a single face turn changes corner parity while leaving wing parity even),
11
+ * so no cross-parity constraint is applied here — the reduction solver must
12
+ * be able to reduce states with all four parity combinations.
13
+ */
14
+ import { createRandomSource } from "../../random/index.js";
15
+ import { CENTER_COUNT, CENTERS_PER_FACE, CORNER_COUNT, FACE_COUNT, WING_COUNT } from "./types.js";
16
+ import { isTrivialCube4State } from "./validation.js";
17
+ export function generateRandomCube4State(options = {}) {
18
+ const random = createRandomSource(options.seed);
19
+ const rejectTrivial = options.rejectTrivial ?? true;
20
+ for (let attempt = 0; attempt < 100; attempt += 1) {
21
+ // The state is legal by construction (permutations shuffled, corner
22
+ // orientation sum fixed, 4 centers per color), so the only meaningful
23
+ // rejection is the trivial set (solved + the 36 single-move states).
24
+ const state = createLegalRandomState(random);
25
+ if (!rejectTrivial || !isTrivialCube4State(state)) {
26
+ return state;
27
+ }
28
+ }
29
+ throw new Error("Unable to generate a non-trivial random 4x4 state.");
30
+ }
31
+ function createLegalRandomState(random) {
32
+ const cp = shuffledRange(CORNER_COUNT, random);
33
+ const wp = shuffledRange(WING_COUNT, random);
34
+ return {
35
+ cp,
36
+ co: randomOrientations(CORNER_COUNT, 3, random),
37
+ wp,
38
+ centers: shuffledCenters(random)
39
+ };
40
+ }
41
+ function shuffledRange(length, random) {
42
+ const values = Uint8Array.from({ length }, (_, index) => index);
43
+ for (let index = values.length - 1; index > 0; index -= 1) {
44
+ const swapIndex = random.nextInt(index + 1);
45
+ const temporary = values[index];
46
+ values[index] = values[swapIndex];
47
+ values[swapIndex] = temporary;
48
+ }
49
+ return values;
50
+ }
51
+ function randomOrientations(length, modulo, random) {
52
+ const orientations = new Uint8Array(length);
53
+ let total = 0;
54
+ for (let index = 0; index < length - 1; index += 1) {
55
+ const orientation = random.nextInt(modulo);
56
+ orientations[index] = orientation;
57
+ total += orientation;
58
+ }
59
+ orientations[length - 1] = (modulo - (total % modulo)) % modulo;
60
+ return orientations;
61
+ }
62
+ function shuffledCenters(random) {
63
+ const centers = new Uint8Array(CENTER_COUNT);
64
+ for (let face = 0; face < FACE_COUNT; face += 1) {
65
+ for (let index = 0; index < CENTERS_PER_FACE; index += 1) {
66
+ centers[face * CENTERS_PER_FACE + index] = face;
67
+ }
68
+ }
69
+ for (let index = centers.length - 1; index > 0; index -= 1) {
70
+ const swapIndex = random.nextInt(index + 1);
71
+ const temporary = centers[index];
72
+ centers[index] = centers[swapIndex];
73
+ centers[swapIndex] = temporary;
74
+ }
75
+ return centers;
76
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Browser loader for the cube4 reduction tables (the committed .bin files).
3
+ * The manifests are per-phase (`center1-manifest.json`, ...).
4
+ */
5
+ import type { Center1Tables } from "./center1.js";
6
+ import type { Center2Tables } from "./center2.js";
7
+ import type { Center3Tables } from "./center3.js";
8
+ import type { Edge3Tables } from "./edge3.js";
9
+ export interface Cube4ReductionTables {
10
+ center1: Center1Tables;
11
+ center2: Center2Tables;
12
+ center3: Center3Tables;
13
+ edge3: Edge3Tables;
14
+ }
15
+ export declare function getCachedCube4Tables(): Cube4ReductionTables | undefined;
16
+ export declare function clearCachedCube4Tables(): void;
17
+ export declare function loadCube4ReductionTablesBrowser(baseUrl?: string): Promise<Cube4ReductionTables>;
18
+ //# sourceMappingURL=browser-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/browser-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;CACpB;AAOD,wBAAgB,oBAAoB,IAAI,oBAAoB,GAAG,SAAS,CAEvE;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAgBD,wBAAsB,+BAA+B,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAarG"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Browser loader for the cube4 reduction tables (the committed .bin files).
3
+ * The manifests are per-phase (`center1-manifest.json`, ...).
4
+ */
5
+ import { edge3DistanceLookup } from "./edge3.js";
6
+ import { fetchTableBuffer } from "../../shared/compressed-fetch.js";
7
+ const GENERATED_DIRECTORY = "puzzles/cube4/reduction/generated";
8
+ const CACHE_NAME = "cubingcenter-scrambler-tables-v1";
9
+ let cachedTables;
10
+ export function getCachedCube4Tables() {
11
+ return cachedTables;
12
+ }
13
+ export function clearCachedCube4Tables() {
14
+ cachedTables = undefined;
15
+ }
16
+ function resolveBaseUrl(provided) {
17
+ if (provided) {
18
+ return provided.replace(/\/$/, "");
19
+ }
20
+ if (typeof document !== "undefined") {
21
+ const script = document.currentScript;
22
+ if (script?.src) {
23
+ const base = new URL(".", script.src).href;
24
+ return base.replace(/\/$/, "");
25
+ }
26
+ }
27
+ return ".";
28
+ }
29
+ export async function loadCube4ReductionTablesBrowser(baseUrl) {
30
+ if (cachedTables) {
31
+ return cachedTables;
32
+ }
33
+ const resolved = resolveBaseUrl(baseUrl);
34
+ const [center1, center2, center3, edge3] = await Promise.all([
35
+ loadCenter1(resolved),
36
+ loadCenter2(resolved),
37
+ loadCenter3(resolved),
38
+ loadEdge3(resolved)
39
+ ]);
40
+ cachedTables = { center1, center2, center3, edge3 };
41
+ return cachedTables;
42
+ }
43
+ async function fetchJson(baseUrl, file) {
44
+ const url = `${baseUrl}/${GENERATED_DIRECTORY}/${file}`;
45
+ const response = await fetch(url);
46
+ if (!response.ok) {
47
+ throw new Error(`Failed to load ${file}: ${response.status} ${response.statusText}`);
48
+ }
49
+ return response.json();
50
+ }
51
+ async function loadCenter1(baseUrl) {
52
+ const manifest = await fetchJson(baseUrl, "center1-manifest.json");
53
+ const [distanceBuffer, moveMapsBuffer] = await Promise.all([
54
+ fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.distanceTable.file, { cacheName: CACHE_NAME, compressed: manifest.distanceTable.compressed !== false }),
55
+ fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.moveMaps.file, { cacheName: CACHE_NAME, compressed: manifest.moveMaps.compressed !== false })
56
+ ]);
57
+ return {
58
+ distance: new Uint8Array(distanceBuffer),
59
+ moveMaps: new Int32Array(moveMapsBuffer)
60
+ };
61
+ }
62
+ async function loadCenter2(baseUrl) {
63
+ const manifest = await fetchJson(baseUrl, "center2-manifest.json");
64
+ const [ctMoveBuffer, rlMoveBuffer, pruningBuffer] = await Promise.all([
65
+ fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.ctMove.file, { cacheName: CACHE_NAME, compressed: manifest.ctMove.compressed !== false }),
66
+ fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.rlMove.file, { cacheName: CACHE_NAME, compressed: manifest.rlMove.compressed !== false }),
67
+ fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.pruningTable.file, { cacheName: CACHE_NAME, compressed: manifest.pruningTable.compressed !== false })
68
+ ]);
69
+ const ctMove = new Int32Array(ctMoveBuffer);
70
+ const rlMove = new Int32Array(rlMoveBuffer);
71
+ const pruningPacked = new Uint8Array(pruningBuffer);
72
+ const pruning = new Uint8Array(450450);
73
+ for (let i = 0; i < 450450; i++) {
74
+ const byte = pruningPacked[i >> 1];
75
+ pruning[i] = (i & 1) === 0 ? byte & 0x0f : byte >> 4;
76
+ }
77
+ return { ctMove, rlMove, pruning };
78
+ }
79
+ async function loadCenter3(baseUrl) {
80
+ const manifest = await fetchJson(baseUrl, "center3-manifest.json");
81
+ const [moveTableBuffer, pruningBuffer] = await Promise.all([
82
+ fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.moveTable.file, { cacheName: CACHE_NAME, compressed: manifest.moveTable.compressed !== false }),
83
+ fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.pruningTable.file, { cacheName: CACHE_NAME, compressed: manifest.pruningTable.compressed !== false })
84
+ ]);
85
+ return {
86
+ moveTable: new Int32Array(moveTableBuffer),
87
+ pruning: new Uint8Array(pruningBuffer)
88
+ };
89
+ }
90
+ async function loadEdge3(baseUrl) {
91
+ const manifest = await fetchJson(baseUrl, "edge3-manifest.json");
92
+ const distancesBuffer = await fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.distances.file, { cacheName: CACHE_NAME, compressed: manifest.distances.compressed !== false });
93
+ const distances = new Uint8Array(distancesBuffer);
94
+ return { distances, distanceOfEdge: edge3DistanceLookup(distances) };
95
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Center1 (phase 1 of the 4x4 reduction solver): reduce the two colors of a
3
+ * chosen axis (U/D, R/L or F/B) onto a single face-pair.
4
+ *
5
+ * Design (simpler than the reference's symmetry-reduced tables, which use a
6
+ * fragile 48-element non-group of "block-turn symmetries"):
7
+ * - coordinate: the raw C(24,8) = 735,471 mask of the positions holding the
8
+ * two axis colors — NO symmetry reduction;
9
+ * - pruning: the exact BFS distance table from the three face-pair goal
10
+ * masks (God's number 9; the 27-move set reaches the whole space);
11
+ * - search: greedy descent — any move that decreases the exact distance by
12
+ * exactly 1 is on an optimal path, so repeatedly applying the first such
13
+ * move reaches the goal in exactly `distance` moves, with zero
14
+ * backtracking and no iterative deepening.
15
+ *
16
+ * One table serves all three axes (the face-pairs are equivalent goals).
17
+ * The phase-1 move set: 9 axes [U,R,F,D,L,B,Uw,Rw,Fw] × 3 powers = 27.
18
+ */
19
+ import type { Cube4Move } from "../types.js";
20
+ export declare const CENTER1_AXES: readonly Cube4Move[];
21
+ export declare const CENTER1_MOVES: readonly Cube4Move[];
22
+ export declare const CENTER1_RAW_STATES = 735471;
23
+ /** The mask of the positions holding the two axis colors (colors % 3 == urf). */
24
+ export declare function center1Mask(centers: Uint8Array, urf: 0 | 1 | 2): number;
25
+ /** C(24,8) combination rank of a mask. */
26
+ export declare function rankCenter1Mask(mask: number): number;
27
+ export declare function unrankCenter1Mask(idx: number): number;
28
+ /** The three face-pair goal masks (bit i = center position i, face-major). */
29
+ export declare const CENTER1_GOAL_MASKS: {
30
+ readonly ud: 61455;
31
+ readonly rl: 983280;
32
+ readonly fb: 15732480;
33
+ };
34
+ /** The 27 phase-1 moves as bit-permutations of the 24-bit mask. */
35
+ export declare function buildCenter1MoveMaps(): Int32Array;
36
+ export declare function applyCenter1Move(mask: number, moveMap: Int32Array, move: number): number;
37
+ export interface Center1Tables {
38
+ /** Exact BFS distance per rank (nibble-packed, 4 bits per state). */
39
+ distance: Uint8Array;
40
+ /** The 27 move bit-permutations (24 entries per move). */
41
+ moveMaps: Int32Array;
42
+ }
43
+ export declare function generateCenter1Tables(): Center1Tables;
44
+ export interface Center1Phase1Result {
45
+ /** Phase-1 solution moves (27-move set). */
46
+ moves: Cube4Move[];
47
+ /** Total move count. */
48
+ length: number;
49
+ }
50
+ export declare function getCenter1Distance(mask: number, tables: Center1Tables): number;
51
+ /**
52
+ * Phase-1 greedy descent: bring the axis colors onto some face-pair in
53
+ * exactly `distance` moves (optimal). Every move that decreases the exact
54
+ * distance by 1 is on an optimal path, so no backtracking is needed.
55
+ * Returns null if the state is unreachable (should not happen).
56
+ */
57
+ export declare function solveCenter1(centers: Uint8Array, urf: 0 | 1 | 2, tables: Center1Tables): Center1Phase1Result | null;
58
+ /**
59
+ * Enumerate phase-1 solutions (the optimal descending paths, then longer
60
+ * alternatives up to `cap`). The phase-1 → phase-3 pipeline needs variety:
61
+ * the phase-2 gate rate depends strongly on the phase-1 output's wings.
62
+ */
63
+ export declare function solveCenter1All(centers: Uint8Array, urf: 0 | 1 | 2, tables: Center1Tables, cap?: number): Center1Phase1Result[];
64
+ export declare function verifyCenter1(tables: Center1Tables): string[];
65
+ //# sourceMappingURL=center1.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"center1.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/center1.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAO7C,eAAO,MAAM,YAAY,EAAE,SAAS,SAAS,EAE5C,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,SAAS,SAAS,EAE7C,CAAC;AAEF,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAoBzC,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAMvE;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOpD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWrD;AAED,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC;AAMX,mEAAmE;AACnE,wBAAgB,oBAAoB,IAAI,UAAU,CAejD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMxF;AAMD,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,QAAQ,EAAE,UAAU,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED,wBAAgB,qBAAqB,IAAI,aAAa,CAmDrD;AAMD,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,GACpB,MAAM,CAIR;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EACd,MAAM,EAAE,aAAa,GACpB,mBAAmB,GAAG,IAAI,CAG5B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EACd,MAAM,EAAE,aAAa,EACrB,GAAG,SAAK,GACP,mBAAmB,EAAE,CAyBvB;AAMD,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CA8B7D"}