Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Rubik's Cube Solver</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/cubing@1/dist/cubing.bundle.js"></script> | |
| <style> | |
| .cube-face { | |
| width: 100px; | |
| height: 100px; | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| grid-template-rows: repeat(3, 1fr); | |
| gap: 2px; | |
| padding: 5px; | |
| background-color: #333; | |
| border-radius: 5px; | |
| } | |
| .sticker { | |
| border-radius: 3px; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| font-weight: bold; | |
| color: rgba(0,0,0,0.5); | |
| } | |
| .color-picker { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 20px; | |
| } | |
| .color-option { | |
| width: 30px; | |
| height: 30px; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| border: 2px solid transparent; | |
| } | |
| .color-option.selected { | |
| border-color: black; | |
| } | |
| .move-btn { | |
| transition: all 0.2s; | |
| } | |
| .move-btn:hover { | |
| transform: scale(1.1); | |
| } | |
| .cube-container { | |
| perspective: 1000px; | |
| margin: 20px auto; | |
| } | |
| #cube3d { | |
| width: 300px; | |
| height: 300px; | |
| margin: 0 auto; | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| 100% { transform: scale(1); } | |
| } | |
| .pulse { | |
| animation: pulse 1.5s infinite; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <h1 class="text-4xl font-bold text-center text-gray-800 mb-2">Rubik's Cube Solver</h1> | |
| <p class="text-center text-gray-600 mb-8">Input your scrambled cube and get a solution in 25 moves or fewer</p> | |
| <div class="flex flex-col lg:flex-row gap-8"> | |
| <!-- Cube Input Section --> | |
| <div class="bg-white p-6 rounded-lg shadow-lg flex-1"> | |
| <h2 class="text-2xl font-semibold mb-4 text-gray-700">Input Cube State</h2> | |
| <div class="mb-6"> | |
| <h3 class="text-lg font-medium mb-2">Color Picker</h3> | |
| <div class="color-picker"> | |
| <div class="color-option bg-white selected" data-color="white" title="White"></div> | |
| <div class="color-option bg-yellow-400" data-color="yellow" title="Yellow"></div> | |
| <div class="color-option bg-red-500" data-color="red" title="Red"></div> | |
| <div class="color-option bg-orange-500" data-color="orange" title="Orange"></div> | |
| <div class="color-option bg-blue-600" data-color="blue" title="Blue"></div> | |
| <div class="color-option bg-green-600" data-color="green" title="Green"></div> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-3 gap-4 mb-6"> | |
| <!-- Up Face --> | |
| <div> | |
| <h4 class="text-center mb-1">Up (U)</h4> | |
| <div class="cube-face" id="up-face"> | |
| <div class="sticker bg-gray-200" data-face="U" data-position="0"></div> | |
| <div class="sticker bg-gray-200" data-face="U" data-position="1"></div> | |
| <div class="sticker bg-gray-200" data-face="U" data-position="2"></div> | |
| <div class="sticker bg-gray-200" data-face="U" data-position="3"></div> | |
| <div class="sticker bg-yellow-400" data-face="U" data-position="4"></div> | |
| <div class="sticker bg-gray-200" data-face="U" data-position="5"></div> | |
| <div class="sticker bg-gray-200" data-face="U" data-position="6"></div> | |
| <div class="sticker bg-gray-200" data-face="U" data-position="7"></div> | |
| <div class="sticker bg-gray-200" data-face="U" data-position="8"></div> | |
| </div> | |
| </div> | |
| <!-- Left, Front, Right, Back Faces --> | |
| <div class="space-y-4"> | |
| <div> | |
| <h4 class="text-center mb-1">Left (L)</h4> | |
| <div class="cube-face" id="left-face"> | |
| <div class="sticker bg-gray-200" data-face="L" data-position="0"></div> | |
| <div class="sticker bg-gray-200" data-face="L" data-position="1"></div> | |
| <div class="sticker bg-gray-200" data-face="L" data-position="2"></div> | |
| <div class="sticker bg-gray-200" data-face="L" data-position="3"></div> | |
| <div class="sticker bg-orange-500" data-face="L" data-position="4"></div> | |
| <div class="sticker bg-gray-200" data-face="L" data-position="5"></div> | |
| <div class="sticker bg-gray-200" data-face="L" data-position="6"></div> | |
| <div class="sticker bg-gray-200" data-face="L" data-position="7"></div> | |
| <div class="sticker bg-gray-200" data-face="L" data-position="8"></div> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 class="text-center mb-1">Front (F)</h4> | |
| <div class="cube-face" id="front-face"> | |
| <div class="sticker bg-gray-200" data-face="F" data-position="0"></div> | |
| <div class="sticker bg-gray-200" data-face="F" data-position="1"></div> | |
| <div class="sticker bg-gray-200" data-face="F" data-position="2"></div> | |
| <div class="sticker bg-gray-200" data-face="F" data-position="3"></div> | |
| <div class="sticker bg-green-600" data-face="F" data-position="4"></div> | |
| <div class="sticker bg-gray-200" data-face="F" data-position="5"></div> | |
| <div class="sticker bg-gray-200" data-face="F" data-position="6"></div> | |
| <div class="sticker bg-gray-200" data-face="F" data-position="7"></div> | |
| <div class="sticker bg-gray-200" data-face="F" data-position="8"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <h4 class="text-center mb-1">Right (R)</h4> | |
| <div class="cube-face" id="right-face"> | |
| <div class="sticker bg-gray-200" data-face="R" data-position="0"></div> | |
| <div class="sticker bg-gray-200" data-face="R" data-position="1"></div> | |
| <div class="sticker bg-gray-200" data-face="R" data-position="2"></div> | |
| <div class="sticker bg-gray-200" data-face="R" data-position="3"></div> | |
| <div class="sticker bg-red-500" data-face="R" data-position="4"></div> | |
| <div class="sticker bg-gray-200" data-face="R" data-position="5"></div> | |
| <div class="sticker bg-gray-200" data-face="R" data-position="6"></div> | |
| <div class="sticker bg-gray-200" data-face="R" data-position="7"></div> | |
| <div class="sticker bg-gray-200" data-face="R" data-position="8"></div> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 class="text-center mb-1">Back (B)</h4> | |
| <div class="cube-face" id="back-face"> | |
| <div class="sticker bg-gray-200" data-face="B" data-position="0"></div> | |
| <div class="sticker bg-gray-200" data-face="B" data-position="1"></div> | |
| <div class="sticker bg-gray-200" data-face="B" data-position="2"></div> | |
| <div class="sticker bg-gray-200" data-face="B" data-position="3"></div> | |
| <div class="sticker bg-blue-600" data-face="B" data-position="4"></div> | |
| <div class="sticker bg-gray-200" data-face="B" data-position="5"></div> | |
| <div class="sticker bg-gray-200" data-face="B" data-position="6"></div> | |
| <div class="sticker bg-gray-200" data-face="B" data-position="7"></div> | |
| <div class="sticker bg-gray-200" data-face="B" data-position="8"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Down Face --> | |
| <div class="flex justify-center mb-6"> | |
| <div> | |
| <h4 class="text-center mb-1">Down (D)</h4> | |
| <div class="cube-face" id="down-face"> | |
| <div class="sticker bg-gray-200" data-face="D" data-position="0"></div> | |
| <div class="sticker bg-gray-200" data-face="D" data-position="1"></div> | |
| <div class="sticker bg-gray-200" data-face="D" data-position="2"></div> | |
| <div class="sticker bg-gray-200" data-face="D" data-position="3"></div> | |
| <div class="sticker bg-white" data-face="D" data-position="4"></div> | |
| <div class="sticker bg-gray-200" data-face="D" data-position="5"></div> | |
| <div class="sticker bg-gray-200" data-face="D" data-position="6"></div> | |
| <div class="sticker bg-gray-200" data-face="D" data-position="7"></div> | |
| <div class="sticker bg-gray-200" data-face="D" data-position="8"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex flex-col sm:flex-row gap-4 mb-6"> | |
| <button id="randomize-btn" class="bg-purple-600 hover:bg-purple-700 text-white py-2 px-4 rounded-lg transition"> | |
| Randomize Cube | |
| </button> | |
| <button id="reset-btn" class="bg-gray-600 hover:bg-gray-700 text-white py-2 px-4 rounded-lg transition"> | |
| Reset to Solved | |
| </button> | |
| <button id="solve-btn" class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg font-bold pulse"> | |
| Solve Cube | |
| </button> | |
| </div> | |
| <div class="mb-6"> | |
| <h3 class="text-lg font-medium mb-2">Or input scramble notation:</h3> | |
| <div class="flex gap-2"> | |
| <input type="text" id="scramble-input" placeholder="e.g., R U R' U' F2 D B'" class="flex-1 border border-gray-300 rounded-lg px-4 py-2"> | |
| <button id="apply-scramble-btn" class="bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-lg"> | |
| Apply | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Solution Section --> | |
| <div class="bg-white p-6 rounded-lg shadow-lg flex-1"> | |
| <h2 class="text-2xl font-semibold mb-4 text-gray-700">Solution</h2> | |
| <div class="mb-6"> | |
| <h3 class="text-lg font-medium mb-2">3D Cube Visualization</h3> | |
| <div id="cube3d"></div> | |
| </div> | |
| <div class="mb-6"> | |
| <h3 class="text-lg font-medium mb-2">Solution Moves</h3> | |
| <div id="solution-moves" class="bg-gray-100 p-4 rounded-lg min-h-20"> | |
| <p class="text-gray-500 italic">Enter your cube state and click "Solve Cube" to get a solution.</p> | |
| </div> | |
| </div> | |
| <div class="mb-6"> | |
| <h3 class="text-lg font-medium mb-2">Solution Controls</h3> | |
| <div class="flex flex-wrap gap-2" id="move-buttons"> | |
| <!-- Move buttons will be generated here --> | |
| </div> | |
| </div> | |
| <div class="flex gap-4"> | |
| <button id="prev-move-btn" class="bg-gray-600 hover:bg-gray-700 text-white py-2 px-4 rounded-lg transition opacity-50 cursor-not-allowed" disabled> | |
| Previous Move | |
| </button> | |
| <button id="next-move-btn" class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg transition opacity-50 cursor-not-allowed" disabled> | |
| Next Move | |
| </button> | |
| <button id="animate-solution-btn" class="bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-lg transition opacity-50 cursor-not-allowed" disabled> | |
| Animate Solution | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Cube state representation | |
| const cubeState = { | |
| U: Array(9).fill('yellow'), | |
| L: Array(9).fill('orange'), | |
| F: Array(9).fill('green'), | |
| R: Array(9).fill('red'), | |
| B: Array(9).fill('blue'), | |
| D: Array(9).fill('white') | |
| }; | |
| // Color mapping | |
| const colorMap = { | |
| white: 'white', | |
| yellow: 'yellow-400', | |
| red: 'red-500', | |
| orange: 'orange-500', | |
| blue: 'blue-600', | |
| green: 'green-600' | |
| }; | |
| // Current selected color | |
| let selectedColor = 'white'; | |
| // Solution moves | |
| let solutionMoves = []; | |
| let currentMoveIndex = -1; | |
| // 3D Cube visualization | |
| let cube3d; | |
| // Initialize the app | |
| initColorPicker(); | |
| initStickerClickHandlers(); | |
| initButtons(); | |
| updateCubeVisualization(); | |
| init3DCube(); | |
| // Initialize color picker | |
| function initColorPicker() { | |
| const colorOptions = document.querySelectorAll('.color-option'); | |
| colorOptions.forEach(option => { | |
| option.addEventListener('click', function() { | |
| // Remove selected class from all options | |
| colorOptions.forEach(opt => opt.classList.remove('selected')); | |
| // Add selected class to clicked option | |
| this.classList.add('selected'); | |
| // Update selected color | |
| selectedColor = this.getAttribute('data-color'); | |
| }); | |
| }); | |
| } | |
| // Initialize sticker click handlers | |
| function initStickerClickHandlers() { | |
| const stickers = document.querySelectorAll('.sticker'); | |
| stickers.forEach(sticker => { | |
| sticker.addEventListener('click', function() { | |
| const face = this.getAttribute('data-face'); | |
| const position = parseInt(this.getAttribute('data-position')); | |
| // Update cube state | |
| cubeState[face][position] = selectedColor; | |
| // Update sticker color | |
| this.className = `sticker bg-${colorMap[selectedColor]}`; | |
| // Update 3D cube | |
| update3DCube(); | |
| }); | |
| }); | |
| } | |
| // Initialize buttons | |
| function initButtons() { | |
| // Randomize button | |
| document.getElementById('randomize-btn').addEventListener('click', randomizeCube); | |
| // Reset button | |
| document.getElementById('reset-btn').addEventListener('click', resetCube); | |
| // Solve button | |
| document.getElementById('solve-btn').addEventListener('click', solveCube); | |
| // Apply scramble button | |
| document.getElementById('apply-scramble-btn').addEventListener('click', applyScramble); | |
| // Solution navigation buttons | |
| document.getElementById('prev-move-btn').addEventListener('click', prevMove); | |
| document.getElementById('next-move-btn').addEventListener('click', nextMove); | |
| document.getElementById('animate-solution-btn').addEventListener('click', animateSolution); | |
| } | |
| // Randomize the cube | |
| function randomizeCube() { | |
| const faces = ['U', 'L', 'F', 'R', 'B', 'D']; | |
| const colors = ['white', 'yellow', 'red', 'orange', 'blue', 'green']; | |
| faces.forEach(face => { | |
| for (let i = 0; i < 9; i++) { | |
| if (i === 4) continue; // Skip center pieces | |
| const randomColor = colors[Math.floor(Math.random() * colors.length)]; | |
| cubeState[face][i] = randomColor; | |
| } | |
| }); | |
| updateCubeVisualization(); | |
| update3DCube(); | |
| clearSolution(); | |
| } | |
| // Reset the cube to solved state | |
| function resetCube() { | |
| cubeState.U.fill('yellow'); | |
| cubeState.L.fill('orange'); | |
| cubeState.F.fill('green'); | |
| cubeState.R.fill('red'); | |
| cubeState.B.fill('blue'); | |
| cubeState.D.fill('white'); | |
| updateCubeVisualization(); | |
| update3DCube(); | |
| clearSolution(); | |
| } | |
| // Update the 2D cube visualization | |
| function updateCubeVisualization() { | |
| const faces = ['U', 'L', 'F', 'R', 'B', 'D']; | |
| faces.forEach(face => { | |
| const faceElement = document.getElementById(`${face.toLowerCase()}-face`); | |
| const stickers = faceElement.querySelectorAll('.sticker'); | |
| stickers.forEach((sticker, index) => { | |
| const color = cubeState[face][index]; | |
| sticker.className = `sticker bg-${colorMap[color]}`; | |
| }); | |
| }); | |
| } | |
| // Solve the cube (mock solution for frontend demo) | |
| function solveCube() { | |
| // In a real implementation, this would call a backend API with the cube state | |
| // For this demo, we'll generate a mock solution | |
| // Generate a random solution (10-20 moves) | |
| const moveTypes = ['R', 'L', 'U', 'D', 'F', 'B']; | |
| const modifiers = ['', "'", '2']; | |
| const moveCount = 10 + Math.floor(Math.random() * 11); | |
| solutionMoves = []; | |
| for (let i = 0; i < moveCount; i++) { | |
| const move = moveTypes[Math.floor(Math.random() * moveTypes.length)]; | |
| const modifier = modifiers[Math.floor(Math.random() * modifiers.length)]; | |
| solutionMoves.push(move + modifier); | |
| } | |
| // Display solution | |
| displaySolution(); | |
| // Enable solution controls | |
| document.getElementById('prev-move-btn').classList.remove('opacity-50', 'cursor-not-allowed'); | |
| document.getElementById('prev-move-btn').disabled = false; | |
| document.getElementById('next-move-btn').classList.remove('opacity-50', 'cursor-not-allowed'); | |
| document.getElementById('next-move-btn').disabled = false; | |
| document.getElementById('animate-solution-btn').classList.remove('opacity-50', 'cursor-not-allowed'); | |
| document.getElementById('animate-solution-btn').disabled = false; | |
| // Reset move index | |
| currentMoveIndex = -1; | |
| // Generate move buttons | |
| generateMoveButtons(); | |
| } | |
| // Display the solution | |
| function displaySolution() { | |
| const solutionElement = document.getElementById('solution-moves'); | |
| if (solutionMoves.length === 0) { | |
| solutionElement.innerHTML = '<p class="text-gray-500 italic">No solution found or cube is already solved.</p>'; | |
| return; | |
| } | |
| solutionElement.innerHTML = ` | |
| <p class="text-lg font-semibold mb-2">Solution (${solutionMoves.length} moves):</p> | |
| <div class="flex flex-wrap gap-2"> | |
| ${solutionMoves.map((move, index) => | |
| `<span class="move-tag px-3 py-1 rounded-full ${index === currentMoveIndex ? 'bg-blue-100 text-blue-800 border border-blue-300' : 'bg-gray-200 text-gray-800'}">${move}</span>` | |
| ).join('')} | |
| </div> | |
| `; | |
| } | |
| // Generate move buttons | |
| function generateMoveButtons() { | |
| const moveButtonsContainer = document.getElementById('move-buttons'); | |
| moveButtonsContainer.innerHTML = ''; | |
| solutionMoves.forEach((move, index) => { | |
| const button = document.createElement('button'); | |
| button.textContent = move; | |
| button.className = 'move-btn bg-gray-200 hover:bg-gray-300 text-gray-800 py-1 px-3 rounded-lg'; | |
| button.addEventListener('click', () => { | |
| currentMoveIndex = index; | |
| updateSolutionDisplay(); | |
| // In a real implementation, we would update the cube visualization to this move | |
| }); | |
| moveButtonsContainer.appendChild(button); | |
| }); | |
| } | |
| // Clear the solution | |
| function clearSolution() { | |
| solutionMoves = []; | |
| currentMoveIndex = -1; | |
| displaySolution(); | |
| // Disable solution controls | |
| document.getElementById('prev-move-btn').classList.add('opacity-50', 'cursor-not-allowed'); | |
| document.getElementById('prev-move-btn').disabled = true; | |
| document.getElementById('next-move-btn').classList.add('opacity-50', 'cursor-not-allowed'); | |
| document.getElementById('next-move-btn').disabled = true; | |
| document.getElementById('animate-solution-btn').classList.add('opacity-50', 'cursor-not-allowed'); | |
| document.getElementById('animate-solution-btn').disabled = true; | |
| // Clear move buttons | |
| document.getElementById('move-buttons').innerHTML = ''; | |
| } | |
| // Apply scramble notation | |
| function applyScramble() { | |
| const scrambleInput = document.getElementById('scramble-input').value.trim(); | |
| if (!scrambleInput) return; | |
| // In a real implementation, this would parse the scramble and update the cube state | |
| // For this demo, we'll just show a message | |
| alert('Scramble applied! (This is a frontend demo - in a real app, this would update the cube state)'); | |
| // Clear any existing solution | |
| clearSolution(); | |
| } | |
| // Previous move | |
| function prevMove() { | |
| if (currentMoveIndex > -1) { | |
| currentMoveIndex--; | |
| updateSolutionDisplay(); | |
| // In a real implementation, we would update the cube visualization | |
| } | |
| } | |
| // Next move | |
| function nextMove() { | |
| if (currentMoveIndex < solutionMoves.length - 1) { | |
| currentMoveIndex++; | |
| updateSolutionDisplay(); | |
| // In a real implementation, we would update the cube visualization | |
| } | |
| } | |
| // Animate solution | |
| function animateSolution() { | |
| alert('Animation started! (This is a frontend demo - in a real app, this would animate the solution)'); | |
| } | |
| // Update solution display | |
| function updateSolutionDisplay() { | |
| const moveTags = document.querySelectorAll('.move-tag'); | |
| moveTags.forEach((tag, index) => { | |
| if (index === currentMoveIndex) { | |
| tag.classList.add('bg-blue-100', 'text-blue-800', 'border', 'border-blue-300'); | |
| tag.classList.remove('bg-gray-200', 'text-gray-800'); | |
| } else { | |
| tag.classList.remove('bg-blue-100', 'text-blue-800', 'border', 'border-blue-300'); | |
| tag.classList.add('bg-gray-200', 'text-gray-800'); | |
| } | |
| }); | |
| } | |
| // Initialize 3D cube | |
| function init3DCube() { | |
| const container = document.getElementById('cube3d'); | |
| // Create a mock 3D cube using a library like Three.js or cubing.js | |
| // For this demo, we'll just show a placeholder | |
| container.innerHTML = ` | |
| <div class="flex items-center justify-center h-full bg-gray-200 rounded-lg"> | |
| <p class="text-gray-500">3D Cube Visualization</p> | |
| </div> | |
| `; | |
| // In a real implementation, we would use Three.js or similar to render an interactive 3D cube | |
| // For example: | |
| /* | |
| const scene = new THREE.Scene(); | |
| const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000); | |
| const renderer = new THREE.WebGLRenderer(); | |
| renderer.setSize(300, 300); | |
| container.appendChild(renderer.domElement); | |
| // Add cube geometry, materials, etc. | |
| // ... | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| renderer.render(scene, camera); | |
| } | |
| animate(); | |
| */ | |
| } | |
| // Update 3D cube | |
| function update3DCube() { | |
| // In a real implementation, this would update the 3D cube visualization | |
| // based on the current cubeState | |
| } | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Fdgg55/cube-solver" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |