@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,16 @@
1
+ /**
2
+ * OLL and PLL cases as data — numbering, names and groups as on
3
+ * speedcubedb.com (OLL 1–57, PLL Aa…Z). `alg` is one standard algorithm per
4
+ * case (speedcubedb's main one); recognition is built from it, so any
5
+ * algorithm for the case works with the recogniser.
6
+ */
7
+ export interface LastLayerCase {
8
+ /** "OLL 27", "T"… */
9
+ id: string;
10
+ /** speedcubedb group ("Dot Case", "Adj Swap"…). */
11
+ group: string;
12
+ /** A standard algorithm (from the case, as held, to oriented / solved up to AUF). */
13
+ alg: string;
14
+ }
15
+ export declare const OLL_CASES: readonly LastLayerCase[];
16
+ export declare const PLL_CASES: readonly LastLayerCase[];
@@ -0,0 +1,88 @@
1
+ /**
2
+ * OLL and PLL cases as data — numbering, names and groups as on
3
+ * speedcubedb.com (OLL 1–57, PLL Aa…Z). `alg` is one standard algorithm per
4
+ * case (speedcubedb's main one); recognition is built from it, so any
5
+ * algorithm for the case works with the recogniser.
6
+ */
7
+ export const OLL_CASES = [
8
+ { id: "OLL 1", group: "Dot Case", alg: "R U2 R2 F R F' U2 R' F R F'" },
9
+ { id: "OLL 2", group: "Dot Case", alg: "R U' R2 D' r U r' D R2 U R'" },
10
+ { id: "OLL 3", group: "Dot Case", alg: "R' F2 R2 U2 R' F R U2 R2 F2 R" },
11
+ { id: "OLL 4", group: "Dot Case", alg: "R' F2 R2 U2 R' F' R U2 R2 F2 R" },
12
+ { id: "OLL 5", group: "Square Shapes", alg: "r' U2 R U R' U r" },
13
+ { id: "OLL 6", group: "Square Shapes", alg: "r U2 R' U' R U' r'" },
14
+ { id: "OLL 7", group: "Lightning Shapes", alg: "r U R' U R U2 r'" },
15
+ { id: "OLL 8", group: "Lightning Shapes", alg: "r' U' R U' R' U2 r" },
16
+ { id: "OLL 9", group: "Fish Shapes", alg: "R U R' U' R' F R2 U R' U' F'" },
17
+ { id: "OLL 10", group: "Fish Shapes", alg: "R U R' U R' F R F' R U2 R'" },
18
+ { id: "OLL 11", group: "Lightning Shapes", alg: "r' R2 U R' U R U2 R' U M'" },
19
+ { id: "OLL 12", group: "Lightning Shapes", alg: "M' R' U' R U' R' U2 R U' M" },
20
+ { id: "OLL 13", group: "Knight Move Shapes", alg: "F U R U2 R' U' R U R' F'" },
21
+ { id: "OLL 14", group: "Knight Move Shapes", alg: "R' F R U R' F' R F U' F'" },
22
+ { id: "OLL 15", group: "Knight Move Shapes", alg: "r' U' r R' U' R U r' U r" },
23
+ { id: "OLL 16", group: "Knight Move Shapes", alg: "r U r' R U R' U' r U' r'" },
24
+ { id: "OLL 17", group: "Dot Case", alg: "R U R' U R' F R F' U2 R' F R F'" },
25
+ { id: "OLL 18", group: "Dot Case", alg: "R U2 R2 F R F' U2 M' U R U' r'" },
26
+ { id: "OLL 19", group: "Dot Case", alg: "S' R U R' S U' R' F R F'" },
27
+ { id: "OLL 20", group: "Dot Case", alg: "r U R' U' M2 U R U' R' U' M'" },
28
+ { id: "OLL 21", group: "OCLL", alg: "R U R' U R U' R' U R U2 R'" },
29
+ { id: "OLL 22", group: "OCLL", alg: "R U2 R2 U' R2 U' R2 U2 R" },
30
+ { id: "OLL 23", group: "OCLL", alg: "R2 D R' U2 R D' R' U2 R'" },
31
+ { id: "OLL 24", group: "OCLL", alg: "r U R' U' r' F R F'" },
32
+ { id: "OLL 25", group: "OCLL", alg: "R U2 R D R' U2 R D' R2" },
33
+ { id: "OLL 26", group: "OCLL", alg: "R U2 R' U' R U' R'" },
34
+ { id: "OLL 27", group: "OCLL", alg: "R U R' U R U2 R'" },
35
+ { id: "OLL 28", group: "All Corners Oriented", alg: "r U R' U' M U R U' R'" },
36
+ { id: "OLL 29", group: "Awkward Shapes", alg: "r2 D' r U r' D r2 U' r' U' r" },
37
+ { id: "OLL 30", group: "Awkward Shapes", alg: "r' D' r U' r' D r2 U' r' U r U r'" },
38
+ { id: "OLL 31", group: "P Shapes", alg: "R' U' F U R U' R' F' R" },
39
+ { id: "OLL 32", group: "P Shapes", alg: "S R U R' U' R' F R f'" },
40
+ { id: "OLL 33", group: "T Shapes", alg: "R U R' U' R' F R F'" },
41
+ { id: "OLL 34", group: "C Shapes", alg: "f R f' U' r' U' R U M'" },
42
+ { id: "OLL 35", group: "Fish Shapes", alg: "R U2 R2 F R F' R U2 R'" },
43
+ { id: "OLL 36", group: "W Shapes", alg: "R U R2 F' U' F U R2 U2 R'" },
44
+ { id: "OLL 37", group: "Fish Shapes", alg: "F R' F' R U R U' R'" },
45
+ { id: "OLL 38", group: "W Shapes", alg: "R U R' U R U' R' U' R' F R F'" },
46
+ { id: "OLL 39", group: "Lightning Shapes", alg: "R U R' F' U' F U R U2 R'" },
47
+ { id: "OLL 40", group: "Lightning Shapes", alg: "R' F R U R' U' F' U R" },
48
+ { id: "OLL 41", group: "Awkward Shapes", alg: "R U R' U R U2 R' F R U R' U' F'" },
49
+ { id: "OLL 42", group: "Awkward Shapes", alg: "R' U' R U' R' U2 R F R U R' U' F'" },
50
+ { id: "OLL 43", group: "P Shapes", alg: "R' U' F' U F R" },
51
+ { id: "OLL 44", group: "P Shapes", alg: "f R U R' U' f'" },
52
+ { id: "OLL 45", group: "T Shapes", alg: "F R U R' U' F'" },
53
+ { id: "OLL 46", group: "C Shapes", alg: "R' U' R' F R F' U R" },
54
+ { id: "OLL 47", group: "L Shapes", alg: "F R' F' R U2 R U' R' U R U2 R'" },
55
+ { id: "OLL 48", group: "L Shapes", alg: "F R U R' U' R U R' U' F'" },
56
+ { id: "OLL 49", group: "L Shapes", alg: "r U' r2 U r2 U r2 U' r" },
57
+ { id: "OLL 50", group: "L Shapes", alg: "r' U r2 U' r2 U' r2 U r'" },
58
+ { id: "OLL 51", group: "Line Shapes", alg: "F U R U' R' U R U' R' F'" },
59
+ { id: "OLL 52", group: "Line Shapes", alg: "R' F' U' F U' R U R' U R" },
60
+ { id: "OLL 53", group: "L Shapes", alg: "r' U' R U' R' U R U' R' U2 r" },
61
+ { id: "OLL 54", group: "L Shapes", alg: "r U R' U R U' R' U R U2 r'" },
62
+ { id: "OLL 55", group: "Line Shapes", alg: "R' F U R U' R2 F' R2 U R' U' R" },
63
+ { id: "OLL 56", group: "Line Shapes", alg: "r U r' U R U' R' U R U' R' r U' r'" },
64
+ { id: "OLL 57", group: "All Corners Oriented", alg: "R U R' U' M' U R U' r'" },
65
+ ];
66
+ export const PLL_CASES = [
67
+ { id: "Aa", group: "Adj Swap", alg: "x R' U R' D2 R U' R' D2 R2 x'" },
68
+ { id: "Ab", group: "Adj Swap", alg: "x R2 D2 R U R' D2 R U' R x'" },
69
+ { id: "E", group: "Opp Swap", alg: "x' R U' R' D R U R' D' R U R' D R U' R' D' x" },
70
+ { id: "F", group: "Adj Swap", alg: "R' U' F' R U R' U' R' F R2 U' R' U' R U R' U R" },
71
+ { id: "Ga", group: "Adj Swap", alg: "R2 U R' U R' U' R U' R2 D U' R' U R D'" },
72
+ { id: "Gb", group: "Adj Swap", alg: "D R' U' R U D' R2 U R' U R U' R U' R2" },
73
+ { id: "Gc", group: "Adj Swap", alg: "R2 U' R U' R U R' U R2 D' U R U' R' D" },
74
+ { id: "Gd", group: "Adj Swap", alg: "R U R' U' D R2 U' R U' R' U R' U R2 D'" },
75
+ { id: "H", group: "EPLL", alg: "M2 U' M2 U2 M2 U' M2" },
76
+ { id: "Ja", group: "Adj Swap", alg: "x R2 F R F' R U2 r' U r U2 x'" },
77
+ { id: "Jb", group: "Adj Swap", alg: "R U R' F' R U R' U' R' F R2 U' R'" },
78
+ { id: "Na", group: "Opp Swap", alg: "R U R' U R U R' F' R U R' U' R' F R2 U' R' U2 R U' R'" },
79
+ { id: "Nb", group: "Opp Swap", alg: "R' U R U' R' F' U' F R U R' F R' F' R U' R" },
80
+ { id: "Ra", group: "Adj Swap", alg: "R U' R' U' R U R D R' U' R D' R' U2 R'" },
81
+ { id: "Rb", group: "Adj Swap", alg: "R' U2 R U2 R' F R U R' U' R' F' R2" },
82
+ { id: "T", group: "Adj Swap", alg: "R U R' U' R' F R2 U' R' U' R U R' F'" },
83
+ { id: "Ua", group: "EPLL", alg: "M2 U M U2 M' U M2" },
84
+ { id: "Ub", group: "EPLL", alg: "M2 U' M U2 M' U' M2" },
85
+ { id: "V", group: "Opp Swap", alg: "R' U R' U' R D' R' D R' U D' R2 U' R2 D R2" },
86
+ { id: "Y", group: "Opp Swap", alg: "F R U' R' U' R U R' F' R U R' U' R' F R F'" },
87
+ { id: "Z", group: "EPLL", alg: "M' U' M2 U' M2 U' M' U2 M2" },
88
+ ];
@@ -0,0 +1,42 @@
1
+ /**
2
+ * OLL / PLL recognition — colour neutral, any colour scheme.
3
+ *
4
+ * Works on colours relative to the centres, like a solver looking at the
5
+ * cube: OLL is where the top colour faces on the eight last-layer pieces;
6
+ * PLL is the colours of the top rows on the four sides, relative to their
7
+ * centres. Both are compared up to the angle you look from (pre-AUF) and,
8
+ * for PLL, up to a final U turn. The patterns are built from the cases'
9
+ * algorithms (caseData.ts), so every pattern is exactly one case — the
10
+ * tests check all 216 orientations and 288 permutations.
11
+ *
12
+ * recognizeLastLayer(cube.state)
13
+ * // { face: "U", oll: { id: "OLL 27", preAuf: "U'" } | "skip", pll: null } (null until OLL is done)
14
+ */
15
+ import { type Face, type State } from "../core/index.js";
16
+ export { OLL_CASES, PLL_CASES, type LastLayerCase } from "./caseData.js";
17
+ export interface CaseMatch {
18
+ /** "OLL 27", "T"… */
19
+ id: string;
20
+ group: string;
21
+ /** The U turn before the case's algorithm, as the cube is held now ("" = none). */
22
+ preAuf: string;
23
+ alg: string;
24
+ }
25
+ /** The OLL case of a cube held with the last layer on U (F2L solved) — null for an OLL skip. */
26
+ export declare function recognizeOll(s: State): CaseMatch | null;
27
+ /** The PLL case of a cube held with the last layer on U (oriented) — null when it's solved up to AUF. */
28
+ export declare function recognizePll(s: State): CaseMatch | null;
29
+ export interface LastLayer {
30
+ /** Physical face the last layer is on. */
31
+ face: Face;
32
+ /** The OLL case, or "skip" (already oriented). */
33
+ oll: CaseMatch | "skip";
34
+ /** The PLL case once oriented ("skip": solved up to AUF); null while OLL isn't done. */
35
+ pll: CaseMatch | "skip" | null;
36
+ }
37
+ /**
38
+ * OLL and PLL of a cube with F2L solved on any face (colour neutral), or null
39
+ * if no F2L is solved. `preAuf` is said as the cube is held with the last
40
+ * layer on top and that frame's front in front.
41
+ */
42
+ export declare function recognizeLastLayer(state: State): LastLayer | null;
package/cfop/cases.js ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * OLL / PLL recognition — colour neutral, any colour scheme.
3
+ *
4
+ * Works on colours relative to the centres, like a solver looking at the
5
+ * cube: OLL is where the top colour faces on the eight last-layer pieces;
6
+ * PLL is the colours of the top rows on the four sides, relative to their
7
+ * centres. Both are compared up to the angle you look from (pre-AUF) and,
8
+ * for PLL, up to a final U turn. The patterns are built from the cases'
9
+ * algorithms (caseData.ts), so every pattern is exactly one case — the
10
+ * tests check all 216 orientations and 288 permutations.
11
+ *
12
+ * recognizeLastLayer(cube.state)
13
+ * // { face: "U", oll: { id: "OLL 27", preAuf: "U'" } | "skip", pll: null } (null until OLL is done)
14
+ */
15
+ import { CORNER_FACELETS, EDGE_FACELETS, FRAMES, applyMoves, colorAt, checks, invert, parseAlg, solvedState, view } from "../core/index.js";
16
+ import { OLL_CASES, PLL_CASES } from "./caseData.js";
17
+ export { OLL_CASES, PLL_CASES } from "./caseData.js";
18
+ /** Last-layer pieces in turning order (index + 1 = the next one round the top): corners URF UFL ULB UBR, edges UF UL UB UR. */
19
+ const LL_CORNERS = [0, 1, 2, 3].map((i) => CORNER_FACELETS[i]); // U sticker first, then clockwise
20
+ const LL_EDGES = [1, 2, 3, 0].map((i) => EDGE_FACELETS[i]); // U sticker first
21
+ /** Top rows of the side faces in the same turning order as the pieces (F, L, B, R), each left to right. */
22
+ const SIDE_ROWS = [[18, 19, 20], [36, 37, 38], [45, 46, 47], [9, 10, 11]];
23
+ const SIDE_CENTRES = [22, 40, 49, 13];
24
+ const U_CENTRE = 4;
25
+ const AUFS = ["", "U", "U2", "U'"];
26
+ const shift = (a, k) => a.map((_, i) => a[(i + k) % a.length]);
27
+ /** Where the top colour faces on each last-layer piece (corners 0–2 clockwise from U, edges 0–1), in turning order. */
28
+ function orientation(s) {
29
+ const top = colorAt(s, U_CENTRE);
30
+ return {
31
+ corners: LL_CORNERS.map((fs) => fs.findIndex((f) => colorAt(s, f) === top)),
32
+ edges: LL_EDGES.map((fs) => fs.findIndex((f) => colorAt(s, f) === top)),
33
+ };
34
+ }
35
+ const ollRaw = (s) => {
36
+ const o = orientation(s);
37
+ return `${o.corners.join("")}|${o.edges.join("")}`;
38
+ };
39
+ /** The same up to the angle it's seen from. */
40
+ const ollKey = (s) => {
41
+ const o = orientation(s);
42
+ return [0, 1, 2, 3].map((k) => `${shift(o.corners, k).join("")}|${shift(o.edges, k).join("")}`).sort()[0];
43
+ };
44
+ /** Each top-row sticker's colour as a step round the sides from its own face (0 = the face's centre colour). */
45
+ function sideOffsets(s) {
46
+ const ring = SIDE_CENTRES.map((c) => colorAt(s, c));
47
+ return SIDE_ROWS.map((row, face) => row.map((f) => (ring.indexOf(colorAt(s, f)) - face + 4) % 4));
48
+ }
49
+ const pllText = (rows, add) => rows.map((r) => r.map((v) => (v + add) % 4).join("")).join("|");
50
+ /** Up to a final U turn (all offsets move together). */
51
+ const pllRaw = (s) => {
52
+ const rows = sideOffsets(s);
53
+ return [0, 1, 2, 3].map((a) => pllText(rows, a)).sort()[0];
54
+ };
55
+ /** …and up to the angle it's seen from. */
56
+ const pllKey = (s) => {
57
+ const rows = sideOffsets(s);
58
+ return [0, 1, 2, 3].flatMap((k) => [0, 1, 2, 3].map((a) => pllText(shift(rows, k), a))).sort()[0];
59
+ };
60
+ function table(cases, key, raw) {
61
+ const map = new Map();
62
+ for (const kase of cases) {
63
+ const s = applyMoves(solvedState(), invert(parseAlg(kase.alg)));
64
+ const k = key(s);
65
+ if (map.has(k))
66
+ throw new Error(`${kase.id} looks like ${map.get(k).kase.id}`);
67
+ map.set(k, { kase, raw: raw(s) });
68
+ }
69
+ return map;
70
+ }
71
+ let ollTable = null;
72
+ let pllTable = null;
73
+ const SOLVED_OLL = ollKey(solvedState());
74
+ const SOLVED_PLL = pllKey(solvedState());
75
+ /** The OLL case of a cube held with the last layer on U (F2L solved) — null for an OLL skip. */
76
+ export function recognizeOll(s) {
77
+ ollTable ??= table(OLL_CASES, ollKey, ollRaw);
78
+ const k = ollKey(s);
79
+ if (k === SOLVED_OLL)
80
+ return null;
81
+ const e = ollTable.get(k);
82
+ if (!e)
83
+ throw new Error("Not a last-layer orientation");
84
+ const preAuf = AUFS.find((a) => ollRaw(a ? applyMoves(s, a) : s) === e.raw) ?? "";
85
+ return { id: e.kase.id, group: e.kase.group, preAuf, alg: e.kase.alg };
86
+ }
87
+ /** The PLL case of a cube held with the last layer on U (oriented) — null when it's solved up to AUF. */
88
+ export function recognizePll(s) {
89
+ pllTable ??= table(PLL_CASES, pllKey, pllRaw);
90
+ const k = pllKey(s);
91
+ if (k === SOLVED_PLL)
92
+ return null;
93
+ const e = pllTable.get(k);
94
+ if (!e)
95
+ throw new Error("Not a last-layer permutation");
96
+ const preAuf = AUFS.find((a) => pllRaw(a ? applyMoves(s, a) : s) === e.raw) ?? "";
97
+ return { id: e.kase.id, group: e.kase.group, preAuf, alg: e.kase.alg };
98
+ }
99
+ /** One frame per bottom face — which one has F2L solved tells where the last layer is. */
100
+ const BY_BOTTOM = ["U", "R", "F", "D", "L", "B"].map((d) => FRAMES.find((f) => f.face.D === d));
101
+ /**
102
+ * OLL and PLL of a cube with F2L solved on any face (colour neutral), or null
103
+ * if no F2L is solved. `preAuf` is said as the cube is held with the last
104
+ * layer on top and that frame's front in front.
105
+ */
106
+ export function recognizeLastLayer(state) {
107
+ for (const frame of BY_BOTTOM) {
108
+ const s = view(state, frame);
109
+ if (!checks.f2lSolved(s))
110
+ continue;
111
+ const oll = recognizeOll(s);
112
+ const pll = oll ? null : recognizePll(s);
113
+ return { face: frame.face.U, oll: oll ?? "skip", pll: oll ? null : (pll ?? "skip") };
114
+ }
115
+ return null;
116
+ }
package/cfop/f2l.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * F2L case recognition — which of the 41 standard cases a slot's pair is
3
+ * in, colour neutral and scheme-free (colours relative to the centres), for
4
+ * a cube held with the cross on D. The pair counts when its corner and edge
5
+ * are in the top layer or in their own slot (a piece stuck in another slot
6
+ * is not one of the 41 — "advanced F2L").
7
+ *
8
+ * recognizeF2L(state, "FR") // { id: "F2L 7", group: "Connected Pairs", preAuf: "U'", alg: "U' R U2 R' U2 R U' R'" } | "solved" | null
9
+ */
10
+ import { type State } from "../core/index.js";
11
+ import type { F2LSlot } from "./slots.js";
12
+ export { F2L_CASES, type F2LCase } from "./f2lData.js";
13
+ export interface F2LMatch {
14
+ id: string;
15
+ group: string;
16
+ slot: F2LSlot;
17
+ /** The U turn before the algorithm, as the cube is held (cross on D). */
18
+ preAuf: string;
19
+ alg: string;
20
+ }
21
+ /** Is the slot's pair in the top layer or its own slot (one of the 41 cases, or solved)? */
22
+ export declare function isStandardF2L(s: State, slot: F2LSlot): boolean;
23
+ /**
24
+ * The F2L case of `slot` for a cube held with the cross on D: the case,
25
+ * "solved" (pair in place), or null when a piece is in another slot
26
+ * (not one of the 41).
27
+ */
28
+ export declare function recognizeF2L(s: State, slot: F2LSlot): F2LMatch | "solved" | null;
package/cfop/f2l.js ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * F2L case recognition — which of the 41 standard cases a slot's pair is
3
+ * in, colour neutral and scheme-free (colours relative to the centres), for
4
+ * a cube held with the cross on D. The pair counts when its corner and edge
5
+ * are in the top layer or in their own slot (a piece stuck in another slot
6
+ * is not one of the 41 — "advanced F2L").
7
+ *
8
+ * recognizeF2L(state, "FR") // { id: "F2L 7", group: "Connected Pairs", preAuf: "U'", alg: "U' R U2 R' U2 R U' R'" } | "solved" | null
9
+ */
10
+ import { CORNER_FACELETS, EDGE_FACELETS, applyMoves, colorAt, invert, parseAlg, solvedState } from "../core/index.js";
11
+ import { F2L_CASES } from "./f2lData.js";
12
+ export { F2L_CASES } from "./f2lData.js";
13
+ const AUFS = ["", "U", "U2", "U'"];
14
+ const CENTRE = { U: 4, R: 13, F: 22, D: 31, L: 40, B: 49 };
15
+ const SIDES = { FR: ["F", "R"], FL: ["F", "L"], BL: ["B", "L"], BR: ["B", "R"] };
16
+ /** Kociemba positions of each slot's corner and edge (DFR / FR…). */
17
+ const HOME = {
18
+ FR: { corner: 4, edge: 8 },
19
+ FL: { corner: 5, edge: 9 },
20
+ BL: { corner: 6, edge: 10 },
21
+ BR: { corner: 7, edge: 11 },
22
+ };
23
+ const TOP_CORNERS = [0, 1, 2, 3], TOP_EDGES = [0, 1, 2, 3];
24
+ /** Where the slot's pair is (Kociemba positions) and how it's turned, read by colour. */
25
+ function pairOf(s, slot) {
26
+ const bottom = colorAt(s, CENTRE.D);
27
+ const [a, b] = SIDES[slot].map((f) => colorAt(s, CENTRE[f]));
28
+ const corner = CORNER_FACELETS.findIndex((fs) => [bottom, a, b].every((c) => fs.some((f) => colorAt(s, f) === c)));
29
+ const edge = EDGE_FACELETS.findIndex((fs) => [a, b].every((c) => fs.some((f) => colorAt(s, f) === c)));
30
+ if (corner < 0 || edge < 0)
31
+ return null;
32
+ return {
33
+ corner,
34
+ cornerOri: CORNER_FACELETS[corner].findIndex((f) => colorAt(s, f) === bottom),
35
+ edge,
36
+ edgeOri: EDGE_FACELETS[edge].findIndex((f) => colorAt(s, f) === a),
37
+ };
38
+ }
39
+ const pairText = (p) => `${p.corner}.${p.cornerOri}|${p.edge}.${p.edgeOri}`;
40
+ /** Up to the angle (a U turn before the algorithm). */
41
+ function key(s, slot) {
42
+ const texts = AUFS.map((u) => pairOf(u ? applyMoves(s, u) : s, slot)).map((p) => (p ? pairText(p) : ""));
43
+ return texts.includes("") ? null : texts.sort()[0];
44
+ }
45
+ const tables = new Map();
46
+ function table(slot) {
47
+ let t = tables.get(slot);
48
+ if (t)
49
+ return t;
50
+ t = new Map();
51
+ for (const kase of F2L_CASES) {
52
+ const s = applyMoves(solvedState(), invert(parseAlg(kase.algs[slot])));
53
+ const k = key(s, slot);
54
+ if (t.has(k))
55
+ throw new Error(`${slot} ${kase.id} looks like ${t.get(k).kase.id}`);
56
+ t.set(k, { kase, raw: pairText(pairOf(s, slot)) });
57
+ }
58
+ tables.set(slot, t);
59
+ return t;
60
+ }
61
+ /** Is the slot's pair in the top layer or its own slot (one of the 41 cases, or solved)? */
62
+ export function isStandardF2L(s, slot) {
63
+ const p = pairOf(s, slot);
64
+ return !!p && (TOP_CORNERS.includes(p.corner) || p.corner === HOME[slot].corner) && (TOP_EDGES.includes(p.edge) || p.edge === HOME[slot].edge);
65
+ }
66
+ /**
67
+ * The F2L case of `slot` for a cube held with the cross on D: the case,
68
+ * "solved" (pair in place), or null when a piece is in another slot
69
+ * (not one of the 41).
70
+ */
71
+ export function recognizeF2L(s, slot) {
72
+ const p = pairOf(s, slot);
73
+ if (!p)
74
+ return null;
75
+ if (p.corner === HOME[slot].corner && p.cornerOri === 0 && p.edge === HOME[slot].edge && p.edgeOri === 0)
76
+ return "solved";
77
+ if (!isStandardF2L(s, slot))
78
+ return null;
79
+ const k = key(s, slot);
80
+ const e = k ? table(slot).get(k) : undefined;
81
+ if (!e)
82
+ return null;
83
+ const preAuf = AUFS.find((u) => {
84
+ const q = pairOf(u ? applyMoves(s, u) : s, slot);
85
+ return q !== null && pairText(q) === e.raw;
86
+ }) ?? "";
87
+ return { id: e.kase.id, group: e.kase.group, slot, preAuf, alg: e.kase.algs[slot] };
88
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * F2L cases as data — numbering and groups as on speedcubedb.com (F2L 1–41:
3
+ * the pair in the top layer or its own slot), one standard algorithm per
4
+ * case for each slot (speedcubedb's main one). Recognition is built from
5
+ * them, so any algorithm for the case works.
6
+ */
7
+ import type { F2LSlot } from "./slots.js";
8
+ export interface F2LCase {
9
+ /** "F2L 1"… */
10
+ id: string;
11
+ group: string;
12
+ /** An algorithm per slot (cube held with the cross on D). */
13
+ algs: Readonly<Record<F2LSlot, string>>;
14
+ }
15
+ export declare const F2L_CASES: readonly F2LCase[];
@@ -0,0 +1,49 @@
1
+ /**
2
+ * F2L cases as data — numbering and groups as on speedcubedb.com (F2L 1–41:
3
+ * the pair in the top layer or its own slot), one standard algorithm per
4
+ * case for each slot (speedcubedb's main one). Recognition is built from
5
+ * them, so any algorithm for the case works.
6
+ */
7
+ export const F2L_CASES = [
8
+ { id: "F2L 1", group: "Free Pairs", algs: { FR: "U R U' R'", FL: "F' r U r'", BL: "U L U' L'", BR: "U f R' f'" } },
9
+ { id: "F2L 2", group: "Free Pairs", algs: { FR: "F R' F' R", FL: "U' L' U L", BL: "U' f' L f", BR: "U' R' U R" } },
10
+ { id: "F2L 3", group: "Free Pairs", algs: { FR: "F' U' F", FL: "L' U' L", BL: "y R' U' R y'", BR: "R' U' R" } },
11
+ { id: "F2L 4", group: "Free Pairs", algs: { FR: "R U R'", FL: "F U F'", BL: "L U L'", BR: "f R f'" } },
12
+ { id: "F2L 5", group: "Disconnected Pairs", algs: { FR: "U' R U R' U2 R U' R'", FL: "U R' F r U' r' F' R", BL: "U' L U L' U2 L U' L'", BR: "U' R' F R U R' U' F' R" } },
13
+ { id: "F2L 6", group: "Disconnected Pairs", algs: { FR: "U' r U' R' U R U r'", FL: "U L' U' L U2 L' U L", BL: "U r U' r' U' L U F L'", BR: "U R' U' R U2 R' U R" } },
14
+ { id: "F2L 7", group: "Disconnected Pairs", algs: { FR: "U' R U2 R' U' R U2 R'", FL: "F U R U2 R' U F'", BL: "U' L U2 L' U2 L U' L'", BR: "r U2 R2 U' R2 U' r'" } },
15
+ { id: "F2L 8", group: "Disconnected Pairs", algs: { FR: "d R' U2 R U R' U2 R y", FL: "U L' U2 L U L' U2 L", BL: "l' U2 L2 U L2 U l", BR: "U R' U2 R U R' U2 R" } },
16
+ { id: "F2L 9", group: "Disconnected Pairs", algs: { FR: "U' R U' R' U F' U' F", FL: "U L' U' L U' L' U' L", BL: "y U R' U' R U' R' U' R y'", BR: "U R' U' R U' R' U' R" } },
17
+ { id: "F2L 10", group: "Disconnected Pairs", algs: { FR: "U' R U R' U R U R'", FL: "U L' U L U' F U F'", BL: "U' L U L' U L U L'", BR: "U R' U R U' f R f'" } },
18
+ { id: "F2L 11", group: "Connected Pairs", algs: { FR: "U' R U2 R' U F' U' F", FL: "L' U L U' L' U L U2 L' U L", BL: "U' L U2 L' U f' L' f", BR: "R' U R U' R' U R U2 R' U R" } },
19
+ { id: "F2L 12", group: "Connected Pairs", algs: { FR: "R U' R' U R U' R' U2 R U' R'", FL: "U L' U2 L U' F U F'", BL: "L' U2 L2 U L2 U L", BR: "U R' U2 R U' f R f'" } },
20
+ { id: "F2L 13", group: "Connected Pairs", algs: { FR: "y' U R' U R U' R' U' R y", FL: "U L' U L U' L' U' L", BL: "d L' U L U' L' U' L y", BR: "U R' U R U' R' U' R" } },
21
+ { id: "F2L 14", group: "Connected Pairs", algs: { FR: "U' R U' R' U R U R'", FL: "d' L U' L' U L U L' y'", BL: "U' L U' L' U L U L'", BR: "y U' R U' R' U R U R' y'" } },
22
+ { id: "F2L 15", group: "Connected Pairs", algs: { FR: "R' D' R U' R' D R U R U' R'", FL: "L' U L U2 F U F'", BL: "L U L' U2 L U' L' U L U' L'", BR: "R' U R U2 f R f'" } },
23
+ { id: "F2L 16", group: "Connected Pairs", algs: { FR: "R U' R' U2 F' U' F", FL: "F U' R U' R' U2 F'", BL: "L U' L' U2 f' L' f", BR: "R' U' R U2 R' U R U' R' U R" } },
24
+ { id: "F2L 17", group: "Connected Pairs", algs: { FR: "R U2 R' U' R U R'", FL: "y L U2 L' U' L U L' y'", BL: "L U2 L' U' L U L'", BR: "y' L U2 L' U' L U L' y" } },
25
+ { id: "F2L 18", group: "Connected Pairs", algs: { FR: "y' R' U2 R U R' U' R y", FL: "L' U2 L U L' U' L", BL: "y R' U2 R U R' U' R y'", BR: "R' U2 R U R' U' R" } },
26
+ { id: "F2L 19", group: "Disconnected Pairs", algs: { FR: "U R U2 R' U R U' R'", FL: "U L' U L2 F' L' F L' U L", BL: "U L U2 L' U L U' L'", BR: "y U R U2 R' U R U' R' y'" } },
27
+ { id: "F2L 20", group: "Disconnected Pairs", algs: { FR: "y' U' R' U2 R U' R' U R y", FL: "U' L' U2 L U' L' U L", BL: "y U' R' U2 R U' R' U R y'", BR: "U' R' U2 R U' R' U R" } },
28
+ { id: "F2L 21", group: "Disconnected Pairs", algs: { FR: "U2 R U R' U R U' R'", FL: "l' U l U2 l' U' l", BL: "L U' L' U2 L U L'", BR: "r' U r U2 r' U' r" } },
29
+ { id: "F2L 22", group: "Disconnected Pairs", algs: { FR: "r U' r' U2 r U r'", FL: "L' U L U2 L' U' L", BL: "l U' l' U2 l U l'", BR: "R' U R U2 R' U' R" } },
30
+ { id: "F2L 23", group: "Connected Pairs", algs: { FR: "U R U' R' U' R U' R' U R U' R'", FL: "F' U' L' U L F L' U L", BL: "U L U' L' U' L U' L' U L U' L'", BR: "U R' F R' F' R2 U' R' U R" } },
31
+ { id: "F2L 24", group: "Connected Pairs", algs: { FR: "F U R U' R' F' R U' R'", FL: "U' L' U L U L' U L U' L' U L", BL: "U2 r U R' U R U2 B r'", BR: "R' U' R U2 R' U' R U R' U' R" } },
32
+ { id: "F2L 25", group: "Corner In Slot", algs: { FR: "U' R' F R F' R U R'", FL: "U' L' U L F' r U r'", BL: "L U' L' U' L U' L' U L U L'", BR: "U' R' U M U' R U M'" } },
33
+ { id: "F2L 26", group: "Corner In Slot", algs: { FR: "U R U' R' F R' F' R", FL: "r U r' U' r' F r F'", BL: "L S L' U L S' L'", BR: "U f R f' U' R' U' R" } },
34
+ { id: "F2L 27", group: "Corner In Slot", algs: { FR: "R U' R' U R U' R'", FL: "L' U' L U F' r U r'", BL: "L U' L' U L U' L'", BR: "R' U2 R' F R F' R" } },
35
+ { id: "F2L 28", group: "Corner In Slot", algs: { FR: "R U R' U' F R' F' R", FL: "L' U L U' L' U L", BL: "L U2 L F' L' F L'", BR: "R' U R U' R' U R" } },
36
+ { id: "F2L 29", group: "Corner In Slot", algs: { FR: "R' F R F' U R U' R'", FL: "L' U' L U L' U' L", BL: "y R' U' R U R' U' R y'", BR: "R' U' R U R' U' R" } },
37
+ { id: "F2L 30", group: "Corner In Slot", algs: { FR: "R U R' U' R U R'", FL: "L F' L' F U' L' U L", BL: "L U L' U' L U L'", BR: "y' L U L' U' L U L' y" } },
38
+ { id: "F2L 31", group: "Edge In Slot", algs: { FR: "U' R' F R F' R U' R'", FL: "U L F' L' F L' U L", BL: "L U' L F' L' F L'", BR: "R' U R' F R F' R" } },
39
+ { id: "F2L 32", group: "Edge In Slot", algs: { FR: "U R U' R' U R U' R' U R U' R'", FL: "U' L' U L U' L' U L U' L' U L", BL: "L U L' U' L U L' U' L U L'", BR: "U' R' U R U' R' U R U' R' U R" } },
40
+ { id: "F2L 33", group: "Edge In Slot", algs: { FR: "U' R U' R' U2 R U' R'", FL: "R' D R U' R' D' R", BL: "U' L U' L' U2 L U' L'", BR: "U' R D R' U R D' R'" } },
41
+ { id: "F2L 34", group: "Edge In Slot", algs: { FR: "U R U R' U2 R U R'", FL: "U L' U L U2 L' U L", BL: "U L U L' U2 L U L'", BR: "U R' U R U R' U2 R" } },
42
+ { id: "F2L 35", group: "Edge In Slot", algs: { FR: "U' R U R' U F' U' F", FL: "U2 F U F' U' L' U L", BL: "U' L U L' U f' L' f", BR: "U' f R f' U R' U' R" } },
43
+ { id: "F2L 36", group: "Edge In Slot", algs: { FR: "U F' U' F U' R U R'", FL: "U L' U' L d' L U L' y'", BL: "U f' L' f U' L U L'", BR: "U R' U' R U' f R f'" } },
44
+ { id: "F2L 37", group: "Pieces In Slot", algs: { FR: "R2 U2 F R2 F' U2 R' U R'", FL: "L2 U2 F' L2 F U2 L U' L", BL: "L U' L' l' U2 L2 U L2 U l", BR: "R' U R r U2 R2 U' R2 U' r'" } },
45
+ { id: "F2L 38", group: "Pieces In Slot", algs: { FR: "R U' R' U' R U R' U2 R U' R'", FL: "L' U L U' L' U2 L U' L' U L", BL: "L U L' U' L U2 L' U' L U L'", BR: "R' U' R U2 R' U R U' R' U' R" } },
46
+ { id: "F2L 39", group: "Pieces In Slot", algs: { FR: "R U' R' U R U2 R' U R U' R'", FL: "L' U' L U L' U2 L U L' U' L", BL: "L U L' U2 L U' L' U L U L'", BR: "R' U' R U R' U2 R U R' U' R" } },
47
+ { id: "F2L 40", group: "Pieces In Slot", algs: { FR: "r U' r' U2 r U r' R U R'", FL: "L' U L F R U2 R' F'", BL: "l U' l' U2 l U l' L U L'", BR: "R' U R r' U r U2 r' U' r" } },
48
+ { id: "F2L 41", group: "Pieces In Slot", algs: { FR: "R U' R' r U' r' U2 r U r'", FL: "l' U l U2 l' U' l L' U' L", BL: "f' L f U' L U L' U L U L'", BR: "r' U r U2 r' U' r R' U' R" } },
49
+ ];
@@ -0,0 +1,41 @@
1
+ /**
2
+ * CFOP — cross, F2L (four pairs, any order), OLL, PLL, AUF — and its masks.
3
+ * Built only from @cubecore/core check primitives, so it is colour and
4
+ * orientation neutral: the cross can be on any face, in any colour scheme.
5
+ *
6
+ * Later here: OLL / PLL / COLL case recognition and alg sets as data.
7
+ */
8
+ import { type Frame, type Mask, type MaskRule, type Method, type Stage, type StageDef } from "../core/index.js";
9
+ export declare const CFOP: Method;
10
+ export type CfopMask = "cross" | "f2l" | "oll" | "pll" | "coll" | "ocll" | "ell" | "cll" | "ls" | "els" | "cls" | "vls" | "zbls" | "epll" | "cpll" | "zbll";
11
+ export declare const CFOP_MASKS: Record<CfopMask, MaskRule>;
12
+ /** A CFOP mask for a cube held in `frame` (e.g. the frame the tracker found the cross in). */
13
+ export declare const cfopMask: (name: CfopMask, frame?: Frame) => Mask;
14
+ import type { F2LSlot } from "./slots.js";
15
+ export type { F2LSlot } from "./slots.js";
16
+ /**
17
+ * CFOP trainer stages (canonical: cross on D). Each is a StageDef — plain
18
+ * data; `stageSolver(def)` answers distance / optimal solutions / next moves
19
+ * and `stageScramble({ stage: def, length })` draws a case at an exact level.
20
+ */
21
+ export declare const CFOP_TRAINERS: {
22
+ readonly cross: () => StageDef;
23
+ readonly xcross: (slot?: F2LSlot) => StageDef;
24
+ /** Two slots: adjacent (FR + FL…) or opposite (FR + BL…). */
25
+ readonly xxcross: (a?: F2LSlot, b?: F2LSlot) => StageDef;
26
+ /**
27
+ * Free pair: cross solved and the slot's pair FORMED, one standard insert
28
+ * away — the inserted state or any of the 4 extractions × 4 AUFs (17 goal
29
+ * states, as in act's pairing trainer).
30
+ */
31
+ readonly pair: (slot?: F2LSlot) => StageDef;
32
+ };
33
+ export * from "./cases.js";
34
+ export * from "./f2l.js";
35
+ /**
36
+ * Last-layer stages that also say which case came up: OLL gets the OLL case
37
+ * after F2L ("OLL 27", "OLL skip"), PLL the PLL case after OLL ("T", "PLL
38
+ * skip") — StageBoundary.case, MethodTracker.current.case. ZZ / Petrus can
39
+ * use the same.
40
+ */
41
+ export declare function withLastLayerCases(stages: readonly Stage[]): Stage[];
package/cfop/index.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * CFOP — cross, F2L (four pairs, any order), OLL, PLL, AUF — and its masks.
3
+ * Built only from @cubecore/core check primitives, so it is colour and
4
+ * orientation neutral: the cross can be on any face, in any colour scheme.
5
+ *
6
+ * Later here: OLL / PLL / COLL case recognition and alg sets as data.
7
+ */
8
+ import { IDENTITY_FRAME, LAST_LAYER_STAGES, PIECE, buildMask, checks as C, checks, countedStages } from "../core/index.js";
9
+ import { recognizeOll, recognizePll } from "./cases.js";
10
+ export const CFOP = {
11
+ id: "cfop",
12
+ name: "CFOP",
13
+ stages: [
14
+ { id: "cross", label: "Cross", done: C.crossSolved },
15
+ ...countedStages("f2l", "F2L", C.solvedPairs, C.crossSolved),
16
+ ...withLastLayerCases(LAST_LAYER_STAGES),
17
+ ],
18
+ };
19
+ export const CFOP_MASKS = {
20
+ cross: (f, c) => (c.kind === "center" || (c.kind === "edge" && c.pos[1] === -1) ? "regular" : "ignored"),
21
+ f2l: (f, c) => (c.pos[1] <= 0 || c.kind === "center" ? "regular" : "ignored"),
22
+ // Orientation: last-layer pieces show only their top-facing colour; centres stay in colour.
23
+ oll: (f, c) => (c.kind === "center" ? "regular" : lastLayer(c) ? (f.face === "U" ? "regular" : "ignored") : "dim"),
24
+ // Permutation: everything in colour, the first two layers dimmed.
25
+ pll: (f, c) => (lastLayer(c) || c.kind === "center" ? "regular" : "dim"),
26
+ coll: (f, c) => (c.kind === "center" ? "regular" : lastLayer(c) ? (c.kind === "corner" || f.face === "U" ? "regular" : "ignored") : "dim"),
27
+ ocll: (f, c) => (c.kind === "center" ? "regular" : lastLayer(c) ? (c.kind === "corner" && f.face === "U" ? "regular" : "ignored") : "dim"),
28
+ cll: (f, c) => (c.kind === "center" ? "regular" : lastLayer(c) ? (c.kind === "corner" ? "regular" : "ignored") : "dim"),
29
+ ell: (f, c) => (c.kind === "center" ? "regular" : lastLayer(c) ? (c.kind === "edge" ? "regular" : "ignored") : "dim"),
30
+ // PLL parts: the pieces being permuted in colour, the others dimmed (EPLL) or hidden (CPLL).
31
+ epll: (f, c) => (c.kind === "center" || (lastLayer(c) && c.kind === "edge") ? "regular" : "dim"),
32
+ cpll: (f, c) => (c.kind === "center" || (lastLayer(c) && c.kind === "corner") ? "regular" : lastLayer(c) ? "ignored" : "dim"),
33
+ zbll: (f, c) => (lastLayer(c) || c.kind === "center" ? "regular" : "dim"),
34
+ // Last slot (canonical FR): the pair in colour, the rest of F2L dimmed, the last layer by variant.
35
+ ls: (f, c) => (c.kind === "center" || inSlot(c) ? "regular" : c.pos[1] <= 0 ? "dim" : "ignored"),
36
+ // ZBLS / ELS orient the last-layer edges while inserting: show them as orientation material.
37
+ zbls: (f, c) => (c.kind === "center" || inSlot(c) ? "regular" : c.pos[1] <= 0 ? "dim" : c.kind === "edge" && f.face === "U" ? "oriented" : "ignored"),
38
+ els: (f, c) => (c.kind === "center" || (inSlot(c) && c.kind === "edge") ? "regular" : c.pos[1] <= 0 ? (inSlot(c) ? "ignored" : "dim") : c.kind === "edge" && f.face === "U" ? "oriented" : "ignored"),
39
+ // CLS / VLS / WVLS orient the last-layer corners while inserting: their top stickers matter.
40
+ cls: (f, c) => (c.kind === "center" || inSlot(c) ? "regular" : c.pos[1] <= 0 ? "dim" : f.face === "U" ? "regular" : "ignored"),
41
+ vls: (f, c) => (c.kind === "center" || inSlot(c) ? "regular" : c.pos[1] <= 0 ? "dim" : f.face === "U" ? "regular" : "ignored"),
42
+ };
43
+ /** The canonical last slot: the FR pair (front-right, bottom two layers). */
44
+ function inSlot(c) {
45
+ return c.pos[0] === 1 && c.pos[2] === 1 && c.pos[1] <= 0;
46
+ }
47
+ function lastLayer(c) {
48
+ return c.pos[1] === 1 && c.kind !== "center";
49
+ }
50
+ /** A CFOP mask for a cube held in `frame` (e.g. the frame the tracker found the cross in). */
51
+ export const cfopMask = (name, frame = IDENTITY_FRAME) => buildMask(CFOP_MASKS[name], frame);
52
+ const CROSS_PIECES = [PIECE.DR, PIECE.DF, PIECE.DL, PIECE.DB];
53
+ const SLOT_PIECES = {
54
+ FR: { edge: PIECE.FR, corner: PIECE.DFR },
55
+ FL: { edge: PIECE.FL, corner: PIECE.DLF },
56
+ BL: { edge: PIECE.BL, corner: PIECE.DBL },
57
+ BR: { edge: PIECE.BR, corner: PIECE.DRB },
58
+ };
59
+ /** The four ways to take a paired slot out (the pair is then one insert away), per slot. */
60
+ const EXTRACTIONS = {
61
+ FR: ["R U R'", "R U' R'", "F' U F", "F' U' F"],
62
+ FL: ["L' U L", "L' U' L", "F U F'", "F U' F'"],
63
+ BL: ["L U L'", "L U' L'", "B' U B", "B' U' B"],
64
+ BR: ["R' U R", "R' U' R", "B U B'", "B U' B'"],
65
+ };
66
+ /**
67
+ * CFOP trainer stages (canonical: cross on D). Each is a StageDef — plain
68
+ * data; `stageSolver(def)` answers distance / optimal solutions / next moves
69
+ * and `stageScramble({ stage: def, length })` draws a case at an exact level.
70
+ */
71
+ export const CFOP_TRAINERS = {
72
+ cross: () => ({ name: "cross", pieces: CROSS_PIECES, groups: [[0, 1, 2, 3]] }),
73
+ xcross: (slot = "FR") => ({
74
+ name: `xcross-${slot}`,
75
+ pieces: [...CROSS_PIECES, SLOT_PIECES[slot].edge, SLOT_PIECES[slot].corner],
76
+ groups: [[0, 1, 2, 3, 4], [0, 1, 2, 3, 5]],
77
+ }),
78
+ /** Two slots: adjacent (FR + FL…) or opposite (FR + BL…). */
79
+ xxcross: (a = "FR", b = "FL") => ({
80
+ name: `xxcross-${a}-${b}`,
81
+ pieces: [...CROSS_PIECES, SLOT_PIECES[a].edge, SLOT_PIECES[a].corner, SLOT_PIECES[b].edge, SLOT_PIECES[b].corner],
82
+ groups: [[0, 1, 2, 3, 4], [0, 1, 2, 3, 5], [0, 1, 2, 3, 6], [0, 1, 2, 3, 7]],
83
+ }),
84
+ /**
85
+ * Free pair: cross solved and the slot's pair FORMED, one standard insert
86
+ * away — the inserted state or any of the 4 extractions × 4 AUFs (17 goal
87
+ * states, as in act's pairing trainer).
88
+ */
89
+ pair: (slot = "FR") => ({
90
+ name: `pair-${slot}`,
91
+ pieces: [...CROSS_PIECES, SLOT_PIECES[slot].edge, SLOT_PIECES[slot].corner],
92
+ groups: [[0, 1, 2, 3, 4], [0, 1, 2, 3, 5]],
93
+ goals: ["", ...EXTRACTIONS[slot].flatMap((x) => ["", " U", " U2", " U'"].map((auf) => x + auf))],
94
+ }),
95
+ };
96
+ export * from "./cases.js";
97
+ export * from "./f2l.js";
98
+ /**
99
+ * Last-layer stages that also say which case came up: OLL gets the OLL case
100
+ * after F2L ("OLL 27", "OLL skip"), PLL the PLL case after OLL ("T", "PLL
101
+ * skip") — StageBoundary.case, MethodTracker.current.case. ZZ / Petrus can
102
+ * use the same.
103
+ */
104
+ export function withLastLayerCases(stages) {
105
+ return stages.map((st) => st.id === "oll"
106
+ ? { ...st, recognize: (s) => (checks.f2lSolved(s) ? (recognizeOll(s)?.id ?? "OLL skip") : undefined) }
107
+ : st.id === "pll"
108
+ ? { ...st, recognize: (s) => (checks.f2lSolved(s) && checks.topOriented(s) ? (recognizePll(s)?.id ?? "PLL skip") : undefined) }
109
+ : st);
110
+ }
@@ -0,0 +1,2 @@
1
+ /** An F2L slot, cube held with the cross on D. */
2
+ export type F2LSlot = "FR" | "FL" | "BL" | "BR";
package/cfop/slots.js ADDED
@@ -0,0 +1 @@
1
+ export {};