@houstonp/rubiks-cube 2.0.0 → 3.0.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 (76) hide show
  1. package/README.md +494 -63
  2. package/package.json +22 -12
  3. package/src/core/index.js +478 -0
  4. package/src/rubiksCube/index.js +3 -0
  5. package/src/rubiksCube/rubiksCubeController.js +111 -0
  6. package/src/rubiksCube3D/centerPiece.js +79 -0
  7. package/src/rubiksCube3D/cornerPiece.js +114 -0
  8. package/src/rubiksCube3D/cubeConfig.js +87 -0
  9. package/src/rubiksCube3D/cubeSettings.js +30 -0
  10. package/src/rubiksCube3D/edgePiece.js +51 -0
  11. package/src/rubiksCube3D/index.js +3 -0
  12. package/src/rubiksCube3D/rubiksCube3D.js +383 -0
  13. package/src/rubiksCube3D/sticker.js +38 -0
  14. package/src/state/index.js +4 -0
  15. package/src/state/rubiksCubeState.js +471 -0
  16. package/src/state/slice.js +236 -0
  17. package/src/state/stickerState.js +185 -0
  18. package/src/{cameraState.js → webComponent/cameraState.js} +17 -25
  19. package/src/webComponent/constants.js +67 -0
  20. package/src/{debouncer.js → webComponent/debouncer.js} +1 -1
  21. package/src/webComponent/index.js +7 -0
  22. package/src/webComponent/rubiksCubeElement.js +379 -0
  23. package/src/{settings.js → webComponent/settings.js} +47 -22
  24. package/tests/common.js +10 -0
  25. package/tests/core.test.js +56 -0
  26. package/tests/rubiksCube.solves.test.js +41 -0
  27. package/tests/rubiksCube3D.solves.test.js +185 -0
  28. package/tests/rubiksCubeState.solves.test.js +35 -0
  29. package/tests/setup.js +36 -0
  30. package/tests/testScrambles.js +194 -0
  31. package/types/core/index.d.ts +451 -0
  32. package/types/rubiksCube/index.d.ts +3 -0
  33. package/types/rubiksCube/rubiksCubeController.d.ts +62 -0
  34. package/types/rubiksCube3D/centerPiece.d.ts +27 -0
  35. package/types/rubiksCube3D/cornerPiece.d.ts +38 -0
  36. package/types/rubiksCube3D/cubeConfig.d.ts +32 -0
  37. package/types/rubiksCube3D/cubeSettings.d.ts +33 -0
  38. package/types/rubiksCube3D/edgePiece.d.ts +18 -0
  39. package/types/rubiksCube3D/index.d.ts +3 -0
  40. package/types/rubiksCube3D/rubiksCube3D.d.ts +120 -0
  41. package/types/rubiksCube3D/sticker.d.ts +18 -0
  42. package/types/state/index.d.ts +5 -0
  43. package/types/state/rubiksCubeState.d.ts +108 -0
  44. package/types/state/slice.d.ts +46 -0
  45. package/types/state/stickerState.d.ts +34 -0
  46. package/types/webComponent/cameraState.d.ts +22 -0
  47. package/types/webComponent/constants.d.ts +57 -0
  48. package/types/webComponent/index.d.ts +6 -0
  49. package/types/webComponent/rubiksCubeElement.d.ts +89 -0
  50. package/types/{settings.d.ts → webComponent/settings.d.ts} +9 -8
  51. package/src/core.js +0 -127
  52. package/src/cube/cube.js +0 -324
  53. package/src/cube/cubeRotation.js +0 -79
  54. package/src/cube/cubeSettings.js +0 -18
  55. package/src/cube/cubeState.js +0 -192
  56. package/src/cube/slice.js +0 -143
  57. package/src/index.js +0 -496
  58. package/src/schema.js +0 -22
  59. package/src/threejs/materials.js +0 -54
  60. package/src/threejs/pieces.js +0 -100
  61. package/src/threejs/stickers.js +0 -40
  62. package/types/cameraState.d.ts +0 -19
  63. package/types/core.d.ts +0 -125
  64. package/types/cube/cube.d.ts +0 -102
  65. package/types/cube/cubeRotation.d.ts +0 -33
  66. package/types/cube/cubeSettings.d.ts +0 -17
  67. package/types/cube/cubeState.d.ts +0 -16
  68. package/types/cube/slice.d.ts +0 -15
  69. package/types/index.d.ts +0 -65
  70. package/types/schema.d.ts +0 -11
  71. package/types/threejs/materials.d.ts +0 -21
  72. package/types/threejs/pieces.d.ts +0 -28
  73. package/types/threejs/stickers.d.ts +0 -6
  74. /package/src/{globals.ts → webComponent/globals.ts} +0 -0
  75. /package/types/{debouncer.d.ts → webComponent/debouncer.d.ts} +0 -0
  76. /package/types/{globals.d.ts → webComponent/globals.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@houstonp/rubiks-cube",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Rubiks Cube Web Component built with threejs",
5
5
  "author": "Houston Pearse",
6
6
  "license": "MIT",
@@ -11,25 +11,32 @@
11
11
  "renderer",
12
12
  "threejs"
13
13
  ],
