交互题本机评测器
为什么会有这篇文章
交互题是 OI 中一种新颖的题型。它可以同时恶心出题人和做题的人。
出题人写交互器要调试半天,做题的人写代码不方便本地评测。
在花费一些时间后,我实现了一个简单的交互器运行模块,可以帮助你进行交互题的本机评测。
如果你并不知道交互题是什么,建议你先去做一道交互题。
如果你还不了解交互题的造题过程,请在 Polygon 上注册一个帐号,观摩一下它默认提供的 example-interactive-binary-search。
用途
交互题包含几个部分:
- 交互器(交互库):这个是出题人实现的,用于和你提交的程序进行交互。如果要本地评测你代码的正确性,那么显然你得自己写一个交互器。不过大多数题目的交互器是不难实现的。
- 数据生成器:交互题仍然是有数据的。交互器大部分读入数据之后才会给你的程序提供输入,监听你程序的输出。这个也是自己写的。
- 评测器:这是本文实现的内容。如果没有评测器,那么你本地测试就只能开两个终端窗口,每次把交互器的窗口输出的内容复制到你程序的窗口,再把你程序的输出复制到交互器的窗口,无法实现自动化。更别提对拍了。
形象地说,评测器是交互器和你程序的桥梁。
代码实现
/**
* judger.cpp: Judger for Interactive Problem
*
* @author Sshwy
* @copyright 2021 Sshwy (Weiyao Huang)
* @license MIT
* @date 2021.4.26
*
* Usage: judger [command1] [command2]
*/
#include <stdio.h>
#include <unistd.h>
using namespace std;
int main(int argc, char ** argv){
if(argc != 3) {
fprintf(stderr, "Usage: %s [command1] [command2]\n", argv[0]);
return 0;
}
int p1[2], p2[2];
if(pipe(p1) == -1 || pipe(p2) == -1)
return fprintf(stderr, "fail to create pipe\n"), 1;
pid_t id = fork();
if(id < 0) return fprintf(stderr, "error fork\n"), 1;
if(id == 0){ // child : a
if (dup2(p2[0], fileno(stdin)) == -1)
return fprintf(stderr,"Error 1"), 1;
if (dup2(p1[1], fileno(stdout)) == -1)
return fprintf(stderr,"Error 2"), 1;
execve(argv[1], nullptr, nullptr);
} else {
if (dup2(p1[0], fileno(stdin)) == -1)
return fprintf(stderr,"Error 3"), 1;
if (dup2(p2[1], fileno(stdout)) == -1)
return fprintf(stderr,"Error 4"), 1;
execve(argv[2], nullptr, nullptr);
}
return 0;
}
样例
目前仅在 Linux 环境下测试过。
选手程序
// a.cpp
#include <bits/stdc++.h>
using namespace std;
bool check(int x) {
printf("? %d\n", x); // check if ? >= x
fflush(stdout);
fprintf(stderr, "? %d\n", x); // check if ? >= x
scanf("%d", &x);
return x == 1;
}
int main() {
int n;
scanf("%d", &n);
fprintf(stderr, "a.cpp: n %d\n", n);
int l = 1, r = n, mid;
while (l < r)
mid = (l + r + 1) >> 1, check(mid) ? l = mid : r = mid - 1;
printf("! %d\n", l);
return 0;
}
交互器
// interactor.cpp
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
int a, b;
int main() {
FILE *inf = fopen("in.txt", "r");
fscanf(inf, "%d%d", &a, &b);
fclose(inf);
assert(a <= b);
printf("%d\n", b);
fflush(stdout);
FOR(_, 1, 30) {
char x[5];
int y;
scanf("%s%d", x, &y);
fprintf(stderr, "b.cpp: x:[%s], y:[%d]\n", x, y);
if (x[0] == '?') {
printf("%d\n", a >= y);
fflush(stdout);
} else {
assert(x[0] == '!');
if (y == a) {
fprintf(stderr, "AC\n");
} else {
fprintf(stderr, "WA\n");
}
return 0;
}
}
fprintf(stderr, "WA\n");
return 0;
}
执行结果
$ g++ a.cpp -o a
$ g++ interactor.cpp -o interactor
$ g++ judger.cpp -o judger
$ echo 5 10 > in.txt
$ ./judger ./a ./interactor
a.cpp: n 10
? 6
b.cpp: x:[?], y:[6]
? 3
b.cpp: x:[?], y:[3]
? 4
b.cpp: x:[?], y:[4]
? 5
b.cpp: x:[?], y:[5]
b.cpp: x:[!], y:[5]
AC
修订记录
- 2021年4月26日 第2次修订
- 2021年4月26日 创建文章