// Time Attack — 60 seconds, multiple choice, count correct answers const TimeAttack = ({ profile, onFinish, onQuit }) => { const t = window.MP_I18N.t; const diff = window.MP_DATA.difficultyForGrade(profile.grade); const TOTAL = 60; const [phase, setPhase] = React.useState('count'); // count | play const [timeLeft, setTimeLeft] = React.useState(TOTAL); const [problem, setProblem] = React.useState(null); const [choices, setChoices] = React.useState([]); const [picked, setPicked] = React.useState(null); const [score, setScore] = React.useState(0); const [correct, setCorrect] = React.useState(0); const [total, setTotal] = React.useState(0); const [status, setStatus] = React.useState(null); const next = React.useCallback(() => { const p = window.MP_DATA.genProblem({ tables: diff.tables, max: diff.max }); setProblem(p); setChoices(window.MP_DATA.genOptions(p.answer, 4)); setPicked(null); setStatus(null); }, []); React.useEffect(() => { if (phase !== 'play') return; next(); const start = Date.now(); const id = setInterval(() => { const elapsed = (Date.now() - start) / 1000; const left = Math.max(0, TOTAL - elapsed); setTimeLeft(left); if (left <= 0) { clearInterval(id); finish(); } }, 100); return () => clearInterval(id); }, [phase]); const finish = () => { setPhase('done'); onFinish({ mode: 'time_attack', score, correct, total, perfect: total > 0 && correct === total }); }; const pick = (c) => { if (picked != null || !problem) return; setPicked(c); setTotal(x => x + 1); if (c === problem.answer) { window.MP_SOUND.correct(); setStatus('right'); setCorrect(x => x + 1); setScore(x => x + 10); setTimeout(next, 380); } else { window.MP_SOUND.wrong(); setStatus('wrong'); setTimeout(next, 700); } }; if (phase === 'count') { return ( setPhase('play')} /> ); } return ( {problem && } {t('you_got')}: {correct}/{total} ); }; window.TimeAttack = TimeAttack;