Trình chấm tùy chỉnh


Trình chấm tùy chỉnh

Một số bài tập có thể có nhiều output đúng, vì vậy đôi lúc ta cần phải viết checker để kiểm tra xem output có đúng hay chưa.

1. Checker mặc định

Sen Code Online Judge! có hỗ trợ các checker mặc định của hệ thống DMOJ, các bạn có thể tham khảo thêm tại đây.

2. Trình chấm tùy chỉnh (Python)

Một checker được viết bằng Python cần cài đặt một hàm như sau:

def check(process_output, judge_output, **kwargs):
    # your code

Trong đó:

  • process_output: output của bài nộp
  • judge_output: đáp án

**kwargs có thể chứa các biến sau:

  • submission_source: source code của bài nộp
  • judge_input: input của đề bài
  • point_value: điểm của test đang chấm
  • case_position: thứ tự của test
  • batch: batch của test case (với các bài chấm theo subtask), đại đa số các bài ta không cần quan tâm tới biến này.
  • submission_language: ngôn ngữ của bài nộp
  • execution_time: thời gian chạy, tính bằng giây

Return

Hàm check có 2 cách return:

  • Return bằng True hoặc False, nếu đáp án đúng thì return True, ngược lại return False.
  • Return bằng CheckerResult, return bằng cách này thì có thể trả về được feedback cho người nộp. Cách dùng như sau: CheckerResult(passed, points_awarded).

Trong đó:

  • passed là có giá trị True/False tùy vào kết quả có đúng hay không.
  • points_awarded: là số điểm nhận được với test đó.
  • feedback: thông tin đưa tới người nộp.

Ví dụ

Dưới đây là checker mẫu cho bài toán POST - A cộng B.

from dmoj.result import CheckerResult

def check(process_output, judge_output, judge_input, point_value, **kwargs):
    # convert from bytes to texts
    process_output = process_output.decode("ascii")
    judge_output = judge_output.decode("ascii")
    judge_input = judge_input.decode("ascii")

    # read data as normal
    a, b = judge_input.split(' ')
    output_sum = int(process_output)
    if a + b != output_sum:
        return CheckerResult(False, 0, f"{a} + {b} != {output_sum}")
    return CheckerResult(True, point_value, "Ok answer is correct")

3. Trình chấm tùy chỉnh (C++)

Để viết được C++ checker, ta cần viết một chương trình C++ nhận vào 3 tham số dòng lệnh theo thứ tự là input_file, output_file, answer_file, tương ứng với đường dẫn tới các file input, output, đáp án.

Return

Chương trình (hàm main) trả về các giá trị sau:

  • 0 nếu AC (100% số điểm)
  • 1 nếu WA (0 điểm)
  • 7 nếu nhận được điểm thành phần. Khi đó cần in ra stderr một số thực trong đoạn [0, 1] thể hiện tỷ lệ điểm. Lưu ý rằng điểm phải được in ra ở dòng đầu tiên của stderr.
  • Nếu chương trình trả về giá trị khác (exit code khác 0, hay run time error) thì bài nộp sẽ được 0 điểm (xem như WA).

Những thông tin được viết ra stdout sẽ được in ra màn hình cho người nộp bài (feedback), nhưng thông tin này chỉ hiển thị được khoảng 20 ký tự.

Nếu muốn người dùng xem được nhiều feedback hơn, thì in feedback vào stderr (lưu ý in sau khi in điểm ở dòng đầu tiên).

Ví dụ

Chương trình sau dùng để chấm bài toán: Cho n là một số nguyên dương. In ra hai số tự nhiên a, b sao cho a + b = n.

Nếu in ra a + b = n và a, b ≥ 0 thì được 100% số điểm, nếu a + b = n nhưng một trong 2 số a, b âm thì được 50% số điểm.

#include <bits/stdc++.h>
using namespace std;

int main(int argc, char** argv) {
    ifstream inp(argv[1]);
    ifstream out(argv[2]);
    ifstream ans(argv[3]);

    int n, a, b, c, d;

    inp >> n;
    out >> a >> b;
    ans >> c >> d;
    if (a + b == c + d) {
        cout << a << " + " << b << " = " << c << " + " << d << endl;

        if (a >= 0 && b >= 0) {
            cerr << "This line will be show to the contestants as a extended feedback";
            return 0; // AC
        }
        else {
            cerr << 0.5 << '\n'; // in ra điểm ở dòng đầu tiên
            cerr << "Thís line will be show to the contestants as a extended feedback";
            return 7; // PARTIAL
        }
    }
    else {
        cout << "a + b = " << a + b << " != " << n << endl;
        return 1; // WA
    }
}

