Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>One-Handed 3x3 Rubik's Cube - OH Algorithms</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: #1a1a2e; | |
| color: #e0e0e0; | |
| overflow-x: hidden; | |
| } | |
| header { | |
| background: linear-gradient(135deg, #16213e, #0f3460); | |
| padding: 20px 40px; | |
| text-align: center; | |
| border-bottom: 2px solid #e94560; | |
| } | |
| header h1 { | |
| font-size: 2rem; | |
| color: #fff; | |
| text-shadow: 0 0 10px rgba(233, 69, 96, 0.5); | |
| } | |
| header p { | |
| color: #aaa; | |
| margin-top: 5px; | |
| } | |
| .container { | |
| display: flex; | |
| min-height: calc(100vh - 100px); | |
| } | |
| .sidebar { | |
| width: 280px; | |
| background: #16213e; | |
| padding: 20px; | |
| border-right: 1px solid #0f3460; | |
| overflow-y: auto; | |
| max-height: calc(100vh - 100px); | |
| position: sticky; | |
| top: 0; | |
| } | |
| .sidebar h3 { | |
| color: #e94560; | |
| margin-bottom: 15px; | |
| font-size: 1.1rem; | |
| } | |
| .nav-item { | |
| padding: 12px 16px; | |
| margin: 4px 0; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| font-size: 0.95rem; | |
| } | |
| .nav-item:hover { | |
| background: #0f3460; | |
| transform: translateX(5px); | |
| } | |
| .nav-item.active { | |
| background: #e94560; | |
| color: #fff; | |
| font-weight: bold; | |
| } | |
| .main-content { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| height: calc(100vh - 100px); | |
| overflow: hidden; | |
| } | |
| .cube-section { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| padding: 20px; | |
| background: radial-gradient(ellipse at center, #1a1a35 0%, #0a0a18 100%); | |
| border-bottom: 1px solid #333; | |
| min-height: 420px; | |
| position: relative; | |
| flex-shrink: 0; | |
| } | |
| .cube-section.collapsed { | |
| min-height: 0; | |
| height: 40px; | |
| padding: 8px 20px; | |
| justify-content: flex-start; | |
| align-items: center; | |
| } | |
| .cube-section.collapsed canvas, | |
| .cube-section.collapsed .controls, | |
| .cube-section.collapsed .scramble-display, | |
| .cube-section.collapsed .move-buttons, | |
| .cube-section.collapsed #step-panel { | |
| display: none ; | |
| } | |
| .cube-section.fullscreen { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| min-height: 100vh; | |
| z-index: 999; | |
| border: none; | |
| padding: 10px; | |
| } | |
| .cube-section.fullscreen #cube-canvas { | |
| height: calc(100vh - 120px); | |
| } | |
| .popout-btn { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| padding: 6px 14px; | |
| border: 1px solid #4fc3f7; | |
| border-radius: 6px; | |
| background: rgba(15, 52, 96, 0.8); | |
| color: #4fc3f7; | |
| cursor: pointer; | |
| font-size: 0.8rem; | |
| z-index: 10; | |
| transition: all 0.2s; | |
| } | |
| .popout-btn:hover { | |
| background: #4fc3f7; | |
| color: #0a0a18; | |
| } | |
| .cube-section.collapsed .popout-btn { | |
| position: static; | |
| } | |
| .fullscreen-btn { | |
| position: absolute; | |
| top: 10px; | |
| right: 100px; | |
| padding: 6px 14px; | |
| border: 1px solid #2ecc71; | |
| border-radius: 6px; | |
| background: rgba(15, 52, 96, 0.8); | |
| color: #2ecc71; | |
| cursor: pointer; | |
| font-size: 0.8rem; | |
| z-index: 10; | |
| transition: all 0.2s; | |
| } | |
| .fullscreen-btn:hover { | |
| background: #2ecc71; | |
| color: #0a0a18; | |
| } | |
| #cube-canvas { | |
| width: 100%; | |
| height: 400px; | |
| border-radius: 12px; | |
| display: block; | |
| } | |
| .controls { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| display: flex; | |
| gap: 8px; | |
| flex-wrap: wrap; | |
| justify-content: center; | |
| z-index: 10; | |
| max-width: 600px; | |
| } | |
| .control-btn { | |
| padding: 8px 14px; | |
| border: none; | |
| border-radius: 6px; | |
| background: #0f3460; | |
| color: #fff; | |
| cursor: pointer; | |
| font-size: 0.85rem; | |
| transition: all 0.2s; | |
| } | |
| .control-btn:hover { | |
| background: #e94560; | |
| transform: scale(1.05); | |
| } | |
| .algorithm-section { | |
| flex: 1; | |
| padding: 30px; | |
| overflow-y: auto; | |
| } | |
| .algorithm-section h2 { | |
| color: #e94560; | |
| margin-bottom: 10px; | |
| font-size: 1.6rem; | |
| } | |
| .algorithm-section .description { | |
| color: #888; | |
| margin-bottom: 25px; | |
| line-height: 1.6; | |
| } | |
| .alg-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); | |
| gap: 16px; | |
| } | |
| .alg-card { | |
| background: #16213e; | |
| border: 1px solid #0f3460; | |
| border-radius: 12px; | |
| padding: 18px; | |
| transition: all 0.3s ease; | |
| } | |
| .alg-card:hover { | |
| border-color: #e94560; | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 15px rgba(233, 69, 96, 0.2); | |
| } | |
| .alg-card h4 { | |
| color: #fff; | |
| margin-bottom: 8px; | |
| font-size: 1rem; | |
| } | |
| .alg-card .case-name { | |
| color: #e94560; | |
| font-weight: bold; | |
| font-size: 0.9rem; | |
| margin-bottom: 6px; | |
| } | |
| .alg-card-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 14px; | |
| margin-bottom: 10px; | |
| } | |
| .alg-diagram { | |
| flex-shrink: 0; | |
| } | |
| .alg-diagram svg { | |
| display: block; | |
| border-radius: 4px; | |
| } | |
| .alg-diagram .f2l-case-img { | |
| display: block; | |
| width: 120px; | |
| height: 120px; | |
| border-radius: 4px; | |
| } | |
| .alg-card-info { | |
| flex: 1; | |
| min-width: 0; | |
| } | |
| .alg-card-info .case-name { | |
| color: #e94560; | |
| font-weight: bold; | |
| font-size: 0.9rem; | |
| margin-bottom: 4px; | |
| } | |
| .alg-card-info h4 { | |
| color: #fff; | |
| font-size: 0.95rem; | |
| margin: 0; | |
| } | |
| .alg-notation { | |
| font-family: 'Courier New', monospace; | |
| background: #0a0a1a; | |
| padding: 10px 14px; | |
| border-radius: 6px; | |
| font-size: 0.85rem; | |
| color: #4fc3f7; | |
| margin: 6px 0; | |
| word-break: break-all; | |
| cursor: pointer; | |
| transition: background 0.2s; | |
| } | |
| .alg-notation:hover { | |
| background: #1a1a3a; | |
| } | |
| .alg-notation:hover::after { | |
| content: ' [click to setup & solve]'; | |
| color: #e94560; | |
| font-size: 0.7rem; | |
| } | |
| .alg-meta { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-top: 8px; | |
| font-size: 0.8rem; | |
| color: #666; | |
| } | |
| .alg-meta .moves { | |
| background: #0f3460; | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| color: #4fc3f7; | |
| } | |
| .info-badge { | |
| display: inline-block; | |
| background: #e94560; | |
| color: #fff; | |
| padding: 2px 8px; | |
| border-radius: 10px; | |
| font-size: 0.75rem; | |
| margin-left: 8px; | |
| } | |
| .scramble-display { | |
| position: absolute; | |
| top: 15px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background: rgba(15, 52, 96, 0.9); | |
| padding: 8px 16px; | |
| border-radius: 6px; | |
| font-family: 'Courier New', monospace; | |
| font-size: 0.9rem; | |
| color: #4fc3f7; | |
| z-index: 10; | |
| } | |
| .sub-section { | |
| margin-bottom: 30px; | |
| } | |
| .sub-section h3 { | |
| color: #fff; | |
| margin-bottom: 12px; | |
| padding-bottom: 6px; | |
| border-bottom: 1px solid #333; | |
| } | |
| @media (max-width: 900px) { | |
| .container { | |
| flex-direction: column; | |
| } | |
| .sidebar { | |
| width: 100%; | |
| max-height: 200px; | |
| position: relative; | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| align-items: center; | |
| } | |
| .sidebar h3:last-of-type, .sidebar p, .sidebar hr { | |
| display: none; | |
| } | |
| .alg-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| .cube-section { | |
| min-height: 350px; | |
| } | |
| } | |
| /* Group filter tabs for ZBLL/COLL */ | |
| .group-tabs { | |
| display: flex; | |
| gap: 6px; | |
| flex-wrap: wrap; | |
| margin-bottom: 20px; | |
| padding-bottom: 15px; | |
| border-bottom: 1px solid #333; | |
| } | |
| .group-tab { | |
| padding: 8px 16px; | |
| border: 1px solid #0f3460; | |
| border-radius: 6px; | |
| background: #0f0f23; | |
| color: #888; | |
| cursor: pointer; | |
| font-size: 0.85rem; | |
| transition: all 0.2s; | |
| } | |
| .group-tab:hover { | |
| background: #0f3460; | |
| color: #fff; | |
| } | |
| .group-tab.active { | |
| background: #e94560; | |
| color: #fff; | |
| border-color: #e94560; | |
| font-weight: bold; | |
| } | |
| /* Step-by-step button in algorithm cards */ | |
| .step-by-step-btn { | |
| padding: 3px 10px; | |
| border: 1px solid #4fc3f7; | |
| border-radius: 4px; | |
| background: transparent; | |
| color: #4fc3f7; | |
| cursor: pointer; | |
| font-size: 0.75rem; | |
| transition: all 0.2s; | |
| white-space: nowrap; | |
| } | |
| .step-by-step-btn:hover { | |
| background: #4fc3f7; | |
| color: #0a0a1a; | |
| } | |
| /* Step panel overlay on cube */ | |
| #step-panel { | |
| display: none; | |
| position: absolute; | |
| top: 15px; | |
| right: 15px; | |
| left: auto; | |
| bottom: auto; | |
| transform: none; | |
| background: rgba(10, 10, 30, 0.95); | |
| border: 1px solid #4fc3f7; | |
| border-radius: 12px; | |
| padding: 0; | |
| z-index: 20; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 12px; | |
| min-width: 280px; | |
| max-width: 40%; | |
| backdrop-filter: blur(8px); | |
| cursor: default; | |
| user-select: none; | |
| } | |
| #step-panel .drag-handle { | |
| width: 100%; | |
| padding: 8px 16px; | |
| cursor: grab; | |
| background: rgba(79, 195, 247, 0.15); | |
| border-radius: 12px 12px 0 0; | |
| text-align: center; | |
| font-size: 0.75rem; | |
| color: #4fc3f7; | |
| border-bottom: 1px solid rgba(79, 195, 247, 0.2); | |
| } | |
| #step-panel .drag-handle:active { | |
| cursor: grabbing; | |
| } | |
| #step-panel .step-content { | |
| padding: 14px 20px; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .step-info { | |
| text-align: center; | |
| width: 100%; | |
| } | |
| .step-counter { | |
| font-size: 0.85rem; | |
| color: #888; | |
| margin-bottom: 8px; | |
| } | |
| .step-moves-display { | |
| font-family: 'Courier New', monospace; | |
| font-size: 1rem; | |
| line-height: 1.8; | |
| word-wrap: break-word; | |
| } | |
| .step-done { | |
| color: #555; | |
| text-decoration: line-through; | |
| } | |
| .step-current { | |
| color: #fff; | |
| background: #e94560; | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| font-weight: bold; | |
| font-size: 1.1rem; | |
| } | |
| .step-pending { | |
| color: #4fc3f7; | |
| } | |
| .step-buttons { | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .step-btn { | |
| padding: 8px 18px; | |
| border: none; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| font-size: 0.9rem; | |
| font-weight: bold; | |
| transition: all 0.2s; | |
| } | |
| .step-btn-next { | |
| background: #e94560; | |
| color: #fff; | |
| } | |
| .step-btn-next:hover { | |
| background: #ff6b85; | |
| transform: scale(1.05); | |
| } | |
| .step-btn-prev { | |
| background: #0f3460; | |
| color: #4fc3f7; | |
| border: 1px solid #4fc3f7; | |
| } | |
| .step-btn-prev:hover { | |
| background: #4fc3f7; | |
| color: #0a0a1a; | |
| } | |
| .step-btn-done { | |
| background: #2ecc71; | |
| color: #fff; | |
| } | |
| .step-btn-cancel { | |
| background: #333; | |
| color: #aaa; | |
| border: 1px solid #555; | |
| } | |
| .step-btn-cancel:hover { | |
| background: #555; | |
| color: #fff; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>🧊 One-Handed 3x3 Rubik's Cube</h1> | |
| <p>Interactive 3D Visualization & OH Algorithm Reference</p> | |
| </header> | |
| <div class="container"> | |
| <nav class="sidebar" role="navigation" aria-label="Algorithm categories"> | |
| <h3>📚 Algorithm Sets</h3> | |
| <div class="nav-item active" data-section="f2l" tabindex="0" role="button" aria-pressed="true">F2L <span class="info-badge">42 cases</span></div> | |
| <div class="nav-item" data-section="oll" tabindex="0" role="button" aria-pressed="false">OLL <span class="info-badge">57 cases</span></div> | |
| <div class="nav-item" data-section="pll" tabindex="0" role="button" aria-pressed="false">PLL <span class="info-badge">21 cases</span></div> | |
| <div class="nav-item" data-section="coll" tabindex="0" role="button" aria-pressed="false">COLL <span class="info-badge">40 cases</span></div> | |
| <div class="nav-item" data-section="sv" tabindex="0" role="button" aria-pressed="false">SV <span class="info-badge">27 cases</span></div> | |
| <div class="nav-item" data-section="wv" tabindex="0" role="button" aria-pressed="false">WV <span class="info-badge">27 cases</span></div> | |
| <div class="nav-item" data-section="sbls" tabindex="0" role="button" aria-pressed="false">SBLS <span class="info-badge">65 cases</span></div> | |
| <div class="nav-item" data-section="zbll" tabindex="0" role="button" aria-pressed="false">ZBLL <span class="info-badge">subset</span></div> | |
| <hr style="border-color: #333; margin: 20px 0;"> | |
| <h3>ℹ️ Tips</h3> | |
| <p style="font-size:0.85rem; color:#888; line-height:1.5;"> | |
| • Click algorithm: auto setup & solve<br> | |
| • "Step by Step": one move at a time<br> | |
| • Space/Arrow to advance steps<br> | |
| • Drag to rotate camera<br> | |
| • Scroll to zoom<br> | |
| • Keyboard: R/U/F/L/D/B (Shift=inverse) | |
| </p> | |
| <div style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #333; font-size: 0.8rem; color: #666; text-align: center;"> | |
| Created by Andy Kong | |
| </div> | |
| <div style="margin-top: 10px; text-align: center;"> | |
| <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> | |
| </div> | |
| </nav> | |
| <div class="main-content"> | |
| <div class="cube-section" id="cube-section"> | |
| <button class="fullscreen-btn" id="fullscreen-btn" onclick="toggleFullscreen()" title="Full screen 3D view">⛶ Full</button> | |
| <button class="popout-btn" id="popout-btn" onclick="toggleCollapse()" title="Collapse 3D view for more algorithm space">▲ Hide</button> | |
| <div class="scramble-display" id="scramble-display">Drag to rotate • Scroll to zoom</div> | |
| <canvas id="cube-canvas"></canvas> | |
| <div class="controls"> | |
| <button class="control-btn" style="background:#333;border:1px solid #4fc3f7;color:#4fc3f7" onclick="toggleMoveButtons()" id="toggle-moves-btn">Moves ▼</button> | |
| <button class="control-btn" style="background:#e94560" onclick="scrambleCube()" aria-label="Scramble cube">Scramble</button> | |
| <button class="control-btn" style="background:#2ecc71" onclick="resetCube()" aria-label="Reset cube">Reset</button> | |
| </div> | |
| <div class="controls move-buttons" id="move-buttons" style="bottom: 60px; display: none;"> | |
| <button class="control-btn" onclick="doMove('R')">R</button> | |
| <button class="control-btn" onclick="doMove('Ri')">R'</button> | |
| <button class="control-btn" onclick="doMove('U')">U</button> | |
| <button class="control-btn" onclick="doMove('Ui')">U'</button> | |
| <button class="control-btn" onclick="doMove('F')">F</button> | |
| <button class="control-btn" onclick="doMove('Fi')">F'</button> | |
| <button class="control-btn" onclick="doMove('L')">L</button> | |
| <button class="control-btn" onclick="doMove('Li')">L'</button> | |
| <button class="control-btn" onclick="doMove('D')">D</button> | |
| <button class="control-btn" onclick="doMove('Di')">D'</button> | |
| <button class="control-btn" onclick="doMove('B')">B</button> | |
| <button class="control-btn" onclick="doMove('Bi')">B'</button> | |
| <button class="control-btn" onclick="doMove('r')">r</button> | |
| <button class="control-btn" onclick="doMove('ri')">r'</button> | |
| <button class="control-btn" onclick="doMove('u')">u</button> | |
| <button class="control-btn" onclick="doMove('ui')">u'</button> | |
| <button class="control-btn" onclick="doMove('l')">l</button> | |
| <button class="control-btn" onclick="doMove('li')">l'</button> | |
| <button class="control-btn" onclick="doMove('x')">x</button> | |
| <button class="control-btn" onclick="doMove('xi')">x'</button> | |
| <button class="control-btn" onclick="doMove('y')">y</button> | |
| <button class="control-btn" onclick="doMove('yi')">y'</button> | |
| <button class="control-btn" onclick="doMove('z')">z</button> | |
| <button class="control-btn" onclick="doMove('zi')">z'</button> | |
| </div> | |
| </div> | |
| <div class="algorithm-section" id="algorithm-section"> | |
| <!-- Algorithms rendered by JS --> | |
| </div> | |
| </div> | |
| </div> | |
| <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;"> | |
| <div style="background:#16213e; border:1px solid #4fc3f7; border-radius:12px; padding:30px; max-width:600px; width:90%; max-height:80vh; overflow-y:auto;"> | |
| <h2 style="color:#e94560; margin-bottom:15px;">About This App</h2> | |
| <p style="color:#ccc; line-height:1.7; margin-bottom:15px;"> | |
| 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. | |
| </p> | |
| <h3 style="color:#4fc3f7; margin-bottom:10px;">Algorithm Sources</h3> | |
| <ul style="color:#aaa; line-height:2; list-style:none; padding:0;"> | |
| <li>• <a href="https://www.speedsolving.com/wiki/index.php/First_Two_Layers" target="_blank" style="color:#4fc3f7;">Speedsolving.com Wiki - F2L Algorithms</a></li> | |
| <li>• <a href="https://cubeskills.com/uploads/pdf/tutorials/onehanded-oll-algorithms.pdf" target="_blank" style="color:#4fc3f7;">CubeSkills - OH OLL Algorithms (PDF)</a></li> | |
| <li>• <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> | |
| <li>• <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> | |
| <li>• <a href="https://cubingapp.com/algorithms/ZBLL" target="_blank" style="color:#4fc3f7;">CubingApp - ZBLL Algorithms</a></li> | |
| <li>• <a href="https://speedcubedb.com/a/3x3/SV" target="_blank" style="color:#4fc3f7;">SpeedCubeDB - Summer Variation (SV)</a></li> | |
| <li>• <a href="https://speedcubedb.com/a/3x3/WV" target="_blank" style="color:#4fc3f7;">SpeedCubeDB - Winter Variation (WV)</a></li> | |
| <li>• <a href="https://speedcubedb.com/a/3x3/SBLS" target="_blank" style="color:#4fc3f7;">SpeedCubeDB - Second Block Last Slot (SBLS)</a></li> | |
| </ul> | |
| <p style="color:#888; margin-top:15px; font-size:0.85rem;"> | |
| 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. | |
| </p> | |
| <div style="text-align:center; margin-top:20px;"> | |
| <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> | |
| </div> | |
| </div> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script> | |
| <script src="app.js"></script> | |
| <script src="json/f2l.json.js"></script> | |
| <script src="json/oll.json.js"></script> | |
| <script src="json/pll.json.js"></script> | |
| <script src="json/coll.json.js"></script> | |
| <script src="json/sv.json.js"></script> | |
| <script src="json/wv.json.js"></script> | |
| <script src="json/sbls.json.js"></script> | |
| <script src="json/zbll.json.js"></script> | |
| <script> | |
| // OH_ALGORITHMS is assembled from the .json.js files above | |
| document.addEventListener('DOMContentLoaded', () => { | |
| init(); | |
| setupNavigation(); | |
| renderAlgorithms('f2l'); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |