CoolFace
Apppublic

Ratan1729/code-execution

sourceHugging Faceupdated 8mo agoView on Hugging Face
1likes
judge.service.js133 linesDownload Raw Back to services
1// ─── Judge Service ───────────────────────────2// Runs code against test cases, compares output, returns verdicts3// NOW USES BATCH MODE: 1 container for ALL test cases4 5const executorService = require("./executor.service");6const logger = require("../utils/logger.util");7 8class JudgeService {9  /**10   * Judge code against multiple test cases in ONE container.11   *12   * Flow:13   *  1. Extract all inputs from test cases14   *  2. Send to batchExecute (ONE Docker container)15   *  3. Compare each output with expected16   *  4. Return detailed results17   */18  async judge({ language, code, testCases, timeLimit, memoryLimit }) {19    logger.info(20      `  ⚖️  Judging ${testCases.length} test cases in ONE container...`,21    );22 23    // Extract all inputs24    const inputs = testCases.map((tc) => tc.input);25 26    // Execute ALL test cases in a single container27    const execResults = await executorService.batchExecute({28      language,29      code,30      inputs,31      timeLimit,32      memoryLimit,33    });34 35    // Build results36    let overallVerdict = "AC";37    let totalTime = 0;38    let maxMemory = 0;39    let firstFailedTestCase = null;40    const results = [];41 42    for (let i = 0; i < testCases.length; i++) {43      const tc = testCases[i];44      const result = execResults[i];45      const verdict = this._getVerdict(result, tc.expectedOutput);46 47      results.push({48        testCase: i + 1,49        verdict,50        executionTime: result.executionTime,51        memoryUsed: result.memoryUsed,52        input: this._truncate(tc.input, 1000),53        expectedOutput: this._truncate(tc.expectedOutput, 1000),54        actualOutput: this._truncate(result.output, 1000),55        error: result.error,56        exitCode: result.exitCode,57      });58 59      totalTime += result.executionTime;60      maxMemory = Math.max(maxMemory, result.memoryUsed);61 62      if (verdict !== "AC" && overallVerdict === "AC") {63        overallVerdict = verdict;64        firstFailedTestCase = i + 1;65      }66    }67 68    return {69      overallVerdict,70      totalTime,71      maxMemory,72      totalTestCases: testCases.length,73      passed: results.filter((r) => r.verdict === "AC").length,74      failed: results.filter((r) => r.verdict !== "AC").length,75      skipped: 0,76      firstFailedTestCase,77      results,78    };79  }80 81  /**82   * Determine verdict by comparing actual output to expected.83   *84   * Priority:85   *  1. If executor verdict is not OK → return that (CE, TLE, MLE, RE)86   *  2. Normalize both outputs and compare87   *  3. AC if match, WA if mismatch88   */89  _getVerdict(result, expectedOutput) {90    // Non-OK verdicts take priority91    if (result.verdict !== "OK") {92      return result.verdict; // CE, TLE, MLE, RE, IE93    }94 95    // Compare normalized outputs96    const actual = this._normalize(result.output);97    const expected = this._normalize(expectedOutput);98 99    if (actual === expected) {100      return "AC";101    }102 103    return "WA";104  }105 106  /**107   * Normalize output for comparison:108   *  - Trim trailing whitespace from each line109   *  - Remove trailing empty lines110   *  - Consistent line endings111   */112  _normalize(str) {113    if (!str) return "";114    return str115      .replace(/\r\n/g, "\n") // Normalize CRLF → LF116      .split("\n")117      .map((line) => line.trimEnd()) // Trim trailing whitespace per line118      .join("\n")119      .trimEnd(); // Remove trailing empty lines120  }121 122  /**123   * Truncate for response size control124   */125  _truncate(str, maxLen) {126    if (!str) return "";127    if (str.length <= maxLen) return str;128    return str.substring(0, maxLen) + "...";129  }130}131 132module.exports = new JudgeService();133