Sen Code Online Judge! cũng hỗ trợ các checker được viết bằng testlib.h, tuy nhiên hiện tại chỉ có thể chấm các bài không phải interactive.

Đây là checker được viết bằng testlib.h cho bài toán trên:

#include "testlib.h"

using namespace std;

int main(int argc, char* argv[]) {
    registerTestlibCmd(argc, argv);

    int n = inf.readInt();
    int a = ouf.readInt();
    int b = ouf.readInt();
    ensuref(a + b == n, "%d + %d != %d", a, b, n);
    if (a < 0) {
        quitf(_points, "0.5 \n a = %d < 0", a);
    }
    if (b < 0) {
        quitf(_points, "0.5 \n b = %d < 0", a);
    }
    quitf(_ok, "%d + %d = %d", a, b, n);
}

4. Trình chấm tùy chỉnh (Java)

Tương tự như C++, một trình chấm tùy chỉnh viết bằng Java sẽ nhận vào 3 tham số dòng lệnh (thông qua args) tương ứng là input_file, output_file, và answer_file.

Return

Để trả về kết quả chấm bài, bạn sử dụng lệnh System.exit(code) với các mã giống như C++:

  • 0 nếu AC (100% số điểm)
  • 1 nếu WA (0 điểm)
  • 7 nếu nhận được điểm thành phần (in tỷ lệ điểm ra dòng đầu tiên của System.err).

Ví dụ

Dưới đây là một ví dụ Checker viết bằng Java chấm bài toán A + B tương tự như trên:

import java.io.File;
import java.util.Scanner;

public class Checker {
    public static void main(String[] args) {
        try {
            Scanner inp = new Scanner(new File(args[0]));
            Scanner out = new Scanner(new File(args[1]));
            Scanner ans = new Scanner(new File(args[2]));

            int n = inp.nextInt();
            int a = out.nextInt();
            int b = out.nextInt();
            int c = ans.nextInt();
            int d = ans.nextInt();

            if (a + b == c + d) {
                if (a >= 0 && b >= 0) {
                    System.err.println("Correct answer");
                    System.exit(0); // AC
                } else {
                    System.err.println("0.5"); // Partial score
                    System.err.println("A and B should be non-negative");
                    System.exit(7); // PARTIAL
                }
            } else {
                System.out.println("WA");
                System.exit(1); // WA
            }
        } catch (Exception e) {
            System.exit(1); // Lỗi khi đọc file
        }
    }
}

5. Trình chấm tùy chỉnh (Pascal)

Với Pascal, bạn có thể lấy tham số dòng lệnh thông qua hàm ParamStr(i) (với i từ 1 đến 3 ứng với input, output, answer). Bạn cũng sử dụng hàm Halt(code) để trả về mã thoát (exit code).

Return

  • Halt(0): AC (100% số điểm)
  • Halt(1): WA (0 điểm)
  • Halt(7): Điểm thành phần (in tỷ lệ điểm ra stderr trước khi Halt).

Ví dụ

Dưới đây là một ví dụ cho ngôn ngữ Pascal chấm bài toán tương tự:

var
  inp, outF, ans: Text;
  n, a, b, c, d: LongInt;
begin
  Assign(inp, ParamStr(1)); Reset(inp);
  Assign(outF, ParamStr(2)); Reset(outF);
  Assign(ans, ParamStr(3)); Reset(ans);

  Read(inp, n);
  Read(outF, a, b);
  Read(ans, c, d);

  if (a + b = c + d) then
  begin
    if (a >= 0) and (b >= 0) then
    begin
      writeln(stderr, 'Correct');
      Halt(0); { AC }
    end
    else
    begin
      writeln(stderr, '0.5'); { Partial score }
      writeln(stderr, 'Extended feedback');
      Halt(7); { PARTIAL }
    end;
  end
  else
  begin
    writeln('WA');
    Halt(1); { WA }
  end;
end.