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.