import React, { useState, useEffect, useRef } from 'react' import { motion, AnimatePresence } from 'framer-motion' export default function Tile({ value, clue, score, setScore }) { const [answered, setAnswered] = useState(false) const [showModal, setShowModal] = useState(false) const [showWager, setShowWager] = useState(false) const [wager, setWager] = useState('') const [userAnswer, setUserAnswer] = useState('') const [showResult, setShowResult] = useState(false) const [isCorrect, setIsCorrect] = useState(false) const isDailyDouble = clue.isDailyDouble || false const finalValue = isDailyDouble ? (wager ? parseInt(wager) : value) : value const dailyDoubleAudioRef = useRef(null) const correctAudioRef = useRef(null) const incorrectAudioRef = useRef(null) useEffect(() => { dailyDoubleAudioRef.current = new Audio('/daily-double.mp3') correctAudioRef.current = new Audio('/correct.mp3') incorrectAudioRef.current = new Audio('/incorrect.mp3') return () => { dailyDoubleAudioRef.current?.pause() correctAudioRef.current?.pause() incorrectAudioRef.current?.pause() } }, []) useEffect(() => { if (showWager && isDailyDouble) { dailyDoubleAudioRef.current?.play().catch(() => {}) } }, [showWager, isDailyDouble]) useEffect(() => { if (showResult) { if (isCorrect) { correctAudioRef.current?.play().catch(() => {}) } else if (userAnswer) { // Only play incorrect sound if they submitted an answer, not if they passed incorrectAudioRef.current?.play().catch(() => {}) } } }, [showResult, isCorrect, userAnswer]) const handleClick = () => { if (!answered) { if (isDailyDouble) { setShowWager(true) } else { setShowModal(true) } } } const handleWagerSubmit = () => { const wagerAmount = parseInt(wager) || value // Max wager is the higher of: current score or the max value for the round const maxWager = Math.max(score, value) // Ensure wager is between $5 and maxWager const finalWager = Math.max(5, Math.min(wagerAmount, maxWager)) setWager(finalWager.toString()) setShowWager(false) setShowModal(true) } // Helper function to normalize answers for comparison const normalizeAnswer = (answer) => { if (!answer) return '' // Convert to lowercase and remove punctuation let normalized = answer.trim().toLowerCase().replace(/[^\w\s]/g, '') // Remove question prefixes (what is, what are, who is, who are, etc.) const questionPrefixes = [ /^what\s+is\s+/i, /^what\s+are\s+/i, /^who\s+is\s+/i, /^who\s+are\s+/i, /^where\s+is\s+/i, /^where\s+are\s+/i, /^when\s+is\s+/i, /^when\s+are\s+/i, /^how\s+is\s+/i, /^how\s+are\s+/i, /^which\s+is\s+/i, /^which\s+are\s+/i ] for (const prefix of questionPrefixes) { normalized = normalized.replace(prefix, '').trim() } // Remove leading articles (a, an, the) normalized = normalized.replace(/^(the|a|an)\s+/i, '').trim() return normalized.trim() } // Helper function to compare answers, handling pluralization const compareAnswers = (userAns, correctAns) => { // Empty answers are always incorrect if (!userAns || !userAns.trim()) return false const userNorm = normalizeAnswer(userAns) const correctNorm = normalizeAnswer(correctAns) // If normalized user answer is empty, it's incorrect if (!userNorm || userNorm.length === 0) return false // Exact match if (userNorm === correctNorm) return true // Handle pluralization - remove trailing 's' and compare const userSingular = userNorm.replace(/s$/, '') const correctSingular = correctNorm.replace(/s$/, '') if (userSingular === correctNorm || userNorm === correctSingular) return true if (userSingular === correctSingular && userSingular.length > 0) return true // Check if singular forms match when one has 's' and other doesn't if (userNorm.endsWith('s') && userNorm.slice(0, -1) === correctNorm) return true if (correctNorm.endsWith('s') && correctNorm.slice(0, -1) === userNorm) return true // For multi-word answers, check if they match word-by-word (handles word order differences) const userWords = userNorm.split(/\s+/).sort().join(' ') const correctWords = correctNorm.split(/\s+/).sort().join(' ') if (userWords === correctWords && userWords.length > 0) return true return false } const handleSubmit = () => { const correct = compareAnswers(userAnswer, clue.answer) setIsCorrect(correct) setShowResult(true) if (correct) { setScore(prevScore => prevScore + finalValue) } else { setScore(prevScore => prevScore - finalValue) } } const handlePass = () => { setIsCorrect(false) setShowResult(true) } const closeModal = () => { setAnswered(true) setShowModal(false) setShowWager(false) setShowResult(false) setUserAnswer('') setWager('') } if (answered) { return (
) } return ( <>
${value} {showWager && (
DAILY DOUBLE
Current Score: ${score}
setWager(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleWagerSubmit()} className="w-full px-4 sm:px-6 py-3 sm:py-4 text-xl sm:text-2xl md:text-3xl text-center font-bold rounded-xl border-4 border-white bg-white text-black focus:outline-none focus:ring-4 focus:ring-yellow-400 transition-all" style={{ fontFamily: 'Georgia, serif' }} placeholder={`Max: $${Math.max(score, value)}`} min="5" max={Math.max(score, value)} autoFocus />
Maximum wager: ${Math.max(score, value)}
)} {showModal && ( {!showResult ? ( /* Question Display Screen */ {/* The Jeopardy Question - Full Screen Centered */}
{clue.question}
{/* Answer Input Section - Appears Below Question */}
setUserAnswer(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSubmit()} className="w-full px-4 sm:px-6 py-3 sm:py-4 text-lg sm:text-xl md:text-2xl text-center font-bold rounded-xl border-4 border-white bg-white text-black focus:outline-none focus:ring-4 focus:ring-yellow-400 transition-all" style={{ fontFamily: 'Georgia, serif' }} autoFocus />
) : ( /* Result Display Screen */
{isCorrect ? ( <>
CORRECT!
You earned ${finalValue}!
) : ( <>
{userAnswer ? 'INCORRECT!' : 'PASSED'}
CORRECT ANSWER:
{clue.answer}
{userAnswer && (
You lost ${finalValue}!
)} )}
)}
)}
) }