sanket3280/code-execution
0
1/**2 * Answer Verification System3 * Handles problems with multiple valid answers4 */5 6class AnswerVerifier {7 8 /**9 * Verify palindrome problems (e.g., Longest Palindromic Substring)10 */11 static verifyPalindrome(input, userOutput, metadata = {}) {12 try {13 // Parse input if it's JSON14 let inputStr = String(input);15 if (inputStr.startsWith('{')) {16 try {17 const parsed = JSON.parse(inputStr);18 inputStr = parsed.s || parsed.str || parsed.string || inputStr;19 } catch (e) {20 // Not JSON, use as is21 }22 }23 24 const outputStr = String(userOutput).trim();25 26 // 1. Check if substring27 if (!inputStr.includes(outputStr)) {28 return { 29 valid: false, 30 reason: `"${outputStr}" is not a substring of input "${inputStr}"` 31 };32 }33 34 // 2. Check if palindrome35 const reversed = outputStr.split('').reverse().join('');36 if (outputStr !== reversed) {37 return { 38 valid: false, 39 reason: `"${outputStr}" is not a palindrome` 40 };41 }42 43 // 3. Check if longest (if maxLength provided)44 if (metadata.maxLength !== undefined) {45 if (outputStr.length !== metadata.maxLength) {46 return { 47 valid: false, 48 reason: `Length ${outputStr.length}, expected ${metadata.maxLength}` 49 };50 }51 } else {52 // Calculate longest palindrome length53 const maxLen = this.findLongestPalindromeLength(inputStr);54 if (outputStr.length !== maxLen) {55 return { 56 valid: false, 57 reason: `Length ${outputStr.length}, expected ${maxLen}` 58 };59 }60 }61 62 // Find all valid palindromes of the same length63 const allValidPalindromes = this.findAllPalindromesOfLength(inputStr, outputStr.length);64 65 return { 66 valid: true, 67 reason: "Multiple valid palindrome answers accepted",68 allValidOutputs: allValidPalindromes.length > 1 ? allValidPalindromes : null69 };70 71 } catch (error) {72 return { 73 valid: false, 74 reason: `Verification error: ${error.message}` 75 };76 }77 }78 79 /**80 * Find longest palindrome length in string81 */82 static findLongestPalindromeLength(s) {83 if (!s || s.length === 0) return 0;84 85 let maxLen = 0;86 87 const expandAroundCenter = (left, right) => {88 while (left >= 0 && right < s.length && s[left] === s[right]) {89 left--;90 right++;91 }92 return right - left - 1;93 };94 95 for (let i = 0; i < s.length; i++) {96 const len1 = expandAroundCenter(i, i);97 const len2 = expandAroundCenter(i, i + 1);98 maxLen = Math.max(maxLen, len1, len2);99 }100 101 return maxLen;102 }103 104 /**105 * Find all palindromes of a specific length in string106 */107 static findAllPalindromesOfLength(s, targetLength) {108 if (!s || s.length === 0 || targetLength <= 0) return [];109 110 const palindromes = new Set();111 112 const expandAroundCenter = (left, right) => {113 while (left >= 0 && right < s.length && s[left] === s[right]) {114 const len = right - left + 1;115 if (len === targetLength) {116 palindromes.add(s.substring(left, right + 1));117 }118 left--;119 right++;120 }121 };122 123 for (let i = 0; i < s.length; i++) {124 expandAroundCenter(i, i); // odd length125 expandAroundCenter(i, i + 1); // even length126 }127 128 return Array.from(palindromes);129 }130 131 /**132 * Verify array problems where order doesn't matter133 */134 static verifyArrayAnyOrder(expected, userOutput) {135 try {136 // Parse if strings137 const exp = typeof expected === 'string' ? JSON.parse(expected) : expected;138 const user = typeof userOutput === 'string' ? JSON.parse(userOutput) : userOutput;139 140 // Check if arrays141 if (!Array.isArray(exp) || !Array.isArray(user)) {142 return { 143 valid: false, 144 reason: "Expected array output" 145 };146 }147 148 // Check length149 if (exp.length !== user.length) {150 return { 151 valid: false, 152 reason: `Array length ${user.length}, expected ${exp.length}` 153 };154 }155 156 // Sort and compare157 const sortedExp = JSON.stringify([...exp].sort());158 const sortedUser = JSON.stringify([...user].sort());159 160 if (sortedExp === sortedUser) {161 return { 162 valid: true, 163 reason: "Arrays match (order independent)",164 allValidOutputs: exp.length > 1 ? exp : null165 };166 }167 168 return { 169 valid: false, 170 reason: "Array elements don't match" 171 };172 173 } catch (error) {174 return { 175 valid: false, 176 reason: `Verification error: ${error.message}` 177 };178 }179 }180 181 /**182 * Verify if user output is one of the valid outputs in expected array183 */184 static verifyArrayMembership(expected, userOutput) {185 try {186 // Parse expected if string187 const expectedArray = typeof expected === 'string' ? JSON.parse(expected) : expected;188 189 // Check if expected is array190 if (!Array.isArray(expectedArray)) {191 return { 192 valid: false, 193 reason: "Expected should be an array of valid outputs" 194 };195 }196 197 // Normalize user output198 let userStr = String(userOutput).trim();199 200 // Try to parse if it looks like JSON201 let userValue = userStr;202 try {203 if (userStr.startsWith('[') || userStr.startsWith('{') || userStr.startsWith('"')) {204 userValue = JSON.parse(userStr);205 }206 } catch (e) {207 // Keep as string208 }209 210 // Check if user output matches any expected output211 const userJson = JSON.stringify(userValue);212 const isValid = expectedArray.some(exp => {213 const expJson = JSON.stringify(exp);214 return expJson === userJson;215 });216 217 if (isValid) {218 return {219 valid: true,220 reason: "Output matches one of the valid answers",221 allValidOutputs: expectedArray222 };223 }224 225 return {226 valid: false,227 reason: `Output not in valid answers. Expected one of: ${expectedArray.slice(0, 3).map(v => JSON.stringify(v)).join(', ')}${expectedArray.length > 3 ? '...' : ''}`228 };229 230 } catch (error) {231 return { 232 valid: false, 233 reason: `Verification error: ${error.message}` 234 };235 }236 }237 238 /**239 * Verify against set of valid outputs240 */241 static verifySetMembership(expectedOutputs, userOutput) {242 try {243 // Normalize244 const userStr = String(userOutput).trim();245 const validOutputs = expectedOutputs.map(o => String(o).trim());246 247 if (validOutputs.includes(userStr)) {248 return { 249 valid: true, 250 reason: "Output matches valid answer",251 matchedOutput: userStr,252 allValidOutputs: validOutputs253 };254 }255 256 return { 257 valid: false, 258 reason: "Output doesn't match any valid answer",259 validOptions: validOutputs.slice(0, 3) // Show first 3260 };261 262 } catch (error) {263 return { 264 valid: false, 265 reason: `Verification error: ${error.message}` 266 };267 }268 }269 270 /**271 * Main verification dispatcher272 */273 static verify(verifierType, input, userOutput, expected, metadata = {}) {274 switch (verifierType) {275 case 'palindrome':276 return this.verifyPalindrome(input, userOutput, metadata);277 278 case 'array_any_order':279 return this.verifyArrayAnyOrder(expected, userOutput);280 281 case 'array_membership':282 return this.verifyArrayMembership(expected, userOutput);283 284 case 'set_membership': {285 const expectedOutputs = metadata.expectedOutputs || [expected];286 return this.verifySetMembership(expectedOutputs, userOutput);287 }288 289 default:290 // Fallback to strict comparison291 return {292 valid: String(userOutput).trim() === String(expected).trim(),293 reason: "Exact match comparison"294 };295 }296 }297}298 299module.exports = AnswerVerifier;300 