构建一个自己的 OJ

构建一个自己的 OJ

什么是 OJ

OJ 全称 Online Judge System (在线评测系统),用户在线的提交程序源代码,OJ 对这些源代码进行编译和运行,并且通过每道题目预先设计的数据和时空限制等标准来检验用户程序的正确性。OJ 最初使用于 ICPC 和 OI 竞赛中的自动判题和排名。随着时代的发展,现已经广泛应用于各高校、各组织的算法竞赛中。现如今,OJ 往往附加了其他功能以提供给用户更好的学习或比赛环境。例如大家熟知的力扣就是一个适合用来学习的 OJ。还有一些有名的 OJ :杭州电子科技大学(acm.hdu.edu.cn/)、北京大学(http://poj.org/)、牛客网https://codeforces.com/等等。

搭建一个 OJ

这里我选择了 qduoj 这个开源 OJ 为例,简单演示一下搭建

github:https://github.com/QingdaoU/OnlineJudge

官方文档:https://opensource.qduoj.com/#/

官方 DEMO :https://qduoj.com/

搭建环境:ubuntu 16.04

安装

1
2
3
git clone -b 2.0 https://github.com/QingdaoU/OnlineJudgeDeploy.git 
cd OnlineJudgeDeploy
docker-compose up

问题处理

看图可以很明确的 80 端口被占用了,可以通过杀 80 端口进程或者改 OJ 的端口处理,我就选择了改 OJ 端口

这里我就要吐槽一下了,在测试的适合我一不小心多打了了 :没注意到就直接启动了服务

结果启动过程中居然没有任何报错~!!!!(当然也是不能正常使用啦)

后台管理

通过浏览器访问服务器的 HTTP 80 端口或者 HTTPS 443 端口(不改端口的情况下),就可以开始使用了。后台管理路径为/admin, 安装过程中自动添加的超级管理员用户名为 root,密码为 rootroot, 请务必及时修改密码

展示一下

汉化

之前在参加 ACM 竞赛的时候遇到过一些纯英文的网站,我用着不是很习惯(题目写不出来已经很烦了,什么破 OJ 还是英文的就更烦了),所以我在尝试开源 OJ 的时候特意选择了有汉化功能的

拉取前端文件夹

1
git clone https://github.com/QingdaoU/OnlineJudgeFE.git

进入配置文件夹

1
cd OnlineJudgeFE/src/i18n/

修改配置文件

1
vim index.js

根据 languags 中的选项,修改 VueI18n 即可

生成dist文件 (需要 npm)

1
npm run build

将 dist 文件夹复制到 OnlineJudgeDeploy/data/backend/dist

在 docker-compose.yml 中添加

1
- ./data/backend/dist:/app/dist

重启

1
docker-compose up -d

使用 OJ

进入后台管理

后台管理路径:ip:port/admin, 填写相关题目描述即可

测试类型:ACM 类型是需要通过全部测试数据,其最后的结果只有通过不通过(还有一些超出限制、编译错误之类的)。OI 则是按点给分,通过多少样例就给多少分。

还有一个比较特殊的就是 Special Judge 。其实也好理解的,就是有些题目答案不唯一,就无法使用测试数据这种方式了,则需要通过 Special Judge 的代码验证是否通过。

添加测试数据

创建步骤:

  • 新建一个文件夹
  • 在文件夹里添加第一个测试用例: 新建一个1.in文件和一个1.out文件,在1.in文件中添加输输入用例,在1.out文件中添加输出用例
  • 再在文件夹里添加第二个测试用例: 新建一个2.in文件和一个2.out文件,在2.in文件中添加输输入用例,在2.out文件中添加输出用例
  • 以此类推创建多个测试用例
  • 将整个文件夹打包成压缩包,上传到此处

出一道题

一般情况下出题的数据都是生成随机数然后执行正确的代码生成的。不过这样的方式还是不行的,需要人工制造出一些特殊数据来,例如上图就需要手动构造出一个 b = 0 的测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <bits/stdc++.h>
using namespace std;

int main() {
// 随机种子数
srand(time(0));
// 输出流
ofstream ofs1;
ofstream ofs2;
// 生成测试数据组数
for(int i = 1; i <= 10; i++) {
int a,b;
a = rand()%1000;
b = rand()%1000;
char str[20];
// 写入输入数据
sprintf(str, "%d.in", i);
ofs1.open(str, ios::out);
ofs1 << a << " " << b;
sprintf(str, "%d.out", i);
// 写入输出数据
ofs2.open(str , ios::out);
if (b == 0) {
ofs2 << -1;
} else {
ofs2 << a/b;
}
ofs1.close();
ofs2.close();
}

return 0;
}

压缩上述文件后缀为 .zip (不能有二级目录即压缩包解压后就是上图这样)

题外话

在大部分 OJ 上我们并不能看到错误的数据是哪个(毕竟这是人家自己出题的,不给数据也合理),在题目复杂的情况下,对着标程来看,有时候也是看不出来自己的程序有什么问题,这时候也可以使用上述产生随机数的方式分别跑自己的程序和标程,通过比较结果来找到自己代码的漏洞。

当然,在上时间的刷题下我对于标程不是百分百的信任的,所以也会通过这个方式找到标程的问题。比较典型的就是 codeforces 中的 hack(通过构造特殊的数据,用来将人家已经通过的代码“拉落马下”),很可惜,我大部分时间都是被 hack 的那个。

题库

有时候如果不想自己出题的话,可以直接从别的网站中下好现有的题目,直接导入即可
推荐一个题库网站 TK题库

  • 点击链接跳转后选择问题
  • 找到一道合适的题目后点击下载
  • 下载后会得到一个fps格式的文件,在”导入导出题目“中选择导入fps格式的题目即可

拉一个比赛

  • 新建一个比赛,按照页面输入相关信息即可
  • 在比赛中添加题目
  • 查看实时排名