cube-solver / index.html
Fdgg55's picture
Add 3 files
4c7b4df verified
Raw
History Blame Contribute Delete
29.2 kB
<!DOCTYPE html>
<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>