@wodzik/cubecore 0.1.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 (279) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +118 -0
  3. package/analyze/client.d.ts +18 -0
  4. package/analyze/client.js +42 -0
  5. package/analyze/index.d.ts +114 -0
  6. package/analyze/index.js +151 -0
  7. package/analyze/rotations.d.ts +3 -0
  8. package/analyze/rotations.js +15 -0
  9. package/analyze/roux.d.ts +67 -0
  10. package/analyze/roux.js +95 -0
  11. package/analyze/worker.d.ts +6 -0
  12. package/analyze/worker.js +31 -0
  13. package/analyze/zz.d.ts +47 -0
  14. package/analyze/zz.js +102 -0
  15. package/bld/index.d.ts +14 -0
  16. package/bld/index.js +14 -0
  17. package/bld/memo.d.ts +66 -0
  18. package/bld/memo.js +139 -0
  19. package/bld/scheme.d.ts +30 -0
  20. package/bld/scheme.js +35 -0
  21. package/bld/skin.d.ts +26 -0
  22. package/bld/skin.js +46 -0
  23. package/bld/tracker.d.ts +68 -0
  24. package/bld/tracker.js +145 -0
  25. package/bluetooth/clock.d.ts +22 -0
  26. package/bluetooth/clock.js +37 -0
  27. package/bluetooth/cubeSkins.d.ts +31 -0
  28. package/bluetooth/cubeSkins.js +32 -0
  29. package/bluetooth/gyro.d.ts +31 -0
  30. package/bluetooth/gyro.js +46 -0
  31. package/bluetooth/index.d.ts +5 -0
  32. package/bluetooth/index.js +5 -0
  33. package/bluetooth/session.d.ts +140 -0
  34. package/bluetooth/session.js +201 -0
  35. package/bluetooth/simulated.d.ts +52 -0
  36. package/bluetooth/simulated.js +80 -0
  37. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.d.ts +38 -0
  38. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.js +51 -0
  39. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.d.ts +35 -0
  40. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.js +70 -0
  41. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.d.ts +273 -0
  42. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.js +846 -0
  43. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.d.ts +38 -0
  44. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.js +340 -0
  45. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.d.ts +11 -0
  46. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.js +118 -0
  47. package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.d.ts +2 -0
  48. package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.js +15 -0
  49. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.d.ts +18 -0
  50. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.js +152 -0
  51. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.d.ts +74 -0
  52. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.js +161 -0
  53. package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.d.ts +7 -0
  54. package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.js +11 -0
  55. package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.d.ts +2 -0
  56. package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.js +36 -0
  57. package/bluetooth/vendor/smartcube-web-bluetooth/index.d.ts +4 -0
  58. package/bluetooth/vendor/smartcube-web-bluetooth/index.js +4 -0
  59. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.d.ts +12 -0
  60. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.js +122 -0
  61. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.d.ts +11 -0
  62. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.js +55 -0
  63. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.d.ts +21 -0
  64. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.js +47 -0
  65. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.d.ts +6 -0
  66. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.js +24 -0
  67. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.d.ts +6 -0
  68. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.js +57 -0
  69. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.d.ts +11 -0
  70. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.js +67 -0
  71. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.d.ts +8 -0
  72. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.js +98 -0
  73. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.d.ts +8 -0
  74. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.js +128 -0
  75. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.d.ts +5 -0
  76. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.js +60 -0
  77. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.d.ts +2 -0
  78. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.js +8 -0
  79. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.d.ts +7 -0
  80. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.js +142 -0
  81. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.d.ts +6 -0
  82. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.js +21 -0
  83. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.d.ts +32 -0
  84. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.js +1 -0
  85. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.d.ts +6 -0
  86. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.js +68 -0
  87. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.d.ts +3 -0
  88. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.js +178 -0
  89. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.d.ts +16 -0
  90. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.js +141 -0
  91. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.d.ts +11 -0
  92. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.js +10 -0
  93. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.d.ts +26 -0
  94. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.js +8 -0
  95. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.d.ts +3 -0
  96. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.js +265 -0
  97. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.d.ts +3 -0
  98. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.js +345 -0
  99. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.d.ts +15 -0
  100. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.js +382 -0
  101. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.d.ts +3 -0
  102. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.js +352 -0
  103. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.d.ts +57 -0
  104. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.js +243 -0
  105. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.d.ts +3 -0
  106. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.js +454 -0
  107. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.d.ts +3 -0
  108. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.js +424 -0
  109. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.d.ts +78 -0
  110. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.js +1 -0
  111. package/bluetooth/vendor/smartcube-web-bluetooth/utils.d.ts +45 -0
  112. package/bluetooth/vendor/smartcube-web-bluetooth/utils.js +150 -0
  113. package/cfop/caseData.d.ts +16 -0
  114. package/cfop/caseData.js +88 -0
  115. package/cfop/cases.d.ts +42 -0
  116. package/cfop/cases.js +116 -0
  117. package/cfop/f2l.d.ts +28 -0
  118. package/cfop/f2l.js +88 -0
  119. package/cfop/f2lData.d.ts +15 -0
  120. package/cfop/f2lData.js +49 -0
  121. package/cfop/index.d.ts +41 -0
  122. package/cfop/index.js +110 -0
  123. package/cfop/slots.d.ts +2 -0
  124. package/cfop/slots.js +1 -0
  125. package/core/arrows.d.ts +24 -0
  126. package/core/arrows.js +22 -0
  127. package/core/bytes.d.ts +27 -0
  128. package/core/bytes.js +145 -0
  129. package/core/centers.d.ts +32 -0
  130. package/core/centers.js +83 -0
  131. package/core/checks.d.ts +53 -0
  132. package/core/checks.js +85 -0
  133. package/core/cubies.d.ts +45 -0
  134. package/core/cubies.js +169 -0
  135. package/core/frames.d.ts +61 -0
  136. package/core/frames.js +126 -0
  137. package/core/geometry.d.ts +58 -0
  138. package/core/geometry.js +132 -0
  139. package/core/index.d.ts +19 -0
  140. package/core/index.js +19 -0
  141. package/core/masks.d.ts +32 -0
  142. package/core/masks.js +39 -0
  143. package/core/method.d.ts +35 -0
  144. package/core/method.js +30 -0
  145. package/core/moves.d.ts +48 -0
  146. package/core/moves.js +88 -0
  147. package/core/notation.d.ts +48 -0
  148. package/core/notation.js +226 -0
  149. package/core/physical.d.ts +53 -0
  150. package/core/physical.js +99 -0
  151. package/core/practice.d.ts +62 -0
  152. package/core/practice.js +87 -0
  153. package/core/sequence.d.ts +90 -0
  154. package/core/sequence.js +157 -0
  155. package/core/stages.d.ts +90 -0
  156. package/core/stages.js +21 -0
  157. package/core/state.d.ts +55 -0
  158. package/core/state.js +140 -0
  159. package/core/stateCodec.d.ts +12 -0
  160. package/core/stateCodec.js +51 -0
  161. package/core/stream.d.ts +45 -0
  162. package/core/stream.js +75 -0
  163. package/core/tracker.d.ts +62 -0
  164. package/core/tracker.js +121 -0
  165. package/element/alg.d.ts +45 -0
  166. package/element/alg.js +150 -0
  167. package/element/base.d.ts +2 -0
  168. package/element/base.js +3 -0
  169. package/element/bld.d.ts +82 -0
  170. package/element/bld.js +222 -0
  171. package/element/index.d.ts +8 -0
  172. package/element/index.js +21 -0
  173. package/element/model.d.ts +36 -0
  174. package/element/model.js +64 -0
  175. package/element/player.d.ts +196 -0
  176. package/element/player.js +680 -0
  177. package/element/practice.d.ts +75 -0
  178. package/element/practice.js +150 -0
  179. package/element/scramble.d.ts +50 -0
  180. package/element/scramble.js +107 -0
  181. package/element/sequence.d.ts +126 -0
  182. package/element/sequence.js +254 -0
  183. package/element/styles.d.ts +32 -0
  184. package/element/styles.js +196 -0
  185. package/image/cache.d.ts +21 -0
  186. package/image/cache.js +66 -0
  187. package/image/index.d.ts +2 -0
  188. package/image/index.js +2 -0
  189. package/image/png-node.d.ts +6 -0
  190. package/image/png-node.js +10 -0
  191. package/image/svg.d.ts +36 -0
  192. package/image/svg.js +215 -0
  193. package/lbl/index.d.ts +10 -0
  194. package/lbl/index.js +31 -0
  195. package/methods/index.d.ts +20 -0
  196. package/methods/index.js +33 -0
  197. package/package.json +133 -0
  198. package/petrus/index.d.ts +11 -0
  199. package/petrus/index.js +27 -0
  200. package/react/index.d.ts +176 -0
  201. package/react/index.js +228 -0
  202. package/render/arrows.d.ts +68 -0
  203. package/render/arrows.js +190 -0
  204. package/render/build/attachments.d.ts +35 -0
  205. package/render/build/attachments.js +94 -0
  206. package/render/build/models.d.ts +9 -0
  207. package/render/build/models.js +27 -0
  208. package/render/build/tiles.d.ts +48 -0
  209. package/render/build/tiles.js +194 -0
  210. package/render/gltf.d.ts +10 -0
  211. package/render/gltf.js +106 -0
  212. package/render/index.d.ts +12 -0
  213. package/render/index.js +12 -0
  214. package/render/layers.d.ts +21 -0
  215. package/render/layers.js +24 -0
  216. package/render/pictures.d.ts +46 -0
  217. package/render/pictures.js +80 -0
  218. package/render/pieceModels.d.ts +38 -0
  219. package/render/pieceModels.js +43 -0
  220. package/render/renderer.d.ts +165 -0
  221. package/render/renderer.js +647 -0
  222. package/render/tile.d.ts +114 -0
  223. package/render/tile.js +390 -0
  224. package/render/viewports.d.ts +25 -0
  225. package/render/viewports.js +26 -0
  226. package/roux/cmll.d.ts +35 -0
  227. package/roux/cmll.js +121 -0
  228. package/roux/cmllData.d.ts +12 -0
  229. package/roux/cmllData.js +49 -0
  230. package/roux/index.d.ts +52 -0
  231. package/roux/index.js +175 -0
  232. package/skin/attachments.d.ts +82 -0
  233. package/skin/attachments.js +110 -0
  234. package/skin/index.d.ts +3 -0
  235. package/skin/index.js +3 -0
  236. package/skin/shapes.d.ts +112 -0
  237. package/skin/shapes.js +147 -0
  238. package/skin/skin.d.ts +911 -0
  239. package/skin/skin.js +412 -0
  240. package/solve/client.d.ts +58 -0
  241. package/solve/client.js +56 -0
  242. package/solve/handle.d.ts +6 -0
  243. package/solve/handle.js +38 -0
  244. package/solve/index.d.ts +8 -0
  245. package/solve/index.js +7 -0
  246. package/solve/lse.d.ts +42 -0
  247. package/solve/lse.js +151 -0
  248. package/solve/pieces.d.ts +18 -0
  249. package/solve/pieces.js +24 -0
  250. package/solve/protocol.d.ts +70 -0
  251. package/solve/protocol.js +16 -0
  252. package/solve/scramble.d.ts +76 -0
  253. package/solve/scramble.js +187 -0
  254. package/solve/stage.d.ts +108 -0
  255. package/solve/stage.js +532 -0
  256. package/solve/twophase.d.ts +36 -0
  257. package/solve/twophase.js +229 -0
  258. package/solve/worker.d.ts +6 -0
  259. package/solve/worker.js +15 -0
  260. package/timeline/codec.d.ts +23 -0
  261. package/timeline/codec.js +84 -0
  262. package/timeline/index.d.ts +7 -0
  263. package/timeline/index.js +7 -0
  264. package/timeline/recording.d.ts +19 -0
  265. package/timeline/recording.js +20 -0
  266. package/timeline/replay.d.ts +68 -0
  267. package/timeline/replay.js +145 -0
  268. package/timeline/segments.d.ts +31 -0
  269. package/timeline/segments.js +39 -0
  270. package/timeline/share.d.ts +30 -0
  271. package/timeline/share.js +62 -0
  272. package/timeline/stats.d.ts +34 -0
  273. package/timeline/stats.js +56 -0
  274. package/timeline/timings.d.ts +30 -0
  275. package/timeline/timings.js +54 -0
  276. package/zz/index.d.ts +29 -0
  277. package/zz/index.js +68 -0
  278. package/zz/seeds.d.ts +9 -0
  279. package/zz/seeds.js +29 -0
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Scramble analysis for CFOP (in the spirit of speedcubedb.com/analyze):
3
+ * for each cross colour, a good way through — the cross (or XCross…), the
4
+ * F2L pairs in the best order, the last layer with its cases — so an app
5
+ * can show the optimal cross, the recommended first pair, the pair order,
6
+ * which cases come up, and how much of it the solver's own algorithms
7
+ * cover.
8
+ *
9
+ * const a = analyzeScramble("R U2 F' …", { f2l: "optimal", start: "cross" });
10
+ * a.best // the shortest analysis over the six cross colours
11
+ * a.byCross[0].steps // [{ step: "cross", moves }, { step: "pair", slot: "FL", moves, case? }, … { step: "oll", case } …]
12
+ * a.byCross[0].rotation // "x2" — hold like this (cross on the bottom), then do the moves as written
13
+ *
14
+ * F2L modes:
15
+ * "optimal" each pair the fewest moves that keep the cross and the pairs
16
+ * already in (StageSolver), over all 24 pair orders;
17
+ * "algorithms" each pair its F2L case's algorithm (pre-AUF + alg), over all
18
+ * orders; a pair not in one of the 41 cases (a piece stuck in
19
+ * another slot) falls back to the fewest-moves insertion.
20
+ * The last layer always goes by OLL + PLL algorithms (+ AUF).
21
+ */
22
+ import { FRAMES, PIECE, applyMoves, checks, isSolved, moveCount, parseAlg, reframe, simplify, solvedState, toFaceTurns, } from "../core/index.js";
23
+ import { recognizeF2L, recognizeOll, recognizePll } from "../cfop/index.js";
24
+ import { stageSolver } from "../solve/index.js";
25
+ import { rotationFor } from "./rotations.js";
26
+ export * from "./roux.js";
27
+ export * from "./zz.js";
28
+ export { rotationFor } from "./rotations.js";
29
+ const SLOTS = ["FR", "FL", "BL", "BR"];
30
+ const CROSS = [PIECE.DR, PIECE.DF, PIECE.DL, PIECE.DB];
31
+ const SLOT_PIECES = { FR: [PIECE.FR, PIECE.DFR], FL: [PIECE.FL, PIECE.DLF], BL: [PIECE.BL, PIECE.DBL], BR: [PIECE.BR, PIECE.DRB] };
32
+ /** Cross plus the given pairs solved — one stage (tables: cross + each piece); `moves` limits the faces turned (ZZ: R U L). */
33
+ export function f2lStage(slots, moves) {
34
+ const pieces = [...CROSS, ...slots.flatMap((s) => SLOT_PIECES[s])];
35
+ const groups = slots.flatMap((_, i) => [[0, 1, 2, 3, 4 + 2 * i], [0, 1, 2, 3, 5 + 2 * i]]);
36
+ return { name: `cross+${[...slots].sort().join("+")}`, pieces, groups: groups.length ? groups : [[0, 1, 2, 3]], ...(moves ? { moves } : {}) };
37
+ }
38
+ const combos = (items, k) => k === 0 ? [[]] : items.flatMap((x, i) => combos(items.slice(i + 1), k - 1).map((c) => [x, ...c]));
39
+ /** The best way through the remaining pairs (all orders). */
40
+ export function planPairs(state, done, mode, known, moves) {
41
+ const left = SLOTS.filter((s) => !done.includes(s));
42
+ if (!left.length)
43
+ return { steps: [], length: 0, order: [], state };
44
+ let best = null;
45
+ for (const slot of left) {
46
+ const step = pairStep(state, done, slot, mode, known, moves);
47
+ if (!step)
48
+ continue;
49
+ const after = applyMoves(state, step.moves);
50
+ const rest = planPairs(after, [...done, slot], mode, known, moves);
51
+ const length = step.moves.length + rest.length;
52
+ if (!best || length < best.length)
53
+ best = { steps: [step, ...rest.steps], length, order: [slot, ...rest.order], state: rest.state };
54
+ }
55
+ return best;
56
+ }
57
+ function pairStep(state, done, slot, mode, known, moves) {
58
+ if (mode === "algorithms") {
59
+ const r = recognizeF2L(state, slot);
60
+ if (r === "solved")
61
+ return { step: "pair", slot, moves: [], case: "solved", how: "algorithm" };
62
+ if (r) {
63
+ const moves = simplify(parseAlg(`${r.preAuf} ${r.alg}`));
64
+ // The case's algorithm must leave the cross and the pairs already in untouched.
65
+ if (stageSolver(f2lStage([...done, slot])).distance(applyMoves(state, moves), { maxDepth: 0 }) === 0) {
66
+ const alg = `${r.preAuf} ${r.alg}`.trim();
67
+ return { step: "pair", slot, moves: simplify(toFaceTurns(moves).moves), case: r.id, alg, how: "algorithm", ...(known ? { known: known.has(r.id) } : {}) };
68
+ }
69
+ }
70
+ }
71
+ const sol = stageSolver(f2lStage([...done, slot], moves)).solve(state, { maxDepth: 16 })[0];
72
+ if (!sol)
73
+ return null;
74
+ const r = recognizeF2L(state, slot);
75
+ return { step: "pair", slot, moves: sol, ...(r ? { case: r === "solved" ? "solved" : r.id } : {}), how: "optimal" };
76
+ }
77
+ export function lastLayer(state, known) {
78
+ const steps = [];
79
+ let s = state;
80
+ const oll = recognizeOll(s);
81
+ if (oll) {
82
+ const moves = simplify(toFaceTurns(`${oll.preAuf} ${oll.alg}`).moves);
83
+ steps.push({ step: "oll", moves, case: oll.id, alg: `${oll.preAuf} ${oll.alg}`.trim(), ...(known ? { known: known.has(oll.id) } : {}) });
84
+ s = applyMoves(s, moves);
85
+ }
86
+ else
87
+ steps.push({ step: "oll", moves: [], case: "OLL skip" });
88
+ const pll = recognizePll(s);
89
+ if (pll) {
90
+ const moves = simplify(toFaceTurns(`${pll.preAuf} ${pll.alg}`).moves);
91
+ steps.push({ step: "pll", moves, case: pll.id, alg: `${pll.preAuf} ${pll.alg}`.trim(), ...(known ? { known: known.has(pll.id) } : {}) });
92
+ s = applyMoves(s, moves);
93
+ }
94
+ else
95
+ steps.push({ step: "pll", moves: [], case: "PLL skip" });
96
+ const auf = ["", "U", "U2", "U'"].find((a) => isSolved(a ? applyMoves(s, a) : s)) ?? "";
97
+ if (auf) {
98
+ steps.push({ step: "auf", moves: parseAlg(auf) });
99
+ s = applyMoves(s, auf);
100
+ }
101
+ return { steps, state: s };
102
+ }
103
+ /** One cross colour: the whole way through, as held with the cross on the bottom. */
104
+ export function analyzeCross(scrambled, face, options = {}) {
105
+ const frame = FRAMES.find((f) => f.face.D === face);
106
+ const known = options.known ? new Set(options.known) : null;
107
+ const mode = options.f2l ?? "optimal";
108
+ // As held: sticker names renamed so the cross face is D (colour logic is unaffected).
109
+ let s = reframe(scrambled, frame);
110
+ const steps = [];
111
+ const first = { cross: 0, xcross: 1, xxcross: 2, xxxcross: 3 }[options.start ?? "cross"];
112
+ let done = [];
113
+ // Cross (+ pairs): the best choice of slots for Cross+N.
114
+ let crossBest = null;
115
+ for (const slots of combos(SLOTS, first)) {
116
+ const sol = stageSolver(f2lStage(slots)).solve(s, { maxDepth: 16 })[0];
117
+ if (sol && (!crossBest || sol.length < crossBest.moves.length))
118
+ crossBest = { moves: sol, slots };
119
+ }
120
+ steps.push({ step: "cross", moves: crossBest.moves, ...(first ? { slots: crossBest.slots } : {}) });
121
+ s = applyMoves(s, crossBest.moves);
122
+ done = crossBest.slots;
123
+ const pairs = planPairs(s, done, mode, known);
124
+ steps.push(...pairs.steps);
125
+ s = pairs.state;
126
+ const ll = lastLayer(s, known);
127
+ steps.push(...ll.steps);
128
+ return {
129
+ face,
130
+ rotation: rotationFor(frame),
131
+ frame,
132
+ steps,
133
+ length: steps.reduce((n, st) => n + moveCount(st.moves), 0),
134
+ pairOrder: pairs.order,
135
+ };
136
+ }
137
+ /** Analyse a scramble (moves from solved, or a state) for each cross colour. */
138
+ export function analyzeScramble(scramble, options = {}) {
139
+ const state = scramble instanceof Uint8Array ? scramble : applyMoves(solvedState(), typeof scramble === "string" ? parseAlg(scramble) : scramble);
140
+ const faces = options.crosses ?? ["D", "U", "F", "B", "L", "R"];
141
+ const byCross = faces.map((face) => analyzeCross(state, face, options)).sort((a, b) => a.length - b.length);
142
+ return { byCross, best: byCross[0] };
143
+ }
144
+ /** Does following the analysis (rotation, then every step) solve the cube? — for tests / sanity checks. */
145
+ export function followsThrough(scramble, a) {
146
+ let s = reframe(scramble, a.frame);
147
+ for (const st of a.steps)
148
+ s = applyMoves(s, st.moves);
149
+ return isSolved(s) && checks.f2lSolved(s);
150
+ }
151
+ export * from "./client.js";
@@ -0,0 +1,3 @@
1
+ /** A whole-cube rotation for each frame ("x2", "z y'"…): hold the cube like this to see it as the frame does. */
2
+ import { type Frame } from "../core/index.js";
3
+ export declare const rotationFor: (frame: Frame) => string;
@@ -0,0 +1,15 @@
1
+ /** A whole-cube rotation for each frame ("x2", "z y'"…): hold the cube like this to see it as the frame does. */
2
+ import { toFaceTurns } from "../core/index.js";
3
+ const ROTATIONS = (() => {
4
+ const out = new Map();
5
+ for (const tilt of ["", "x", "x2", "x'", "z", "z'"]) {
6
+ for (const turn of ["", "y", "y2", "y'"]) {
7
+ const alg = `${tilt} ${turn}`.trim();
8
+ const frame = toFaceTurns(alg).frame;
9
+ if (!out.has(frame.id))
10
+ out.set(frame.id, alg);
11
+ }
12
+ }
13
+ return out;
14
+ })();
15
+ export const rotationFor = (frame) => ROTATIONS.get(frame.id) ?? "";
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Scramble analysis for Roux: for each side the first block can go on,
3
+ * the best way through — first block (with the best bottom colour),
4
+ * second square and the rest of the second block, CMLL (case named), LSE
5
+ * (M / U, split into EO, UL/UR and the last four edges).
6
+ *
7
+ * const a = analyzeRoux("R U2 F' …");
8
+ * a.best.rotation // hold like this: the first block goes on the left, its bottom down
9
+ * a.best.steps // fb, ss (front / back), sb, cmll, lse
10
+ *
11
+ * Blocks by fewest face turns (optimal FB; second square, then the last
12
+ * pair of the second block, each optimal keeping what's built); CMLL by
13
+ * its algorithm; LSE optimal in M and U (exact table). Lengths in STM
14
+ * (a slice counts one).
15
+ */
16
+ import { type Face, type Frame, type Move, type State } from "../core/index.js";
17
+ import { type RouxSide } from "../roux/index.js";
18
+ export interface RouxAnalyzeOptions {
19
+ /** Physical faces the first block may go on (its left side); default all six. */
20
+ sides?: readonly Face[];
21
+ /** Case ids the solver knows ("Sune Left Bar"…) — the CMLL step gets `known`. */
22
+ known?: Iterable<string>;
23
+ }
24
+ export type RouxStep = {
25
+ step: "fb";
26
+ moves: Move[];
27
+ } | {
28
+ step: "ss";
29
+ side: RouxSide;
30
+ moves: Move[];
31
+ } | {
32
+ step: "sb";
33
+ moves: Move[];
34
+ } | {
35
+ step: "cmll";
36
+ moves: Move[];
37
+ case: string;
38
+ alg?: string;
39
+ known?: boolean;
40
+ } | {
41
+ step: "lse";
42
+ moves: Move[];
43
+ eo: number;
44
+ ulur: number;
45
+ l4e: number;
46
+ };
47
+ export interface RouxAnalysis {
48
+ /** Physical face the first block is built against (left side as held). */
49
+ side: Face;
50
+ /** Physical face its bottom is on. */
51
+ bottom: Face;
52
+ rotation: string;
53
+ frame: Frame;
54
+ steps: RouxStep[];
55
+ /** STM. */
56
+ length: number;
57
+ }
58
+ export interface RouxAnalysisResult {
59
+ bySide: RouxAnalysis[];
60
+ best: RouxAnalysis;
61
+ }
62
+ /** One side (and its best bottom): the whole way through, as held. */
63
+ export declare function analyzeRouxSide(scrambled: State, side: Face, known?: Set<string> | null): RouxAnalysis;
64
+ /** Analyse a scramble for Roux, for each side the first block can go on. */
65
+ export declare function analyzeRoux(scramble: string | readonly Move[] | State, options?: RouxAnalyzeOptions): RouxAnalysisResult;
66
+ /** Does following it (as held) solve the cube? */
67
+ export declare function rouxFollowsThrough(scramble: State, a: RouxAnalysis): boolean;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Scramble analysis for Roux: for each side the first block can go on,
3
+ * the best way through — first block (with the best bottom colour),
4
+ * second square and the rest of the second block, CMLL (case named), LSE
5
+ * (M / U, split into EO, UL/UR and the last four edges).
6
+ *
7
+ * const a = analyzeRoux("R U2 F' …");
8
+ * a.best.rotation // hold like this: the first block goes on the left, its bottom down
9
+ * a.best.steps // fb, ss (front / back), sb, cmll, lse
10
+ *
11
+ * Blocks by fewest face turns (optimal FB; second square, then the last
12
+ * pair of the second block, each optimal keeping what's built); CMLL by
13
+ * its algorithm; LSE optimal in M and U (exact table). Lengths in STM
14
+ * (a slice counts one).
15
+ */
16
+ import { FRAMES, applyMoves, isSolved, moveCount, parseAlg, reframe, simplify, solvedState, toFaceTurns } from "../core/index.js";
17
+ import { ROUX_TRAINERS, cmll as cmllDone, lseEo, recognizeCmll, ulUr } from "../roux/index.js";
18
+ import { STAGES, lseSolver, stageSolver } from "../solve/index.js";
19
+ import { rotationFor } from "./rotations.js";
20
+ const FB_FIXED = { ...ROUX_TRAINERS.fb(), name: "roux-fb-fixed", neutral: undefined };
21
+ const SIDES = ["front", "back"];
22
+ /** One side (and its best bottom): the whole way through, as held. */
23
+ export function analyzeRouxSide(scrambled, side, known = null) {
24
+ // The best bottom colour for the first block on this side.
25
+ const fb = stageSolver(FB_FIXED);
26
+ let best = null;
27
+ for (const frame of FRAMES.filter((f) => f.face.L === side)) {
28
+ const sol = fb.solve(reframe(scrambled, frame), { maxDepth: 12 })[0];
29
+ if (sol && (!best || sol.length < best.moves.length))
30
+ best = { frame, moves: sol };
31
+ }
32
+ const { frame } = best;
33
+ const steps = [{ step: "fb", moves: best.moves }];
34
+ let s = applyMoves(reframe(scrambled, frame), best.moves);
35
+ // Second square (the better side), then the rest of the second block.
36
+ let ss = null;
37
+ for (const sq of SIDES) {
38
+ const sol = stageSolver(ROUX_TRAINERS.ss(sq)).solve(s, { maxDepth: 14 })[0];
39
+ if (sol && (!ss || sol.length < ss.moves.length))
40
+ ss = { side: sq, moves: sol };
41
+ }
42
+ steps.push({ step: "ss", side: ss.side, moves: ss.moves });
43
+ s = applyMoves(s, ss.moves);
44
+ const sb = stageSolver(STAGES["roux-blocks"]()).solve(s, { maxDepth: 14 })[0];
45
+ steps.push({ step: "sb", moves: sb });
46
+ s = applyMoves(s, sb);
47
+ // CMLL by its algorithm.
48
+ const c = recognizeCmll(s);
49
+ if (c) {
50
+ const moves = simplify(toFaceTurns(`${c.preAuf} ${c.alg}`).moves);
51
+ s = applyMoves(s, moves);
52
+ if (!cmllDone(s))
53
+ throw new Error(`CMLL ${c.id} didn't solve the corners`);
54
+ steps.push({ step: "cmll", moves, case: c.id, alg: `${c.preAuf} ${c.alg}`.trim(), ...(known ? { known: known.has(c.id) } : {}) });
55
+ }
56
+ else
57
+ steps.push({ step: "cmll", moves: [], case: "CMLL skip" });
58
+ // LSE, optimal in M / U; where EO and UL/UR were done along the way.
59
+ const lse = lseSolver(ROUX_TRAINERS.lse()).solve(s)[0] ?? [];
60
+ let eo = -1, ulur = -1;
61
+ let t = s;
62
+ for (let i = 0; i <= lse.length; i++) {
63
+ if (eo < 0 && lseEo(t))
64
+ eo = i;
65
+ if (ulur < 0 && ulUr(t))
66
+ ulur = i;
67
+ if (i < lse.length)
68
+ t = applyMoves(t, [lse[i]]);
69
+ }
70
+ const eoLen = Math.max(eo, 0), ulurLen = Math.max(ulur, eoLen) - eoLen;
71
+ steps.push({ step: "lse", moves: lse, eo: eoLen, ulur: ulurLen, l4e: lse.length - eoLen - ulurLen });
72
+ return {
73
+ side,
74
+ bottom: frame.face.D,
75
+ rotation: rotationFor(frame),
76
+ frame,
77
+ steps,
78
+ length: steps.reduce((n, st) => n + moveCount(st.moves, "stm"), 0),
79
+ };
80
+ }
81
+ /** Analyse a scramble for Roux, for each side the first block can go on. */
82
+ export function analyzeRoux(scramble, options = {}) {
83
+ const state = scramble instanceof Uint8Array ? scramble : applyMoves(solvedState(), typeof scramble === "string" ? parseAlg(scramble) : scramble);
84
+ const known = options.known ? new Set(options.known) : null;
85
+ const sides = options.sides ?? ["L", "R", "F", "B", "U", "D"];
86
+ const bySide = sides.map((side) => analyzeRouxSide(state, side, known)).sort((a, b) => a.length - b.length);
87
+ return { bySide, best: bySide[0] };
88
+ }
89
+ /** Does following it (as held) solve the cube? */
90
+ export function rouxFollowsThrough(scramble, a) {
91
+ let s = reframe(scramble, a.frame);
92
+ for (const st of a.steps)
93
+ s = applyMoves(s, st.moves);
94
+ return isSolved(s);
95
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The analyser in a Web Worker (the F2L tables build here, once, ~10 s;
3
+ * then an analysis of all six cross colours takes ~0.5 s optimal / ~50 ms
4
+ * by algorithms). Use it through `createAnalyzerWorker()`.
5
+ */
6
+ export {};
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The analyser in a Web Worker (the F2L tables build here, once, ~10 s;
3
+ * then an analysis of all six cross colours takes ~0.5 s optimal / ~50 ms
4
+ * by algorithms). Use it through `createAnalyzerWorker()`.
5
+ */
6
+ import { ROUX_TRAINERS } from "../roux/index.js";
7
+ import { STAGES, indexedDbTableStore, preloadStageTables } from "../solve/index.js";
8
+ import { analyzeRoux, analyzeScramble, analyzeZZ, f2lStage } from "./index.js";
9
+ import { ZZ_TRAINERS } from "../zz/index.js";
10
+ // The tables (CFOP: cross + each pair piece; Roux: blocks, squares) survive reloads in IndexedDB: built once per device.
11
+ const STAGE_TABLES = [f2lStage(["FR", "FL", "BL", "BR"]), ZZ_TRAINERS.eocross(), ROUX_TRAINERS.fb(), ROUX_TRAINERS.ss("front"), ROUX_TRAINERS.ss("back"), STAGES["roux-blocks"]()];
12
+ const ready = typeof indexedDB !== "undefined" ? preloadStageTables(STAGE_TABLES, indexedDbTableStore()).catch(() => undefined) : Promise.resolve();
13
+ self.onmessage = async (e) => {
14
+ const { id, scramble, options, method } = e.data;
15
+ await ready;
16
+ try {
17
+ const input = typeof scramble === "string" ? scramble : Uint8Array.from(scramble);
18
+ self.postMessage({
19
+ id,
20
+ ok: true,
21
+ value: method === "roux"
22
+ ? analyzeRoux(input, options)
23
+ : method === "zz"
24
+ ? analyzeZZ(input, options)
25
+ : analyzeScramble(input, options),
26
+ });
27
+ }
28
+ catch (err) {
29
+ self.postMessage({ id, ok: false, error: err instanceof Error ? err.message : String(err) });
30
+ }
31
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Scramble analysis for ZZ, either way in:
3
+ * "eocross" — EOCross (every edge oriented, on the better of the two EO
4
+ * axes), then the four pairs with R, U and L only in the best order;
5
+ * "eoline" — EOLine (edges oriented, DF + DB), then the left and right
6
+ * blocks with R U L (the better order);
7
+ * then the last layer: with the edges oriented the OLL is an OCLL case (or a
8
+ * skip), then PLL.
9
+ *
10
+ * const z = analyzeZZ(scramble);
11
+ * z.best.steps // eocross, pair ×4 (R U L), oll (OCLL), pll, auf
12
+ * z.best.eoAxis // the physical faces EO is judged against, e.g. ["F", "B"]
13
+ */
14
+ import { type Face, type Frame, type Move, type State } from "../core/index.js";
15
+ import type { F2LSlot } from "../cfop/index.js";
16
+ import { type AnalysisStep } from "./index.js";
17
+ export interface ZZAnalyzeOptions {
18
+ /** "eocross" (default) or classic "eoline" + blocks. */
19
+ start?: "eocross" | "eoline";
20
+ /** Physical faces to put the cross on (default all six). */
21
+ crosses?: readonly Face[];
22
+ /** Case ids the solver knows ("OLL 27", "T"…). */
23
+ known?: Iterable<string>;
24
+ }
25
+ export interface ZZAnalysis {
26
+ face: Face;
27
+ /** The physical faces the edges are oriented against (the front / back as held). */
28
+ eoAxis: [Face, Face];
29
+ rotation: string;
30
+ frame: Frame;
31
+ /** "cross" is the EOCross; pairs are R / U / L only. */
32
+ steps: AnalysisStep[];
33
+ length: number;
34
+ /** EOCross: the order of the pairs; EOLine: empty (see blockOrder). */
35
+ pairOrder: F2LSlot[];
36
+ /** EOLine: which block went first. */
37
+ blockOrder?: ("left" | "right")[];
38
+ }
39
+ export interface ZZAnalysisResult {
40
+ byCross: ZZAnalysis[];
41
+ best: ZZAnalysis;
42
+ }
43
+ /** EOLine, then the two blocks (R U L) in the better order. */
44
+ export declare function analyzeZZLine(scrambled: State, face: Face, known?: Set<string> | null): ZZAnalysis;
45
+ export declare function analyzeZZCross(scrambled: State, face: Face, known?: Set<string> | null): ZZAnalysis;
46
+ export declare function analyzeZZ(scramble: string | readonly Move[] | State, options?: ZZAnalyzeOptions): ZZAnalysisResult;
47
+ export declare function zzFollowsThrough(scramble: State, a: ZZAnalysis): boolean;
package/analyze/zz.js ADDED
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Scramble analysis for ZZ, either way in:
3
+ * "eocross" — EOCross (every edge oriented, on the better of the two EO
4
+ * axes), then the four pairs with R, U and L only in the best order;
5
+ * "eoline" — EOLine (edges oriented, DF + DB), then the left and right
6
+ * blocks with R U L (the better order);
7
+ * then the last layer: with the edges oriented the OLL is an OCLL case (or a
8
+ * skip), then PLL.
9
+ *
10
+ * const z = analyzeZZ(scramble);
11
+ * z.best.steps // eocross, pair ×4 (R U L), oll (OCLL), pll, auf
12
+ * z.best.eoAxis // the physical faces EO is judged against, e.g. ["F", "B"]
13
+ */
14
+ import { FRAMES, applyMoves, isSolved, moveCount, parseAlg, reframe, solvedState } from "../core/index.js";
15
+ import { ZZ_TRAINERS } from "../zz/index.js";
16
+ import { stageSolver } from "../solve/index.js";
17
+ import { lastLayer, planPairs } from "./index.js";
18
+ import { rotationFor } from "./rotations.js";
19
+ const RUL = ["R", "U", "L"];
20
+ /** EOLine, then the two blocks (R U L) in the better order. */
21
+ export function analyzeZZLine(scrambled, face, known = null) {
22
+ const line = stageSolver(ZZ_TRAINERS.eoline());
23
+ let best = null;
24
+ for (const frame of FRAMES.filter((f) => f.face.D === face)) {
25
+ const sol = line.solve(reframe(scrambled, frame), { maxDepth: 10 })[0];
26
+ if (sol && (!best || sol.length < best.moves.length))
27
+ best = { frame, moves: sol };
28
+ }
29
+ const { frame } = best;
30
+ const afterLine = applyMoves(reframe(scrambled, frame), best.moves);
31
+ const both = stageSolver(ZZ_TRAINERS.block("right", true));
32
+ let blocks = null;
33
+ for (const first of ["left", "right"]) {
34
+ const a = stageSolver(ZZ_TRAINERS.block(first)).solve(afterLine, { maxDepth: 18 })[0];
35
+ if (!a)
36
+ continue;
37
+ const b = both.solve(applyMoves(afterLine, a), { maxDepth: 18 })[0];
38
+ if (!b)
39
+ continue;
40
+ if (!blocks || a.length + b.length < blocks.first.length + blocks.second.length) {
41
+ blocks = { order: [first, first === "left" ? "right" : "left"], first: a, second: b };
42
+ }
43
+ }
44
+ const steps = [
45
+ { step: "eoline", moves: best.moves },
46
+ { step: "block", side: blocks.order[0], moves: blocks.first },
47
+ { step: "block", side: blocks.order[1], moves: blocks.second },
48
+ ];
49
+ const ll = lastLayer(applyMoves(afterLine, [...blocks.first, ...blocks.second]), known);
50
+ steps.push(...ll.steps);
51
+ return {
52
+ face,
53
+ eoAxis: [frame.face.F, frame.face.B],
54
+ rotation: rotationFor(frame),
55
+ frame,
56
+ steps,
57
+ length: steps.reduce((n, st) => n + moveCount(st.moves), 0),
58
+ pairOrder: [],
59
+ blockOrder: blocks.order,
60
+ };
61
+ }
62
+ export function analyzeZZCross(scrambled, face, known = null) {
63
+ // EOCross on the better axis: frames with this bottom differ in which faces are front / back.
64
+ const eo = stageSolver(ZZ_TRAINERS.eocross());
65
+ let best = null;
66
+ for (const frame of FRAMES.filter((f) => f.face.D === face)) {
67
+ const sol = eo.solve(reframe(scrambled, frame), { maxDepth: 12 })[0];
68
+ if (sol && (!best || sol.length < best.moves.length))
69
+ best = { frame, moves: sol };
70
+ }
71
+ const { frame } = best;
72
+ const steps = [{ step: "cross", moves: best.moves }];
73
+ let s = applyMoves(reframe(scrambled, frame), best.moves);
74
+ const pairs = planPairs(s, [], "optimal", known, [...RUL]);
75
+ steps.push(...pairs.steps);
76
+ s = pairs.state;
77
+ const ll = lastLayer(s, known);
78
+ steps.push(...ll.steps);
79
+ return {
80
+ face,
81
+ eoAxis: [frame.face.F, frame.face.B],
82
+ rotation: rotationFor(frame),
83
+ frame,
84
+ steps,
85
+ length: steps.reduce((n, st) => n + moveCount(st.moves), 0),
86
+ pairOrder: pairs.order,
87
+ };
88
+ }
89
+ export function analyzeZZ(scramble, options = {}) {
90
+ const state = scramble instanceof Uint8Array ? scramble : applyMoves(solvedState(), typeof scramble === "string" ? parseAlg(scramble) : scramble);
91
+ const known = options.known ? new Set(options.known) : null;
92
+ const faces = options.crosses ?? ["D", "U", "F", "B", "L", "R"];
93
+ const analyse = options.start === "eoline" ? analyzeZZLine : analyzeZZCross;
94
+ const byCross = faces.map((f) => analyse(state, f, known)).sort((a, b) => a.length - b.length);
95
+ return { byCross, best: byCross[0] };
96
+ }
97
+ export function zzFollowsThrough(scramble, a) {
98
+ let s = reframe(scramble, a.frame);
99
+ for (const st of a.steps)
100
+ s = applyMoves(s, st.moves);
101
+ return isSolved(s);
102
+ }
package/bld/index.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Blindfolded solving (Old Pochmann): letter schemes, memorisation (the
3
+ * letters to remember for a scrambled cube), following the execution letter
4
+ * by letter on a smart cube, and a skin with the letters on the stickers.
5
+ *
6
+ * const m = memo(state); // { edges: ["Q","U",…], corners: [...], parity, … }
7
+ * formatMemo(m.edges) // "QU SR NX IV PR DE"
8
+ * const t = new BldTracker(state); // push the cube's moves; t.progress says which letter is done
9
+ * player.skin = letterSkin(SKINS.default);
10
+ */
11
+ export * from "./scheme.js";
12
+ export * from "./memo.js";
13
+ export * from "./tracker.js";
14
+ export * from "./skin.js";
package/bld/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Blindfolded solving (Old Pochmann): letter schemes, memorisation (the
3
+ * letters to remember for a scrambled cube), following the execution letter
4
+ * by letter on a smart cube, and a skin with the letters on the stickers.
5
+ *
6
+ * const m = memo(state); // { edges: ["Q","U",…], corners: [...], parity, … }
7
+ * formatMemo(m.edges) // "QU SR NX IV PR DE"
8
+ * const t = new BldTracker(state); // push the cube's moves; t.progress says which letter is done
9
+ * player.skin = letterSkin(SKINS.default);
10
+ */
11
+ export * from "./scheme.js";
12
+ export * from "./memo.js";
13
+ export * from "./tracker.js";
14
+ export * from "./skin.js";
package/bld/memo.d.ts ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Old Pochmann memorisation. Starting from the buffer, follow where each
3
+ * sticker belongs: the sticker in the buffer belongs at A → remember A; the
4
+ * sticker at A belongs at B → remember B; … until the buffer's own piece
5
+ * comes back. Pieces left unsolved start new cycles (a cycle break: shoot
6
+ * to one of their stickers, follow, and finish on that piece again) — which
7
+ * also covers flipped edges and twisted corners in place.
8
+ *
9
+ * Executing: each letter is one swap of the buffer piece with the lettered
10
+ * sticker's piece (edges: T-perm with setup moves, corners: Y-perm). An odd
11
+ * number of edge letters means parity (an extra algorithm between edges and
12
+ * corners).
13
+ *
14
+ * Buffers: UR for edges, ULB for corners (the usual OP buffers).
15
+ */
16
+ import { type Move, type State } from "../core/index.js";
17
+ import { type LetterScheme } from "./scheme.js";
18
+ /** Edge buffer: the U sticker of UR. */
19
+ export declare const EDGE_BUFFER = 5;
20
+ /** Corner buffer: the U sticker of ULB. */
21
+ export declare const CORNER_BUFFER = 0;
22
+ export interface Memo {
23
+ /** Letters to remember, edges first. */
24
+ edges: string[];
25
+ corners: string[];
26
+ /** The sticker position of each letter (what to shoot to). */
27
+ edgeTargets: number[];
28
+ cornerTargets: number[];
29
+ /** Odd number of edge letters: do the parity algorithm between edges and corners. */
30
+ parity: boolean;
31
+ }
32
+ export interface MemoOptions {
33
+ scheme?: LetterScheme;
34
+ /**
35
+ * How the cube is held while solving, as rotations from its own U on top /
36
+ * F in front — e.g. "x2 y'" = yellow on top, orange in front for a cube
37
+ * with white U and green F. Letters go by the positions as held.
38
+ */
39
+ rotation?: string | readonly Move[];
40
+ edgeBuffer?: number;
41
+ cornerBuffer?: number;
42
+ }
43
+ /**
44
+ * The state with positions and sticker names as held: turned by `rotation`
45
+ * if given, and with the centres where they are now counted as home (a
46
+ * state after rotations, e.g. "… x2 y'", reads the way the cube is now held).
47
+ */
48
+ export declare function asHeld(state: State, rotation?: string | readonly Move[]): State;
49
+ /** The letters to remember for `state`. */
50
+ export declare function memo(input: State, options?: MemoOptions): Memo;
51
+ /** Letters in pairs, the usual way to write memo: "QU SR NX IV PR DE". */
52
+ export declare const formatMemo: (letters: readonly string[]) => string;
53
+ /**
54
+ * The effect of one letter on the pieces of its kind: the buffer piece and
55
+ * the target's piece swap, the buffer sticker going to the target sticker
56
+ * (what the setup + T-perm / Y-perm + undo do to that kind of piece).
57
+ */
58
+ export declare function swapToTarget(state: State, kind: "edge" | "corner", target: number, buffer?: number): State;
59
+ /** Sticker positions of edges / corners (to compare only one kind of piece). */
60
+ export declare const EDGE_POSITIONS: readonly number[];
61
+ export declare const CORNER_POSITIONS: readonly number[];
62
+ export declare const samePieces: (a: State, b: State, positions: readonly number[]) => boolean;
63
+ /** Two sticker positions on the same edge / corner. */
64
+ export declare const samePiece: (kind: "edge" | "corner", a: number, b: number) => boolean;
65
+ /** The whole-cube rotation state with these centres (a solved cube as held), or null. */
66
+ export declare const rotationState: (rotation: string | readonly Move[]) => State;