14
- "main": "./src/index.js",
15
- "types": "./types/index.d.ts",
16
14
  "exports": {
17
- ".": {
18
- "types": "./types/index.d.ts",
19
- "default": "./src/index.js"
15
+ "./view": {
16
+ "types": "./types/webComponent/index.d.ts",
17
+ "default": "./src/webComponent/index.js"
20
18
  },
21
- "./schema": {
22
- "types": "./types/schema.d.ts",
23
- "default": "./src/schema.js"
19
+ "./three": {
20
+ "types": "./types/rubiksCube3D/index.d.ts",
21
+ "default": "./src/rubiksCube3D/index.js"
22
+ },
23
+ "./controller": {
24
+ "types": "./types/rubiksCube/index.d.ts",
25
+ "default": "./src/rubiksCube/index.js"
24
26
  },
25
27
  "./core": {
26
- "types": "./types/core.d.ts",
27
- "default": "./src/core.js"
28
+ "types": "./types/core/index.d.ts",
29
+ "default": "./src/core/index.js"
30
+ },
31
+ "./state": {
32
+ "types": "./types/state/index.d.ts",
33
+ "default": "./src/state/index.js"
28
34
  }
29
35
  },
30
36
  "scripts": {
31
37
  "build:types": "tsc",
32
- "watch:types": "tsc --watch"
38
+ "watch:types": "tsc --watch",
39
+ "test": "bun test"
33
40
  },
34
41
  "dependencies": {
35
42
  "gsap": "^3.14.2",
@@ -44,7 +51,10 @@
44
51
  },
45
52
  "homepage": "https://github.com/houstonpearse/rubiks-cube#readme",
46
53
  "devDependencies": {
54
+ "@types/bun": "^1.3.9",
55
+ "@types/jsdom": "^27.0.0",
47
56
  "@types/three": "^0.182.0",
57
+ "jsdom": "^28.1.0",
48
58
  "typescript": "^5.9.3"
49
59
  }
50
60
  }
@@ -0,0 +1,478 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * reverses the direction of a movement or rotation
5
+ * @template {Movement | Rotation} T
6
+ * @param {T} action
7
+ * @returns {T}
8
+ * */
9
+ export function reverse(action) {
10
+ let reversedAction = action;
11
+ if (action.at(-1) === "'") {
12
+ reversedAction = /** @type {T} */ (action.slice(0, -1));
13
+ } else {
14
+ const newAction = /** @type {T} */ (action + "'");
15
+ reversedAction = newAction;
16
+ }
17
+ return reversedAction;
18
+ }
19
+
20
+ /**
21
+ * Translates notation meant for a 3x3 into notation a big cube. This is so that 3x3 algorithms can be used on a big cube if desired. eg. for a 6x6 r -> 5r
22
+ * @template {Movement | Rotation} T
23
+ * @param {T} action
24
+ * @param {CubeType} cubeType
25
+ * @returns {T}
26
+ * */
27
+ export function translate(action, cubeType) {
28
+ if (Object.values(Movements.Wide).includes(/** @type {WideMove} **/ (action))) {
29
+ return /** @type {T} */ (LayerCount[cubeType] - 1 + action);
30
+ }
31
+ return action;
32
+ }
33
+
34
+ /**
35
+ * @typedef {typeof Movements.Single[keyof typeof Movements.Single]} SingleMove
36
+ * @typedef {typeof Movements.Wide[keyof typeof Movements.Wide]} WideMove
37
+ * @typedef {typeof Movements.Two[keyof typeof Movements.Two]} TwoMove
38
+ * @typedef {typeof Movements.Three[keyof typeof Movements.Three]} ThreeMove
39
+ * @typedef {typeof Movements.Four[keyof typeof Movements.Four]} FourMove
40
+ * @typedef {typeof Movements.Five[keyof typeof Movements.Five]} FiveMove
41
+ * @typedef {typeof Movements.Six[keyof typeof Movements.Six]} SixMove
42
+ * @typedef {SingleMove | WideMove | TwoMove | ThreeMove | FourMove | FiveMove | SixMove} Movement
43
+ */
44
+ export const Movements = Object.freeze({
45
+ Single: Object.freeze({
46
+ R: 'R',
47
+ R2: 'R2',
48
+ RP: "R'",
49
+ L: 'L',
50
+ L2: 'L2',
51
+ LP: "L'",
52
+ U: 'U',
53
+ U2: 'U2',
54
+ UP: "U'",
55
+ D: 'D',
56
+ D2: 'D2',
57
+ DP: "D'",
58
+ F: 'F',
59
+ F2: 'F2',
60
+ FP: "F'",
61
+ B: 'B',
62
+ B2: 'B2',
63
+ BP: "B'",
64
+ M: 'M',
65
+ M2: 'M2',
66
+ MP: "M'",
67
+ E: 'E',
68
+ E2: 'E2',
69
+ EP: "E'",
70
+ S: 'S',
71
+ S2: 'S2',
72
+ SP: "S'",
73
+ }),
74
+ Wide: Object.freeze({
75
+ Rw: 'Rw',
76
+ Rw2: 'Rw2',
77
+ RwP: "Rw'",
78
+ r: 'r',
79
+ r2: 'r2',
80
+ rP: "r'",
81
+ Lw: 'Lw',
82
+ Lw2: 'Lw2',
83
+ LwP: "Lw'",
84
+ l: 'l',
85
+ l2: 'l2',
86
+ lP: "l'",
87
+ Fw: 'Fw',
88
+ Fw2: 'Fw2',
89
+ FwP: "Fw'",
90
+ f: 'f',
91
+ f2: 'f2',
92
+ fP: "f'",
93
+ Bw: 'Bw',
94
+ Bw2: 'Bw2',
95
+ BwP: "Bw'",
96
+ b: 'b',
97
+ b2: 'b2',
98
+ bP: "b'",
99
+ Uw: 'Uw',
100
+ Uw2: 'Uw2',
101
+ UwP: "Uw'",
102
+ u: 'u',
103
+ u2: 'u2',
104
+ uP: "u'",
105
+ Dw: 'Dw',
106
+ Dw2: 'Dw2',
107
+ DwP: "Dw'",
108
+ d: 'd',
109
+ d2: 'd2',
110
+ dP: "d'",
111
+ }),
112
+ Two: Object.freeze({
113
+ Rw: '2Rw',
114
+ Rw2: '2Rw2',
115
+ RwP: "2Rw'",
116
+ r: '2r',
117
+ r2: '2r2',
118
+ rP: "2r'",
119
+ R: '2R',
120
+ R2: '2R2',
121
+ RP: "2R'",
122
+ Lw: '2Lw',
123
+ Lw2: '2Lw2',
124
+ LwP: "2Lw'",
125
+ l: '2l',
126
+ l2: '2l2',
127
+ lP: "2l'",
128
+ L: '2L',
129
+ L2: '2L2',
130
+ LP: "2L'",
131
+ Fw: '2Fw',
132
+ Fw2: '2Fw2',
133
+ FwP: "2Fw'",
134
+ f: '2f',
135
+ f2: '2f2',
136
+ fP: "2f'",
137
+ F: '2F',
138
+ F2: '2F2',
139
+ FP: "2F'",
140
+ Bw: '2Bw',
141
+ Bw2: '2Bw2',
142
+ BwP: "2Bw'",
143
+ b: '2b',
144
+ b2: '2b2',
145
+ bP: "2b'",
146
+ B: '2B',
147
+ B2: '2B2',
148
+ BP: "2B'",
149
+ Uw: '2Uw',
150
+ Uw2: '2Uw2',
151
+ UwP: "2Uw'",
152
+ u: '2u',
153
+ u2: '2u2',
154
+ uP: "2u'",
155
+ U: '2U',
156
+ U2: '2U2',
157
+ UP: "2U'",
158
+ Dw: '2Dw',
159
+ Dw2: '2Dw2',
160
+ DwP: "2Dw'",
161
+ d: '2d',
162
+ d2: '2d2',
163
+ dP: "2d'",
164
+ D: '2D',
165
+ D2: '2D2',
166
+ DP: "2D'",
167
+ }),
168
+ Three: Object.freeze({
169
+ Rw: '3Rw',
170
+ Rw2: '3Rw2',
171
+ RwP: "3Rw'",
172
+ r: '3r',
173
+ r2: '3r2',
174
+ rP: "3r'",
175
+ R: '3R',
176
+ R2: '3R2',
177
+ RP: "3R'",
178
+ Lw: '3Lw',
179
+ Lw2: '3Lw2',
180
+ LwP: "3Lw'",
181
+ l: '3l',
182
+ l2: '3l2',
183
+ lP: "3l'",
184
+ L: '3L',
185
+ L2: '3L2',
186
+ LP: "3L'",
187
+ Fw: '3Fw',
188
+ Fw2: '3Fw2',
189
+ FwP: "3Fw'",
190
+ f: '3f',
191
+ f2: '3f2',
192
+ fP: "3f'",
193
+ F: '3F',
194
+ F2: '3F2',
195
+ FP: "3F'",
196
+ Bw: '3Bw',
197
+ Bw2: '3Bw2',
198
+ BwP: "3Bw'",
199
+ b: '3b',
200
+ b2: '3b2',
201
+ bP: "3b'",
202
+ B: '3B',
203
+ B2: '3B2',
204
+ BP: "3B'",
205
+ Uw: '3Uw',
206
+ Uw2: '3Uw2',
207
+ UwP: "3Uw'",
208
+ u: '3u',
209
+ u2: '3u2',
210
+ uP: "3u'",
211
+ U: '3U',
212
+ U2: '3U2',
213
+ UP: "3U'",
214
+ Dw: '3Dw',
215
+ Dw2: '3Dw2',
216
+ DwP: "3Dw'",
217
+ d: '3d',
218
+ d2: '3d2',
219
+ dP: "3d'",
220
+ D: '3D',
221
+ D2: '3D2',
222
+ DP: "3D'",
223
+ }),
224
+ Four: Object.freeze({
225
+ Rw: '4Rw',
226
+ Rw2: '4Rw2',
227
+ RwP: "4Rw'",
228
+ r: '4r',
229
+ r2: '4r2',
230
+ rP: "4r'",
231
+ R: '4R',
232
+ R2: '4R2',
233
+ RP: "4R'",
234
+ Lw: '4Lw',
235
+ Lw2: '4Lw2',
236
+ LwP: "4Lw'",
237
+ l: '4l',
238
+ l2: '4l2',
239
+ lP: "4l'",
240
+ L: '4L',
241
+ L2: '4L2',
242
+ LP: "4L'",
243
+ Fw: '4Fw',
244
+ Fw2: '4Fw2',
245
+ FwP: "4Fw'",
246
+ f: '4f',
247
+ f2: '4f2',
248
+ fP: "4f'",
249
+ F: '4F',
250
+ F2: '4F2',
251
+ FP: "4F'",
252
+ Bw: '4Bw',
253
+ Bw2: '4Bw2',
254
+ BwP: "4Bw'",
255
+ b: '4b',
256
+ b2: '4b2',
257
+ bP: "4b'",
258
+ B: '4B',
259
+ B2: '4B2',
260
+ BP: "4B'",
261
+ Uw: '4Uw',
262
+ Uw2: '4Uw2',
263
+ UwP: "4Uw'",
264
+ u: '4u',
265
+ u2: '4u2',
266
+ uP: "4u'",
267
+ U: '4U',
268
+ U2: '4U2',
269
+ UP: "4U'",
270
+ Dw: '4Dw',
271
+ Dw2: '4Dw2',
272
+ DwP: "4Dw'",
273
+ d: '4d',
274
+ d2: '4d2',
275
+ dP: "4d'",
276
+ D: '4D',
277
+ D2: '4D2',
278
+ DP: "4D'",
279
+ }),
280
+ Five: Object.freeze({
281
+ Rw: '5Rw',
282
+ Rw2: '5Rw2',
283
+ RwP: "5Rw'",
284
+ r: '5r',
285
+ r2: '5r2',
286
+ rP: "5r'",
287
+ R: '5R',
288
+ R2: '5R2',
289
+ RP: "5R'",
290
+ Lw: '5Lw',
291
+ Lw2: '5Lw2',
292
+ LwP: "5Lw'",
293
+ l: '5l',
294
+ l2: '5l2',
295
+ lP: "5l'",
296
+ L: '5L',
297
+ L2: '5L2',
298
+ LP: "5L'",
299
+ Fw: '5Fw',
300
+ Fw2: '5Fw2',
301
+ FwP: "5Fw'",
302
+ f: '5f',
303
+ f2: '5f2',
304
+ fP: "5f'",
305
+ F: '5F',
306
+ F2: '5F2',
307
+ FP: "5F'",
308
+ Bw: '5Bw',
309
+ Bw2: '5Bw2',
310
+ BwP: "5Bw'",
311
+ b: '5b',
312
+ b2: '5b2',
313
+ bP: "5b'",
314
+ B: '5B',
315
+ B2: '5B2',
316
+ BP: "5B'",
317
+ Uw: '5Uw',
318
+ Uw2: '5Uw2',
319
+ UwP: "5Uw'",
320
+ u: '5u',
321
+ u2: '5u2',
322
+ uP: "5u'",
323
+ U: '5U',
324
+ U2: '5U2',
325
+ UP: "5U'",
326
+ Dw: '5Dw',
327
+ Dw2: '5Dw2',
328
+ DwP: "5Dw'",
329
+ d: '5d',
330
+ d2: '5d2',
331
+ dP: "5d'",
332
+ D: '5D',
333
+ D2: '5D2',
334
+ DP: "5D'",
335
+ }),
336
+ Six: Object.freeze({
337
+ Rw: '6Rw',
338
+ Rw2: '6Rw2',
339
+ RwP: "6Rw'",
340
+ r: '6r',
341
+ r2: '6r2',
342
+ rP: "6r'",
343
+ R: '6R',
344
+ R2: '6R2',
345
+ RP: "6R'",
346
+ Lw: '6Lw',
347
+ Lw2: '6Lw2',
348
+ LwP: "6Lw'",
349
+ l: '6l',
350
+ l2: '6l2',
351
+ lP: "6l'",
352
+ L: '6L',
353
+ L2: '6L2',
354
+ LP: "6L'",
355
+ Fw: '6Fw',
356
+ Fw2: '6Fw2',
357
+ FwP: "6Fw'",
358
+ f: '6f',
359
+ f2: '6f2',
360
+ fP: "6f'",
361
+ F: '6F',
362
+ F2: '6F2',
363
+ FP: "6F'",
364
+ Bw: '6Bw',
365
+ Bw2: '6Bw2',
366
+ BwP: "6Bw'",
367
+ b: '6b',
368
+ b2: '6b2',
369
+ bP: "6b'",
370
+ B: '6B',
371
+ B2: '6B2',
372
+ BP: "6B'",
373
+ Uw: '6Uw',
374
+ Uw2: '6Uw2',
375
+ UwP: "6Uw'",
376
+ u: '6u',
377
+ u2: '6u2',
378
+ uP: "6u'",
379
+ U: '6U',
380
+ U2: '6U2',
381
+ UP: "6U'",
382
+ Dw: '6Dw',
383
+ Dw2: '6Dw2',
384
+ DwP: "6Dw'",
385
+ d: '6d',
386
+ d2: '6d2',
387
+ dP: "6d'",
388
+ D: '6D',
389
+ D2: '6D2',
390
+ DP: "6D'",
391
+ }),
392
+ /**
393
+ * Build a layer-range move for big-cube notation. e.g. Movements.Range(2, 4, Movements.Wide.Rw) returns '2-4Rw',
394
+ * meaning "rotate layers 2 through 4 from the right face." Accepts wide moves (`Movements.Wide.*`), face
395
+ * moves (`Movements.Single.{R,L,U,D,F,B}` and modifiers), and slice moves (`Movements.Single.{M,E,S}` and
396
+ * modifiers). Already-prefixed moves (`2R`, `2-4Rw`) are rejected.
397
+ * @param {number} lower
398
+ * @param {number} upper
399
+ * @param {WideMove | SingleMove} baseMove
400
+ * @returns {Movement}
401
+ */
402
+ Range: (lower, upper, baseMove) => {
403
+ if (!Number.isInteger(lower) || !Number.isInteger(upper) || lower < 1 || lower >= upper || upper > 7) {
404
+ throw new Error(`Invalid layer range [${lower}-${upper}]: require integers with 1 <= lower < upper <= 7`);
405
+ }
406
+ const move = /** @type {Movement} */ (`${lower}-${upper}${baseMove}`);
407
+ if (!isMovement(move)) {
408
+ throw new Error(`Invalid range movement: ${move}`);
409
+ }
410
+
411
+ return move;
412
+ },
413
+ });
414
+
415
+ /**
416
+ * @typedef {typeof Rotations[keyof typeof Rotations]} Rotation
417
+ */
418
+ export const Rotations = Object.freeze({
419
+ x: 'x',
420
+ x2: 'x2',
421
+ xP: "x'",
422
+ y: 'y',
423
+ y2: 'y2',
424
+ yP: "y'",
425
+ z: 'z',
426
+ z2: 'z2',
427
+ zP: "z'",
428
+ });
429
+
430
+ /**
431
+ * @typedef {typeof Faces [keyof typeof Faces]} Face
432
+ */
433
+ export const Faces = Object.freeze({
434
+ U: 'U',
435
+ D: 'D',
436
+ L: 'L',
437
+ R: 'R',
438
+ F: 'F',
439
+ B: 'B',
440
+ });
441
+
442
+ /**
443
+ * @typedef {typeof CubeTypes [keyof typeof CubeTypes]} CubeType
444
+ */
445
+ export const CubeTypes = Object.freeze({
446
+ Two: 'Two',
447
+ Three: 'Three',
448
+ Four: 'Four',
449
+ Five: 'Five',
450
+ Six: 'Six',
451
+ Seven: 'Seven',
452
+ });
453
+
454
+ export const LayerCount = Object.freeze({
455
+ [CubeTypes.Two]: 2,
456
+ [CubeTypes.Three]: 3,
457
+ [CubeTypes.Four]: 4,
458
+ [CubeTypes.Five]: 5,
459
+ [CubeTypes.Six]: 6,
460
+ [CubeTypes.Seven]: 7,
461
+ });
462
+
463
+ /**
464
+ *
465
+ * @param {string} rotation
466
+ */
467
+ export function IsRotation(rotation) {
468
+ return /^([xyz])(\d)?(\')?$/.test(rotation);
469
+ }
470
+
471
+ /**
472
+ *
473
+ * @param {string} movement
474
+ * @return {boolean}
475
+ */
476
+ export function isMovement(movement) {
477
+ return /^([1234567]|[123456]-[1234567])?([RLUDFB]w|[RLUDFBMES]|[rludfbmes])([123])?(\')?$/.test(movement);
478
+ }
@@ -0,0 +1,3 @@
1
+ import RubiksCubeController from './rubiksCubeController';
2
+ export { RubiksCubeController };
3
+ /** @typedef {import('./rubiksCubeController').AnimationOptions} AnimationOptions */
@@ -0,0 +1,111 @@
1
+ // @ts-check
2
+ /** @import {Slice} from '../state/slice' */
3
+ /** @import {StickerState} from '../state/stickerState' */
4
+ /** @import {CubeType, Movement, Rotation} from '../core' */
5
+ /**
6
+ * @typedef AnimationOptions
7
+ * @property {boolean} [translate]
8
+ * @property {number} [animationSpeedMs]
9
+ * @property {boolean} [reverse]
10
+ */
11
+ /**
12
+ * @typedef {Object} RubiksCubeViewInterface
13
+ * @property {function(Slice, any=): Promise<void>} slice
14
+ * @property {function(StickerState): void} setState
15
+ * @property {function(): void} reset
16
+ * @property {function(CubeType): void} setType
17
+ **/
18
+
19
+ import { CubeTypes } from '../core';
20
+ import { RubiksCubeState } from '../state';
21
+ import { fromKociemba, toKociemba } from '../state/stickerState';
22
+
23
+ export default class RubiksCubeController {
24
+ /**
25
+ * @param {CubeType} cubeType
26
+ * @param {RubiksCubeViewInterface} view
27
+ * */
28
+ constructor(cubeType, view) {
29
+ this.state = new RubiksCubeState(cubeType);
30
+ this.view = view;
31
+ }
32
+ /**
33
+ * @param {Movement} movement
34
+ * @param {AnimationOptions} [options]
35
+ * @returns {Promise<string>}
36
+ */
37
+ movement(movement, options) {
38
+ const slice = this.state.move(movement, { reverse: options?.reverse, translate: options?.translate });
39
+ if (slice == null) {
40
+ return Promise.reject(new Error(`Invalid movement: ${movement}`));
41
+ }
42
+ return this.view.slice(slice, { animationSpeedMs: options?.animationSpeedMs }).then(() => toKociemba(this.state.getState()));
43
+ }
44
+
45
+ /**
46
+ * @param {Rotation} rotation
47
+ * @param {AnimationOptions} [options]
48
+ * @returns {Promise<string>}
49
+ */
50
+ rotation(rotation, options) {
51
+ const slice = this.state.rotate(rotation, { reverse: options?.reverse });
52
+ if (slice == null) {
53
+ return Promise.reject(new Error(`Invalid rotation: ${rotation}`));
54
+ }
55
+ return this.view.slice(slice, { animationSpeedMs: options?.animationSpeedMs }).then(() => toKociemba(this.state.getState()));
56
+ }
57
+
58
+ /**
59
+ * @param {(Rotation | Movement)[]} actions
60
+ * @param {AnimationOptions} [options]
61
+ * @returns {string}
62
+ */
63
+ do(actions, options) {
64
+ this.state.do(actions, { translate: options?.translate, reverse: options?.reverse });
65
+ this.view.setState(this.state.getState());
66
+ return toKociemba(this.state.getState());
67
+ }
68
+
69
+ /**
70
+ * @returns {string}
71
+ */
72
+ reset() {
73
+ this.state.reset();
74
+ this.view.reset();
75
+ return toKociemba(this.state.getState());
76
+ }
77
+
78
+ /**
79
+ * @param {string} kociembaState
80
+ * @returns {boolean}
81
+ */
82
+ setState(kociembaState) {
83
+ const state = fromKociemba(kociembaState);
84
+ if (state) {
85
+ this.state.setState(state);
86
+ this.view.setState(state);
87
+ return true;
88
+ }
89
+ return false;
90
+ }
91
+
92
+ /**
93
+ * @returns {string}
94
+ */
95
+ getState() {
96
+ return toKociemba(this.state.getState());
97
+ }
98
+
99
+ /**
100
+ * @param {CubeType} cubeType
101
+ * @returns {string}
102
+ */
103
+ setType(cubeType) {
104
+ if (!Object.values(CubeTypes).includes(cubeType)) {
105
+ throw new Error(`Invalid cube type: ${cubeType}`);
106
+ }
107
+ this.state = new RubiksCubeState(cubeType);
108
+ this.view.setType(cubeType);
109
+ return toKociemba(this.state.getState());
110
+ }
111
+ }