AK51 commited on
Commit
2d79c8e
·
verified ·
1 Parent(s): 6b62a68

Upload 8 files

Browse files
Files changed (8) hide show
  1. README.md +23 -10
  2. app.js +1371 -0
  3. index.html +708 -19
  4. json/coll.json.js +424 -0
  5. json/f2l.json.js +409 -0
  6. json/oll.json.js +633 -0
  7. json/pll.json.js +248 -0
  8. json/zbll.json.js +0 -0
README.md CHANGED
@@ -1,10 +1,23 @@
1
- ---
2
- title: Cube One Handed
3
- emoji: 🐠
4
- colorFrom: red
5
- colorTo: red
6
- sdk: static
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: One-Handed 3x3 Rubik's Cube
3
+ emoji: 🧊
4
+ colorFrom: indigo
5
+ colorTo: pink
6
+ sdk: static
7
+ pinned: false
8
+ license: mit
9
+ ---
10
+
11
+ # One-Handed 3x3 Rubik's Cube
12
+
13
+ Interactive 3D visualization and one-handed (OH) algorithm reference for speedcubing.
14
+
15
+ ## Features
16
+
17
+ - 3D interactive Rubik's Cube (Three.js)
18
+ - OH-optimized algorithms for F2L, OLL, PLL, COLL, and ZBLL
19
+ - Click any algorithm to see it solve the case on the cube
20
+ - Step-by-step mode: advance one move at a time
21
+ - Keyboard shortcuts for moves and step navigation
22
+
23
+ Created by Andy Kong
app.js ADDED
@@ -0,0 +1,1371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ============================================================
2
+ // 3D Rubik's Cube Visualization & OH Algorithm Player
3
+ // Built with Three.js
4
+ // ============================================================
5
+
6
+ let scene, camera, renderer, controls;
7
+ let cubeGroup;
8
+ let cubies = [];
9
+ let isAnimating = false;
10
+ let moveQueue = [];
11
+
12
+ // Colors: White, Yellow, Red, Orange, Blue, Green (as CSS strings for canvas)
13
+ const FACE_COLORS = {
14
+ U: '#ffdd00', // Yellow - top
15
+ D: '#ffffff', // White - bottom
16
+ R: '#ee0000', // Red - right
17
+ L: '#ff8800', // Orange - left
18
+ F: '#0051ff', // Blue - front
19
+ B: '#00aa33', // Green - back
20
+ X: '#1a1a1a' // Black - internal
21
+ };
22
+
23
+ const GAP = 1.02; // Gap between cubies
24
+
25
+ // ============ INITIALIZATION ============
26
+
27
+ function init() {
28
+ const canvas = document.getElementById('cube-canvas');
29
+ const container = canvas.parentElement;
30
+
31
+ scene = new THREE.Scene();
32
+ scene.background = new THREE.Color(0x141425);
33
+
34
+ camera = new THREE.PerspectiveCamera(40, container.clientWidth / 400, 0.1, 100);
35
+ camera.position.set(5.5, 4.2, 5.5);
36
+
37
+ renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
38
+ renderer.setSize(container.clientWidth, 400);
39
+ renderer.setPixelRatio(window.devicePixelRatio);
40
+
41
+ // Lighting - use Phong materials so we just need decent lighting
42
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
43
+ scene.add(ambientLight);
44
+
45
+ const mainLight = new THREE.DirectionalLight(0xffffff, 0.6);
46
+ mainLight.position.set(5, 10, 7);
47
+ scene.add(mainLight);
48
+
49
+ const fillLight = new THREE.DirectionalLight(0xffffff, 0.3);
50
+ fillLight.position.set(-5, 0, -5);
51
+ scene.add(fillLight);
52
+
53
+ // Orbit controls
54
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
55
+ controls.enableDamping = true;
56
+ controls.dampingFactor = 0.08;
57
+ controls.minDistance = 4;
58
+ controls.maxDistance = 14;
59
+ controls.enablePan = false;
60
+
61
+ // Create cube
62
+ createCube();
63
+
64
+ // Handle resize
65
+ window.addEventListener('resize', onResize);
66
+
67
+ // Start render loop
68
+ animate();
69
+ }
70
+
71
+ function onResize() {
72
+ const container = document.getElementById('cube-canvas').parentElement;
73
+ camera.aspect = container.clientWidth / 400;
74
+ camera.updateProjectionMatrix();
75
+ renderer.setSize(container.clientWidth, 400);
76
+ }
77
+
78
+ // ============ CANVAS TEXTURE STICKER ============
79
+
80
+ // Creates a texture with a rounded-corner sticker on a black background
81
+ function createStickerTexture(color) {
82
+ const size = 256;
83
+ const canvas2d = document.createElement('canvas');
84
+ canvas2d.width = size;
85
+ canvas2d.height = size;
86
+ const ctx = canvas2d.getContext('2d');
87
+
88
+ // Black background (the gap / body color)
89
+ ctx.fillStyle = '#111111';
90
+ ctx.fillRect(0, 0, size, size);
91
+
92
+ // Draw rounded rectangle sticker
93
+ const padding = 20; // border around sticker
94
+ const radius = 28; // corner radius
95
+ const x = padding;
96
+ const y = padding;
97
+ const w = size - padding * 2;
98
+ const h = size - padding * 2;
99
+
100
+ ctx.beginPath();
101
+ ctx.moveTo(x + radius, y);
102
+ ctx.lineTo(x + w - radius, y);
103
+ ctx.quadraticCurveTo(x + w, y, x + w, y + radius);
104
+ ctx.lineTo(x + w, y + h - radius);
105
+ ctx.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
106
+ ctx.lineTo(x + radius, y + h);
107
+ ctx.quadraticCurveTo(x, y + h, x, y + h - radius);
108
+ ctx.lineTo(x, y + radius);
109
+ ctx.quadraticCurveTo(x, y, x + radius, y);
110
+ ctx.closePath();
111
+
112
+ ctx.fillStyle = color;
113
+ ctx.fill();
114
+
115
+ // Subtle highlight gradient on sticker for depth
116
+ const grad = ctx.createLinearGradient(x, y, x, y + h);
117
+ grad.addColorStop(0, 'rgba(255,255,255,0.15)');
118
+ grad.addColorStop(0.5, 'rgba(255,255,255,0)');
119
+ grad.addColorStop(1, 'rgba(0,0,0,0.1)');
120
+ ctx.fillStyle = grad;
121
+ ctx.fill();
122
+
123
+ const texture = new THREE.CanvasTexture(canvas2d);
124
+ texture.needsUpdate = true;
125
+ return texture;
126
+ }
127
+
128
+ // Black face texture (for internal faces with no sticker)
129
+ function createBlackTexture() {
130
+ const size = 64;
131
+ const canvas2d = document.createElement('canvas');
132
+ canvas2d.width = size;
133
+ canvas2d.height = size;
134
+ const ctx = canvas2d.getContext('2d');
135
+ ctx.fillStyle = '#111111';
136
+ ctx.fillRect(0, 0, size, size);
137
+ const texture = new THREE.CanvasTexture(canvas2d);
138
+ texture.needsUpdate = true;
139
+ return texture;
140
+ }
141
+
142
+ // Cache textures
143
+ const textureCache = {};
144
+ function getStickerTexture(colorKey) {
145
+ if (!textureCache[colorKey]) {
146
+ if (colorKey === 'X') {
147
+ textureCache[colorKey] = createBlackTexture();
148
+ } else {
149
+ textureCache[colorKey] = createStickerTexture(FACE_COLORS[colorKey]);
150
+ }
151
+ }
152
+ return textureCache[colorKey];
153
+ }
154
+
155
+ // ============ CUBE CREATION ============
156
+
157
+ function createCubie(x, y, z) {
158
+ const size = 0.97;
159
+ const geometry = new THREE.BoxGeometry(size, size, size);
160
+
161
+ // Determine which color each face gets
162
+ // Three.js box face order: +X, -X, +Y, -Y, +Z, -Z
163
+ const faceColors = [
164
+ x === 1 ? 'R' : 'X', // +X Right
165
+ x === -1 ? 'L' : 'X', // -X Left
166
+ y === 1 ? 'U' : 'X', // +Y Up
167
+ y === -1 ? 'D' : 'X', // -Y Down
168
+ z === 1 ? 'F' : 'X', // +Z Front
169
+ z === -1 ? 'B' : 'X', // -Z Back
170
+ ];
171
+
172
+ const materials = faceColors.map(key => {
173
+ return new THREE.MeshPhongMaterial({
174
+ map: getStickerTexture(key),
175
+ specular: 0x222222,
176
+ shininess: 30
177
+ });
178
+ });
179
+
180
+ const cubie = new THREE.Mesh(geometry, materials);
181
+ cubie.position.set(x * GAP, y * GAP, z * GAP);
182
+ cubie.userData = { x, y, z };
183
+
184
+ return cubie;
185
+ }
186
+
187
+ function createCube() {
188
+ cubeGroup = new THREE.Group();
189
+ cubies = [];
190
+
191
+ for (let x = -1; x <= 1; x++) {
192
+ for (let y = -1; y <= 1; y++) {
193
+ for (let z = -1; z <= 1; z++) {
194
+ if (x === 0 && y === 0 && z === 0) continue;
195
+ const cubie = createCubie(x, y, z);
196
+ cubies.push(cubie);
197
+ cubeGroup.add(cubie);
198
+ }
199
+ }
200
+ }
201
+
202
+ scene.add(cubeGroup);
203
+ }
204
+
205
+ // ============ ANIMATION LOOP ============
206
+
207
+ function animate() {
208
+ requestAnimationFrame(animate);
209
+ controls.update();
210
+ renderer.render(scene, camera);
211
+ }
212
+
213
+ // ============ MOVE SYSTEM ============
214
+
215
+ // Wide moves = outer layer + middle layer
216
+ // Rotations = entire cube
217
+ // Returns {axis, layers[], angle} for 3D animation
218
+ function get3DMoveDef(moveStr) {
219
+ const PI = Math.PI;
220
+ const H = PI / 2;
221
+ switch(moveStr) {
222
+ // Face moves
223
+ case 'R': return { axis:'x', layers:[1], angle:-H };
224
+ case 'Ri': return { axis:'x', layers:[1], angle:H };
225
+ case 'R2': return { axis:'x', layers:[1], angle:-PI };
226
+ case 'L': return { axis:'x', layers:[-1], angle:H };
227
+ case 'Li': return { axis:'x', layers:[-1], angle:-H };
228
+ case 'L2': return { axis:'x', layers:[-1], angle:PI };
229
+ case 'U': return { axis:'y', layers:[1], angle:-H };
230
+ case 'Ui': return { axis:'y', layers:[1], angle:H };
231
+ case 'U2': return { axis:'y', layers:[1], angle:-PI };
232
+ case 'D': return { axis:'y', layers:[-1], angle:H };
233
+ case 'Di': return { axis:'y', layers:[-1], angle:-H };
234
+ case 'D2': return { axis:'y', layers:[-1], angle:PI };
235
+ case 'F': return { axis:'z', layers:[1], angle:-H };
236
+ case 'Fi': return { axis:'z', layers:[1], angle:H };
237
+ case 'F2': return { axis:'z', layers:[1], angle:-PI };
238
+ case 'B': return { axis:'z', layers:[-1], angle:H };
239
+ case 'Bi': return { axis:'z', layers:[-1], angle:-H };
240
+ case 'B2': return { axis:'z', layers:[-1], angle:PI };
241
+ // Wide moves (2 layers)
242
+ case 'r': return { axis:'x', layers:[1,0], angle:-H };
243
+ case 'ri': return { axis:'x', layers:[1,0], angle:H };
244
+ case 'r2': return { axis:'x', layers:[1,0], angle:-PI };
245
+ case 'l': return { axis:'x', layers:[-1,0], angle:H };
246
+ case 'li': return { axis:'x', layers:[-1,0], angle:-H };
247
+ case 'l2': return { axis:'x', layers:[-1,0], angle:PI };
248
+ case 'u': return { axis:'y', layers:[1,0], angle:-H };
249
+ case 'ui': return { axis:'y', layers:[1,0], angle:H };
250
+ case 'u2': return { axis:'y', layers:[1,0], angle:-PI };
251
+ case 'd': return { axis:'y', layers:[-1,0], angle:H };
252
+ case 'di': return { axis:'y', layers:[-1,0], angle:-H };
253
+ case 'd2': return { axis:'y', layers:[-1,0], angle:PI };
254
+ case 'f': return { axis:'z', layers:[1,0], angle:-H };
255
+ case 'fi': return { axis:'z', layers:[1,0], angle:H };
256
+ case 'f2': return { axis:'z', layers:[1,0], angle:-PI };
257
+ case 'b': return { axis:'z', layers:[-1,0], angle:H };
258
+ case 'bi': return { axis:'z', layers:[-1,0], angle:-H };
259
+ case 'b2': return { axis:'z', layers:[-1,0], angle:PI };
260
+ // Rotations (all 3 layers)
261
+ case 'x': return { axis:'x', layers:[1,0,-1], angle:-H };
262
+ case 'xi': return { axis:'x', layers:[1,0,-1], angle:H };
263
+ case 'x2': return { axis:'x', layers:[1,0,-1], angle:-PI };
264
+ case 'y': return { axis:'y', layers:[1,0,-1], angle:-H };
265
+ case 'yi': return { axis:'y', layers:[1,0,-1], angle:H };
266
+ case 'y2': return { axis:'y', layers:[1,0,-1], angle:-PI };
267
+ case 'z': return { axis:'z', layers:[1,0,-1], angle:-H };
268
+ case 'zi': return { axis:'z', layers:[1,0,-1], angle:H };
269
+ case 'z2': return { axis:'z', layers:[1,0,-1], angle:-PI };
270
+ // M slice (follows L direction = +x rotation for middle)
271
+ case 'M': return { axis:'x', layers:[0], angle:H };
272
+ case 'Mi': return { axis:'x', layers:[0], angle:-H };
273
+ case 'M2': return { axis:'x', layers:[0], angle:PI };
274
+ // S slice (follows F direction)
275
+ case 'S': return { axis:'z', layers:[0], angle:-H };
276
+ case 'Si': return { axis:'z', layers:[0], angle:H };
277
+ case 'S2': return { axis:'z', layers:[0], angle:PI };
278
+ default: return null;
279
+ }
280
+ }
281
+
282
+ function getCubiesOnLayers(axis, layers) {
283
+ const threshold = 0.5;
284
+ return cubies.filter(cubie => {
285
+ const pos = cubie.position;
286
+ for (const layer of layers) {
287
+ if (axis === 'x' && Math.abs(pos.x - layer * GAP) < threshold) return true;
288
+ if (axis === 'y' && Math.abs(pos.y - layer * GAP) < threshold) return true;
289
+ if (axis === 'z' && Math.abs(pos.z - layer * GAP) < threshold) return true;
290
+ }
291
+ return false;
292
+ });
293
+ }
294
+
295
+ function animateMove(axis, layers, angle, duration = 300) {
296
+ return new Promise(resolve => {
297
+ const layerCubies = getCubiesOnLayers(axis, layers);
298
+ if (layerCubies.length === 0) {
299
+ resolve();
300
+ return;
301
+ }
302
+
303
+ const rotationGroup = new THREE.Group();
304
+ scene.add(rotationGroup);
305
+
306
+ // Move cubies to rotation group
307
+ layerCubies.forEach(cubie => {
308
+ cubeGroup.remove(cubie);
309
+ rotationGroup.add(cubie);
310
+ });
311
+
312
+ const startTime = Date.now();
313
+ const axisVec = new THREE.Vector3(
314
+ axis === 'x' ? 1 : 0,
315
+ axis === 'y' ? 1 : 0,
316
+ axis === 'z' ? 1 : 0
317
+ );
318
+
319
+ function step() {
320
+ const elapsed = Date.now() - startTime;
321
+ const t = Math.min(elapsed / duration, 1);
322
+ // Smooth easing
323
+ const eased = t < 0.5
324
+ ? 4 * t * t * t
325
+ : 1 - Math.pow(-2 * t + 2, 3) / 2;
326
+
327
+ rotationGroup.setRotationFromAxisAngle(axisVec, angle * eased);
328
+
329
+ if (t < 1) {
330
+ requestAnimationFrame(step);
331
+ } else {
332
+ // Finalize - apply rotation to each cubie
333
+ rotationGroup.setRotationFromAxisAngle(axisVec, angle);
334
+ rotationGroup.updateMatrixWorld();
335
+
336
+ layerCubies.forEach(cubie => {
337
+ cubie.applyMatrix4(rotationGroup.matrixWorld);
338
+ cubie.position.x = Math.round(cubie.position.x / GAP) * GAP;
339
+ cubie.position.y = Math.round(cubie.position.y / GAP) * GAP;
340
+ cubie.position.z = Math.round(cubie.position.z / GAP) * GAP;
341
+ rotationGroup.remove(cubie);
342
+ cubeGroup.add(cubie);
343
+ });
344
+
345
+ scene.remove(rotationGroup);
346
+ resolve();
347
+ }
348
+ }
349
+
350
+ step();
351
+ });
352
+ }
353
+
354
+ async function doMove(moveStr) {
355
+ if (isAnimating) return;
356
+ isAnimating = true;
357
+
358
+ const def = get3DMoveDef(moveStr);
359
+ if (def) {
360
+ await animateMove(def.axis, def.layers, def.angle, 250);
361
+ }
362
+
363
+ isAnimating = false;
364
+ processQueue();
365
+ }
366
+
367
+ async function processQueue() {
368
+ if (moveQueue.length === 0 || isAnimating) return;
369
+ isAnimating = true;
370
+
371
+ const move = moveQueue.shift();
372
+ const def = get3DMoveDef(move);
373
+ if (def) {
374
+ await animateMove(def.axis, def.layers, def.angle, 180);
375
+ }
376
+
377
+ isAnimating = false;
378
+
379
+ if (moveQueue.length > 0) {
380
+ processQueue();
381
+ }
382
+ }
383
+
384
+ // ============ NOTATION PARSER ============
385
+
386
+ function parseAlgorithm(notation) {
387
+ // Convert standard notation to our internal format
388
+ // Normalize curly quotes to straight apostrophes
389
+ notation = notation.replace(/\u2019/g, "'");
390
+ // Normalize X'2 to X2 (e.g., U'2 = U2, R'2 = R2)
391
+ notation = notation.replace(/([RUDLFBrudlfb])'2/g, '$12');
392
+ // Normalize X2' to X2 (e.g., U2' = U2, R2' = R2)
393
+ notation = notation.replace(/([RUDLFBrudlfb])2'/g, '$12');
394
+ const moves = [];
395
+ const tokens = notation.replace(/\(/g, '').replace(/\)/g, '').trim().split(/\s+/);
396
+
397
+ for (const token of tokens) {
398
+ let move = '';
399
+ if (token === "R") move = 'R';
400
+ else if (token === "R'" || token === "R\u2019") move = 'Ri';
401
+ else if (token === "R2") move = 'R2';
402
+ else if (token === "L") move = 'L';
403
+ else if (token === "L'" || token === "L\u2019") move = 'Li';
404
+ else if (token === "L2") move = 'L2';
405
+ else if (token === "U") move = 'U';
406
+ else if (token === "U'" || token === "U\u2019") move = 'Ui';
407
+ else if (token === "U2") move = 'U2';
408
+ else if (token === "D") move = 'D';
409
+ else if (token === "D'" || token === "D\u2019") move = 'Di';
410
+ else if (token === "D2") move = 'D2';
411
+ else if (token === "F") move = 'F';
412
+ else if (token === "F'" || token === "F\u2019") move = 'Fi';
413
+ else if (token === "F2") move = 'F2';
414
+ else if (token === "B") move = 'B';
415
+ else if (token === "B'" || token === "B\u2019") move = 'Bi';
416
+ else if (token === "B2") move = 'B2';
417
+ // Wide moves
418
+ else if (token === "r") move = 'r';
419
+ else if (token === "r'" || token === "r\u2019") move = 'ri';
420
+ else if (token === "r2") move = 'r2';
421
+ else if (token === "l") move = 'l';
422
+ else if (token === "l'" || token === "l\u2019") move = 'li';
423
+ else if (token === "l2") move = 'l2';
424
+ else if (token === "u") move = 'u';
425
+ else if (token === "u'" || token === "u\u2019") move = 'ui';
426
+ else if (token === "u2") move = 'u2';
427
+ else if (token === "d") move = 'd';
428
+ else if (token === "d'" || token === "d\u2019") move = 'di';
429
+ else if (token === "d2") move = 'd2';
430
+ else if (token === "f") move = 'f';
431
+ else if (token === "f'" || token === "f\u2019") move = 'fi';
432
+ else if (token === "f2") move = 'f2';
433
+ else if (token === "b") move = 'b';
434
+ else if (token === "b'" || token === "b\u2019") move = 'bi';
435
+ else if (token === "b2") move = 'b2';
436
+ // Rotations
437
+ else if (token === "x") move = 'x';
438
+ else if (token === "x'" || token === "x\u2019") move = 'xi';
439
+ else if (token === "x2") move = 'x2';
440
+ else if (token === "y") move = 'y';
441
+ else if (token === "y'" || token === "y\u2019") move = 'yi';
442
+ else if (token === "y2") move = 'y2';
443
+ else if (token === "z") move = 'z';
444
+ else if (token === "z'" || token === "z\u2019") move = 'zi';
445
+ else if (token === "z2") move = 'z2';
446
+ // M, S, E slice moves
447
+ else if (token === "M") move = 'M';
448
+ else if (token === "M'" || token === "M\u2019") move = 'Mi';
449
+ else if (token === "M2") move = 'M2';
450
+ else if (token === "S") move = 'S';
451
+ else if (token === "S'" || token === "S\u2019") move = 'Si';
452
+ else if (token === "S2") move = 'S2';
453
+
454
+ if (move) moves.push(move);
455
+ }
456
+ return moves;
457
+ }
458
+
459
+ function playAlgorithm(notation) {
460
+ if (isAnimating || moveQueue.length > 0) return;
461
+
462
+ const moves = parseAlgorithm(notation);
463
+ if (moves.length === 0) return;
464
+
465
+ document.getElementById('scramble-display').textContent = 'Playing: ' + notation;
466
+ moveQueue.push(...moves);
467
+ processQueue();
468
+
469
+ // Reset display after animation completes
470
+ const totalTime = moves.length * 280;
471
+ setTimeout(() => {
472
+ document.getElementById('scramble-display').textContent = 'Drag to rotate \u2022 Scroll to zoom';
473
+ }, totalTime);
474
+ }
475
+
476
+ // Invert a single move token (e.g., 'R' -> 'Ri', 'Ri' -> 'R', 'R2' -> 'R2')
477
+ function invertMove(move) {
478
+ if (move.endsWith('2')) return move; // double moves are self-inverse
479
+ if (move.endsWith('i')) return move.slice(0, -1); // Ri -> R
480
+ return move + 'i'; // R -> Ri
481
+ }
482
+
483
+ // Setup the case (apply inverse of algorithm instantly) then play the solve animated
484
+ async function setupAndSolve(notation) {
485
+ if (isAnimating || moveQueue.length > 0) return;
486
+ cancelStepMode();
487
+
488
+ // Focus the pop-out window if cube is popped out
489
+ if (cubePopWindow && !cubePopWindow.closed) {
490
+ cubePopWindow.focus();
491
+ }
492
+
493
+ // Reset cube first
494
+ scene.remove(cubeGroup);
495
+ createCube();
496
+
497
+ const moves = parseAlgorithm(notation);
498
+ if (moves.length === 0) return;
499
+
500
+ // Compute inverse sequence (reversed order, each move inverted)
501
+ const inverseMoves = moves.slice().reverse().map(invertMove);
502
+
503
+ // Apply the inverse instantly (no animation) to set up the case
504
+ document.getElementById('scramble-display').textContent = 'Setting up case...';
505
+
506
+ isAnimating = true;
507
+ for (const move of inverseMoves) {
508
+ const def = get3DMoveDef(move);
509
+ if (def) {
510
+ await animateMove(def.axis, def.layers, def.angle, 40);
511
+ }
512
+ }
513
+ isAnimating = false;
514
+
515
+ // Brief pause to let user see the scrambled state
516
+ await new Promise(r => setTimeout(r, 600));
517
+
518
+ // Now play the solution animated
519
+ document.getElementById('scramble-display').textContent = 'Solving: ' + notation;
520
+ moveQueue.push(...moves);
521
+ processQueue();
522
+
523
+ const totalTime = moves.length * 280;
524
+ setTimeout(() => {
525
+ document.getElementById('scramble-display').textContent = 'Solved! \u2714';
526
+ }, totalTime);
527
+ }
528
+
529
+ // ============ STEP BY STEP MODE ============
530
+
531
+ let stepMoves = [];
532
+ let stepIndex = 0;
533
+ let stepTotal = 0;
534
+ let stepNotation = '';
535
+ let stepActive = false;
536
+
537
+ function cancelStepMode() {
538
+ stepActive = false;
539
+ stepMoves = [];
540
+ stepIndex = 0;
541
+ hideStepPanel();
542
+ }
543
+
544
+ function hideStepPanel() {
545
+ const panel = document.getElementById('step-panel');
546
+ if (panel) panel.style.display = 'none';
547
+ // Also hide step controls in pop-out window
548
+ if (cubePopWindow && !cubePopWindow.closed) {
549
+ const popStepCtrl = cubePopWindow.document.getElementById('step-controls-pop');
550
+ if (popStepCtrl) popStepCtrl.style.display = 'none';
551
+ }
552
+ }
553
+
554
+ function showStepPanel() {
555
+ let panel = document.getElementById('step-panel');
556
+ if (!panel) {
557
+ panel = document.createElement('div');
558
+ panel.id = 'step-panel';
559
+ document.querySelector('.cube-section').appendChild(panel);
560
+ }
561
+ panel.style.display = 'flex';
562
+ // Default position: top-right corner, beside the cube
563
+ panel.style.left = 'auto';
564
+ panel.style.right = '15px';
565
+ panel.style.top = '15px';
566
+ panel.style.bottom = 'auto';
567
+ panel.style.transform = 'none';
568
+ updateStepPanel();
569
+ // Also show step controls in pop-out window
570
+ if (cubePopWindow && !cubePopWindow.closed) {
571
+ const popStepCtrl = cubePopWindow.document.getElementById('step-controls-pop');
572
+ if (popStepCtrl) popStepCtrl.style.display = 'flex';
573
+ }
574
+ }
575
+
576
+ // ============ DRAG FUNCTIONALITY ============
577
+
578
+ let isDragging = false;
579
+ let dragOffsetX = 0;
580
+ let dragOffsetY = 0;
581
+
582
+ function initPanelDrag() {
583
+ const handle = document.getElementById('step-drag-handle');
584
+ if (!handle) return;
585
+
586
+ handle.addEventListener('mousedown', startDrag);
587
+ handle.addEventListener('touchstart', startDragTouch, { passive: false });
588
+ }
589
+
590
+ function startDrag(e) {
591
+ e.preventDefault();
592
+ const panel = document.getElementById('step-panel');
593
+ if (!panel) return;
594
+
595
+ isDragging = true;
596
+
597
+ // Get current panel position
598
+ const rect = panel.getBoundingClientRect();
599
+ const parentRect = panel.parentElement.getBoundingClientRect();
600
+
601
+ // Switch from right-based to left-based absolute positioning
602
+ panel.style.transform = 'none';
603
+ panel.style.right = 'auto';
604
+ panel.style.left = (rect.left - parentRect.left) + 'px';
605
+ panel.style.top = (rect.top - parentRect.top) + 'px';
606
+ panel.style.bottom = 'auto';
607
+
608
+ dragOffsetX = e.clientX - rect.left;
609
+ dragOffsetY = e.clientY - rect.top;
610
+
611
+ document.addEventListener('mousemove', onDrag);
612
+ document.addEventListener('mouseup', stopDrag);
613
+ }
614
+
615
+ function startDragTouch(e) {
616
+ e.preventDefault();
617
+ const touch = e.touches[0];
618
+ const panel = document.getElementById('step-panel');
619
+ if (!panel) return;
620
+
621
+ isDragging = true;
622
+
623
+ const rect = panel.getBoundingClientRect();
624
+ const parentRect = panel.parentElement.getBoundingClientRect();
625
+
626
+ panel.style.transform = 'none';
627
+ panel.style.right = 'auto';
628
+ panel.style.left = (rect.left - parentRect.left) + 'px';
629
+ panel.style.top = (rect.top - parentRect.top) + 'px';
630
+ panel.style.bottom = 'auto';
631
+
632
+ dragOffsetX = touch.clientX - rect.left;
633
+ dragOffsetY = touch.clientY - rect.top;
634
+
635
+ document.addEventListener('touchmove', onDragTouch, { passive: false });
636
+ document.addEventListener('touchend', stopDrag);
637
+ }
638
+
639
+ function onDrag(e) {
640
+ if (!isDragging) return;
641
+ const panel = document.getElementById('step-panel');
642
+ if (!panel) return;
643
+
644
+ const parentRect = panel.parentElement.getBoundingClientRect();
645
+ let newX = e.clientX - parentRect.left - dragOffsetX;
646
+ let newY = e.clientY - parentRect.top - dragOffsetY;
647
+
648
+ // Constrain within parent
649
+ newX = Math.max(0, Math.min(newX, parentRect.width - panel.offsetWidth));
650
+ newY = Math.max(0, Math.min(newY, parentRect.height - panel.offsetHeight));
651
+
652
+ panel.style.left = newX + 'px';
653
+ panel.style.top = newY + 'px';
654
+ }
655
+
656
+ function onDragTouch(e) {
657
+ if (!isDragging) return;
658
+ e.preventDefault();
659
+ const touch = e.touches[0];
660
+ const panel = document.getElementById('step-panel');
661
+ if (!panel) return;
662
+
663
+ const parentRect = panel.parentElement.getBoundingClientRect();
664
+ let newX = touch.clientX - parentRect.left - dragOffsetX;
665
+ let newY = touch.clientY - parentRect.top - dragOffsetY;
666
+
667
+ newX = Math.max(0, Math.min(newX, parentRect.width - panel.offsetWidth));
668
+ newY = Math.max(0, Math.min(newY, parentRect.height - panel.offsetHeight));
669
+
670
+ panel.style.left = newX + 'px';
671
+ panel.style.top = newY + 'px';
672
+ }
673
+
674
+ function stopDrag() {
675
+ isDragging = false;
676
+ document.removeEventListener('mousemove', onDrag);
677
+ document.removeEventListener('mouseup', stopDrag);
678
+ document.removeEventListener('touchmove', onDragTouch);
679
+ document.removeEventListener('touchend', stopDrag);
680
+ }
681
+
682
+ function updateStepPanel() {
683
+ const panel = document.getElementById('step-panel');
684
+ if (!panel) return;
685
+
686
+ const tokens = parseAlgorithmTokens(stepNotation);
687
+
688
+ // Build move display with highlighting
689
+ let movesHtml = '';
690
+ for (let i = 0; i < tokens.length; i++) {
691
+ if (i < stepIndex) {
692
+ movesHtml += '<span class="step-done">' + tokens[i] + '</span> ';
693
+ } else if (i === stepIndex) {
694
+ movesHtml += '<span class="step-current">' + tokens[i] + '</span> ';
695
+ } else {
696
+ movesHtml += '<span class="step-pending">' + tokens[i] + '</span> ';
697
+ }
698
+ }
699
+
700
+ const isComplete = stepIndex >= stepTotal;
701
+
702
+ let html = '<div class="drag-handle" id="step-drag-handle">&#x2630; Drag to move</div>';
703
+ html += '<div class="step-content">';
704
+ html += '<div class="step-info">';
705
+ html += '<div class="step-counter">Step ' + Math.min(stepIndex + 1, stepTotal) + ' / ' + stepTotal + '</div>';
706
+ html += '<div class="step-moves-display">' + movesHtml + '</div>';
707
+ html += '</div>';
708
+ html += '<div class="step-buttons">';
709
+ if (stepIndex > 0) {
710
+ html += '<button class="step-btn step-btn-prev" onclick="executePrevStep()">&#x25C0; Prev</button>';
711
+ }
712
+ if (isComplete) {
713
+ html += '<button class="step-btn step-btn-done" onclick="cancelStepMode()">&#x2714; Done</button>';
714
+ } else {
715
+ html += '<button class="step-btn step-btn-next" onclick="executeNextStep()">Next Step &#x25B6;</button>';
716
+ }
717
+ html += '<button class="step-btn step-btn-cancel" onclick="cancelStepMode()">Cancel</button>';
718
+ html += '</div>';
719
+ html += '</div>';
720
+
721
+ panel.innerHTML = html;
722
+
723
+ // Attach drag listeners after render
724
+ initPanelDrag();
725
+
726
+ // Sync step info to pop-out window
727
+ if (cubePopWindow && !cubePopWindow.closed) {
728
+ const popInfo = cubePopWindow.document.getElementById('step-info-pop');
729
+ if (popInfo) {
730
+ let popHtml = '<div class="step-counter">Step ' + Math.min(stepIndex + 1, stepTotal) + ' / ' + stepTotal + '</div>';
731
+ popHtml += '<div class="step-moves">' + movesHtml + '</div>';
732
+ popInfo.innerHTML = popHtml;
733
+ }
734
+ }
735
+ }
736
+
737
+ // Parse notation keeping original tokens for display
738
+ function parseAlgorithmTokens(notation) {
739
+ notation = notation.replace(/\u2019/g, "'");
740
+ return notation.replace(/\(/g, '').replace(/\)/g, '').trim().split(/\s+/);
741
+ }
742
+
743
+ async function startStepByStep(notation) {
744
+ if (isAnimating || moveQueue.length > 0) return;
745
+ cancelStepMode();
746
+
747
+ // Focus the pop-out window if cube is popped out
748
+ if (cubePopWindow && !cubePopWindow.closed) {
749
+ cubePopWindow.focus();
750
+ }
751
+
752
+ // Reset cube first
753
+ scene.remove(cubeGroup);
754
+ createCube();
755
+
756
+ const moves = parseAlgorithm(notation);
757
+ if (moves.length === 0) return;
758
+
759
+ // Compute inverse sequence to set up the case
760
+ const inverseMoves = moves.slice().reverse().map(invertMove);
761
+
762
+ document.getElementById('scramble-display').textContent = 'Setting up case...';
763
+
764
+ isAnimating = true;
765
+ for (const move of inverseMoves) {
766
+ const def = get3DMoveDef(move);
767
+ if (def) {
768
+ await animateMove(def.axis, def.layers, def.angle, 40);
769
+ }
770
+ }
771
+ isAnimating = false;
772
+
773
+ // Enter step mode
774
+ stepMoves = moves;
775
+ stepIndex = 0;
776
+ stepTotal = moves.length;
777
+ stepNotation = notation;
778
+ stepActive = true;
779
+
780
+ document.getElementById('scramble-display').textContent = 'Step-by-step: Click "Next Step" to advance';
781
+ showStepPanel();
782
+
783
+ // Ensure pop-out step controls are visible (retry after short delay for timing)
784
+ if (cubePopWindow && !cubePopWindow.closed) {
785
+ setTimeout(function() {
786
+ const popStepCtrl = cubePopWindow.document.getElementById('step-controls-pop');
787
+ if (popStepCtrl) popStepCtrl.style.display = 'flex';
788
+ }, 200);
789
+ }
790
+ }
791
+
792
+ async function executeNextStep() {
793
+ if (!stepActive || isAnimating || stepIndex >= stepTotal) return;
794
+
795
+ const move = stepMoves[stepIndex];
796
+ const def = get3DMoveDef(move);
797
+
798
+ if (def) {
799
+ isAnimating = true;
800
+ await animateMove(def.axis, def.layers, def.angle, 350);
801
+ isAnimating = false;
802
+ }
803
+
804
+ stepIndex++;
805
+
806
+ if (stepIndex >= stepTotal) {
807
+ document.getElementById('scramble-display').textContent = 'Solved! \u2714';
808
+ } else {
809
+ const tokens = parseAlgorithmTokens(stepNotation);
810
+ document.getElementById('scramble-display').textContent =
811
+ 'Step ' + (stepIndex + 1) + '/' + stepTotal + ': next is ' + tokens[stepIndex];
812
+ }
813
+
814
+ updateStepPanel();
815
+ }
816
+
817
+ async function executePrevStep() {
818
+ if (!stepActive || isAnimating || stepIndex <= 0) return;
819
+
820
+ // Undo the previous move by applying its inverse
821
+ stepIndex--;
822
+ const move = stepMoves[stepIndex];
823
+ const inverseMove = invertMove(move);
824
+ const def = get3DMoveDef(inverseMove);
825
+
826
+ if (def) {
827
+ isAnimating = true;
828
+ await animateMove(def.axis, def.layers, def.angle, 350);
829
+ isAnimating = false;
830
+ }
831
+
832
+ const tokens = parseAlgorithmTokens(stepNotation);
833
+ document.getElementById('scramble-display').textContent =
834
+ 'Step ' + (stepIndex + 1) + '/' + stepTotal + ': next is ' + tokens[stepIndex];
835
+
836
+ updateStepPanel();
837
+ }
838
+
839
+ // ============ SCRAMBLE & RESET ============
840
+
841
+ function scrambleCube() {
842
+ if (isAnimating || moveQueue.length > 0) return;
843
+
844
+ const scramblePool = ['R','Ri','R2','L','Li','L2','U','Ui','U2','D','Di','D2','F','Fi','F2','B','Bi','B2'];
845
+ const scrambleMoves = [];
846
+
847
+ for (let i = 0; i < 20; i++) {
848
+ const move = scramblePool[Math.floor(Math.random() * scramblePool.length)];
849
+ scrambleMoves.push(move);
850
+ }
851
+
852
+ document.getElementById('scramble-display').textContent = 'Scrambling...';
853
+ moveQueue.push(...scrambleMoves);
854
+ processQueue();
855
+
856
+ setTimeout(() => {
857
+ document.getElementById('scramble-display').textContent = 'Scrambled! Try an algorithm.';
858
+ }, scrambleMoves.length * 210);
859
+ }
860
+
861
+ function resetCube() {
862
+ if (isAnimating) return;
863
+ moveQueue = [];
864
+
865
+ // Remove existing cube and recreate
866
+ scene.remove(cubeGroup);
867
+ createCube();
868
+
869
+ document.getElementById('scramble-display').textContent = 'Cube reset! Drag to rotate \u2022 Scroll to zoom';
870
+ }
871
+
872
+ // ============ UI RENDERING ============
873
+
874
+ let currentSection = 'f2l';
875
+
876
+ // ============ TOP VIEW DIAGRAM GENERATOR ============
877
+ // Computes the top-view diagram by simulating the algorithm inverse on a solved cube
878
+ // This guarantees the diagram matches exactly what the 3D cube shows
879
+
880
+ const DIAG_COLORS = {
881
+ U: '#ffdd00', // Yellow (top)
882
+ D: '#ffffff', // White (bottom)
883
+ R: '#ee0000', // Red
884
+ L: '#ff8800', // Orange
885
+ F: '#0051ff', // Blue
886
+ B: '#00aa33', // Green
887
+ X: '#444444' // Grey (unknown/internal)
888
+ };
889
+
890
+ // Mini cube state simulator for computing top-view diagrams
891
+ // State: 6 faces x 9 stickers = 54 values
892
+ // Face order: U, D, R, L, F, B
893
+ // Sticker order per face (looking at face):
894
+ // 0 1 2
895
+ // 3 4 5
896
+ // 6 7 8
897
+ function createSolvedState() {
898
+ return {
899
+ U: ['U','U','U','U','U','U','U','U','U'],
900
+ D: ['D','D','D','D','D','D','D','D','D'],
901
+ R: ['R','R','R','R','R','R','R','R','R'],
902
+ L: ['L','L','L','L','L','L','L','L','L'],
903
+ F: ['F','F','F','F','F','F','F','F','F'],
904
+ B: ['B','B','B','B','B','B','B','B','B']
905
+ };
906
+ }
907
+
908
+ function cloneState(s) {
909
+ return {
910
+ U: [...s.U], D: [...s.D], R: [...s.R],
911
+ L: [...s.L], F: [...s.F], B: [...s.B]
912
+ };
913
+ }
914
+
915
+ // Apply a single move to the state
916
+ function applyMoveToState(state, move) {
917
+ let s = cloneState(state);
918
+ switch(move) {
919
+ case 'U': return moveU(s);
920
+ case 'Ui': return moveU(moveU(moveU(s)));
921
+ case 'U2': return moveU(moveU(s));
922
+ case 'D': return moveD(s);
923
+ case 'Di': return moveD(moveD(moveD(s)));
924
+ case 'D2': return moveD(moveD(s));
925
+ case 'R': return moveR(s);
926
+ case 'Ri': return moveR(moveR(moveR(s)));
927
+ case 'R2': return moveR(moveR(s));
928
+ case 'L': return moveL(s);
929
+ case 'Li': return moveL(moveL(moveL(s)));
930
+ case 'L2': return moveL(moveL(s));
931
+ case 'F': return moveF(s);
932
+ case 'Fi': return moveF(moveF(moveF(s)));
933
+ case 'F2': return moveF(moveF(s));
934
+ case 'B': return moveB(s);
935
+ case 'Bi': return moveB(moveB(moveB(s)));
936
+ case 'B2': return moveB(moveB(s));
937
+ // Wide moves: r = R + M' (M' same direction as R)
938
+ case 'r': s = moveR(s); return moveMx(s);
939
+ case 'ri': s = moveR(moveR(moveR(s))); return moveMx(moveMx(moveMx(s)));
940
+ case 'r2': s = moveR(moveR(s)); return moveMx(moveMx(s));
941
+ case 'l': s = moveL(s); return moveMxi(s);
942
+ case 'li': s = moveL(moveL(moveL(s))); return moveMxi(moveMxi(moveMxi(s)));
943
+ case 'l2': s = moveL(moveL(s)); return moveMxi(moveMxi(s));
944
+ case 'u': s = moveU(s); return moveMy(s);
945
+ case 'ui': s = moveU(moveU(moveU(s))); return moveMy(moveMy(moveMy(s)));
946
+ case 'u2': s = moveU(moveU(s)); return moveMy(moveMy(s));
947
+ case 'd': s = moveD(s); return moveMyi(s);
948
+ case 'di': s = moveD(moveD(moveD(s))); return moveMyi(moveMyi(moveMyi(s)));
949
+ case 'd2': s = moveD(moveD(s)); return moveMyi(moveMyi(s));
950
+ case 'f': s = moveF(s); return moveMz(s);
951
+ case 'fi': s = moveF(moveF(moveF(s))); return moveMz(moveMz(moveMz(s)));
952
+ case 'f2': s = moveF(moveF(s)); return moveMz(moveMz(s));
953
+ case 'b': s = moveB(s); return moveMzi(s);
954
+ case 'bi': s = moveB(moveB(moveB(s))); return moveMzi(moveMzi(moveMzi(s)));
955
+ case 'b2': s = moveB(moveB(s)); return moveMzi(moveMzi(s));
956
+ // Rotations: x follows R, y follows U, z follows F
957
+ // Physical R direction: F→U→B→D (sticker at F moves to U position)
958
+ // moveR implements this: new U = old F (F→U) ✓
959
+ // Physical L direction: U→F→D→B (opposite of R)
960
+ // So for x (R direction on ALL layers): left layer needs L' = moveL³
961
+ case 'x': s = moveR(s); s = moveMx(s); return moveL(moveL(moveL(s)));
962
+ case 'xi': s = moveR(moveR(moveR(s))); s = moveMx(moveMx(moveMx(s))); return moveL(s);
963
+ case 'x2': s = moveR(moveR(s)); s = moveMx(moveMx(s)); return moveL(moveL(s));
964
+ case 'y': s = moveU(s); s = moveMy(s); return moveD(moveD(moveD(s)));
965
+ case 'yi': s = moveU(moveU(moveU(s))); s = moveMy(moveMy(moveMy(s))); return moveD(s);
966
+ case 'y2': s = moveU(moveU(s)); s = moveMy(moveMy(s)); return moveD(moveD(s));
967
+ case 'z': s = moveF(s); s = moveMz(s); return moveB(moveB(moveB(s)));
968
+ case 'zi': s = moveF(moveF(moveF(s))); s = moveMz(moveMz(moveMz(s))); return moveB(s);
969
+ case 'z2': s = moveF(moveF(s)); s = moveMz(moveMz(s)); return moveB(moveB(s));
970
+ // M slice (between R and L, follows L direction = opposite of R)
971
+ case 'M': return moveMx(moveMx(moveMx(s)));
972
+ case 'Mi': return moveMx(s);
973
+ case 'M2': return moveMx(moveMx(s));
974
+ // S slice (between F and B, follows F direction)
975
+ case 'S': return moveMz(s);
976
+ case 'Si': return moveMz(moveMz(moveMz(s)));
977
+ case 'S2': return moveMz(moveMz(s));
978
+ default: return s;
979
+ }
980
+ }
981
+
982
+ // Middle slice moves for state simulation
983
+ // Mx = M slice following R direction (between R and L, rotates like R)
984
+ // Mx = Middle slice (same cycle direction as moveR for consistency)
985
+ function moveMx(s) {
986
+ const tmp = [s.U[1],s.U[4],s.U[7]];
987
+ s.U[1]=s.F[1]; s.U[4]=s.F[4]; s.U[7]=s.F[7];
988
+ s.F[1]=s.D[1]; s.F[4]=s.D[4]; s.F[7]=s.D[7];
989
+ s.D[1]=s.B[7]; s.D[4]=s.B[4]; s.D[7]=s.B[1];
990
+ s.B[7]=tmp[0]; s.B[4]=tmp[1]; s.B[1]=tmp[2];
991
+ return s;
992
+ }
993
+ function moveMxi(s) { return moveMx(moveMx(moveMx(s))); }
994
+
995
+ // My = E slice following U direction (between U and D, rotates like U)
996
+ function moveMy(s) {
997
+ const tmp = [s.F[3],s.F[4],s.F[5]];
998
+ s.F[3]=s.R[3]; s.F[4]=s.R[4]; s.F[5]=s.R[5];
999
+ s.R[3]=s.B[3]; s.R[4]=s.B[4]; s.R[5]=s.B[5];
1000
+ s.B[3]=s.L[3]; s.B[4]=s.L[4]; s.B[5]=s.L[5];
1001
+ s.L[3]=tmp[0]; s.L[4]=tmp[1]; s.L[5]=tmp[2];
1002
+ return s;
1003
+ }
1004
+ function moveMyi(s) { return moveMy(moveMy(moveMy(s))); }
1005
+
1006
+ // Mz = S slice following F direction (between F and B, rotates like F)
1007
+ function moveMz(s) {
1008
+ const tmp = [s.U[3],s.U[4],s.U[5]];
1009
+ s.U[3]=s.L[7]; s.U[4]=s.L[4]; s.U[5]=s.L[1];
1010
+ s.L[1]=s.D[3]; s.L[4]=s.D[4]; s.L[7]=s.D[5];
1011
+ s.D[3]=s.R[7]; s.D[4]=s.R[4]; s.D[5]=s.R[1];
1012
+ s.R[1]=tmp[0]; s.R[4]=tmp[1]; s.R[7]=tmp[2];
1013
+ return s;
1014
+ }
1015
+ function moveMzi(s) { return moveMz(moveMz(moveMz(s))); }
1016
+
1017
+ function rotateFaceCW(face) {
1018
+ return [face[6],face[3],face[0],face[7],face[4],face[1],face[8],face[5],face[2]];
1019
+ }
1020
+
1021
+ function moveU(s) {
1022
+ s.U = rotateFaceCW(s.U);
1023
+ const tmp = [s.F[0],s.F[1],s.F[2]];
1024
+ s.F[0]=s.R[0]; s.F[1]=s.R[1]; s.F[2]=s.R[2];
1025
+ s.R[0]=s.B[0]; s.R[1]=s.B[1]; s.R[2]=s.B[2];
1026
+ s.B[0]=s.L[0]; s.B[1]=s.L[1]; s.B[2]=s.L[2];
1027
+ s.L[0]=tmp[0]; s.L[1]=tmp[1]; s.L[2]=tmp[2];
1028
+ return s;
1029
+ }
1030
+
1031
+ function moveD(s) {
1032
+ s.D = rotateFaceCW(s.D);
1033
+ const tmp = [s.F[6],s.F[7],s.F[8]];
1034
+ s.F[6]=s.L[6]; s.F[7]=s.L[7]; s.F[8]=s.L[8];
1035
+ s.L[6]=s.B[6]; s.L[7]=s.B[7]; s.L[8]=s.B[8];
1036
+ s.B[6]=s.R[6]; s.B[7]=s.R[7]; s.B[8]=s.R[8];
1037
+ s.R[6]=tmp[0]; s.R[7]=tmp[1]; s.R[8]=tmp[2];
1038
+ return s;
1039
+ }
1040
+
1041
+ function moveR(s) {
1042
+ s.R = rotateFaceCW(s.R);
1043
+ const tmp = [s.U[2],s.U[5],s.U[8]];
1044
+ s.U[2]=s.F[2]; s.U[5]=s.F[5]; s.U[8]=s.F[8];
1045
+ s.F[2]=s.D[2]; s.F[5]=s.D[5]; s.F[8]=s.D[8];
1046
+ s.D[2]=s.B[6]; s.D[5]=s.B[3]; s.D[8]=s.B[0];
1047
+ s.B[6]=tmp[0]; s.B[3]=tmp[1]; s.B[0]=tmp[2];
1048
+ return s;
1049
+ }
1050
+
1051
+ function moveL(s) {
1052
+ s.L = rotateFaceCW(s.L);
1053
+ const tmp = [s.U[0],s.U[3],s.U[6]];
1054
+ s.U[0]=s.B[8]; s.U[3]=s.B[5]; s.U[6]=s.B[2];
1055
+ s.B[8]=s.D[0]; s.B[5]=s.D[3]; s.B[2]=s.D[6];
1056
+ s.D[0]=s.F[0]; s.D[3]=s.F[3]; s.D[6]=s.F[6];
1057
+ s.F[0]=tmp[0]; s.F[3]=tmp[1]; s.F[6]=tmp[2];
1058
+ return s;
1059
+ }
1060
+
1061
+ function moveF(s) {
1062
+ s.F = rotateFaceCW(s.F);
1063
+ const tmp = [s.U[6],s.U[7],s.U[8]];
1064
+ s.U[6]=s.L[8]; s.U[7]=s.L[5]; s.U[8]=s.L[2];
1065
+ s.L[2]=s.D[0]; s.L[5]=s.D[1]; s.L[8]=s.D[2];
1066
+ s.D[0]=s.R[6]; s.D[1]=s.R[3]; s.D[2]=s.R[0];
1067
+ s.R[0]=tmp[0]; s.R[3]=tmp[1]; s.R[6]=tmp[2];
1068
+ return s;
1069
+ }
1070
+
1071
+ function moveB(s) {
1072
+ s.B = rotateFaceCW(s.B);
1073
+ const tmp = [s.U[0],s.U[1],s.U[2]];
1074
+ s.U[0]=s.R[2]; s.U[1]=s.R[5]; s.U[2]=s.R[8];
1075
+ s.R[2]=s.D[8]; s.R[5]=s.D[7]; s.R[8]=s.D[6];
1076
+ s.D[6]=s.L[0]; s.D[7]=s.L[3]; s.D[8]=s.L[6];
1077
+ s.L[0]=tmp[2]; s.L[3]=tmp[1]; s.L[6]=tmp[0];
1078
+ return s;
1079
+ }
1080
+
1081
+ // Compute the cube state after applying the inverse of an algorithm (setup state)
1082
+ function computeSetupState(notation) {
1083
+ const moves = parseAlgorithm(notation);
1084
+ if (moves.length === 0) return createSolvedState();
1085
+
1086
+ // Inverse: reverse order, invert each move
1087
+ const inverseMoves = moves.slice().reverse().map(invertMove);
1088
+
1089
+ let state = createSolvedState();
1090
+ for (const move of inverseMoves) {
1091
+ state = applyMoveToState(state, move);
1092
+ }
1093
+ return state;
1094
+ }
1095
+
1096
+ function generateTopViewSVG(section, caseName, caseDesc, firstAlgNotation) {
1097
+ // Only generate for OLL, PLL, COLL, ZBLL
1098
+ if (section === 'f2l') return '';
1099
+ if (!firstAlgNotation) return '';
1100
+
1101
+ const state = computeSetupState(firstAlgNotation);
1102
+
1103
+ const size = 80;
1104
+ const cell = size / 5;
1105
+ const topStart = cell;
1106
+ const r = 2;
1107
+
1108
+ let svg = '<svg width="' + size + '" height="' + size + '" viewBox="0 0 ' + size + ' ' + size + '" xmlns="http://www.w3.org/2000/svg">';
1109
+ svg += '<rect width="' + size + '" height="' + size + '" fill="#1a1a1a" rx="4"/>';
1110
+
1111
+ // Draw top face (3x3 grid) - state.U
1112
+ for (let row = 0; row < 3; row++) {
1113
+ for (let col = 0; col < 3; col++) {
1114
+ const color = DIAG_COLORS[state.U[row * 3 + col]] || '#444';
1115
+ const x = topStart + col * cell;
1116
+ const y = topStart + row * cell;
1117
+ svg += '<rect x="' + (x+1) + '" y="' + (y+1) + '" width="' + (cell-2) + '" height="' + (cell-2) + '" fill="' + color + '" rx="' + r + '"/>';
1118
+ }
1119
+ }
1120
+
1121
+ // Side stickers visible from top:
1122
+ // Top of diagram = Back face top row (reversed because viewing from opposite side): B[2], B[1], B[0]
1123
+ for (let i = 0; i < 3; i++) {
1124
+ const color = DIAG_COLORS[state.B[2 - i]] || '#444';
1125
+ const x = topStart + i * cell;
1126
+ const y = 0;
1127
+ svg += '<rect x="' + (x+1) + '" y="' + (y+1) + '" width="' + (cell-2) + '" height="' + (cell-2) + '" fill="' + color + '" rx="' + r + '"/>';
1128
+ }
1129
+ // Right of diagram = Right face top row (back to front): R[2], R[1], R[0]
1130
+ for (let i = 0; i < 3; i++) {
1131
+ const color = DIAG_COLORS[state.R[2 - i]] || '#444';
1132
+ const x = topStart + 3 * cell;
1133
+ const y = topStart + i * cell;
1134
+ svg += '<rect x="' + (x+1) + '" y="' + (y+1) + '" width="' + (cell-2) + '" height="' + (cell-2) + '" fill="' + color + '" rx="' + r + '"/>';
1135
+ }
1136
+ // Bottom of diagram = Front face top row: F[0], F[1], F[2]
1137
+ for (let i = 0; i < 3; i++) {
1138
+ const color = DIAG_COLORS[state.F[i]] || '#444';
1139
+ const x = topStart + i * cell;
1140
+ const y = topStart + 3 * cell;
1141
+ svg += '<rect x="' + (x+1) + '" y="' + (y+1) + '" width="' + (cell-2) + '" height="' + (cell-2) + '" fill="' + color + '" rx="' + r + '"/>';
1142
+ }
1143
+ // Left of diagram = Left face top row: L[0], L[1], L[2] (front to back)
1144
+ for (let i = 0; i < 3; i++) {
1145
+ const color = DIAG_COLORS[state.L[i]] || '#444';
1146
+ const x = 0;
1147
+ const y = topStart + i * cell;
1148
+ svg += '<rect x="' + (x+1) + '" y="' + (y+1) + '" width="' + (cell-2) + '" height="' + (cell-2) + '" fill="' + color + '" rx="' + r + '"/>';
1149
+ }
1150
+
1151
+ svg += '</svg>';
1152
+ return svg;
1153
+ }
1154
+
1155
+ function renderAlgorithms(section, filterGroup) {
1156
+ currentSection = section;
1157
+ const data = OH_ALGORITHMS[section];
1158
+ const container = document.getElementById('algorithm-section');
1159
+
1160
+ let html = `<h2>${data.title}</h2>`;
1161
+ html += `<p class="description">${data.description}</p>`;
1162
+
1163
+ // Add group filter tabs for ZBLL, COLL, and F2L
1164
+ if (section === 'zbll' || section === 'coll' || section === 'f2l') {
1165
+ const groups = [];
1166
+ const groupSet = new Set();
1167
+ for (const c of data.cases) {
1168
+ const g = c.case || c.name.split(' ')[0];
1169
+ if (!groupSet.has(g)) { groupSet.add(g); groups.push(g); }
1170
+ }
1171
+ html += '<div class="group-tabs">';
1172
+ html += `<button class="group-tab ${!filterGroup ? 'active' : ''}" onclick="renderAlgorithms('${section}')">All</button>`;
1173
+ for (const g of groups) {
1174
+ const active = filterGroup === g ? 'active' : '';
1175
+ html += `<button class="group-tab ${active}" onclick="renderAlgorithms('${section}','${g}')">${g}</button>`;
1176
+ }
1177
+ html += '</div>';
1178
+ }
1179
+
1180
+ // Filter cases by group if specified
1181
+ let cases = data.cases;
1182
+ if (filterGroup) {
1183
+ cases = data.cases.filter(c => (c.case || c.name.split(' ')[0]) === filterGroup);
1184
+ }
1185
+
1186
+ html += '<div class="alg-grid">';
1187
+
1188
+ for (const caseData of cases) {
1189
+ html += `<div class="alg-card">`;
1190
+
1191
+ // Top view diagram + case info in a row
1192
+ const firstAlg = caseData.algs[0] ? caseData.algs[0].notation : '';
1193
+
1194
+ // Generate VisualCube 3D image for F2L cases
1195
+ if (section === 'f2l' && firstAlg) {
1196
+ const vcAlg = firstAlg.replace(/\(/g, '').replace(/\)/g, '').replace(/\u2019/g, "'").replace(/\u2032/g, "'");
1197
+ const vcUrl = `https://visualcube.api.cubing.net/visualcube.php?fmt=svg&size=150&case=${encodeURIComponent(vcAlg)}&bg=t&cc=black`;
1198
+ html += `<div class="alg-card-header">`;
1199
+ html += `<div class="alg-diagram"><img src="${vcUrl}" alt="${caseData.name}" class="f2l-case-img" loading="lazy"></div>`;
1200
+ html += `<div class="alg-card-info">`;
1201
+ html += `<div class="case-name">${caseData.name}</div>`;
1202
+ html += `<h4>${caseData.case}</h4>`;
1203
+ html += `</div></div>`;
1204
+ } else if (section === 'f2l' && !firstAlg) {
1205
+ // Solved case (F2L 37)
1206
+ html += `<div class="alg-card-header">`;
1207
+ html += `<div class="alg-diagram"><img src="https://visualcube.api.cubing.net/visualcube.php?fmt=svg&size=150&bg=t&cc=black" alt="${caseData.name}" class="f2l-case-img" loading="lazy"></div>`;
1208
+ html += `<div class="alg-card-info">`;
1209
+ html += `<div class="case-name">${caseData.name}</div>`;
1210
+ html += `<h4>${caseData.case}</h4>`;
1211
+ html += `</div></div>`;
1212
+ } else {
1213
+ const diagram = generateTopViewSVG(section, caseData.name, caseData.case, firstAlg);
1214
+ if (diagram) {
1215
+ html += `<div class="alg-card-header">`;
1216
+ html += `<div class="alg-diagram">${diagram}</div>`;
1217
+ html += `<div class="alg-card-info">`;
1218
+ html += `<div class="case-name">${caseData.name}</div>`;
1219
+ html += `<h4>${caseData.case}</h4>`;
1220
+ html += `</div></div>`;
1221
+ } else {
1222
+ html += `<div class="case-name">${caseData.name}</div>`;
1223
+ html += `<h4>${caseData.case}</h4>`;
1224
+ }
1225
+ }
1226
+
1227
+ for (const alg of caseData.algs) {
1228
+ const escaped = alg.notation.replace(/'/g, "\\'").replace(/\u2019/g, "\\'");
1229
+ html += `<div class="alg-row">`;
1230
+ html += `<div class="alg-notation" onclick="setupAndSolve('${escaped}')" title="Click to setup case & watch the solve">${alg.notation}</div>`;
1231
+ html += `<div class="alg-meta">`;
1232
+ html += `<span>${alg.note}</span>`;
1233
+ html += `<span class="moves">${alg.moves} moves</span>`;
1234
+ html += `<button class="step-by-step-btn" onclick="startStepByStep('${escaped}')" title="Step through one move at a time">Step by Step</button>`;
1235
+ html += `</div>`;
1236
+ html += `</div>`;
1237
+ }
1238
+
1239
+ html += `</div>`;
1240
+ }
1241
+
1242
+ html += '</div>';
1243
+ container.innerHTML = html;
1244
+ }
1245
+
1246
+ // ============ NAVIGATION ============
1247
+
1248
+ function setupNavigation() {
1249
+ const navItems = document.querySelectorAll('.nav-item');
1250
+
1251
+ navItems.forEach(item => {
1252
+ item.addEventListener('click', () => {
1253
+ navItems.forEach(n => {
1254
+ n.classList.remove('active');
1255
+ n.setAttribute('aria-pressed', 'false');
1256
+ });
1257
+ item.classList.add('active');
1258
+ item.setAttribute('aria-pressed', 'true');
1259
+
1260
+ const section = item.getAttribute('data-section');
1261
+ renderAlgorithms(section);
1262
+ });
1263
+
1264
+ // Keyboard support
1265
+ item.addEventListener('keydown', (e) => {
1266
+ if (e.key === 'Enter' || e.key === ' ') {
1267
+ e.preventDefault();
1268
+ item.click();
1269
+ }
1270
+ });
1271
+ });
1272
+ }
1273
+
1274
+ // ============ KEYBOARD SHORTCUTS ============
1275
+
1276
+ document.addEventListener('keydown', (e) => {
1277
+ if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
1278
+
1279
+ // Step mode: space or right arrow advances, left arrow goes back
1280
+ if (stepActive) {
1281
+ if (e.key === ' ' || e.key === 'ArrowRight' || e.key === 'Enter') {
1282
+ e.preventDefault();
1283
+ executeNextStep();
1284
+ return;
1285
+ }
1286
+ if (e.key === 'ArrowLeft') {
1287
+ e.preventDefault();
1288
+ executePrevStep();
1289
+ return;
1290
+ }
1291
+ if (e.key === 'Escape') {
1292
+ cancelStepMode();
1293
+ return;
1294
+ }
1295
+ }
1296
+
1297
+ const key = e.key.toLowerCase();
1298
+ const shift = e.shiftKey;
1299
+
1300
+ switch (key) {
1301
+ case 'r': doMove(shift ? 'Ri' : 'R'); break;
1302
+ case 'u': doMove(shift ? 'Ui' : 'U'); break;
1303
+ case 'f': doMove(shift ? 'Fi' : 'F'); break;
1304
+ case 'l': doMove(shift ? 'Li' : 'L'); break;
1305
+ case 'd': doMove(shift ? 'Di' : 'D'); break;
1306
+ case 'b': doMove(shift ? 'Bi' : 'B'); break;
1307
+ case 's': scrambleCube(); break;
1308
+ case 'escape': resetCube(); break;
1309
+ }
1310
+ });
1311
+
1312
+ // ============ STARTUP ============
1313
+
1314
+ function toggleMoveButtons() {
1315
+ const panel = document.getElementById('move-buttons');
1316
+ const btn = document.getElementById('toggle-moves-btn');
1317
+ if (panel.style.display === 'none') {
1318
+ panel.style.display = 'flex';
1319
+ btn.textContent = 'Moves ▲';
1320
+ } else {
1321
+ panel.style.display = 'none';
1322
+ btn.textContent = 'Moves ▼';
1323
+ }
1324
+ }
1325
+
1326
+ // ============ COLLAPSE / FULLSCREEN 3D VIEW ============
1327
+
1328
+ let cubePopWindow = null; // kept for compatibility with showStepPanel/hideStepPanel references
1329
+
1330
+ function toggleCollapse() {
1331
+ const cubeSection = document.getElementById('cube-section');
1332
+ const btn = document.getElementById('popout-btn');
1333
+
1334
+ if (cubeSection.classList.contains('collapsed')) {
1335
+ cubeSection.classList.remove('collapsed');
1336
+ btn.innerHTML = '&#x25B2; Hide';
1337
+ btn.title = 'Collapse 3D view for more algorithm space';
1338
+ onResize();
1339
+ } else {
1340
+ cubeSection.classList.remove('fullscreen');
1341
+ cubeSection.classList.add('collapsed');
1342
+ btn.innerHTML = '&#x25BC; Show';
1343
+ btn.title = 'Show 3D view';
1344
+ }
1345
+ }
1346
+
1347
+ function toggleFullscreen() {
1348
+ const cubeSection = document.getElementById('cube-section');
1349
+ const fsBtn = document.getElementById('fullscreen-btn');
1350
+
1351
+ if (cubeSection.classList.contains('fullscreen')) {
1352
+ cubeSection.classList.remove('fullscreen');
1353
+ fsBtn.innerHTML = '&#x26F6; Full';
1354
+ fsBtn.title = 'Full screen 3D view';
1355
+ onResize();
1356
+ } else {
1357
+ cubeSection.classList.remove('collapsed');
1358
+ cubeSection.classList.add('fullscreen');
1359
+ fsBtn.innerHTML = '&#x2716; Exit';
1360
+ fsBtn.title = 'Exit full screen';
1361
+ // Update canvas size for fullscreen
1362
+ const container = document.getElementById('cube-canvas').parentElement;
1363
+ camera.aspect = container.clientWidth / (window.innerHeight - 120);
1364
+ camera.updateProjectionMatrix();
1365
+ renderer.setSize(container.clientWidth, window.innerHeight - 120);
1366
+ // Also show the collapse button properly
1367
+ document.getElementById('popout-btn').innerHTML = '&#x25B2; Hide';
1368
+ }
1369
+ }
1370
+
1371
+ // Startup is handled by index.html after JSON files load
index.html CHANGED
@@ -1,19 +1,708 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>One-Handed 3x3 Rubik's Cube - OH Algorithms</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ background: #1a1a2e;
17
+ color: #e0e0e0;
18
+ overflow-x: hidden;
19
+ }
20
+
21
+ header {
22
+ background: linear-gradient(135deg, #16213e, #0f3460);
23
+ padding: 20px 40px;
24
+ text-align: center;
25
+ border-bottom: 2px solid #e94560;
26
+ }
27
+
28
+ header h1 {
29
+ font-size: 2rem;
30
+ color: #fff;
31
+ text-shadow: 0 0 10px rgba(233, 69, 96, 0.5);
32
+ }
33
+
34
+ header p {
35
+ color: #aaa;
36
+ margin-top: 5px;
37
+ }
38
+
39
+ .container {
40
+ display: flex;
41
+ min-height: calc(100vh - 100px);
42
+ }
43
+
44
+ .sidebar {
45
+ width: 280px;
46
+ background: #16213e;
47
+ padding: 20px;
48
+ border-right: 1px solid #0f3460;
49
+ overflow-y: auto;
50
+ max-height: calc(100vh - 100px);
51
+ position: sticky;
52
+ top: 0;
53
+ }
54
+
55
+ .sidebar h3 {
56
+ color: #e94560;
57
+ margin-bottom: 15px;
58
+ font-size: 1.1rem;
59
+ }
60
+
61
+ .nav-item {
62
+ padding: 12px 16px;
63
+ margin: 4px 0;
64
+ border-radius: 8px;
65
+ cursor: pointer;
66
+ transition: all 0.3s ease;
67
+ font-size: 0.95rem;
68
+ }
69
+
70
+ .nav-item:hover {
71
+ background: #0f3460;
72
+ transform: translateX(5px);
73
+ }
74
+
75
+ .nav-item.active {
76
+ background: #e94560;
77
+ color: #fff;
78
+ font-weight: bold;
79
+ }
80
+
81
+ .main-content {
82
+ flex: 1;
83
+ display: flex;
84
+ flex-direction: column;
85
+ height: calc(100vh - 100px);
86
+ overflow: hidden;
87
+ }
88
+
89
+ .cube-section {
90
+ display: flex;
91
+ justify-content: center;
92
+ align-items: center;
93
+ padding: 20px;
94
+ background: radial-gradient(ellipse at center, #1a1a35 0%, #0a0a18 100%);
95
+ border-bottom: 1px solid #333;
96
+ min-height: 420px;
97
+ position: relative;
98
+ flex-shrink: 0;
99
+ }
100
+
101
+ .cube-section.collapsed {
102
+ min-height: 0;
103
+ height: 40px;
104
+ padding: 8px 20px;
105
+ justify-content: flex-start;
106
+ align-items: center;
107
+ }
108
+
109
+ .cube-section.collapsed canvas,
110
+ .cube-section.collapsed .controls,
111
+ .cube-section.collapsed .scramble-display,
112
+ .cube-section.collapsed .move-buttons,
113
+ .cube-section.collapsed #step-panel {
114
+ display: none !important;
115
+ }
116
+
117
+ .cube-section.fullscreen {
118
+ position: fixed;
119
+ top: 0;
120
+ left: 0;
121
+ right: 0;
122
+ bottom: 0;
123
+ min-height: 100vh;
124
+ z-index: 999;
125
+ border: none;
126
+ padding: 10px;
127
+ }
128
+
129
+ .cube-section.fullscreen #cube-canvas {
130
+ height: calc(100vh - 120px);
131
+ }
132
+
133
+ .popout-btn {
134
+ position: absolute;
135
+ top: 10px;
136
+ right: 10px;
137
+ padding: 6px 14px;
138
+ border: 1px solid #4fc3f7;
139
+ border-radius: 6px;
140
+ background: rgba(15, 52, 96, 0.8);
141
+ color: #4fc3f7;
142
+ cursor: pointer;
143
+ font-size: 0.8rem;
144
+ z-index: 10;
145
+ transition: all 0.2s;
146
+ }
147
+
148
+ .popout-btn:hover {
149
+ background: #4fc3f7;
150
+ color: #0a0a18;
151
+ }
152
+
153
+ .cube-section.collapsed .popout-btn {
154
+ position: static;
155
+ }
156
+
157
+ .fullscreen-btn {
158
+ position: absolute;
159
+ top: 10px;
160
+ right: 100px;
161
+ padding: 6px 14px;
162
+ border: 1px solid #2ecc71;
163
+ border-radius: 6px;
164
+ background: rgba(15, 52, 96, 0.8);
165
+ color: #2ecc71;
166
+ cursor: pointer;
167
+ font-size: 0.8rem;
168
+ z-index: 10;
169
+ transition: all 0.2s;
170
+ }
171
+
172
+ .fullscreen-btn:hover {
173
+ background: #2ecc71;
174
+ color: #0a0a18;
175
+ }
176
+
177
+ #cube-canvas {
178
+ width: 100%;
179
+ height: 400px;
180
+ border-radius: 12px;
181
+ display: block;
182
+ }
183
+
184
+ .controls {
185
+ position: absolute;
186
+ bottom: 20px;
187
+ left: 50%;
188
+ transform: translateX(-50%);
189
+ display: flex;
190
+ gap: 8px;
191
+ flex-wrap: wrap;
192
+ justify-content: center;
193
+ z-index: 10;
194
+ max-width: 600px;
195
+ }
196
+
197
+ .control-btn {
198
+ padding: 8px 14px;
199
+ border: none;
200
+ border-radius: 6px;
201
+ background: #0f3460;
202
+ color: #fff;
203
+ cursor: pointer;
204
+ font-size: 0.85rem;
205
+ transition: all 0.2s;
206
+ }
207
+
208
+ .control-btn:hover {
209
+ background: #e94560;
210
+ transform: scale(1.05);
211
+ }
212
+
213
+ .algorithm-section {
214
+ flex: 1;
215
+ padding: 30px;
216
+ overflow-y: auto;
217
+ }
218
+
219
+ .algorithm-section h2 {
220
+ color: #e94560;
221
+ margin-bottom: 10px;
222
+ font-size: 1.6rem;
223
+ }
224
+
225
+ .algorithm-section .description {
226
+ color: #888;
227
+ margin-bottom: 25px;
228
+ line-height: 1.6;
229
+ }
230
+
231
+ .alg-grid {
232
+ display: grid;
233
+ grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
234
+ gap: 16px;
235
+ }
236
+
237
+ .alg-card {
238
+ background: #16213e;
239
+ border: 1px solid #0f3460;
240
+ border-radius: 12px;
241
+ padding: 18px;
242
+ transition: all 0.3s ease;
243
+ }
244
+
245
+ .alg-card:hover {
246
+ border-color: #e94560;
247
+ transform: translateY(-2px);
248
+ box-shadow: 0 4px 15px rgba(233, 69, 96, 0.2);
249
+ }
250
+
251
+ .alg-card h4 {
252
+ color: #fff;
253
+ margin-bottom: 8px;
254
+ font-size: 1rem;
255
+ }
256
+
257
+ .alg-card .case-name {
258
+ color: #e94560;
259
+ font-weight: bold;
260
+ font-size: 0.9rem;
261
+ margin-bottom: 6px;
262
+ }
263
+
264
+ .alg-card-header {
265
+ display: flex;
266
+ align-items: center;
267
+ gap: 14px;
268
+ margin-bottom: 10px;
269
+ }
270
+
271
+ .alg-diagram {
272
+ flex-shrink: 0;
273
+ }
274
+
275
+ .alg-diagram svg {
276
+ display: block;
277
+ border-radius: 4px;
278
+ }
279
+
280
+ .alg-diagram .f2l-case-img {
281
+ display: block;
282
+ width: 120px;
283
+ height: 120px;
284
+ border-radius: 4px;
285
+ }
286
+
287
+ .alg-card-info {
288
+ flex: 1;
289
+ min-width: 0;
290
+ }
291
+
292
+ .alg-card-info .case-name {
293
+ color: #e94560;
294
+ font-weight: bold;
295
+ font-size: 0.9rem;
296
+ margin-bottom: 4px;
297
+ }
298
+
299
+ .alg-card-info h4 {
300
+ color: #fff;
301
+ font-size: 0.95rem;
302
+ margin: 0;
303
+ }
304
+
305
+ .alg-notation {
306
+ font-family: 'Courier New', monospace;
307
+ background: #0a0a1a;
308
+ padding: 10px 14px;
309
+ border-radius: 6px;
310
+ font-size: 0.85rem;
311
+ color: #4fc3f7;
312
+ margin: 6px 0;
313
+ word-break: break-all;
314
+ cursor: pointer;
315
+ transition: background 0.2s;
316
+ }
317
+
318
+ .alg-notation:hover {
319
+ background: #1a1a3a;
320
+ }
321
+
322
+ .alg-notation:hover::after {
323
+ content: ' [click to setup & solve]';
324
+ color: #e94560;
325
+ font-size: 0.7rem;
326
+ }
327
+
328
+ .alg-meta {
329
+ display: flex;
330
+ justify-content: space-between;
331
+ align-items: center;
332
+ margin-top: 8px;
333
+ font-size: 0.8rem;
334
+ color: #666;
335
+ }
336
+
337
+ .alg-meta .moves {
338
+ background: #0f3460;
339
+ padding: 2px 8px;
340
+ border-radius: 4px;
341
+ color: #4fc3f7;
342
+ }
343
+
344
+ .info-badge {
345
+ display: inline-block;
346
+ background: #e94560;
347
+ color: #fff;
348
+ padding: 2px 8px;
349
+ border-radius: 10px;
350
+ font-size: 0.75rem;
351
+ margin-left: 8px;
352
+ }
353
+
354
+ .scramble-display {
355
+ position: absolute;
356
+ top: 15px;
357
+ left: 50%;
358
+ transform: translateX(-50%);
359
+ background: rgba(15, 52, 96, 0.9);
360
+ padding: 8px 16px;
361
+ border-radius: 6px;
362
+ font-family: 'Courier New', monospace;
363
+ font-size: 0.9rem;
364
+ color: #4fc3f7;
365
+ z-index: 10;
366
+ }
367
+
368
+ .sub-section {
369
+ margin-bottom: 30px;
370
+ }
371
+
372
+ .sub-section h3 {
373
+ color: #fff;
374
+ margin-bottom: 12px;
375
+ padding-bottom: 6px;
376
+ border-bottom: 1px solid #333;
377
+ }
378
+
379
+ @media (max-width: 900px) {
380
+ .container {
381
+ flex-direction: column;
382
+ }
383
+ .sidebar {
384
+ width: 100%;
385
+ max-height: 200px;
386
+ position: relative;
387
+ display: flex;
388
+ flex-wrap: wrap;
389
+ gap: 8px;
390
+ align-items: center;
391
+ }
392
+ .sidebar h3:last-of-type, .sidebar p, .sidebar hr {
393
+ display: none;
394
+ }
395
+ .alg-grid {
396
+ grid-template-columns: 1fr;
397
+ }
398
+ .cube-section {
399
+ min-height: 350px;
400
+ }
401
+ }
402
+
403
+ /* Group filter tabs for ZBLL/COLL */
404
+ .group-tabs {
405
+ display: flex;
406
+ gap: 6px;
407
+ flex-wrap: wrap;
408
+ margin-bottom: 20px;
409
+ padding-bottom: 15px;
410
+ border-bottom: 1px solid #333;
411
+ }
412
+
413
+ .group-tab {
414
+ padding: 8px 16px;
415
+ border: 1px solid #0f3460;
416
+ border-radius: 6px;
417
+ background: #0f0f23;
418
+ color: #888;
419
+ cursor: pointer;
420
+ font-size: 0.85rem;
421
+ transition: all 0.2s;
422
+ }
423
+
424
+ .group-tab:hover {
425
+ background: #0f3460;
426
+ color: #fff;
427
+ }
428
+
429
+ .group-tab.active {
430
+ background: #e94560;
431
+ color: #fff;
432
+ border-color: #e94560;
433
+ font-weight: bold;
434
+ }
435
+
436
+ /* Step-by-step button in algorithm cards */
437
+ .step-by-step-btn {
438
+ padding: 3px 10px;
439
+ border: 1px solid #4fc3f7;
440
+ border-radius: 4px;
441
+ background: transparent;
442
+ color: #4fc3f7;
443
+ cursor: pointer;
444
+ font-size: 0.75rem;
445
+ transition: all 0.2s;
446
+ white-space: nowrap;
447
+ }
448
+
449
+ .step-by-step-btn:hover {
450
+ background: #4fc3f7;
451
+ color: #0a0a1a;
452
+ }
453
+
454
+ /* Step panel overlay on cube */
455
+ #step-panel {
456
+ display: none;
457
+ position: absolute;
458
+ top: 15px;
459
+ right: 15px;
460
+ left: auto;
461
+ bottom: auto;
462
+ transform: none;
463
+ background: rgba(10, 10, 30, 0.95);
464
+ border: 1px solid #4fc3f7;
465
+ border-radius: 12px;
466
+ padding: 0;
467
+ z-index: 20;
468
+ flex-direction: column;
469
+ align-items: center;
470
+ gap: 12px;
471
+ min-width: 280px;
472
+ max-width: 40%;
473
+ backdrop-filter: blur(8px);
474
+ cursor: default;
475
+ user-select: none;
476
+ }
477
+
478
+ #step-panel .drag-handle {
479
+ width: 100%;
480
+ padding: 8px 16px;
481
+ cursor: grab;
482
+ background: rgba(79, 195, 247, 0.15);
483
+ border-radius: 12px 12px 0 0;
484
+ text-align: center;
485
+ font-size: 0.75rem;
486
+ color: #4fc3f7;
487
+ border-bottom: 1px solid rgba(79, 195, 247, 0.2);
488
+ }
489
+
490
+ #step-panel .drag-handle:active {
491
+ cursor: grabbing;
492
+ }
493
+
494
+ #step-panel .step-content {
495
+ padding: 14px 20px;
496
+ display: flex;
497
+ flex-direction: column;
498
+ align-items: center;
499
+ gap: 12px;
500
+ }
501
+
502
+ .step-info {
503
+ text-align: center;
504
+ width: 100%;
505
+ }
506
+
507
+ .step-counter {
508
+ font-size: 0.85rem;
509
+ color: #888;
510
+ margin-bottom: 8px;
511
+ }
512
+
513
+ .step-moves-display {
514
+ font-family: 'Courier New', monospace;
515
+ font-size: 1rem;
516
+ line-height: 1.8;
517
+ word-wrap: break-word;
518
+ }
519
+
520
+ .step-done {
521
+ color: #555;
522
+ text-decoration: line-through;
523
+ }
524
+
525
+ .step-current {
526
+ color: #fff;
527
+ background: #e94560;
528
+ padding: 2px 6px;
529
+ border-radius: 4px;
530
+ font-weight: bold;
531
+ font-size: 1.1rem;
532
+ }
533
+
534
+ .step-pending {
535
+ color: #4fc3f7;
536
+ }
537
+
538
+ .step-buttons {
539
+ display: flex;
540
+ gap: 10px;
541
+ }
542
+
543
+ .step-btn {
544
+ padding: 8px 18px;
545
+ border: none;
546
+ border-radius: 6px;
547
+ cursor: pointer;
548
+ font-size: 0.9rem;
549
+ font-weight: bold;
550
+ transition: all 0.2s;
551
+ }
552
+
553
+ .step-btn-next {
554
+ background: #e94560;
555
+ color: #fff;
556
+ }
557
+
558
+ .step-btn-next:hover {
559
+ background: #ff6b85;
560
+ transform: scale(1.05);
561
+ }
562
+
563
+ .step-btn-prev {
564
+ background: #0f3460;
565
+ color: #4fc3f7;
566
+ border: 1px solid #4fc3f7;
567
+ }
568
+
569
+ .step-btn-prev:hover {
570
+ background: #4fc3f7;
571
+ color: #0a0a1a;
572
+ }
573
+
574
+ .step-btn-done {
575
+ background: #2ecc71;
576
+ color: #fff;
577
+ }
578
+
579
+ .step-btn-cancel {
580
+ background: #333;
581
+ color: #aaa;
582
+ border: 1px solid #555;
583
+ }
584
+
585
+ .step-btn-cancel:hover {
586
+ background: #555;
587
+ color: #fff;
588
+ }
589
+ </style>
590
+ </head>
591
+ <body>
592
+ <header>
593
+ <h1>&#x1F9CA; One-Handed 3x3 Rubik's Cube</h1>
594
+ <p>Interactive 3D Visualization &amp; OH Algorithm Reference</p>
595
+ </header>
596
+
597
+ <div class="container">
598
+ <nav class="sidebar" role="navigation" aria-label="Algorithm categories">
599
+ <h3>&#x1F4DA; Algorithm Sets</h3>
600
+ <div class="nav-item active" data-section="f2l" tabindex="0" role="button" aria-pressed="true">F2L <span class="info-badge">42 cases</span></div>
601
+ <div class="nav-item" data-section="oll" tabindex="0" role="button" aria-pressed="false">OLL <span class="info-badge">57 cases</span></div>
602
+ <div class="nav-item" data-section="pll" tabindex="0" role="button" aria-pressed="false">PLL <span class="info-badge">21 cases</span></div>
603
+ <div class="nav-item" data-section="coll" tabindex="0" role="button" aria-pressed="false">COLL <span class="info-badge">40 cases</span></div>
604
+ <div class="nav-item" data-section="zbll" tabindex="0" role="button" aria-pressed="false">ZBLL <span class="info-badge">subset</span></div>
605
+ <hr style="border-color: #333; margin: 20px 0;">
606
+ <h3>&#x2139;&#xFE0F; Tips</h3>
607
+ <p style="font-size:0.85rem; color:#888; line-height:1.5;">
608
+ &#x2022; Click algorithm: auto setup &amp; solve<br>
609
+ &#x2022; "Step by Step": one move at a time<br>
610
+ &#x2022; Space/Arrow to advance steps<br>
611
+ &#x2022; Drag to rotate camera<br>
612
+ &#x2022; Scroll to zoom<br>
613
+ &#x2022; Keyboard: R/U/F/L/D/B (Shift=inverse)
614
+ </p>
615
+ <div style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #333; font-size: 0.8rem; color: #666; text-align: center;">
616
+ Created by Andy Kong
617
+ </div>
618
+ <div style="margin-top: 10px; text-align: center;">
619
+ <button onclick="document.getElementById('about-modal').style.display='flex'" style="padding: 6px 16px; border: 1px solid #4fc3f7; border-radius: 6px; background: transparent; color: #4fc3f7; cursor: pointer; font-size: 0.8rem;">About</button>
620
+ </div>
621
+ </nav>
622
+
623
+ <div class="main-content">
624
+ <div class="cube-section" id="cube-section">
625
+ <button class="fullscreen-btn" id="fullscreen-btn" onclick="toggleFullscreen()" title="Full screen 3D view">&#x26F6; Full</button>
626
+ <button class="popout-btn" id="popout-btn" onclick="toggleCollapse()" title="Collapse 3D view for more algorithm space">&#x25B2; Hide</button>
627
+ <div class="scramble-display" id="scramble-display">Drag to rotate &#x2022; Scroll to zoom</div>
628
+ <canvas id="cube-canvas"></canvas>
629
+ <div class="controls">
630
+ <button class="control-btn" style="background:#333;border:1px solid #4fc3f7;color:#4fc3f7" onclick="toggleMoveButtons()" id="toggle-moves-btn">Moves ▼</button>
631
+ <button class="control-btn" style="background:#e94560" onclick="scrambleCube()" aria-label="Scramble cube">Scramble</button>
632
+ <button class="control-btn" style="background:#2ecc71" onclick="resetCube()" aria-label="Reset cube">Reset</button>
633
+ </div>
634
+ <div class="controls move-buttons" id="move-buttons" style="bottom: 60px; display: none;">
635
+ <button class="control-btn" onclick="doMove('R')">R</button>
636
+ <button class="control-btn" onclick="doMove('Ri')">R'</button>
637
+ <button class="control-btn" onclick="doMove('U')">U</button>
638
+ <button class="control-btn" onclick="doMove('Ui')">U'</button>
639
+ <button class="control-btn" onclick="doMove('F')">F</button>
640
+ <button class="control-btn" onclick="doMove('Fi')">F'</button>
641
+ <button class="control-btn" onclick="doMove('L')">L</button>
642
+ <button class="control-btn" onclick="doMove('Li')">L'</button>
643
+ <button class="control-btn" onclick="doMove('D')">D</button>
644
+ <button class="control-btn" onclick="doMove('Di')">D'</button>
645
+ <button class="control-btn" onclick="doMove('B')">B</button>
646
+ <button class="control-btn" onclick="doMove('Bi')">B'</button>
647
+ <button class="control-btn" onclick="doMove('r')">r</button>
648
+ <button class="control-btn" onclick="doMove('ri')">r'</button>
649
+ <button class="control-btn" onclick="doMove('u')">u</button>
650
+ <button class="control-btn" onclick="doMove('ui')">u'</button>
651
+ <button class="control-btn" onclick="doMove('l')">l</button>
652
+ <button class="control-btn" onclick="doMove('li')">l'</button>
653
+ <button class="control-btn" onclick="doMove('x')">x</button>
654
+ <button class="control-btn" onclick="doMove('xi')">x'</button>
655
+ <button class="control-btn" onclick="doMove('y')">y</button>
656
+ <button class="control-btn" onclick="doMove('yi')">y'</button>
657
+ <button class="control-btn" onclick="doMove('z')">z</button>
658
+ <button class="control-btn" onclick="doMove('zi')">z'</button>
659
+ </div>
660
+ </div>
661
+
662
+ <div class="algorithm-section" id="algorithm-section">
663
+ <!-- Algorithms rendered by JS -->
664
+ </div>
665
+ </div>
666
+ </div>
667
+
668
+ <div id="about-modal" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:1000; justify-content:center; align-items:center;">
669
+ <div style="background:#16213e; border:1px solid #4fc3f7; border-radius:12px; padding:30px; max-width:600px; width:90%; max-height:80vh; overflow-y:auto;">
670
+ <h2 style="color:#e94560; margin-bottom:15px;">About This App</h2>
671
+ <p style="color:#ccc; line-height:1.7; margin-bottom:15px;">
672
+ This is an interactive 3D visualization and algorithm reference for <strong>one-handed (OH) 3x3 Rubik's Cube solving</strong>. It includes algorithms for F2L, OLL, PLL, COLL, and ZBLL — all optimized for one-handed execution using primarily R, U, and F moves.
673
+ </p>
674
+ <h3 style="color:#4fc3f7; margin-bottom:10px;">Algorithm Sources</h3>
675
+ <ul style="color:#aaa; line-height:2; list-style:none; padding:0;">
676
+ <li>&#x2022; <a href="https://www.speedsolving.com/wiki/index.php/First_Two_Layers" target="_blank" style="color:#4fc3f7;">Speedsolving.com Wiki - F2L Algorithms</a></li>
677
+ <li>&#x2022; <a href="https://cubeskills.com/uploads/pdf/tutorials/onehanded-oll-algorithms.pdf" target="_blank" style="color:#4fc3f7;">CubeSkills - OH OLL Algorithms (PDF)</a></li>
678
+ <li>&#x2022; <a href="https://www.cubeskills.com/uploads/pdf/tutorials/onehanded-pll-algorithms.pdf" target="_blank" style="color:#4fc3f7;">CubeSkills - OH PLL Algorithms (PDF)</a></li>
679
+ <li>&#x2022; <a href="https://www.cubeskills.com/uploads/pdf/tutorials/onehanded-coll-algorithms.pdf" target="_blank" style="color:#4fc3f7;">CubeSkills - OH COLL Algorithms (PDF)</a></li>
680
+ <li>&#x2022; <a href="https://cubingapp.com/algorithms/ZBLL" target="_blank" style="color:#4fc3f7;">CubingApp - ZBLL Algorithms</a></li>
681
+ </ul>
682
+ <p style="color:#888; margin-top:15px; font-size:0.85rem;">
683
+ One-handed solving uses the same CFOP method as two-handed, but algorithms are chosen for ergonomics with a single hand. Common techniques include table-assisted solving and z-rotation for better grip.
684
+ </p>
685
+ <div style="text-align:center; margin-top:20px;">
686
+ <button onclick="document.getElementById('about-modal').style.display='none'" style="padding:10px 30px; background:#e94560; color:#fff; border:none; border-radius:6px; cursor:pointer; font-size:0.9rem;">Close</button>
687
+ </div>
688
+ </div>
689
+ </div>
690
+
691
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
692
+ <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
693
+ <script src="app.js"></script>
694
+ <script src="json/f2l.json.js"></script>
695
+ <script src="json/oll.json.js"></script>
696
+ <script src="json/pll.json.js"></script>
697
+ <script src="json/coll.json.js"></script>
698
+ <script src="json/zbll.json.js"></script>
699
+ <script>
700
+ // OH_ALGORITHMS is assembled from the .json.js files above
701
+ document.addEventListener('DOMContentLoaded', () => {
702
+ init();
703
+ setupNavigation();
704
+ renderAlgorithms('f2l');
705
+ });
706
+ </script>
707
+ </body>
708
+ </html>
json/coll.json.js ADDED
@@ -0,0 +1,424 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ OH_ALGORITHMS.coll = {
2
+ "title": "OH COLL - Corners of Last Layer",
3
+ "description": "COLL algorithms from CubeSkills OH COLL PDF by Feliks Zemdegs. Solves corner orientation and permutation while edges are oriented.",
4
+ "cases": [
5
+ {
6
+ "name": "S 1",
7
+ "case": "Sune",
8
+ "algs": [
9
+ {
10
+ "notation": "R U R' U R U2 R'",
11
+ "moves": 7,
12
+ "note": "Sune"
13
+ }
14
+ ]
15
+ },
16
+ {
17
+ "name": "S 2",
18
+ "case": "Sune",
19
+ "algs": [
20
+ {
21
+ "notation": "R' U2 L U' R U L' U R' U R",
22
+ "moves": 11,
23
+ "note": "COLL"
24
+ }
25
+ ]
26
+ },
27
+ {
28
+ "name": "S 3",
29
+ "case": "Sune",
30
+ "algs": [
31
+ {
32
+ "notation": "R U' L' U R' U' L",
33
+ "moves": 7,
34
+ "note": "COLL"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "name": "S 4",
40
+ "case": "Sune",
41
+ "algs": [
42
+ {
43
+ "notation": "L' R U R' U' L U2 R U2 R'",
44
+ "moves": 10,
45
+ "note": "COLL"
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "name": "S 5",
51
+ "case": "Sune",
52
+ "algs": [
53
+ {
54
+ "notation": "L' U2 L U2 R U' L' U L R'",
55
+ "moves": 10,
56
+ "note": "COLL"
57
+ }
58
+ ]
59
+ },
60
+ {
61
+ "name": "S 6",
62
+ "case": "Sune",
63
+ "algs": [
64
+ {
65
+ "notation": "y' R U R' U R U' R D R' U' R D' R2",
66
+ "moves": 13,
67
+ "note": "COLL"
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ "name": "AS 1",
73
+ "case": "Antisune",
74
+ "algs": [
75
+ {
76
+ "notation": "y R U2 R' U' R U' R'",
77
+ "moves": 7,
78
+ "note": "Antisune"
79
+ }
80
+ ]
81
+ },
82
+ {
83
+ "name": "AS 2",
84
+ "case": "Antisune",
85
+ "algs": [
86
+ {
87
+ "notation": "y2 R' U' R U' L U' R' U L' U2 R",
88
+ "moves": 11,
89
+ "note": "COLL"
90
+ }
91
+ ]
92
+ },
93
+ {
94
+ "name": "AS 3",
95
+ "case": "Antisune",
96
+ "algs": [
97
+ {
98
+ "notation": "y2 L' U R U' L U R'",
99
+ "moves": 7,
100
+ "note": "COLL"
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ "name": "AS 4",
106
+ "case": "Antisune",
107
+ "algs": [
108
+ {
109
+ "notation": "y2 R L' U' L U R' U2 L' U2 L",
110
+ "moves": 10,
111
+ "note": "COLL"
112
+ }
113
+ ]
114
+ },
115
+ {
116
+ "name": "AS 5",
117
+ "case": "Antisune",
118
+ "algs": [
119
+ {
120
+ "notation": "y2 R U2 R' U2 L' U R U' R' L",
121
+ "moves": 10,
122
+ "note": "COLL"
123
+ }
124
+ ]
125
+ },
126
+ {
127
+ "name": "AS 6",
128
+ "case": "Antisune",
129
+ "algs": [
130
+ {
131
+ "notation": "y R' U' R U' R' U R' D' R U R' D R2",
132
+ "moves": 13,
133
+ "note": "COLL"
134
+ }
135
+ ]
136
+ },
137
+ {
138
+ "name": "L 1",
139
+ "case": "L",
140
+ "algs": [
141
+ {
142
+ "notation": "y R U2 R' U' R U R' U' R U R' U' R U' R'",
143
+ "moves": 15,
144
+ "note": "COLL"
145
+ }
146
+ ]
147
+ },
148
+ {
149
+ "name": "L 2",
150
+ "case": "L",
151
+ "algs": [
152
+ {
153
+ "notation": "y2 R U2 L' U L U2 R' L' U L",
154
+ "moves": 10,
155
+ "note": "COLL"
156
+ }
157
+ ]
158
+ },
159
+ {
160
+ "name": "L 3",
161
+ "case": "L",
162
+ "algs": [
163
+ {
164
+ "notation": "y' R U2 R D R' U2 R D' R2",
165
+ "moves": 9,
166
+ "note": "COLL"
167
+ }
168
+ ]
169
+ },
170
+ {
171
+ "name": "L 4",
172
+ "case": "L",
173
+ "algs": [
174
+ {
175
+ "notation": "y2 R' U2 R' D' R U2 R' D R2",
176
+ "moves": 9,
177
+ "note": "COLL"
178
+ }
179
+ ]
180
+ },
181
+ {
182
+ "name": "L 5",
183
+ "case": "L",
184
+ "algs": [
185
+ {
186
+ "notation": "y' L' R U R' U' L U R U' R'",
187
+ "moves": 10,
188
+ "note": "COLL"
189
+ }
190
+ ]
191
+ },
192
+ {
193
+ "name": "L 6",
194
+ "case": "L",
195
+ "algs": [
196
+ {
197
+ "notation": "y2 x' U' R U L' U' R' U r",
198
+ "moves": 8,
199
+ "note": "COLL"
200
+ }
201
+ ]
202
+ },
203
+ {
204
+ "name": "T 1",
205
+ "case": "T",
206
+ "algs": [
207
+ {
208
+ "notation": "R U2 R' U' R U' R2 U2 R U R' U R",
209
+ "moves": 13,
210
+ "note": "COLL"
211
+ }
212
+ ]
213
+ },
214
+ {
215
+ "name": "T 2",
216
+ "case": "T",
217
+ "algs": [
218
+ {
219
+ "notation": "y2 F R U R' U' R U' R' U' R U R' F'",
220
+ "moves": 13,
221
+ "note": "COLL"
222
+ }
223
+ ]
224
+ },
225
+ {
226
+ "name": "T 3",
227
+ "case": "T",
228
+ "algs": [
229
+ {
230
+ "notation": "R' U R U2 L' R' U R U' L",
231
+ "moves": 10,
232
+ "note": "COLL"
233
+ }
234
+ ]
235
+ },
236
+ {
237
+ "name": "T 4",
238
+ "case": "T",
239
+ "algs": [
240
+ {
241
+ "notation": "y2 R U' R2 D' r U2 r' D R2 U R'",
242
+ "moves": 11,
243
+ "note": "COLL"
244
+ }
245
+ ]
246
+ },
247
+ {
248
+ "name": "T 5",
249
+ "case": "T",
250
+ "algs": [
251
+ {
252
+ "notation": "y' r' U' R U L U' R' U x",
253
+ "moves": 9,
254
+ "note": "COLL"
255
+ }
256
+ ]
257
+ },
258
+ {
259
+ "name": "T 6",
260
+ "case": "T",
261
+ "algs": [
262
+ {
263
+ "notation": "y' r U R' U' L' U R U' x'",
264
+ "moves": 9,
265
+ "note": "COLL"
266
+ }
267
+ ]
268
+ },
269
+ {
270
+ "name": "U 1",
271
+ "case": "U",
272
+ "algs": [
273
+ {
274
+ "notation": "y2 R U R' U R U2 R2 U' R U' R' U2 R",
275
+ "moves": 13,
276
+ "note": "COLL"
277
+ }
278
+ ]
279
+ },
280
+ {
281
+ "name": "U 2",
282
+ "case": "U",
283
+ "algs": [
284
+ {
285
+ "notation": "F R U' R' U R U R' U R U' R' F'",
286
+ "moves": 13,
287
+ "note": "COLL"
288
+ }
289
+ ]
290
+ },
291
+ {
292
+ "name": "U 3",
293
+ "case": "U",
294
+ "algs": [
295
+ {
296
+ "notation": "y2 R2 D R' U2 R D' R' U2 R'",
297
+ "moves": 9,
298
+ "note": "COLL"
299
+ }
300
+ ]
301
+ },
302
+ {
303
+ "name": "U 4",
304
+ "case": "U",
305
+ "algs": [
306
+ {
307
+ "notation": "R2 D' R U2 R' D R U2 R",
308
+ "moves": 9,
309
+ "note": "COLL"
310
+ }
311
+ ]
312
+ },
313
+ {
314
+ "name": "U 5",
315
+ "case": "U",
316
+ "algs": [
317
+ {
318
+ "notation": "y2 R' U R U' x' U L' U L U2 R U' R' U x",
319
+ "moves": 13,
320
+ "note": "COLL"
321
+ }
322
+ ]
323
+ },
324
+ {
325
+ "name": "U 6",
326
+ "case": "U",
327
+ "algs": [
328
+ {
329
+ "notation": "R' U2 R F U' R' U' R U F'",
330
+ "moves": 10,
331
+ "note": "COLL"
332
+ }
333
+ ]
334
+ },
335
+ {
336
+ "name": "Pi 1",
337
+ "case": "Pi",
338
+ "algs": [
339
+ {
340
+ "notation": "R U2 R2 U' R2 U' R2 U2 R",
341
+ "moves": 9,
342
+ "note": "COLL"
343
+ }
344
+ ]
345
+ },
346
+ {
347
+ "name": "Pi 2",
348
+ "case": "Pi",
349
+ "algs": [
350
+ {
351
+ "notation": "R U' R' U2 L' U R U' L R' U2 R U R'",
352
+ "moves": 14,
353
+ "note": "COLL"
354
+ }
355
+ ]
356
+ },
357
+ {
358
+ "name": "Pi 3",
359
+ "case": "Pi",
360
+ "algs": [
361
+ {
362
+ "notation": "y L' U R U' L U R' R' U' R U' R' U2 R",
363
+ "moves": 14,
364
+ "note": "COLL"
365
+ }
366
+ ]
367
+ },
368
+ {
369
+ "name": "Pi 4",
370
+ "case": "Pi",
371
+ "algs": [
372
+ {
373
+ "notation": "y' R' U2 R U R' U R R U' L' U R' U' L",
374
+ "moves": 14,
375
+ "note": "COLL"
376
+ }
377
+ ]
378
+ },
379
+ {
380
+ "name": "H 1",
381
+ "case": "H",
382
+ "algs": [
383
+ {
384
+ "notation": "R U R' U R U' R' U R U2 R'",
385
+ "moves": 11,
386
+ "note": "COLL"
387
+ }
388
+ ]
389
+ },
390
+ {
391
+ "name": "H 2",
392
+ "case": "H",
393
+ "algs": [
394
+ {
395
+ "notation": "y F R U R' U' R U R' U' R U R' U' F'",
396
+ "moves": 14,
397
+ "note": "COLL"
398
+ }
399
+ ]
400
+ },
401
+ {
402
+ "name": "H 3",
403
+ "case": "H",
404
+ "algs": [
405
+ {
406
+ "notation": "F R U' R' U R U2 R' U' R U R' U' F'",
407
+ "moves": 14,
408
+ "note": "COLL"
409
+ }
410
+ ]
411
+ },
412
+ {
413
+ "name": "H 4",
414
+ "case": "H",
415
+ "algs": [
416
+ {
417
+ "notation": "R U R' U R U L' U R' U' L",
418
+ "moves": 11,
419
+ "note": "COLL"
420
+ }
421
+ ]
422
+ }
423
+ ]
424
+ };
json/f2l.json.js ADDED
@@ -0,0 +1,409 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const OH_ALGORITHMS = {};
2
+ OH_ALGORITHMS.f2l = {
3
+ "title": "F2L - First Two Layers",
4
+ "description": "F2L algorithms from Speedsolving.com Wiki. 42 cases (including solved case). Multiple algorithms per case - select your preferred one.",
5
+ "cases": [
6
+ {
7
+ "name": "F2L 1",
8
+ "case": "Basic Insert",
9
+ "algs": [
10
+ { "notation": "U (R U' R')", "moves": 4, "note": "Main" },
11
+ { "notation": "R' F R F'", "moves": 4, "note": "Alt" },
12
+ { "notation": "U2 (R U2 R')", "moves": 4, "note": "Alt 2" }
13
+ ]
14
+ },
15
+ {
16
+ "name": "F2L 2",
17
+ "case": "Basic Insert",
18
+ "algs": [
19
+ { "notation": "y' U' (R' U R)", "moves": 4, "note": "Main" },
20
+ { "notation": "U' (F' U F)", "moves": 4, "note": "Alt" },
21
+ { "notation": "F R' F' R", "moves": 4, "note": "Alt 2" },
22
+ { "notation": "y U' (L' U L)", "moves": 4, "note": "Alt 3" },
23
+ { "notation": "d' L' U L", "moves": 4, "note": "Alt 4" }
24
+ ]
25
+ },
26
+ {
27
+ "name": "F2L 3",
28
+ "case": "Basic Insert",
29
+ "algs": [
30
+ { "notation": "F' U' F", "moves": 3, "note": "Main" },
31
+ { "notation": "y' (R' U' R)", "moves": 3, "note": "Alt" },
32
+ { "notation": "y (L' U' L)", "moves": 3, "note": "Alt 2" }
33
+ ]
34
+ },
35
+ {
36
+ "name": "F2L 4",
37
+ "case": "Basic Insert",
38
+ "algs": [
39
+ { "notation": "(R U R')", "moves": 3, "note": "Main" },
40
+ { "notation": "y F U F'", "moves": 3, "note": "Alt" },
41
+ { "notation": "y' f R f'", "moves": 3, "note": "Alt 2" }
42
+ ]
43
+ },
44
+ {
45
+ "name": "F2L 5",
46
+ "case": "Reposition Edge",
47
+ "algs": [
48
+ { "notation": "(U' R U R') U2 (R U' R')", "moves": 8, "note": "Main" },
49
+ { "notation": "(U' R U R') U (R' F R F')", "moves": 9, "note": "Alt" },
50
+ { "notation": "U' (R U R') U' R U2 R'", "moves": 8, "note": "Alt 2" }
51
+ ]
52
+ },
53
+ {
54
+ "name": "F2L 6",
55
+ "case": "Reposition Edge",
56
+ "algs": [
57
+ { "notation": "d (R' U' R) U2' (R' U R)", "moves": 8, "note": "Main" },
58
+ { "notation": "y' (U R' U' R) U2 (R' U R)", "moves": 8, "note": "Alt" },
59
+ { "notation": "U' (r U' R' U) (R U r')", "moves": 7, "note": "Alt 2" },
60
+ { "notation": "y F2 (R U R' U') F2", "moves": 6, "note": "Alt 3" }
61
+ ]
62
+ },
63
+ {
64
+ "name": "F2L 7",
65
+ "case": "Reposition Edge",
66
+ "algs": [
67
+ { "notation": "U' (R U2' R') U2 (R U' R')", "moves": 8, "note": "Main" },
68
+ { "notation": "(U' R U2 R') (U' R U2 R')", "moves": 8, "note": "Alt" }
69
+ ]
70
+ },
71
+ {
72
+ "name": "F2L 8",
73
+ "case": "Reposition Edge",
74
+ "algs": [
75
+ { "notation": "d (R' U2 R) U2' (R' U R)", "moves": 8, "note": "Main" },
76
+ { "notation": "y' U (R' U2 R) U2 (R' U R)", "moves": 8, "note": "Alt" },
77
+ { "notation": "U (R' F R F') (R' F R F') (R U' R')", "moves": 11, "note": "Alt 2" },
78
+ { "notation": "(r' U2) (R2 U R2 U r)", "moves": 7, "note": "Alt 3" }
79
+ ]
80
+ },
81
+ {
82
+ "name": "F2L 9",
83
+ "case": "Reposition Edge and Flip Corner",
84
+ "algs": [
85
+ { "notation": "U' R U' R' U (F' U' F)", "moves": 8, "note": "Main" },
86
+ { "notation": "U' R U' R' d R' U' R", "moves": 8, "note": "Alt" },
87
+ { "notation": "d (R' U' R U') (R' U' R)", "moves": 8, "note": "Alt 2" },
88
+ { "notation": "F (R U R' U') F' R U' R'", "moves": 9, "note": "Alt 3" },
89
+ { "notation": "y F2 (U R U' R') F2", "moves": 6, "note": "Alt 4" }
90
+ ]
91
+ },
92
+ {
93
+ "name": "F2L 10",
94
+ "case": "Reposition Edge and Flip Corner",
95
+ "algs": [
96
+ { "notation": "U' (R U R' U)(R U R')", "moves": 8, "note": "Main" },
97
+ { "notation": "R d' R U R' U2 F'", "moves": 7, "note": "Alt" },
98
+ { "notation": "U F' U F U' R U R'", "moves": 8, "note": "Alt 2" },
99
+ { "notation": "d R' U R d' R U R'", "moves": 8, "note": "Alt 3" }
100
+ ]
101
+ },
102
+ {
103
+ "name": "F2L 11",
104
+ "case": "Reposition Edge and Flip Corner",
105
+ "algs": [
106
+ { "notation": "U' (R U2' R') U (F' U' F)", "moves": 8, "note": "Main" },
107
+ { "notation": "U' (R U2' R') d (R' U' R)", "moves": 8, "note": "Alt" },
108
+ { "notation": "y' R U2 R2 U' R2 U' R'", "moves": 7, "note": "Alt 2" }
109
+ ]
110
+ },
111
+ {
112
+ "name": "F2L 12",
113
+ "case": "Reposition Edge and Flip Corner",
114
+ "algs": [
115
+ { "notation": "R U' R' U R U' R' U2 R U' R'", "moves": 11, "note": "Main" },
116
+ { "notation": "R' U2 R2 U R2 U R", "moves": 7, "note": "Alt" },
117
+ { "notation": "(U R U' R') U' (R U R' U') (R U R')", "moves": 12, "note": "Alt 2" },
118
+ { "notation": "R U R' U (R' F R F') (R U R')", "moves": 11, "note": "Alt 3" }
119
+ ]
120
+ },
121
+ {
122
+ "name": "F2L 13",
123
+ "case": "Reposition Edge and Flip Corner",
124
+ "algs": [
125
+ { "notation": "d (R' U R U') (R' U' R)", "moves": 8, "note": "Main" },
126
+ { "notation": "y' U (R' U R U') (R' U' R)", "moves": 8, "note": "Alt" },
127
+ { "notation": "(R U' R') U (R' F R F') (R U' R')", "moves": 11, "note": "Alt 2" }
128
+ ]
129
+ },
130
+ {
131
+ "name": "F2L 14",
132
+ "case": "Reposition Edge and Flip Corner",
133
+ "algs": [
134
+ { "notation": "U' (R U' R' U) (R U R')", "moves": 8, "note": "Main" }
135
+ ]
136
+ },
137
+ {
138
+ "name": "F2L 15",
139
+ "case": "Split Pair by Going Over",
140
+ "algs": [
141
+ { "notation": "(F' U F) U2 (R U R')", "moves": 7, "note": "Main" },
142
+ { "notation": "U (R' F R F') U (R U R')", "moves": 9, "note": "Alt" },
143
+ { "notation": "R U2 R' U R U R' U R U' R'", "moves": 11, "note": "Alt 2" },
144
+ { "notation": "R U R' U2 R U' R' U R U' R'", "moves": 11, "note": "Alt 3" },
145
+ { "notation": "R U' R U2' R2' U' R2 U' R2'", "moves": 9, "note": "Alt 4" }
146
+ ]
147
+ },
148
+ {
149
+ "name": "F2L 16",
150
+ "case": "Split Pair by Going Over",
151
+ "algs": [
152
+ { "notation": "(R U' R') U2 (F' U' F)", "moves": 7, "note": "Main" },
153
+ { "notation": "(R U' R' U) d (R' U' R)", "moves": 8, "note": "Alt" },
154
+ { "notation": "U R (F R U R' U' F') R'", "moves": 9, "note": "Alt 2" }
155
+ ]
156
+ },
157
+ {
158
+ "name": "F2L 17",
159
+ "case": "Split Pair by Going Over",
160
+ "algs": [
161
+ { "notation": "(R U2 R') U' (R U R')", "moves": 7, "note": "Main" }
162
+ ]
163
+ },
164
+ {
165
+ "name": "F2L 18",
166
+ "case": "Split Pair by Going Over",
167
+ "algs": [
168
+ { "notation": "y' (R' U2 R) U (R' U' R)", "moves": 7, "note": "Main" },
169
+ { "notation": "y (L' U2 L) U (L' U' L)", "moves": 7, "note": "Alt" },
170
+ { "notation": "(R U R' U') (R U R' U') F R' F' R", "moves": 12, "note": "Alt 2" },
171
+ { "notation": "(R' F R F') (R U' R' U) (R U' R')", "moves": 11, "note": "Alt 3" }
172
+ ]
173
+ },
174
+ {
175
+ "name": "F2L 19",
176
+ "case": "Pair Made on Side",
177
+ "algs": [
178
+ { "notation": "U (R U2 R') U (R U' R')", "moves": 8, "note": "Main" },
179
+ { "notation": "(R U' R' U) (R U' R' U) R U R'", "moves": 11, "note": "Alt" }
180
+ ]
181
+ },
182
+ {
183
+ "name": "F2L 20",
184
+ "case": "Pair Made on Side",
185
+ "algs": [
186
+ { "notation": "y' U' (R' U2 R) U' (R' U R)", "moves": 8, "note": "Main" },
187
+ { "notation": "U' (R U' R2' F R F') (R U' R')", "moves": 10, "note": "Alt" },
188
+ { "notation": "y' (R' U R U') (R' U R U') R' U' R", "moves": 11, "note": "Alt 2" }
189
+ ]
190
+ },
191
+ {
192
+ "name": "F2L 21",
193
+ "case": "Pair Made on Side",
194
+ "algs": [
195
+ { "notation": "(R U' R') U2 (R U R')", "moves": 7, "note": "Main" },
196
+ { "notation": "U2 (R U R' U)(R U' R')", "moves": 8, "note": "Alt" }
197
+ ]
198
+ },
199
+ {
200
+ "name": "F2L 22",
201
+ "case": "Pair Made on Side",
202
+ "algs": [
203
+ { "notation": "y' (R' U R) U2 (R' U' R)", "moves": 7, "note": "Main" },
204
+ { "notation": "y' U2 (R' U' R U') (R' U R)", "moves": 8, "note": "Alt" },
205
+ { "notation": "r U' r' U2 r U r'", "moves": 7, "note": "Alt 2" },
206
+ { "notation": "U (F R U R' U' F') R U R' U2'", "moves": 11, "note": "Alt 3" }
207
+ ]
208
+ },
209
+ {
210
+ "name": "F2L 23",
211
+ "case": "Weird",
212
+ "algs": [
213
+ { "notation": "U2 R2 U2 (R' U' R U') R2", "moves": 8, "note": "Main" },
214
+ { "notation": "U (F R' F' R) U (R U R')", "moves": 8, "note": "Alt" },
215
+ { "notation": "U R U' R' U' R U' R' U R U' R'", "moves": 12, "note": "Alt 2" },
216
+ { "notation": "(R U R' U') U' (R U R' U')(R U R')", "moves": 12, "note": "Alt 3" },
217
+ { "notation": "R U' R' U' (R U R') U2 (R U R')", "moves": 11, "note": "Alt 4" },
218
+ { "notation": "R2 U R' U R U2 R' U' R'", "moves": 9, "note": "Alt 5" }
219
+ ]
220
+ },
221
+ {
222
+ "name": "F2L 24",
223
+ "case": "Weird",
224
+ "algs": [
225
+ { "notation": "F U (R U' R' F')(R U' R')", "moves": 9, "note": "Main" },
226
+ { "notation": "y' (R' U' R U) U (R' U' R U)(R' U' R)", "moves": 12, "note": "Alt" },
227
+ { "notation": "y' U2 R2 U2 (R U R' U) R2", "moves": 8, "note": "Alt 2" },
228
+ { "notation": "U' (R U) (R2' F R F') (R U' R')", "moves": 10, "note": "Alt 3" },
229
+ { "notation": "(R U R' U) (R U R' U') (F R' F' R)", "moves": 12, "note": "Alt 4" },
230
+ { "notation": "(R U R' U) (R U2 R') F' U2 F", "moves": 11, "note": "Alt 5" }
231
+ ]
232
+ },
233
+ {
234
+ "name": "F2L 25",
235
+ "case": "Corner in Place, Edge in U Face",
236
+ "algs": [
237
+ { "notation": "U' (R' F R F') (R U R')", "moves": 8, "note": "Main" },
238
+ { "notation": "R' U' R' U' R' U R U R", "moves": 9, "note": "Alt" },
239
+ { "notation": "U' (F' U F) U (R U' R')", "moves": 7, "note": "Alt 2" },
240
+ { "notation": "R' F' R U R U' R' F", "moves": 8, "note": "Alt 3" },
241
+ { "notation": "(U') F' R U R' U' R' F R", "moves": 9, "note": "Alt 4" },
242
+ { "notation": "(R U' R' U') (R U' R' U) (R U R')", "moves": 11, "note": "Alt 5" }
243
+ ]
244
+ },
245
+ {
246
+ "name": "F2L 26",
247
+ "case": "Corner in Place, Edge in U Face",
248
+ "algs": [
249
+ { "notation": "U (R U' R') (F R' F' R)", "moves": 8, "note": "Main" },
250
+ { "notation": "y' R U R U R U' R' U' R'", "moves": 9, "note": "Alt" },
251
+ { "notation": "U (R U' R') U' (F' U F)", "moves": 8, "note": "Alt 2" },
252
+ { "notation": "r U r' U2 r U r' U2 r U' r'", "moves": 11, "note": "Alt 3" }
253
+ ]
254
+ },
255
+ {
256
+ "name": "F2L 27",
257
+ "case": "Corner in Place, Edge in U Face",
258
+ "algs": [
259
+ { "notation": "(R U' R' U)(R U' R')", "moves": 7, "note": "Main" },
260
+ { "notation": "(R U' R2)(F R F')", "moves": 6, "note": "Alt" }
261
+ ]
262
+ },
263
+ {
264
+ "name": "F2L 28",
265
+ "case": "Corner in Place, Edge in U Face",
266
+ "algs": [
267
+ { "notation": "(R U R' U') F R' F' R", "moves": 8, "note": "Main" },
268
+ { "notation": "(R U R') d (R' U2 R)", "moves": 7, "note": "Alt" },
269
+ { "notation": "y (L' U L U') (L' U L)", "moves": 7, "note": "Alt 2" },
270
+ { "notation": "y' (R' U R U')(R' U R)", "moves": 7, "note": "Alt 3" },
271
+ { "notation": "(R U R') U (F' U2 F)", "moves": 7, "note": "Alt 4" }
272
+ ]
273
+ },
274
+ {
275
+ "name": "F2L 29",
276
+ "case": "Corner in Place, Edge in U Face",
277
+ "algs": [
278
+ { "notation": "y' (R' U' R U)(R' U' R)", "moves": 7, "note": "Main" },
279
+ { "notation": "U2 (R U' R') y' (R' U' R)", "moves": 7, "note": "Alt" },
280
+ { "notation": "(R' F R F') (R' F R F')", "moves": 8, "note": "Alt 2" },
281
+ { "notation": "(R' F R F') (U R U' R')", "moves": 8, "note": "Alt 3" }
282
+ ]
283
+ },
284
+ {
285
+ "name": "F2L 30",
286
+ "case": "Corner in Place, Edge in U Face",
287
+ "algs": [
288
+ { "notation": "(R U R' U')(R U R')", "moves": 7, "note": "Main" },
289
+ { "notation": "U2 (F' U F)(R U R')", "moves": 7, "note": "Alt" },
290
+ { "notation": "U' (R U2 R') U2 (R U R')", "moves": 8, "note": "Alt 2" }
291
+ ]
292
+ },
293
+ {
294
+ "name": "F2L 31",
295
+ "case": "Edge in Place, Corner in U Face",
296
+ "algs": [
297
+ { "notation": "(R U' R') d (R' U R)", "moves": 7, "note": "Main" },
298
+ { "notation": "(R U' R' U)(F' U F)", "moves": 7, "note": "Alt" },
299
+ { "notation": "U' (R' F R F') (R U' R')", "moves": 8, "note": "Alt 2" },
300
+ { "notation": "R U2 R' U' F R' F' R", "moves": 8, "note": "Alt 3" }
301
+ ]
302
+ },
303
+ {
304
+ "name": "F2L 32",
305
+ "case": "Edge in Place, Corner in U Face",
306
+ "algs": [
307
+ { "notation": "(R U R' U') (R U R' U') (R U R')", "moves": 11, "note": "Main" },
308
+ { "notation": "(U R U' R') (U R U' R') (U R U' R')", "moves": 12, "note": "Alt" },
309
+ { "notation": "U' (R U R' U') (R U R' U) (R U' R')", "moves": 12, "note": "Alt 2" }
310
+ ]
311
+ },
312
+ {
313
+ "name": "F2L 33",
314
+ "case": "Edge in Place, Corner in U Face",
315
+ "algs": [
316
+ { "notation": "U' (R U' R') U2' (R U' R')", "moves": 8, "note": "Main" },
317
+ { "notation": "(U' R U' R') U2 (R U' R')", "moves": 8, "note": "Alt" },
318
+ { "notation": "y U' (L' U' L) U2 (L' U' L)", "moves": 8, "note": "Alt 2" }
319
+ ]
320
+ },
321
+ {
322
+ "name": "F2L 34",
323
+ "case": "Edge in Place, Corner in U Face",
324
+ "algs": [
325
+ { "notation": "U (F' U F) U2 (F' U F)", "moves": 8, "note": "Main" },
326
+ { "notation": "U' (R U2' R') U (R U R')", "moves": 8, "note": "Alt" },
327
+ { "notation": "U (R U R') U2 (R U R')", "moves": 8, "note": "Alt 2" },
328
+ { "notation": "d (R' U R) U2 (R' U R)", "moves": 8, "note": "Alt 3" },
329
+ { "notation": "y (U L' U L) U2 (L' U L)", "moves": 8, "note": "Alt 4" }
330
+ ]
331
+ },
332
+ {
333
+ "name": "F2L 35",
334
+ "case": "Edge in Place, Corner in U Face",
335
+ "algs": [
336
+ { "notation": "U' R U R' U (F' U' F)", "moves": 8, "note": "Main" },
337
+ { "notation": "(U' R U R') d (R' U' R)", "moves": 8, "note": "Alt" },
338
+ { "notation": "U2 (R U' R') U' (F' U' F)", "moves": 8, "note": "Alt 2" }
339
+ ]
340
+ },
341
+ {
342
+ "name": "F2L 36",
343
+ "case": "Edge in Place, Corner in U Face",
344
+ "algs": [
345
+ { "notation": "U F' U' F U' (R U R')", "moves": 8, "note": "Main" },
346
+ { "notation": "U2 (R' F R F') U2 (R U R')", "moves": 9, "note": "Alt" },
347
+ { "notation": "y' U (R' U' R U') y (R U R')", "moves": 8, "note": "Alt 2" }
348
+ ]
349
+ },
350
+ {
351
+ "name": "F2L 37",
352
+ "case": "Edge and Corner in Place",
353
+ "algs": [
354
+ { "notation": "", "moves": 0, "note": "Solved" }
355
+ ]
356
+ },
357
+ {
358
+ "name": "F2L 38",
359
+ "case": "Edge and Corner in Place",
360
+ "algs": [
361
+ { "notation": "(R' F R F') (R U' R' U) (R U' R' U2) (R U' R')", "moves": 15, "note": "Main" },
362
+ { "notation": "(R U' R') d (R' U2 R) U2' (R' U R)", "moves": 10, "note": "Alt" },
363
+ { "notation": "(R U R') U2 (R U2 R') d (R' U' R)", "moves": 11, "note": "Alt 2" },
364
+ { "notation": "(R2 U2) (F R2 F') (U2 R' U R')", "moves": 9, "note": "Alt 3" },
365
+ { "notation": "(R U R' U')(R U2 R' d)(R' U' R U' R' U R)", "moves": 15, "note": "Alt 4" }
366
+ ]
367
+ },
368
+ {
369
+ "name": "F2L 39",
370
+ "case": "Edge and Corner in Place",
371
+ "algs": [
372
+ { "notation": "(R U' R') U' (R U R') U2 (R U' R')", "moves": 11, "note": "Main" },
373
+ { "notation": "y' (R' U' R) U2 (R' U R U') (R' U' R)", "moves": 11, "note": "Alt" },
374
+ { "notation": "(R U R' U') (R U2 R') U' (R U R')", "moves": 11, "note": "Alt 2" }
375
+ ]
376
+ },
377
+ {
378
+ "name": "F2L 40",
379
+ "case": "Edge and Corner in Place",
380
+ "algs": [
381
+ { "notation": "(R U' R' U)(R U2' R') U (R U' R')", "moves": 11, "note": "Main" },
382
+ { "notation": "(R U R') U2 (R U' R' U)(R U R')", "moves": 11, "note": "Alt" },
383
+ { "notation": "(R U2) (R U R' U) (R U2 R2')", "moves": 9, "note": "Alt 2" },
384
+ { "notation": "y' R2 U2 R U R' U R U2 R", "moves": 9, "note": "Alt 3" }
385
+ ]
386
+ },
387
+ {
388
+ "name": "F2L 41",
389
+ "case": "Edge and Corner in Place",
390
+ "algs": [
391
+ { "notation": "(r U' r') U2 (r U r') (R U R')", "moves": 11, "note": "Main" },
392
+ { "notation": "(R U' R') d (R' U' R U') (R' U' R)", "moves": 11, "note": "Alt" },
393
+ { "notation": "R U' R' U' R U' R' d R' U' R", "moves": 11, "note": "Alt 2" },
394
+ { "notation": "R (F U R U' R' F') U' R'", "moves": 9, "note": "Alt 3" }
395
+ ]
396
+ },
397
+ {
398
+ "name": "F2L 42",
399
+ "case": "Edge and Corner in Place",
400
+ "algs": [
401
+ { "notation": "(R U' R') (r U' r') U2 (r U r')", "moves": 11, "note": "Main" },
402
+ { "notation": "(R U' R' U) d (R' U' R U') (R' U R)", "moves": 12, "note": "Alt" },
403
+ { "notation": "(R U' R' U2) y' (R' U' R U' R' U R)", "moves": 12, "note": "Alt 2" },
404
+ { "notation": "(R U R' U') (R U' R') U2 (F' U' F)", "moves": 11, "note": "Alt 3" },
405
+ { "notation": "R U (F R U R' U' F') R'", "moves": 9, "note": "Alt 4" }
406
+ ]
407
+ }
408
+ ]
409
+ };
json/oll.json.js ADDED
@@ -0,0 +1,633 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ OH_ALGORITHMS.oll = {
2
+ "title": "OH OLL - Orientation of Last Layer",
3
+ "description": "OH OLL algorithms from CubeSkills by Feliks Zemdegs. 57 cases.",
4
+ "cases": [
5
+ {
6
+ "name": "OLL 1",
7
+ "case": "No Edges",
8
+ "algs": [
9
+ {
10
+ "notation": "y R U' R2 D' r U' r' D R2 U R'",
11
+ "moves": 12,
12
+ "note": "OH OLL"
13
+ }
14
+ ]
15
+ },
16
+ {
17
+ "name": "OLL 2",
18
+ "case": "No Edges",
19
+ "algs": [
20
+ {
21
+ "notation": "y r U r' U2 R U2 R' U2 r U' r'",
22
+ "moves": 12,
23
+ "note": "OH OLL"
24
+ }
25
+ ]
26
+ },
27
+ {
28
+ "name": "OLL 3",
29
+ "case": "No Edges",
30
+ "algs": [
31
+ {
32
+ "notation": "y r' R2 U R' U r U2 r' U r R'",
33
+ "moves": 12,
34
+ "note": "OH OLL"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "name": "OLL 4",
40
+ "case": "No Edges",
41
+ "algs": [
42
+ {
43
+ "notation": "y r' R U' r U2 r' U' R U' R2 r",
44
+ "moves": 12,
45
+ "note": "OH OLL"
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "name": "OLL 5",
51
+ "case": "Square",
52
+ "algs": [
53
+ {
54
+ "notation": "r' U2 R U R' U r",
55
+ "moves": 7,
56
+ "note": "OH OLL"
57
+ }
58
+ ]
59
+ },
60
+ {
61
+ "name": "OLL 6",
62
+ "case": "Square",
63
+ "algs": [
64
+ {
65
+ "notation": "r U2 R' U' R U' r'",
66
+ "moves": 7,
67
+ "note": "OH OLL"
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ "name": "OLL 7",
73
+ "case": "Lightning Bolt",
74
+ "algs": [
75
+ {
76
+ "notation": "r U R' U R U2 r'",
77
+ "moves": 7,
78
+ "note": "OH OLL"
79
+ }
80
+ ]
81
+ },
82
+ {
83
+ "name": "OLL 8",
84
+ "case": "Lightning Bolt",
85
+ "algs": [
86
+ {
87
+ "notation": "r' U' R U' R' U2 r",
88
+ "moves": 7,
89
+ "note": "OH OLL"
90
+ }
91
+ ]
92
+ },
93
+ {
94
+ "name": "OLL 9",
95
+ "case": "Fish",
96
+ "algs": [
97
+ {
98
+ "notation": "R U R' U' R' F R2 U R' U' F'",
99
+ "moves": 11,
100
+ "note": "OH OLL"
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ "name": "OLL 10",
106
+ "case": "Fish",
107
+ "algs": [
108
+ {
109
+ "notation": "y r R2 U2 R U R' U R U r' R",
110
+ "moves": 12,
111
+ "note": "OH OLL"
112
+ }
113
+ ]
114
+ },
115
+ {
116
+ "name": "OLL 11",
117
+ "case": "Lightning Bolt",
118
+ "algs": [
119
+ {
120
+ "notation": "r' R2 U R' U R U2 R' U r R'",
121
+ "moves": 11,
122
+ "note": "OH OLL"
123
+ }
124
+ ]
125
+ },
126
+ {
127
+ "name": "OLL 12",
128
+ "case": "Lightning Bolt",
129
+ "algs": [
130
+ {
131
+ "notation": "r R2 U' R U' R' U2 R U' r' R",
132
+ "moves": 11,
133
+ "note": "OH OLL"
134
+ }
135
+ ]
136
+ },
137
+ {
138
+ "name": "OLL 13",
139
+ "case": "Knight Move",
140
+ "algs": [
141
+ {
142
+ "notation": "F U R U2 R' U' R U R' F'",
143
+ "moves": 10,
144
+ "note": "OH OLL"
145
+ }
146
+ ]
147
+ },
148
+ {
149
+ "name": "OLL 14",
150
+ "case": "Knight Move",
151
+ "algs": [
152
+ {
153
+ "notation": "y2 r' U r U r' U' r y R U' R'",
154
+ "moves": 12,
155
+ "note": "OH OLL"
156
+ }
157
+ ]
158
+ },
159
+ {
160
+ "name": "OLL 15",
161
+ "case": "Knight Move",
162
+ "algs": [
163
+ {
164
+ "notation": "r' U' r R' U' R U r' U r",
165
+ "moves": 10,
166
+ "note": "OH OLL"
167
+ }
168
+ ]
169
+ },
170
+ {
171
+ "name": "OLL 16",
172
+ "case": "Knight Move",
173
+ "algs": [
174
+ {
175
+ "notation": "r U r' R U R' U' r U' r'",
176
+ "moves": 10,
177
+ "note": "OH OLL"
178
+ }
179
+ ]
180
+ },
181
+ {
182
+ "name": "OLL 17",
183
+ "case": "No Edges",
184
+ "algs": [
185
+ {
186
+ "notation": "y F R' F' R U2 R U' y R U' R' F'",
187
+ "moves": 13,
188
+ "note": "OH OLL"
189
+ }
190
+ ]
191
+ },
192
+ {
193
+ "name": "OLL 18",
194
+ "case": "No Edges",
195
+ "algs": [
196
+ {
197
+ "notation": "r U R' U R U2 r' r' U' R U' R' U2 r",
198
+ "moves": 14,
199
+ "note": "OH OLL"
200
+ }
201
+ ]
202
+ },
203
+ {
204
+ "name": "OLL 19",
205
+ "case": "No Edges",
206
+ "algs": [
207
+ {
208
+ "notation": "r' U2 R U R' U r2 U2 R' U' R U' r'",
209
+ "moves": 13,
210
+ "note": "OH OLL"
211
+ }
212
+ ]
213
+ },
214
+ {
215
+ "name": "OLL 20",
216
+ "case": "No Edges",
217
+ "algs": [
218
+ {
219
+ "notation": "r' R U R U R' U' r2 R2 U R U' r'",
220
+ "moves": 13,
221
+ "note": "OH OLL"
222
+ }
223
+ ]
224
+ },
225
+ {
226
+ "name": "OLL 21",
227
+ "case": "OCLL",
228
+ "algs": [
229
+ {
230
+ "notation": "R U2 R' U' R U R' U' R U' R'",
231
+ "moves": 11,
232
+ "note": "OH OLL"
233
+ }
234
+ ]
235
+ },
236
+ {
237
+ "name": "OLL 22",
238
+ "case": "OCLL",
239
+ "algs": [
240
+ {
241
+ "notation": "R U2 R2 U' R2 U' R2 U2 R",
242
+ "moves": 9,
243
+ "note": "OH OLL"
244
+ }
245
+ ]
246
+ },
247
+ {
248
+ "name": "OLL 23",
249
+ "case": "OCLL",
250
+ "algs": [
251
+ {
252
+ "notation": "R U R' U R U2 R2 U' R U' R' U2 R",
253
+ "moves": 13,
254
+ "note": "OH OLL"
255
+ }
256
+ ]
257
+ },
258
+ {
259
+ "name": "OLL 24",
260
+ "case": "OCLL",
261
+ "algs": [
262
+ {
263
+ "notation": "y' R U' R' U2 R U R' U2 R U R' U R U' R'",
264
+ "moves": 16,
265
+ "note": "OH OLL"
266
+ }
267
+ ]
268
+ },
269
+ {
270
+ "name": "OLL 25",
271
+ "case": "OCLL",
272
+ "algs": [
273
+ {
274
+ "notation": "y2 F R U' R' U' R U2 R' U' F'",
275
+ "moves": 11,
276
+ "note": "OH OLL"
277
+ }
278
+ ]
279
+ },
280
+ {
281
+ "name": "OLL 26",
282
+ "case": "OCLL",
283
+ "algs": [
284
+ {
285
+ "notation": "R U2 R' U' R U' R'",
286
+ "moves": 7,
287
+ "note": "OH OLL"
288
+ }
289
+ ]
290
+ },
291
+ {
292
+ "name": "OLL 27",
293
+ "case": "OCLL",
294
+ "algs": [
295
+ {
296
+ "notation": "R U R' U R U2 R'",
297
+ "moves": 7,
298
+ "note": "OH OLL"
299
+ }
300
+ ]
301
+ },
302
+ {
303
+ "name": "OLL 28",
304
+ "case": "Corners Correct",
305
+ "algs": [
306
+ {
307
+ "notation": "r U R' U' r' R U R U' R'",
308
+ "moves": 10,
309
+ "note": "OH OLL"
310
+ }
311
+ ]
312
+ },
313
+ {
314
+ "name": "OLL 29",
315
+ "case": "Awkward",
316
+ "algs": [
317
+ {
318
+ "notation": "y R U R' U' R U' R' F' U' F R U R'",
319
+ "moves": 14,
320
+ "note": "OH OLL"
321
+ }
322
+ ]
323
+ },
324
+ {
325
+ "name": "OLL 30",
326
+ "case": "Awkward",
327
+ "algs": [
328
+ {
329
+ "notation": "y' F U R U2 R' U' R U2 R' U' F'",
330
+ "moves": 12,
331
+ "note": "OH OLL"
332
+ }
333
+ ]
334
+ },
335
+ {
336
+ "name": "OLL 31",
337
+ "case": "P-Shape",
338
+ "algs": [
339
+ {
340
+ "notation": "R' U' F U R U' R' F' R",
341
+ "moves": 9,
342
+ "note": "OH OLL"
343
+ }
344
+ ]
345
+ },
346
+ {
347
+ "name": "OLL 32",
348
+ "case": "P-Shape",
349
+ "algs": [
350
+ {
351
+ "notation": "y x' U' R U R' x U' R' U' R U R' U R",
352
+ "moves": 15,
353
+ "note": "OH OLL"
354
+ }
355
+ ]
356
+ },
357
+ {
358
+ "name": "OLL 33",
359
+ "case": "T-Shape",
360
+ "algs": [
361
+ {
362
+ "notation": "R U R' U' R' F R F'",
363
+ "moves": 8,
364
+ "note": "OH OLL"
365
+ }
366
+ ]
367
+ },
368
+ {
369
+ "name": "OLL 34",
370
+ "case": "C-Shape",
371
+ "algs": [
372
+ {
373
+ "notation": "R U R2 U' R' F R U R U' F'",
374
+ "moves": 11,
375
+ "note": "OH OLL"
376
+ }
377
+ ]
378
+ },
379
+ {
380
+ "name": "OLL 35",
381
+ "case": "Fish",
382
+ "algs": [
383
+ {
384
+ "notation": "R U2 R2 F R F' R U2 R'",
385
+ "moves": 9,
386
+ "note": "OH OLL"
387
+ }
388
+ ]
389
+ },
390
+ {
391
+ "name": "OLL 36",
392
+ "case": "W-Shape",
393
+ "algs": [
394
+ {
395
+ "notation": "R' U' R U' R' U R U x' R U' R' U",
396
+ "moves": 12,
397
+ "note": "OH OLL"
398
+ }
399
+ ]
400
+ },
401
+ {
402
+ "name": "OLL 37",
403
+ "case": "Fish",
404
+ "algs": [
405
+ {
406
+ "notation": "F R U' R' U' R U R' F'",
407
+ "moves": 9,
408
+ "note": "OH OLL"
409
+ }
410
+ ]
411
+ },
412
+ {
413
+ "name": "OLL 38",
414
+ "case": "W-Shape",
415
+ "algs": [
416
+ {
417
+ "notation": "R U R' U R U' R' U' R' F R F'",
418
+ "moves": 12,
419
+ "note": "OH OLL"
420
+ }
421
+ ]
422
+ },
423
+ {
424
+ "name": "OLL 39",
425
+ "case": "Lightning Bolt",
426
+ "algs": [
427
+ {
428
+ "notation": "y2 R U R' F' U' F U R U2 R'",
429
+ "moves": 11,
430
+ "note": "OH OLL"
431
+ }
432
+ ]
433
+ },
434
+ {
435
+ "name": "OLL 40",
436
+ "case": "Lightning Bolt",
437
+ "algs": [
438
+ {
439
+ "notation": "R' F R U R' U' F' U R",
440
+ "moves": 9,
441
+ "note": "OH OLL"
442
+ }
443
+ ]
444
+ },
445
+ {
446
+ "name": "OLL 41",
447
+ "case": "Awkward",
448
+ "algs": [
449
+ {
450
+ "notation": "R U R' U R U2 R' F R U R' U' F'",
451
+ "moves": 13,
452
+ "note": "OH OLL"
453
+ }
454
+ ]
455
+ },
456
+ {
457
+ "name": "OLL 42",
458
+ "case": "Awkward",
459
+ "algs": [
460
+ {
461
+ "notation": "R' U' R U' R' U2 R F R U R' U' F'",
462
+ "moves": 13,
463
+ "note": "OH OLL"
464
+ }
465
+ ]
466
+ },
467
+ {
468
+ "name": "OLL 43",
469
+ "case": "P-Shape",
470
+ "algs": [
471
+ {
472
+ "notation": "y R' U' F' U F R",
473
+ "moves": 7,
474
+ "note": "OH OLL"
475
+ }
476
+ ]
477
+ },
478
+ {
479
+ "name": "OLL 44",
480
+ "case": "P-Shape",
481
+ "algs": [
482
+ {
483
+ "notation": "f R U R' U' f'",
484
+ "moves": 6,
485
+ "note": "OH OLL"
486
+ }
487
+ ]
488
+ },
489
+ {
490
+ "name": "OLL 45",
491
+ "case": "T-Shape",
492
+ "algs": [
493
+ {
494
+ "notation": "F R U R' U' F'",
495
+ "moves": 6,
496
+ "note": "OH OLL"
497
+ }
498
+ ]
499
+ },
500
+ {
501
+ "name": "OLL 46",
502
+ "case": "C-Shape",
503
+ "algs": [
504
+ {
505
+ "notation": "R' U' R' F R F' U R",
506
+ "moves": 8,
507
+ "note": "OH OLL"
508
+ }
509
+ ]
510
+ },
511
+ {
512
+ "name": "OLL 47",
513
+ "case": "L-Shape",
514
+ "algs": [
515
+ {
516
+ "notation": "R' U' R' F R F' R' F R F' U R",
517
+ "moves": 12,
518
+ "note": "OH OLL"
519
+ }
520
+ ]
521
+ },
522
+ {
523
+ "name": "OLL 48",
524
+ "case": "L-Shape",
525
+ "algs": [
526
+ {
527
+ "notation": "F R U R' U' R U R' U' F'",
528
+ "moves": 10,
529
+ "note": "OH OLL"
530
+ }
531
+ ]
532
+ },
533
+ {
534
+ "name": "OLL 49",
535
+ "case": "L-Shape",
536
+ "algs": [
537
+ {
538
+ "notation": "r U' r2 U r2 U r2 U' r",
539
+ "moves": 9,
540
+ "note": "OH OLL"
541
+ }
542
+ ]
543
+ },
544
+ {
545
+ "name": "OLL 50",
546
+ "case": "L-Shape",
547
+ "algs": [
548
+ {
549
+ "notation": "r' U r2 U' r2 U' r2 U r'",
550
+ "moves": 9,
551
+ "note": "OH OLL"
552
+ }
553
+ ]
554
+ },
555
+ {
556
+ "name": "OLL 51",
557
+ "case": "I-Shape",
558
+ "algs": [
559
+ {
560
+ "notation": "f R U R' U' R U R' U' f'",
561
+ "moves": 10,
562
+ "note": "OH OLL"
563
+ }
564
+ ]
565
+ },
566
+ {
567
+ "name": "OLL 52",
568
+ "case": "I-Shape",
569
+ "algs": [
570
+ {
571
+ "notation": "R U R' U R U' y R U' R' F'",
572
+ "moves": 11,
573
+ "note": "OH OLL"
574
+ }
575
+ ]
576
+ },
577
+ {
578
+ "name": "OLL 53",
579
+ "case": "L-Shape",
580
+ "algs": [
581
+ {
582
+ "notation": "r' U' R U' R' U R U' R' U2 r",
583
+ "moves": 11,
584
+ "note": "OH OLL"
585
+ }
586
+ ]
587
+ },
588
+ {
589
+ "name": "OLL 54",
590
+ "case": "L-Shape",
591
+ "algs": [
592
+ {
593
+ "notation": "r U R' U R U' R' U R U2 r'",
594
+ "moves": 11,
595
+ "note": "OH OLL"
596
+ }
597
+ ]
598
+ },
599
+ {
600
+ "name": "OLL 55",
601
+ "case": "I-Shape",
602
+ "algs": [
603
+ {
604
+ "notation": "r U2 R' U' R2 r' U R' U' r U' r'",
605
+ "moves": 12,
606
+ "note": "OH OLL"
607
+ }
608
+ ]
609
+ },
610
+ {
611
+ "name": "OLL 56",
612
+ "case": "I-Shape",
613
+ "algs": [
614
+ {
615
+ "notation": "r' U' r U' R' U R U' R' U R r' U r",
616
+ "moves": 14,
617
+ "note": "OH OLL"
618
+ }
619
+ ]
620
+ },
621
+ {
622
+ "name": "OLL 57",
623
+ "case": "Corners Correct",
624
+ "algs": [
625
+ {
626
+ "notation": "R U R' U' r R' U R U' r'",
627
+ "moves": 10,
628
+ "note": "OH OLL"
629
+ }
630
+ ]
631
+ }
632
+ ]
633
+ };
json/pll.json.js ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ OH_ALGORITHMS.pll = {
2
+ "title": "OH PLL - Permutation of Last Layer",
3
+ "description": "OH PLL algorithms from CubeSkills by Feliks Zemdegs. All 21 cases.",
4
+ "cases": [
5
+ {
6
+ "name": "Ub (bar back)",
7
+ "case": "Edges Only",
8
+ "algs": [
9
+ {
10
+ "notation": "z U' R U' R' U' R' U' R U R U2",
11
+ "moves": 11,
12
+ "note": "OH"
13
+ }
14
+ ]
15
+ },
16
+ {
17
+ "name": "Ub (bar front)",
18
+ "case": "Edges Only",
19
+ "algs": [
20
+ {
21
+ "notation": "R' U R' U' R' U' R' U R U R2",
22
+ "moves": 11,
23
+ "note": "OH"
24
+ }
25
+ ]
26
+ },
27
+ {
28
+ "name": "Ua",
29
+ "case": "Edges Only",
30
+ "algs": [
31
+ {
32
+ "notation": "R U' R U R U R U' R' U' R2",
33
+ "moves": 11,
34
+ "note": "OH"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "name": "Z",
40
+ "case": "Edges Only",
41
+ "algs": [
42
+ {
43
+ "notation": "y R' U' R U' R U R U' R' U R U R2 U' R' U2",
44
+ "moves": 16,
45
+ "note": "OH"
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "name": "H",
51
+ "case": "Edges Only",
52
+ "algs": [
53
+ {
54
+ "notation": "R2 U2 R U2 R2 U2 R2 U2 R U2 R2",
55
+ "moves": 11,
56
+ "note": "OH"
57
+ }
58
+ ]
59
+ },
60
+ {
61
+ "name": "Aa",
62
+ "case": "Corners Only",
63
+ "algs": [
64
+ {
65
+ "notation": "x R' U R' D2 R U' R' D2 R2 x'",
66
+ "moves": 9,
67
+ "note": "OH"
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ "name": "Ab",
73
+ "case": "Corners Only",
74
+ "algs": [
75
+ {
76
+ "notation": "x R2 D2 R U R' D2 R U' R x'",
77
+ "moves": 9,
78
+ "note": "OH"
79
+ }
80
+ ]
81
+ },
82
+ {
83
+ "name": "E",
84
+ "case": "Corners Only",
85
+ "algs": [
86
+ {
87
+ "notation": "x' R U' R' D R U R' D' R U R' D R U' R' D' x",
88
+ "moves": 16,
89
+ "note": "OH"
90
+ }
91
+ ]
92
+ },
93
+ {
94
+ "name": "Ra",
95
+ "case": "Adjacent Corners",
96
+ "algs": [
97
+ {
98
+ "notation": "R U R' F' R U2 R' U2 R' F R U R U2 R'",
99
+ "moves": 15,
100
+ "note": "OH"
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ "name": "Rb",
106
+ "case": "Adjacent Corners",
107
+ "algs": [
108
+ {
109
+ "notation": "R' U2 R' D' R U' R' D R U R U' R' U' R",
110
+ "moves": 15,
111
+ "note": "OH"
112
+ }
113
+ ]
114
+ },
115
+ {
116
+ "name": "Ja",
117
+ "case": "Adjacent Corners",
118
+ "algs": [
119
+ {
120
+ "notation": "R' U L' U2 R U' R' U2 R L",
121
+ "moves": 10,
122
+ "note": "OH"
123
+ }
124
+ ]
125
+ },
126
+ {
127
+ "name": "Jb",
128
+ "case": "Adjacent Corners",
129
+ "algs": [
130
+ {
131
+ "notation": "R U2 R' U' R U2 L' U R' U' L",
132
+ "moves": 11,
133
+ "note": "OH"
134
+ }
135
+ ]
136
+ },
137
+ {
138
+ "name": "T",
139
+ "case": "Adjacent Corners",
140
+ "algs": [
141
+ {
142
+ "notation": "R U R' U' R' F R2 U' R' U' R U R' F'",
143
+ "moves": 14,
144
+ "note": "OH"
145
+ }
146
+ ]
147
+ },
148
+ {
149
+ "name": "F",
150
+ "case": "Adjacent Corners",
151
+ "algs": [
152
+ {
153
+ "notation": "R' U' F' R U R' U' R' F R2 U' R' U' R U R' U R",
154
+ "moves": 18,
155
+ "note": "OH"
156
+ }
157
+ ]
158
+ },
159
+ {
160
+ "name": "V",
161
+ "case": "Diagonal Corners",
162
+ "algs": [
163
+ {
164
+ "notation": "R' U R U' x' U R U2 R' U' R U' R' U2 R U R' U'",
165
+ "moves": 17,
166
+ "note": "OH"
167
+ }
168
+ ]
169
+ },
170
+ {
171
+ "name": "Y",
172
+ "case": "Diagonal Corners",
173
+ "algs": [
174
+ {
175
+ "notation": "F R U' R' U' R U R' F' R U R' U' R' F R F'",
176
+ "moves": 17,
177
+ "note": "OH"
178
+ }
179
+ ]
180
+ },
181
+ {
182
+ "name": "Na",
183
+ "case": "Diagonal Corners",
184
+ "algs": [
185
+ {
186
+ "notation": "L U' R U2 L' U R' L U' R U2 L' U R'",
187
+ "moves": 14,
188
+ "note": "OH"
189
+ }
190
+ ]
191
+ },
192
+ {
193
+ "name": "Nb",
194
+ "case": "Diagonal Corners",
195
+ "algs": [
196
+ {
197
+ "notation": "R' U L' U2 R U' L R' U L' U2 R U' L",
198
+ "moves": 14,
199
+ "note": "OH"
200
+ }
201
+ ]
202
+ },
203
+ {
204
+ "name": "Ga",
205
+ "case": "G Perms",
206
+ "algs": [
207
+ {
208
+ "notation": "R2 U R' U R' U' R U' R2 D U' R' U R D'",
209
+ "moves": 15,
210
+ "note": "OH"
211
+ }
212
+ ]
213
+ },
214
+ {
215
+ "name": "Gb",
216
+ "case": "G Perms",
217
+ "algs": [
218
+ {
219
+ "notation": "y' R' U' R U D' R2 U R' U R U' R U' R2 D",
220
+ "moves": 15,
221
+ "note": "OH"
222
+ }
223
+ ]
224
+ },
225
+ {
226
+ "name": "Gc",
227
+ "case": "G Perms",
228
+ "algs": [
229
+ {
230
+ "notation": "R2 U' R U' R U R' U R2 D' U R U' R' D",
231
+ "moves": 15,
232
+ "note": "OH"
233
+ }
234
+ ]
235
+ },
236
+ {
237
+ "name": "Gd",
238
+ "case": "G Perms",
239
+ "algs": [
240
+ {
241
+ "notation": "D' R U R' U' D R2 U' R U' R' U R' U R2",
242
+ "moves": 15,
243
+ "note": "OH"
244
+ }
245
+ ]
246
+ }
247
+ ]
248
+ };
json/zbll.json.js ADDED
The diff for this file is too large to render. See raw diff