diff --git a/src/components/GuessingGame.tsx b/src/components/GuessingGame.tsx index 74b3f26..6e31dcf 100644 --- a/src/components/GuessingGame.tsx +++ b/src/components/GuessingGame.tsx @@ -475,7 +475,6 @@ const GuessingGame: React.FC = ({ showSocialShare = true }) = {gameState === 'playing' && gameMode === 'user-guesses' && userGuessingMode === 'questions' && ( = ({ showSocialShare = true }) = setUserGuess={setUserGuess} onGuess={handleGuess} onUpdateValidNumbers={setValidNumbers} + onWin={() => setGameState('won')} /> )} diff --git a/src/components/guessing-game/QuestionGuessingMode.tsx b/src/components/guessing-game/QuestionGuessingMode.tsx index 0504fa4..b12c89d 100644 --- a/src/components/guessing-game/QuestionGuessingMode.tsx +++ b/src/components/guessing-game/QuestionGuessingMode.tsx @@ -7,7 +7,6 @@ import { HelpCircle, CheckCircle, XCircle } from 'lucide-react'; interface QuestionGuessingModeProps { maxRange: number; - secretNumber: number; guesses: number[]; feedback: string; validNumbers: Set; @@ -15,25 +14,26 @@ interface QuestionGuessingModeProps { setUserGuess: (value: string) => void; onGuess: (guessValue?: number) => void; onUpdateValidNumbers: (newValidNumbers: Set) => void; + onWin: () => void; } const QuestionGuessingMode: React.FC = ({ maxRange, - secretNumber, guesses, feedback, validNumbers, userGuess, setUserGuess, onGuess, - onUpdateValidNumbers + onUpdateValidNumbers, + onWin }) => { const [question, setQuestion] = useState(''); const [questionHistory, setQuestionHistory] = useState<{question: string, answer: string, understood: boolean}[]>([]); const [questionFeedback, setQuestionFeedback] = useState(''); - // Function to interpret and answer yes/no questions without revealing the secret number - const interpretQuestion = (question: string, secretNumber: number): { understood: boolean, answer: boolean, questionType?: string, value?: number } => { + // Function to determine the optimal answer to keep search space as large as possible + const getOptimalAnswer = (question: string): { understood: boolean, answer: boolean, questionType?: string, value?: number } => { const q = question.toLowerCase().trim(); // Remove question marks and normalize @@ -43,10 +43,18 @@ const QuestionGuessingMode: React.FC = ({ if (normalizedQ.includes('is') && normalizedQ.includes('number')) { // Is the number... if (normalizedQ.includes('even')) { - return { understood: true, answer: secretNumber % 2 === 0, questionType: 'even' }; + // Count how many valid numbers are even vs odd + const evenCount = Array.from(validNumbers).filter(n => n % 2 === 0).length; + const oddCount = validNumbers.size - evenCount; + // Choose answer that keeps more numbers + const answer = evenCount > oddCount; + return { understood: true, answer, questionType: 'even' }; } if (normalizedQ.includes('odd')) { - return { understood: true, answer: secretNumber % 2 !== 0, questionType: 'odd' }; + const oddCount = Array.from(validNumbers).filter(n => n % 2 !== 0).length; + const evenCount = validNumbers.size - oddCount; + const answer = oddCount > evenCount; + return { understood: true, answer, questionType: 'odd' }; } if (normalizedQ.includes('prime')) { const isPrime = (num: number): boolean => { @@ -56,12 +64,16 @@ const QuestionGuessingMode: React.FC = ({ } return true; }; - const prime = isPrime(secretNumber); - return { understood: true, answer: prime, questionType: 'prime' }; + const primeCount = Array.from(validNumbers).filter(n => isPrime(n)).length; + const nonPrimeCount = validNumbers.size - primeCount; + const answer = primeCount > nonPrimeCount; + return { understood: true, answer, questionType: 'prime' }; } if (normalizedQ.includes('perfect square')) { - const isPerfectSquare = Math.sqrt(secretNumber) % 1 === 0; - return { understood: true, answer: isPerfectSquare, questionType: 'perfectSquare' }; + const perfectSquareCount = Array.from(validNumbers).filter(n => Math.sqrt(n) % 1 === 0).length; + const nonPerfectSquareCount = validNumbers.size - perfectSquareCount; + const answer = perfectSquareCount > nonPerfectSquareCount; + return { understood: true, answer, questionType: 'perfectSquare' }; } } @@ -70,8 +82,10 @@ const QuestionGuessingMode: React.FC = ({ const matches = normalizedQ.match(/(?:divisible by|multiple of)\s+(\d+)/); if (matches) { const divisor = parseInt(matches[1]); - const isDivisible = secretNumber % divisor === 0; - return { understood: true, answer: isDivisible, questionType: 'divisible', value: divisor }; + const divisibleCount = Array.from(validNumbers).filter(n => n % divisor === 0).length; + const nonDivisibleCount = validNumbers.size - divisibleCount; + const answer = divisibleCount > nonDivisibleCount; + return { understood: true, answer, questionType: 'divisible', value: divisor }; } } @@ -80,8 +94,10 @@ const QuestionGuessingMode: React.FC = ({ const matches = normalizedQ.match(/(?:greater than|bigger than|larger than)\s+(\d+)/); if (matches) { const threshold = parseInt(matches[1]); - const isGreater = secretNumber > threshold; - return { understood: true, answer: isGreater, questionType: 'greaterThan', value: threshold }; + const greaterCount = Array.from(validNumbers).filter(n => n > threshold).length; + const lessEqualCount = validNumbers.size - greaterCount; + const answer = greaterCount > lessEqualCount; + return { understood: true, answer, questionType: 'greaterThan', value: threshold }; } } @@ -89,8 +105,10 @@ const QuestionGuessingMode: React.FC = ({ const matches = normalizedQ.match(/(?:less than|smaller than)\s+(\d+)/); if (matches) { const threshold = parseInt(matches[1]); - const isLess = secretNumber < threshold; - return { understood: true, answer: isLess, questionType: 'lessThan', value: threshold }; + const lessCount = Array.from(validNumbers).filter(n => n < threshold).length; + const greaterEqualCount = validNumbers.size - lessCount; + const answer = lessCount > greaterEqualCount; + return { understood: true, answer, questionType: 'lessThan', value: threshold }; } } @@ -99,8 +117,10 @@ const QuestionGuessingMode: React.FC = ({ const matches = normalizedQ.match(/contain(?:s)?\s+(?:the\s+)?digit\s+(\d)/); if (matches) { const digit = matches[1]; - const contains = secretNumber.toString().includes(digit); - return { understood: true, answer: contains, questionType: 'containsDigit', value: parseInt(digit) }; + const containsCount = Array.from(validNumbers).filter(n => n.toString().includes(digit)).length; + const doesntContainCount = validNumbers.size - containsCount; + const answer = containsCount > doesntContainCount; + return { understood: true, answer, questionType: 'containsDigit', value: parseInt(digit) }; } } @@ -109,8 +129,14 @@ const QuestionGuessingMode: React.FC = ({ const matches = normalizedQ.match(/(?:is it|is the number)\s+(\d+)/); if (matches) { const num = parseInt(matches[1]); - const isEqual = secretNumber === num; - return { understood: true, answer: isEqual, questionType: 'equals', value: num }; + // If there's only one number left and they're asking about it, we must answer truthfully + if (validNumbers.size === 1) { + const answer = validNumbers.has(num); + return { understood: true, answer, questionType: 'equals', value: num }; + } + // Otherwise, say no to keep the game going unless this number isn't even valid + const answer = false; + return { understood: true, answer, questionType: 'equals', value: num }; } } @@ -187,7 +213,7 @@ const QuestionGuessingMode: React.FC = ({ const handleQuestionSubmit = () => { if (!question.trim()) return; - const result = interpretQuestion(question, secretNumber); + const result = getOptimalAnswer(question); if (result.understood && result.questionType) { const answer = result.answer ? 'Yes' : 'No'; @@ -213,6 +239,30 @@ const QuestionGuessingMode: React.FC = ({ setQuestion(''); }; + // Handle final number guess - just check if it's the only remaining valid number + const handleFinalGuess = (guessValue?: number) => { + const guess = guessValue || parseInt(userGuess); + if (isNaN(guess) || guess < 1 || guess > maxRange) { + return; + } + + if (validNumbers.size === 1 && validNumbers.has(guess)) { + onWin(); + } else if (validNumbers.has(guess)) { + // Remove this number from valid numbers since it's not the answer + const newValidNumbers = new Set(validNumbers); + newValidNumbers.delete(guess); + onUpdateValidNumbers(newValidNumbers); + + // Check if we're down to one number + if (newValidNumbers.size === 1) { + onWin(); + } + } + + setUserGuess(''); + }; + return (
@@ -285,7 +335,7 @@ const QuestionGuessingMode: React.FC = ({ {/* Number Grid Display */}
-

Click a number to make your final guess:

+

Click a number to make your final guess ({validNumbers.size} possibilities remaining):

{Array.from({ length: maxRange }, (_, i) => i + 1).map(num => { const isValid = validNumbers.has(num); @@ -302,7 +352,7 @@ const QuestionGuessingMode: React.FC = ({ ${isGuessed ? 'bg-accent text-accent-foreground' : ''} ${isValid && !isGuessed ? 'hover:bg-accent/10' : ''} `} - onClick={() => isValid && !isGuessed && onGuess(num)} + onClick={() => isValid && !isGuessed && handleFinalGuess(num)} disabled={!isValid || isGuessed} > {num} @@ -318,11 +368,11 @@ const QuestionGuessingMode: React.FC = ({ placeholder="Or type your final guess" value={userGuess} onChange={(e) => setUserGuess(e.target.value)} - onKeyDown={(e) => e.key === 'Enter' && onGuess()} + onKeyDown={(e) => e.key === 'Enter' && handleFinalGuess()} min={1} max={maxRange} /> -