"use client"; import { useState, useEffect, useCallback } from "react"; import { motion } from "framer-motion"; import { Moon, Sun, Star, Rocket, Sparkles, Orbit, Satellite, Eclipse, Brain, } from "lucide-react"; import { MiniGameResultsScreen } from "@/components/game/MiniGameResultsScreen"; import { MiniGameHeader } from "@/components/game/MiniGameHeader"; import { soundManager } from "@/lib/sound/manager"; import { fireCelebration } from "@/lib/confetti"; const CARD_ICONS = [Moon, Sun, Star, Rocket, Sparkles, Orbit, Satellite, Eclipse]; function nowMs(): number { return Date.now(); } type CardItem = { id: number; iconId: number; Icon: React.ComponentType<{ size?: number; className?: string }>; isFlipped: boolean; isMatched: boolean; }; function shuffleArray(arr: T[]): T[] { const shuffled = [...arr]; for (let i = shuffled.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } return shuffled; } function createCards(): CardItem[] { const pairs: CardItem[] = []; CARD_ICONS.forEach((Icon, index) => { pairs.push({ id: index * 2, iconId: index, Icon, isFlipped: false, isMatched: false }); pairs.push({ id: index * 2 + 1, iconId: index, Icon, isFlipped: false, isMatched: false }); }); return shuffleArray(pairs); } export default function MemoryGame() { const [cards, setCards] = useState([]); const [moves, setMoves] = useState(0); const [flippedIndices, setFlippedIndices] = useState([]); const [isLocked, setIsLocked] = useState(false); const [isWon, setIsWon] = useState(false); const [timeStart, setTimeStart] = useState(null); const [timeElapsed, setTimeElapsed] = useState(0); const [isStarted, setIsStarted] = useState(false); const initGame = useCallback(() => { setCards(createCards()); setMoves(0); setFlippedIndices([]); setIsLocked(false); setIsWon(false); setTimeStart(null); setTimeElapsed(0); setIsStarted(false); }, []); // Initialize on mount useEffect(() => { const timer = setTimeout(() => { initGame(); }, 0); return () => clearTimeout(timer); }, [initGame]); // Timer useEffect(() => { if (!timeStart || isWon) return; const interval = setInterval(() => { setTimeElapsed(Math.floor((Date.now() - timeStart) / 1000)); }, 1000); return () => clearInterval(interval); }, [timeStart, isWon]); const handleCardClick = (index: number) => { if (isLocked || isWon) return; const card = cards[index]; if (card.isFlipped || card.isMatched) return; // Start timer on first card flip if (!timeStart) { setTimeStart(nowMs()); setIsStarted(true); } soundManager.play("click"); const newCards = [...cards]; newCards[index] = { ...newCards[index], isFlipped: true }; setCards(newCards); const newFlipped = [...flippedIndices, index]; setFlippedIndices(newFlipped); if (newFlipped.length === 2) { setMoves((prev) => prev + 1); const [first, second] = newFlipped; if (newCards[first].iconId === newCards[second].iconId) { // Match found soundManager.play("correct"); const matched = newCards.map((c) => c.iconId === newCards[first].iconId ? { ...c, isMatched: true } : c, ); setCards(matched); setFlippedIndices([]); // Check win condition if (matched.every((c) => c.isMatched)) { setIsWon(true); fireCelebration(); soundManager.play("victory"); } } else { // No match -- flip back after a short delay soundManager.play("wrong"); setIsLocked(true); setTimeout(() => { const flippedBack = newCards.map((c, i) => i === first || i === second ? { ...c, isFlipped: false } : c, ); setCards(flippedBack); setFlippedIndices([]); setIsLocked(false); }, 800); } } }; const matchedCount = cards.filter((c) => c.isMatched).length / 2; const formatTime = (seconds: number) => { const m = Math.floor(seconds / 60); const s = seconds % 60; return `${m}:${s.toString().padStart(2, "0")}`; }; if (isWon) { return ( ); } return (
{!isStarted && cards.length > 0 && (

Tap any card to begin. Match all pairs in as few moves as possible.

)}
{cards.map((card, index) => { const isRevealed = card.isFlipped || card.isMatched; return (
handleCardClick(index)} className="relative cursor-pointer aspect-square" style={{ perspective: "1000px" }} > {/* Card Back */}
{/* Card Front */}
); })}
); }