import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Button } from '../components/ui/Button'; import { ArrowLeft, RefreshCw, Trophy, Zap, Check, X, Timer, Activity, Flame, RotateCcw, LogOut, Award, MousePointer2, Palette } from 'lucide-react'; import { StorageService } from '../services/storageService'; import { getRandomChallenge, Challenge } from './mentalAgilityLogic'; interface GameProps { onExit: () => void; } export const MentalAgility: React.FC = ({ onExit }) => { const [gameState, setGameState] = useState<'START' | 'PLAY' | 'OVER'>('START'); const [score, setScore] = useState(0); const [streak, setStreak] = useState(0); const [timeLeft, setTimeLeft] = useState(60); // Total game time const [currentChallenge, setCurrentChallenge] = useState(null); const [highScore, setHighScore] = useState(0); const [scorePulse, setScorePulse] = useState(false); const [feedback, setFeedback] = useState<'correct' | 'wrong' | null>(null); const [shake, setShake] = useState(false); // Stats Tracking const [correctAnswers, setCorrectAnswers] = useState(0); const [totalAttempts, setTotalAttempts] = useState(0); const startTimeRef = useRef(0); const endTimeRef = useRef(0); // Difficulty States const [wordRotation, setWordRotation] = useState(0); const [wordBlur, setWordBlur] = useState(false); // Timer ref for the main countdown const timerRef = useRef(null); useEffect(() => { StorageService.getProfile().then(p => { if (p?.highScores?.['flexibility' as any]) setHighScore(p.highScores['flexibility' as any]); }); }, []); // Blur clearing effect useEffect(() => { if (wordBlur) { const t = setTimeout(() => setWordBlur(false), 50); return () => clearTimeout(t); } }, [currentChallenge]); const playTone = (freq: number, type: OscillatorType = 'sine') => { try { const ctx = new (window.AudioContext || (window as any).webkitAudioContext)(); const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = type; osc.frequency.setValueAtTime(freq, ctx.currentTime); gain.gain.setValueAtTime(0.1, ctx.currentTime); gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.1); osc.connect(gain); gain.connect(ctx.destination); osc.start(); osc.stop(ctx.currentTime + 0.1); } catch (e) {} }; const startGame = () => { setScore(0); setStreak(0); setCorrectAnswers(0); setTotalAttempts(0); setTimeLeft(60); setGameState('PLAY'); setFeedback(null); setWordRotation(0); setWordBlur(false); setCurrentChallenge(getRandomChallenge()); startTimeRef.current = Date.now(); }; // Level based on score (0-999: Lvl 0, 1000-1999: Lvl 1, etc.) const level = Math.floor(score / 1000); // Main Game Timer with Dynamic Speed useEffect(() => { if (gameState === 'PLAY') { // Decrease interval time as level increases (faster ticks) // Level 0: 1000ms, Level 1: 900ms, ... Min: 200ms const intervalSpeed = Math.max(200, 1000 - (level * 100)); timerRef.current = setInterval(() => { setTimeLeft((prev) => { if (prev <= 1) { handleGameOver(); return 0; } return prev - 1; }); }, intervalSpeed); } return () => { if (timerRef.current) clearInterval(timerRef.current); }; }, [gameState, level]); const handleGameOver = async () => { endTimeRef.current = Date.now(); setGameState('OVER'); if (timerRef.current) clearInterval(timerRef.current); // Save Score if (score > highScore) { setHighScore(score); const p = await StorageService.getProfile(); if (p) { const newScores = { ...(p.highScores || {}), flexibility: score }; await StorageService.updateProfile({ ...p, highScores: newScores }); } } // Update addPoints with description await StorageService.addPoints(Math.floor(score / 5), score, 'game_reward', 'Mental Agility Training'); }; const handleInput = (userSaysMatch: boolean) => { if (gameState !== 'PLAY' || !currentChallenge) return; setTotalAttempts(prev => prev + 1); const isCorrect = userSaysMatch === currentChallenge.isMatch; let newScore = score; if (isCorrect) { setCorrectAnswers(prev => prev + 1); // Audio: High pitch 'blip' playTone(800, 'sine'); // Visual: Green Flash (100ms) setFeedback('correct'); setTimeout(() => setFeedback(null), 100); // Scoring: Streak Multiplier const newStreak = streak + 1; setStreak(newStreak); const multiplier = Math.floor(newStreak / 5) + 1; newScore = score + (100 * multiplier); setScore(newScore); setScorePulse(true); setTimeout(() => setScorePulse(false), 300); } else { // Audio: Low pitch buzz playTone(150, 'sawtooth'); // Visual: Red Flash & Shake setFeedback('wrong'); setShake(true); setTimeout(() => { setFeedback(null); setShake(false); }, 400); // Penalties setStreak(0); newScore = Math.max(0, score - 50); setScore(newScore); setTimeLeft(prev => Math.max(0, prev - 2)); } // Prepare next round visuals based on NEW score setTimeout(() => { const nextLevel = Math.floor(newScore / 1000); // Rotation (Level 1+) if (nextLevel >= 1) { setWordRotation(Math.random() * 30 - 15); // -15 to 15 degrees } else { setWordRotation(0); } // Blur (Level 2+, 30% chance) if (nextLevel >= 2 && Math.random() > 0.7) { setWordBlur(true); } else { setWordBlur(false); } setCurrentChallenge(getRandomChallenge()); }, 150); }; // Keyboard controls useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (gameState !== 'PLAY') return; if (e.key === 'ArrowLeft') handleInput(true); // Left for Match if (e.key === 'ArrowRight') handleInput(false); // Right for No Match }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [gameState, currentChallenge, score, streak]); // Dynamic Border Class based on feedback const borderClass = feedback === 'correct' ? 'border-[12px] border-green-500' : feedback === 'wrong' ? 'border-[12px] border-red-500' : 'border-0'; const multiplier = Math.floor(streak / 5) + 1; const tickDuration = Math.max(200, 1000 - (level * 100)); // Sync bar transition with tick rate // Calculate End Stats const calculateStats = () => { const duration = (endTimeRef.current - startTimeRef.current) / 1000; const accuracy = totalAttempts > 0 ? Math.round((correctAnswers / totalAttempts) * 100) : 0; const avgSpeed = correctAnswers > 0 ? (duration / correctAnswers).toFixed(2) : "0.00"; let rank = "Novice"; let rankColor = "text-gray-400"; if (score >= 5000) { rank = "Grandmaster"; rankColor = "text-purple-400"; } else if (score >= 2500) { rank = "Master"; rankColor = "text-yellow-400"; } else if (score >= 1000) { rank = "Sharp"; rankColor = "text-blue-400"; } return { accuracy, avgSpeed, rank, rankColor }; }; return (
{/* --- Top Bar: Countdown & Score --- */}
Time Left
Score {score}
{multiplier > 1 && (
{multiplier}x Streak
)}
{/* --- Main Game Area --- */}
{gameState === 'START' && (

Mental Agility

Does the Meaning match the Ink Color?
Think fast. Don't get tricked.

)} {gameState === 'PLAY' && currentChallenge && ( <> {/* Challenge Card */}
Does this match? {/* The Word with Dynamic Scaling Effects */}
{currentChallenge.displayText}
{/* Controls */}
)} {gameState === 'OVER' && (
{/* Background Glow */}

Session Report

{(() => { const stats = calculateStats(); return (
{/* Score Card */}
Total Points
{score}
{/* Stats Grid */}
Accuracy
{stats.accuracy}%
Top Speed
{stats.avgSpeed}s
{/* Rank */}
Brain Rank: {stats.rank}
); })()}
)}
); };