/** * 答案推導工具 - 從例句和挖空例句中動態推導正確答案 */ export interface AnswerExtractionResult { answers: string[]; isValid: boolean; error?: string; } /** * 從例句和挖空題目中提取答案 * @param example 原始例句 * @param filledQuestion 挖空後的題目 * @returns 提取的答案陣列 */ export function extractAnswerFromBlanks(example: string, filledQuestion: string): AnswerExtractionResult { try { // 輸入驗證 if (!example || !filledQuestion) { return { answers: [], isValid: false, error: "例句或挖空題目為空" }; } if (!filledQuestion.includes('____')) { return { answers: [], isValid: false, error: "挖空題目中沒有找到 ____" }; } // 方法1: 正則匹配法 (推薦用於單個空格) if (filledQuestion.split('____').length === 2) { return extractSingleBlankAnswer(example, filledQuestion); } // 方法2: 差異比對法 (用於多個空格) return extractMultipleBlanksAnswers(example, filledQuestion); } catch (error) { return { answers: [], isValid: false, error: `答案提取失敗: ${error instanceof Error ? error.message : '未知錯誤'}` }; } } /** * 提取單個空格的答案 (正則匹配法) */ function extractSingleBlankAnswer(example: string, filledQuestion: string): AnswerExtractionResult { try { // 轉義特殊字符並替換 ____ 為捕獲群組 const escapedPattern = filledQuestion .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // 轉義正則特殊字符 .replace(/____/g, '(.+?)'); // 替換為非貪婪捕獲群組 const regex = new RegExp(`^${escapedPattern}$`, 'i'); const match = example.match(regex); if (match && match[1]) { const answer = match[1].trim(); return { answers: [answer], isValid: true }; } // 如果完全匹配失敗,嘗試部分匹配 const partialRegex = new RegExp(escapedPattern, 'i'); const partialMatch = example.match(partialRegex); if (partialMatch && partialMatch[1]) { const answer = partialMatch[1].trim(); return { answers: [answer], isValid: true }; } return { answers: [], isValid: false, error: "無法匹配例句和挖空題目" }; } catch (error) { return { answers: [], isValid: false, error: `正則匹配失敗: ${error instanceof Error ? error.message : '未知錯誤'}` }; } } /** * 提取多個空格的答案 (差異比對法) */ function extractMultipleBlanksAnswers(example: string, filledQuestion: string): AnswerExtractionResult { try { const parts = filledQuestion.split('____'); const answers: string[] = []; let currentPos = 0; for (let i = 0; i < parts.length - 1; i++) { const beforePart = parts[i]; const afterPart = parts[i + 1]; // 找到前半部分的結束位置 const startPos = currentPos + beforePart.length; // 找到後半部分的開始位置 let endPos: number; if (afterPart === '') { // 如果是最後一個空格,到句子結尾 endPos = example.length; } else { endPos = example.indexOf(afterPart, startPos); if (endPos === -1) { return { answers: [], isValid: false, error: `無法找到後半部分: "${afterPart}"` }; } } // 提取中間的詞作為答案 const answer = example.substring(startPos, endPos).trim(); answers.push(answer); currentPos = endPos; } return { answers, isValid: answers.length > 0 && answers.every(ans => ans.length > 0) }; } catch (error) { return { answers: [], isValid: false, error: `多空格提取失敗: ${error instanceof Error ? error.message : '未知錯誤'}` }; } } /** * 獲取填空題的第一個答案 (最常用) * @param example 原始例句 * @param filledQuestion 挖空後的題目 * @param fallbackAnswer 降級答案 (通常是 word 屬性) * @returns 正確答案字串 */ export function getCorrectAnswer( example: string, filledQuestion: string | undefined, fallbackAnswer: string ): string { if (!filledQuestion) { return fallbackAnswer; } const result = extractAnswerFromBlanks(example, filledQuestion); if (result.isValid && result.answers.length > 0) { return result.answers[0]; } // 推導失敗時使用降級答案 console.warn('答案推導失敗,使用降級答案:', result.error); return fallbackAnswer; } /** * 驗證用戶答案是否正確 * @param userAnswer 用戶輸入的答案 * @param example 原始例句 * @param filledQuestion 挖空後的題目 * @param fallbackAnswer 降級答案 * @returns 是否正確 */ export function validateAnswer( userAnswer: string, example: string, filledQuestion: string | undefined, fallbackAnswer: string ): boolean { const correctAnswer = getCorrectAnswer(example, filledQuestion, fallbackAnswer); return userAnswer.toLowerCase().trim() === correctAnswer.toLowerCase().trim(); }