From 591b73c0717fc7e937dbcff37f7d096aa45da07c Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 10 Aug 2025 14:15:19 +0000 Subject: [PATCH] Fix negation parsing in guessing game Correctly parse negations in user-provided questions to accurately update the search space. --- .../guessing-game/QuestionGuessingMode.tsx | 108 +++++++++++------- 1 file changed, 66 insertions(+), 42 deletions(-) diff --git a/src/components/guessing-game/QuestionGuessingMode.tsx b/src/components/guessing-game/QuestionGuessingMode.tsx index b12c89d..419a0da 100644 --- a/src/components/guessing-game/QuestionGuessingMode.tsx +++ b/src/components/guessing-game/QuestionGuessingMode.tsx @@ -33,30 +33,42 @@ const QuestionGuessingMode: React.FC = ({ 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 getOptimalAnswer = (question: string): { understood: boolean, answer: boolean, questionType?: string, value?: number, isNegated?: boolean } => { const q = question.toLowerCase().trim(); // Remove question marks and normalize const normalizedQ = q.replace(/\?/g, '').trim(); + // Check for negations + const isNegated = normalizedQ.includes(' not ') || normalizedQ.includes("n't") || normalizedQ.includes(" isn't") || normalizedQ.includes(" doesn't"); + + // Remove negation words for parsing the base question + const baseQuestion = normalizedQ + .replace(/ not /g, ' ') + .replace(/n't/g, '') + .replace(/ isn't/g, ' is') + .replace(/ doesn't/g, ' does') + .replace(/\bis\s+not\b/g, 'is') + .trim(); + // Check for various question patterns - if (normalizedQ.includes('is') && normalizedQ.includes('number')) { + if (baseQuestion.includes('is') && baseQuestion.includes('number')) { // Is the number... - if (normalizedQ.includes('even')) { + if (baseQuestion.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' }; + return { understood: true, answer, questionType: 'even', isNegated }; } - if (normalizedQ.includes('odd')) { + if (baseQuestion.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' }; + return { understood: true, answer, questionType: 'odd', isNegated }; } - if (normalizedQ.includes('prime')) { + if (baseQuestion.includes('prime')) { const isPrime = (num: number): boolean => { if (num < 2) return false; for (let i = 2; i <= Math.sqrt(num); i++) { @@ -67,97 +79,98 @@ const QuestionGuessingMode: React.FC = ({ 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' }; + return { understood: true, answer, questionType: 'prime', isNegated }; } - if (normalizedQ.includes('perfect square')) { + if (baseQuestion.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' }; + return { understood: true, answer, questionType: 'perfectSquare', isNegated }; } } - // Check for divisibility questions - if (normalizedQ.includes('divisible by') || normalizedQ.includes('multiple of')) { - const matches = normalizedQ.match(/(?:divisible by|multiple of)\s+(\d+)/); + // Check for divisibility questions (including "multiple of") + if (baseQuestion.includes('divisible by') || baseQuestion.includes('multiple of')) { + const matches = baseQuestion.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 }; + return { understood: true, answer, questionType: 'divisible', value: divisor, isNegated }; } } // 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 (baseQuestion.includes('greater than') || baseQuestion.includes('bigger than') || baseQuestion.includes('larger than')) { + const matches = baseQuestion.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 }; + return { understood: true, answer, questionType: 'greaterThan', value: threshold, isNegated }; } } - if (normalizedQ.includes('less than') || normalizedQ.includes('smaller than')) { - const matches = normalizedQ.match(/(?:less than|smaller than)\s+(\d+)/); + if (baseQuestion.includes('less than') || baseQuestion.includes('smaller than')) { + const matches = baseQuestion.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 }; + return { understood: true, answer, questionType: 'lessThan', value: threshold, isNegated }; } } // 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 (baseQuestion.includes('contain') && baseQuestion.includes('digit')) { + const matches = baseQuestion.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) }; + return { understood: true, answer, questionType: 'containsDigit', value: parseInt(digit), isNegated }; } } // 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 (baseQuestion.includes('is it') || baseQuestion.includes('is the number')) { + const matches = baseQuestion.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 }; + return { understood: true, answer, questionType: 'equals', value: num, isNegated }; } // 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: true, answer, questionType: 'equals', value: num, isNegated }; } } return { understood: false, answer: false }; }; - // Function to update valid numbers based on question and answer - const updateValidNumbersFromQuestion = (questionType: string, answer: boolean, value?: number) => { + // Function to update valid numbers based on question and answer, handling negations + const updateValidNumbersFromQuestion = (questionType: string, answer: boolean, value?: number, isNegated?: boolean) => { const newValidNumbers = new Set(); for (let num = 1; num <= maxRange; num++) { if (!validNumbers.has(num)) continue; // Skip already invalid numbers let shouldKeep = false; + let baseCondition = false; switch (questionType) { case 'even': - shouldKeep = answer ? (num % 2 === 0) : (num % 2 !== 0); + baseCondition = (num % 2 === 0); break; case 'odd': - shouldKeep = answer ? (num % 2 !== 0) : (num % 2 === 0); + baseCondition = (num % 2 !== 0); break; case 'prime': const isPrimeNum = (n: number): boolean => { @@ -167,41 +180,52 @@ const QuestionGuessingMode: React.FC = ({ } return true; }; - shouldKeep = answer ? isPrimeNum(num) : !isPrimeNum(num); + baseCondition = isPrimeNum(num); break; case 'perfectSquare': - const isPerfectSquareNum = Math.sqrt(num) % 1 === 0; - shouldKeep = answer ? isPerfectSquareNum : !isPerfectSquareNum; + baseCondition = Math.sqrt(num) % 1 === 0; break; case 'divisible': if (value !== undefined) { - shouldKeep = answer ? (num % value === 0) : (num % value !== 0); + baseCondition = (num % value === 0); } break; case 'greaterThan': if (value !== undefined) { - shouldKeep = answer ? (num > value) : (num <= value); + baseCondition = (num > value); } break; case 'lessThan': if (value !== undefined) { - shouldKeep = answer ? (num < value) : (num >= value); + baseCondition = (num < value); } break; case 'containsDigit': if (value !== undefined) { - shouldKeep = answer ? num.toString().includes(value.toString()) : !num.toString().includes(value.toString()); + baseCondition = num.toString().includes(value.toString()); } break; case 'equals': if (value !== undefined) { - shouldKeep = answer ? (num === value) : (num !== value); + baseCondition = (num === value); } break; default: shouldKeep = true; // Keep all numbers if we don't understand the question type } + if (questionType !== 'equals' && shouldKeep !== true) { + // Apply negation logic if present + if (isNegated) { + // If the question was negated, we need to flip the base condition + // e.g., "Is it NOT divisible by 4?" with answer "No" means it IS divisible by 4 + shouldKeep = answer ? !baseCondition : baseCondition; + } else { + // Normal case: answer matches the base condition + shouldKeep = answer ? baseCondition : !baseCondition; + } + } + if (shouldKeep) { newValidNumbers.add(num); } @@ -225,7 +249,7 @@ const QuestionGuessingMode: React.FC = ({ setQuestionFeedback(answer); // Update valid numbers based on the question and answer - updateValidNumbersFromQuestion(result.questionType, result.answer, result.value); + updateValidNumbersFromQuestion(result.questionType, result.answer, result.value, result.isNegated); } else { setQuestionHistory([...questionHistory, { @@ -281,7 +305,7 @@ const QuestionGuessingMode: React.FC = ({