Spaces:
Running
Running
| "use client"; | |
| import { useState, useEffect, useRef } from "react"; | |
| import { Calculator } from "lucide-react"; | |
| import { Button } from "@/components/ui/Button"; | |
| import { MiniGameResultsScreen } from "@/components/game/MiniGameResultsScreen"; | |
| import { MiniGameHeader } from "@/components/game/MiniGameHeader"; | |
| import { soundManager } from "@/lib/sound/manager"; | |
| const GAME_DURATION = 60; | |
| export default function MathGame() { | |
| const [isPlaying, setIsPlaying] = useState(false); | |
| const [isGameOver, setIsGameOver] = useState(false); | |
| const [timeLeft, setTimeLeft] = useState(GAME_DURATION); | |
| const [score, setScore] = useState(0); | |
| const [equation, setEquation] = useState({ text: "", answer: 0 }); | |
| const [inputValue, setInputValue] = useState(""); | |
| const inputRef = useRef<HTMLInputElement>(null); | |
| const timerRef = useRef<NodeJS.Timeout | null>(null); | |
| const generateEquation = () => { | |
| const ops = ["+", "-", "*"]; | |
| const op = ops[Math.floor(Math.random() * ops.length)]; | |
| let a: number, b: number, text: string, answer: number; | |
| const maxVal = 10 + Math.floor(score / 5) * 5; | |
| if (op === "+") { | |
| a = Math.floor(Math.random() * maxVal) + 1; | |
| b = Math.floor(Math.random() * maxVal) + 1; | |
| text = `${a} + ${b}`; | |
| answer = a + b; | |
| } else if (op === "-") { | |
| a = Math.floor(Math.random() * maxVal) + 10; | |
| b = Math.floor(Math.random() * a); | |
| text = `${a} - ${b}`; | |
| answer = a - b; | |
| } else { | |
| a = Math.floor(Math.random() * 12) + 2; | |
| b = Math.floor(Math.random() * 12) + 2; | |
| text = `${a} \u00d7 ${b}`; | |
| answer = a * b; | |
| } | |
| setEquation({ text, answer }); | |
| }; | |
| const startGame = () => { | |
| setIsPlaying(true); | |
| setIsGameOver(false); | |
| setScore(0); | |
| setTimeLeft(GAME_DURATION); | |
| setInputValue(""); | |
| generateEquation(); | |
| soundManager.play("streak5"); | |
| setTimeout(() => inputRef.current?.focus(), 100); | |
| }; | |
| useEffect(() => { | |
| if (!isPlaying || timeLeft <= 0) return; | |
| timerRef.current = setTimeout(() => { | |
| if (timeLeft <= 1) { | |
| setIsPlaying(false); | |
| setIsGameOver(true); | |
| setTimeLeft(0); | |
| soundManager.play("correct"); | |
| } else { | |
| if (timeLeft <= 6) soundManager.play("tick"); | |
| setTimeLeft((prev) => prev - 1); | |
| } | |
| }, 1000); | |
| return () => { | |
| if (timerRef.current) clearTimeout(timerRef.current); | |
| }; | |
| }, [isPlaying, timeLeft]); | |
| const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| if (!isPlaying) return; | |
| const val = e.target.value; | |
| if (!/^\d*$/.test(val)) return; | |
| setInputValue(val); | |
| if (parseInt(val) === equation.answer) { | |
| soundManager.play("tick"); | |
| setScore((prev) => prev + 1); | |
| setInputValue(""); | |
| generateEquation(); | |
| } | |
| }; | |
| if (isGameOver) { | |
| return ( | |
| <MiniGameResultsScreen | |
| gameId="math" | |
| score={score} | |
| stats={[ | |
| { label: "Equations Solved", value: `${score}` }, | |
| { | |
| label: "Rate", | |
| value: `${(score / (GAME_DURATION / 60)).toFixed(1)}/min`, | |
| }, | |
| ]} | |
| onPlayAgain={startGame} | |
| /> | |
| ); | |
| } | |
| return ( | |
| <div className="flex-1 flex flex-col w-full max-w-3xl mx-auto items-center gap-4 px-4"> | |
| <MiniGameHeader | |
| title="Quantum Compute" | |
| icon={Calculator} | |
| accentColor="cyan" | |
| stats={[ | |
| { | |
| label: "Time", | |
| value: `0:${timeLeft.toString().padStart(2, "0")}`, | |
| }, | |
| { label: "Score", value: `${score}` }, | |
| ]} | |
| /> | |
| {!isPlaying ? ( | |
| <div className="flex flex-col items-center justify-center mt-20"> | |
| <Calculator className="w-24 h-24 text-cyan-400 mb-8 opacity-80 drop-shadow-[0_0_15px_rgba(34,211,238,0.5)]" /> | |
| <h2 className="text-3xl font-bold text-white mb-4 uppercase tracking-widest"> | |
| Core Calibration | |
| </h2> | |
| <p className="text-slate-400 mb-8 text-center max-w-md"> | |
| The hyperdrive is failing! Solve as many equations as possible in 60 | |
| seconds to stabilize the core. | |
| </p> | |
| <Button variant="secondary" size="lg" onClick={startGame}> | |
| Initialize Sequence | |
| </Button> | |
| </div> | |
| ) : ( | |
| <div className="w-full flex flex-col items-center mt-20"> | |
| <div className="text-7xl md:text-9xl font-black font-mono text-white mb-12 drop-shadow-[0_0_20px_rgba(255,255,255,0.3)]"> | |
| {equation.text} | |
| </div> | |
| <input | |
| ref={inputRef} | |
| type="text" | |
| inputMode="numeric" | |
| value={inputValue} | |
| onChange={handleInputChange} | |
| autoFocus | |
| className="w-full max-w-sm bg-black/40 border-2 rounded-2xl px-6 py-6 text-center text-white text-5xl font-mono focus:outline-none transition-all border-cyan-500/50 focus:border-cyan-400 focus:ring-4 focus:ring-cyan-500/20 shadow-[0_0_30px_rgba(34,211,238,0.1)]" | |
| placeholder="?" | |
| /> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |