import React, { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Badge } from '@/components/ui/badge'; import { HelpCircle, CheckCircle, XCircle } from 'lucide-react'; interface QuestionGuessingModeProps { maxRange: number; guesses: number[]; feedback: string; validNumbers: Set; userGuess: string; setUserGuess: (value: string) => void; onGuess: (guessValue?: number) => void; onUpdateValidNumbers: (newValidNumbers: Set) => void; onWin: () => void; } const QuestionGuessingMode: React.FC = ({ maxRange, guesses, feedback, validNumbers, userGuess, setUserGuess, onGuess, onUpdateValidNumbers, onWin }) => { const [question, setQuestion] = useState(''); const [questionHistory, setQuestionHistory] = useState<{question: string, answer: string, understood: boolean}[]>([]); const [questionFeedback, setQuestionFeedback] = useState(''); // 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 const normalizedQ = q.replace(/\?/g, '').trim(); // Check for various question patterns if (normalizedQ.includes('is') && normalizedQ.includes('number')) { // Is the number... if (normalizedQ.includes('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')) { 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 => { if (num < 2) return false; for (let i = 2; i <= Math.sqrt(num); i++) { if (num % i === 0) return false; } return true; }; 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 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' }; } } // Check for divisibility questions if (normalizedQ.includes('divisible by') || normalizedQ.includes('multiple of')) { const matches = normalizedQ.match(/(?:divisible by|multiple of)\s+(\d+)/); if (matches) { const divisor = parseInt(matches[1]); 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 }; } } // Check for range questions if (normalizedQ.includes('greater than') || normalizedQ.includes('bigger than') || normalizedQ.includes('larger than')) { const matches = normalizedQ.match(/(?:greater than|bigger than|larger than)\s+(\d+)/); if (matches) { const threshold = parseInt(matches[1]); 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 }; } } if (normalizedQ.includes('less than') || normalizedQ.includes('smaller than')) { const matches = normalizedQ.match(/(?:less than|smaller than)\s+(\d+)/); if (matches) { const threshold = parseInt(matches[1]); 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 }; } } // Check for digit contains questions if (normalizedQ.includes('contain') && normalizedQ.includes('digit')) { const matches = normalizedQ.match(/contain(?:s)?\s+(?:the\s+)?digit\s+(\d)/); if (matches) { const digit = matches[1]; 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) }; } } // Check for specific number questions if (normalizedQ.includes('is it') || normalizedQ.includes('is the number')) { const matches = normalizedQ.match(/(?:is it|is the number)\s+(\d+)/); if (matches) { const num = parseInt(matches[1]); // 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 }; } } return { understood: false, answer: false }; }; // Function to update valid numbers based on question and answer const updateValidNumbersFromQuestion = (questionType: string, answer: boolean, value?: number) => { const newValidNumbers = new Set(); for (let num = 1; num <= maxRange; num++) { if (!validNumbers.has(num)) continue; // Skip already invalid numbers let shouldKeep = false; switch (questionType) { case 'even': shouldKeep = answer ? (num % 2 === 0) : (num % 2 !== 0); break; case 'odd': shouldKeep = answer ? (num % 2 !== 0) : (num % 2 === 0); break; case 'prime': const isPrimeNum = (n: number): boolean => { if (n < 2) return false; for (let i = 2; i <= Math.sqrt(n); i++) { if (n % i === 0) return false; } return true; }; shouldKeep = answer ? isPrimeNum(num) : !isPrimeNum(num); break; case 'perfectSquare': const isPerfectSquareNum = Math.sqrt(num) % 1 === 0; shouldKeep = answer ? isPerfectSquareNum : !isPerfectSquareNum; break; case 'divisible': if (value !== undefined) { shouldKeep = answer ? (num % value === 0) : (num % value !== 0); } break; case 'greaterThan': if (value !== undefined) { shouldKeep = answer ? (num > value) : (num <= value); } break; case 'lessThan': if (value !== undefined) { shouldKeep = answer ? (num < value) : (num >= value); } break; case 'containsDigit': if (value !== undefined) { shouldKeep = answer ? num.toString().includes(value.toString()) : !num.toString().includes(value.toString()); } break; case 'equals': if (value !== undefined) { shouldKeep = answer ? (num === value) : (num !== value); } break; default: shouldKeep = true; // Keep all numbers if we don't understand the question type } if (shouldKeep) { newValidNumbers.add(num); } } onUpdateValidNumbers(newValidNumbers); }; const handleQuestionSubmit = () => { if (!question.trim()) return; const result = getOptimalAnswer(question); if (result.understood && result.questionType) { const answer = result.answer ? 'Yes' : 'No'; setQuestionHistory([...questionHistory, { question: question, answer: answer, understood: true }]); setQuestionFeedback(answer); // Update valid numbers based on the question and answer updateValidNumbersFromQuestion(result.questionType, result.answer, result.value); } else { setQuestionHistory([...questionHistory, { question: question, answer: "I don't understand this question", understood: false }]); setQuestionFeedback("I don't understand that question. Please try rephrasing it as a clear yes/no question about mathematical properties, ranges, or specific values."); } 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 (

Ask yes/no questions to find the number between 1 and {maxRange}

Questions asked: {questionHistory.length}

{feedback && (
{feedback}
)}
{/* Question Input */}