import { useState, useEffect } from 'react'; import { RotateCw, HelpCircle, Volume2, VolumeX, RefreshCw, Trophy, Activity, Compass, ArrowRight } from 'lucide-react'; import { sounds } from './audio'; const R = 95; // Radius of hexagons const dx = Math.sqrt(3) * R; // ~164.54 const dy = 1.5 * R; // 142.5 const POSITIONS = [ { id: 0, label: "Center", x: 0, y: 0, color: "from-stone-800 to-amber-950/40" }, { id: 1, label: "Top-Right", x: dx / 2, y: -dy, color: "from-stone-800 to-emerald-950/30" }, { id: 2, label: "Right", x: dx, y: 0, color: "from-stone-800 to-blue-950/30" }, { id: 3, label: "Bottom-Right", x: dx / 2, y: dy, color: "from-stone-800 to-purple-950/30" }, { id: 4, label: "Bottom-Left", x: -dx / 2, y: dy, color: "from-stone-800 to-teal-950/30" }, { id: 5, label: "Left", x: -dx, y: 0, color: "from-stone-800 to-orange-950/30" }, { id: 6, label: "Top-Left", x: -dx / 2, y: -dy, color: "from-stone-800 to-red-950/30" } ]; const DIALS = [ { id: "left", name: "Left Dial", x: -dx / 2, y: -dy / 3, positions: [0, 5, 6] // Center, Left, Top-Left }, { id: "top-right", name: "Top-Right Dial", x: dx / 2, y: -dy / 3, positions: [0, 1, 2] // Center, Top-Right, Right }, { id: "bottom-right", name: "Bottom-Right Dial", x: 0, y: 2 * dy / 3, positions: [0, 3, 4] // Center, Bottom-Right, Bottom-Left } ]; // Symbols removed to focus purely on the coin segments alignment. export default function App() { const [pieces, setPieces] = useState([ { id: 0, currentPos: 0, visualRotation: 0 }, { id: 1, currentPos: 1, visualRotation: 0 }, { id: 2, currentPos: 2, visualRotation: 0 }, { id: 3, currentPos: 3, visualRotation: 0 }, { id: 4, currentPos: 4, visualRotation: 0 }, { id: 5, currentPos: 5, visualRotation: 0 }, { id: 6, currentPos: 6, visualRotation: 0 } ]); const [moves, setMoves] = useState(0); const [won, setWon] = useState(false); const [muted, setMuted] = useState(false); const [showInfo, setShowInfo] = useState(false); const [scrambling, setScrambling] = useState(false); // Initialize and scramble on startup useEffect(() => { // Small delay to let user load the page, then scramble const timer = setTimeout(() => { handleScramble(true); }, 500); return () => clearTimeout(timer); }, []); // Check win state useEffect(() => { const isWin = pieces.every(p => p.id === p.currentPos && (p.visualRotation % 360) === 0); if (isWin && moves > 0 && !won) { setWon(true); if (!muted) { sounds.playVictory(); } } }, [pieces, moves, won, muted]); // Toggle Mute const toggleMute = () => { setMuted(!muted); // Init Audio Context on interaction sounds.init(); }; // Perform Scramble by executing sequential random dial rotations const handleScramble = (isInitial = false) => { if (scrambling) return; setWon(false); setScrambling(true); const dialIds = ["left", "top-right", "bottom-right"]; const steps = 10 + Math.floor(Math.random() * 6); // 10 to 15 random dial rotations let currentStep = 0; const interval = setInterval(() => { const dialId = dialIds[Math.floor(Math.random() * 3)]; if (!muted) { sounds.playClick(); } setPieces(prevPieces => { const dial = DIALS.find(d => d.id === dialId); const pos = dial.positions; const p0 = prevPieces.find(p => p.currentPos === pos[0]); const p1 = prevPieces.find(p => p.currentPos === pos[1]); const p2 = prevPieces.find(p => p.currentPos === pos[2]); return prevPieces.map(p => { if (p.id === p0.id) return { ...p, currentPos: pos[1], visualRotation: p.visualRotation + 120 }; if (p.id === p1.id) return { ...p, currentPos: pos[2], visualRotation: p.visualRotation + 120 }; if (p.id === p2.id) return { ...p, currentPos: pos[0], visualRotation: p.visualRotation + 120 }; return p; }); }); currentStep++; if (currentStep >= steps) { clearInterval(interval); setScrambling(false); setMoves(0); // Reset move count after scrambling completes } }, isInitial ? 60 : 250); }; // Rotate a Dial Clockwise const handleRotateDial = (dialId) => { if (won || scrambling) return; if (!muted) { sounds.playClick(); } const dial = DIALS.find(d => d.id === dialId); const pos = dial.positions; // e.g. [0, 5, 6] setPieces(prevPieces => { // Find the pieces currently at pos[0], pos[1], pos[2] const p0 = prevPieces.find(p => p.currentPos === pos[0]); const p1 = prevPieces.find(p => p.currentPos === pos[1]); const p2 = prevPieces.find(p => p.currentPos === pos[2]); return prevPieces.map(p => { if (p.id === p0.id) { return { ...p, currentPos: pos[1], visualRotation: p.visualRotation + 120 }; } if (p.id === p1.id) { return { ...p, currentPos: pos[2], visualRotation: p.visualRotation + 120 }; } if (p.id === p2.id) { return { ...p, currentPos: pos[0], visualRotation: p.visualRotation + 120 }; } return p; }); }); setMoves(m => m + 1); }; // Reset to solved state const handleReset = () => { if (!muted) { sounds.playReset(); } setPieces([ { id: 0, currentPos: 0, visualRotation: 0 }, { id: 1, currentPos: 1, visualRotation: 0 }, { id: 2, currentPos: 2, visualRotation: 0 }, { id: 3, currentPos: 3, visualRotation: 0 }, { id: 4, currentPos: 4, visualRotation: 0 }, { id: 5, currentPos: 5, visualRotation: 0 }, { id: 6, currentPos: 6, visualRotation: 0 } ]); setMoves(0); setWon(false); }; return (
{/* Decorative corners */}
{/* Header bar */}

HEXAGON PEDESTAL

Resident Evil 4 Inspired Remake

{/* Info panel */} {showInfo && (

PUZZLE INSTRUCTIONS

  • 1. The puzzle has 7 hexagonal pieces: 1 Center and 6 Outer.
  • 2. Solve the puzzle by rotating the 3 Dials located at the intersection points.
  • 3. Each Dial rotates its 3 adjacent pieces 120° Clockwise.
  • 4. Victory is achieved when all runic lines align perfectly with their original positions and visual rotations.
)} {/* Main Board Container */}
{/* Stone Pedestal Frame */}
{/* Inner Pedestal Details */}
{/* Subtle outer bronze ring guide */}
{/* Hexagons wrapper */}
{pieces.map(piece => { const posConfig = POSITIONS.find(p => p.id === piece.currentPos); const isCorrect = piece.id === piece.currentPos && (piece.visualRotation % 360) === 0; return (
{/* Hexagon Shape Container */}
{/* Dark overlay to make the gold runes pop and look atmospheric */}
{/* Runic Pattern Overlay */} {/* Shimmer background on correct */} {isCorrect && ( )} {/* Outer frame border */}
); })} {/* Dial Interactive Controllers */} {DIALS.map(dial => ( ))}
{/* Victory Overlay */} {won && (

Pedestal Solved

The stone lock opens with {moves} moves

)}
{/* Footer controls & Move indicators */}
); }