// ActivityHost — wraps each activity with timer/attempts/intro UI. // Activities receive { onWin(stars, message), onFail(message), difficulty, speed, theme, tweaks } // Each activity must call onWin or onFail when finished. const { useState: useState_AH, useEffect: useEffect_AH, useRef: useRef_AH, useMemo: useMemo_AH } = React; function ActivityHost({ activity, theme, tweaks, onWin, onFail }) { const [seconds, setSeconds] = useState_AH(0); const [attempts, setAttempts] = useState_AH(getMaxAttempts(activity.kind, tweaks.difficulty)); const [stage, setStage] = useState_AH("intro"); // intro | play const startedRef = useRef_AH(false); useEffect_AH(() => { if (stage !== "play") return; const t = setInterval(() => setSeconds(s => s + 1), 1000); return () => clearInterval(t); }, [stage]); const recordMistake = () => { setAttempts(a => { const next = a - 1; if (next <= 0) { onFail("Se acabaron los intentos. ¡No te rindas!"); } return Math.max(0, next); }); window.SFX && window.SFX.wrong(); }; const finish = (correct, total) => { // Stars based on accuracy + attempts left + speed const accuracy = correct / total; let stars = 1; if (accuracy >= 0.7) stars = 2; if (accuracy >= 0.95 && attempts >= getMaxAttempts(activity.kind, tweaks.difficulty) - 1) stars = 3; const messages = [ "Buen intento. Sigue practicando.", "¡Muy bien! Vas mejorando.", "¡Perfecto! Eres un genio matemático.", ]; onWin(stars, messages[stars - 1]); }; if (stage === "intro") { return ( setStage("play")} /> ); } const props = { theme, difficulty: tweaks.difficulty, speed: tweaks.speed, tweaks, onCorrect: () => { window.SFX && window.SFX.correct(); }, onMistake: recordMistake, onFinish: finish, attemptsLeft: attempts, }; return (
{renderActivity(activity, props)}
); } function getMaxAttempts(kind, difficulty) { if (kind === "memory") return 10; const base = { facil: 8, media: 5, dificil: 3 }[difficulty] || 5; return base; } function ActivityMeta({ seconds, attempts, maxAttempts, theme }) { const m = Math.floor(seconds/60), s = seconds%60; return (
⏱ {m}:{String(s).padStart(2,"0")}
{Array.from({length: maxAttempts}).map((_,i)=>( ))}
); } function ActivityIntro({ activity, theme, difficulty, onStart }) { return (
{activity.icon}
{activity.type}
{activity.title}
{activity.subtitle}
Dificultad: {difficulty}
); } function renderActivity(activity, props) { const p = { ...props, data: activity.data || null }; switch (activity.kind) { case "memory": return ; case "match": return ; case "order": return ; case "fill": return ; case "wordsearch": return ; case "crossword": return ; case "puzzle": return ; case "answer": return ; default: return
Actividad no encontrada
; } } window.ActivityHost = ActivityHost;