Project_Lemon使用经验
如果您还没有安装Project_Lemon,那么请移步https://www.cnblogs.com/CreeperLKF/p/9201859.html去查阅安装教程
然后我们就正式开始教程.
环境配置
首先关于编译器的配置这里就不啰嗦了,可以问度娘去
然后为了方便测评,可以修改一下编译命令:
在选项的编译器下面有一个"高级"按钮,首先选中g++,然后点进去
然后下面 有一个"参数",复制其中的"编译器参数",然后在"配置"复选框中新建配置
例如我需要同时打开C++11和O2,那么
同时一个比较推荐的配置如下:
注意一个新的重要事项:如果编译不通过而显示"Failed to create temporary file in C:\WINDOWS : Permission Denied"或者是运行时提示什么.dll什么.so找不到的话,那么就把环境变量删了,编译命令C++加上-static -static-libgcc -static-libstdc++,C加上-static -static-libgcc,如果不加编译命令的话程序无法运行,会告诉你没有什么什么库,这是大概因为Lemon在Windows10下读到了一堆环境变量,而且第一条就是在根目录下的(系统的TEMP和TMP)
其中线程数按照你的CPU来,不要开到CPU上限,一般来说普通的CPU双线程就够了,8元的CPU请使用单线程,如果线程多了会导致死机(至少校内的老年奔腾死过几次QWQ)
新建比赛
一开始你打开的时候是这样的
然后如果你现在拿到了一个cdf(也就是别人的考试包),那么直接点加号然后打开cdf就好了
如果是自己的题目的话,那么就
比赛文件
首先为了方便教程,建议自己搞一个考试包,然后自己考一场,然后同步操作模拟
首先构建好比赛,对于一场新的比赛,就按照文字新建就好了,比赛的所有数据什么的都会存在一个文件夹内,例如教程使用的文件夹为4-10,然后文件夹如下:
其中红色文件就是Project_Lemon所使用的文件,分别为data测试数据,source源代码以及test.cdf配置文件
然后测试数据文件夹下一道题一个文件夹,例如这里的T3就是
默认输入文件为*x.in,输出文件为*x.out,其中x为序号
然后配置试题
首先在控制->自动添加试题中自己添加一下试题
按照提示统一设置时限
对于一般的题如果使用标准输入输出流的话勾选"标准输入"和"标准输出",否则可以使用文件输入输出,然后填写文件名。对于题答题和交互题等一下再说
选择一下语言和编译命令,特别注意比较模式最好是忽略空格
然后稍微检查一下子任务,查看时限等等
然后选手文件夹下每个选手一个文件夹,不需要子文件夹(默认情况),例如下图:
选手文件夹:
这里直接放上 ${文件名}.cpp 即可,如果有其他文件也没有关系,但是会增加以后考试备份的体积
然后就可以开始了
比赛测评
首先你会发现没有选手,然后点击一下刷新
然后就会有一些选手,并且你可以点击一些每一列来查看排名
然后接下来是测评
直接点击测试全部,然后会有一些成绩,注意到我们的第三题是一道交互题,所以我们现在暂时不测评
点击选手可以看待每题的详细信息
然后可以在控制->导出成绩处到处成绩,注意在Linux下需要手动填写后缀名.html,否则会出现无法导出的情况。
Updata:换了个UI,清真多了。
特别的东西
一些奇怪的情况
- 成群的"运行时错误":请检查您的编译器的配置以及选择,特别注意编译器的32位/64位的区别,Updata:如果您是32位机子而用Dev-Cpp的编译器的话那么编译命令需要加-m32
- "文件错误":就是IO咕咕了
- 卡测评:测评了一个点很久也没有TLE也没有继续评测:Lemon似乎在超时较小的情况下会导致重测,然后有时候会无限重测下去,这个时候应该为选手多开一点时限然后让其不影响后面的评测
- 然后还有注意的是Lemon的内存测评方式受数据大小以及数组大小同时影响,开的数组过大过多可能导致RE,所以写暴力分类讨论需要谨慎。然后如果你的某些不用数组过大,或者是使用pbds然后指针(初始化的)太多了会导致TLE,例如
和
就是某个数组开大了的结果
上面的情况同样适用于数据范围过大导致RE的结果:Lemon最大支持1024MB的测评,但是如果数组太大会RE(不知道为什么),注意这里数组太大的定义:Lemon测评结果内存尽管和实际数据大小有关,但是如果你的所有数组在一开始大于最大内存的话,它会RE而不是MLE
- 待解决:据说还有一个无法运行程序的问题,再看一看。问题已解决。大部分情况下都是因为32位机使用Dev-C++内置的编译器,但是Dev-C++同时有32bit和64bit的TDM-C++,所以编译出来默认是64bit的。需要在编译命令最后加上一个-m32。
- 关于栈:Windows直接编译时开栈,Linux先用ulimit在终端中提前开好栈,然后在终端调用Lemon
子任务
子任务要求子任务内的测试数据全AC子任务才会有分
这个一开始我也没有看懂,但是后面去UOJ群内问了一下然后自己实践了一下自己的题然后就明白了
首先您的目录结构应该是 /example/子任务$\$${x}/name$\$${n}.$\$${in,out} (${}表示这个字符串是个变量),然后例如我们的题目名字是complex,然后第一层目录结构如下:
下一层目录结构如下(马赛克的部分与教程无关,是我造数据的一些东西):
然后如果你尝试自动添加试题的话就会:
所以需要首先手动新建一个试题,然后在试题上右键然后选定添加多组测试数据:
然后配置好分值时间内存之后点击下一步,然后就会让你写正则表达式
正则表达式就像这样:
输入文件格式按照你的data下目录格式填写,前面不要带'/',然后每一栏一般会有两个需要匹配的地方,第一个是子任务编号,第二个是子任务下的测试数据编号,编号内用方括号括起来,然后新建参数,参数按照正确的正则表达式填写,例如这里的意思就是匹配1个或2个({1,2})任意字符(.),目的是匹配子任务和测试数据编号。
前面的那个勾有什么用呢?如果我们直接这么下一步,然后你就会发现每一个测试数据独立成一个测试点。这显然不是你想要的。
前面的勾选了表示这个参数不同的测试数据自成一个子任务。如果你勾选了第二个那么所有文件名为complex1的测试数据都会在一号子任务内,而你希望的应该是在同一个文件夹内的玩意自成子任务,于是你就可以选第一把勾。
效果:
交互题和题答题
交互题就是你的程序动一下然后评测程序动一下这样子,而题答题就是直接提交答案测评,然后你就可以手算数据
题答题直接改一下题目类型就可以了,然后交互题需要提前为交互库写好交互程序,然后测评,但是目前我还没有找到一种方法可以让Lemon复制交互程序过去......如果您知道请联系我......
然后还有一种东西叫做Special Judge,目前这个也不能和程序发生交互......
但是可以对于选手的运行结果特殊处理,例如以前某谷就有一道被封了的题就是根据你的输出的某种哈希来计算得分的.......
所以我们只讲Special Judge(SPJ)
Special Judge
首先你需要一个程序叫做交互程序的东西,它的写法:
首先一个正常的C++程序,然后在Special调用它的时候会通过main函数传入六个参数,传参方法就是你在main函数后面的括号如此写: int main(int argc, char *argv[]) ,然后您的程序就会获得6个参数(编译的时候建议不要加-g选项以免干扰):
argv[]:测试数据输入文件(输入) argv[]:选手输出文件(输入) argv[]:测试数据输出文件(输入) argv[]:该测试点满分分值(输入) argv[]:该测试点选手得到的分数(输出) argv[]:输出测试点信息(输出)
然后在程序内除了argv[4]直接用atoi(argv[4])转为一个整数(得分为整数)之外的字符串都是文件名,然后直接在freopen、fopen之类函数用就可以了
这里建议使用fopen( FILE *$\$${file_stream_name} = fopen($\$${file_path_name(const char *),$\$${open_mode}) ),在最后 fflush() 一下输出的流,然后及时用 fclose(${file_stream_name}) 关闭文件,以及配套的fscanf和fprintf(在原函数上在开头增加一个参数FILE *表示文件流)。因为有时候会因为打开文件数量过多而没有关闭导致打开文件失败以及Lemon无法正常读取文件
例如下面的一个程序就是一个没有实际意义(只是比较整数)而只是一个好玩的SPJ而已:
#include <cstdio> #include <iostream> using namespace std; int main(int argc, char *argv[]){ int std_score, wrong_score; FILE *STD_INP = fopen(argv[], "r"); FILE *PLY_OUT = fopen(argv[], "r"); FILE *STD_OUT = fopen(argv[], "r"); std_score = atoi(argv[]); FILE *PLY_SCR = fopen(argv[], "w"); FILE *ERR_RES = fopen(argv[], "w"); int n; fscanf(STD_INP, "%d", &n); wrong_score = std_score * 0.3; ; i <= n; i++){ long long a, b; fscanf(PLY_OUT, "%lld", &a), fscanf(STD_OUT, "%lld", &b); if(a != b){ fprintf(ERR_RES, "Oh, My God! You told me the answer is %lld on line %d but in fact it is %lld. You Will ONLY Get %d!\n", a, i, b, wrong_score); fprintf(PLY_SCR, "%d\n", wrong_score); goto FILE_END; } } fprintf(ERR_RES, "EXCELLENT! You Got Full Marks!\n"); fprintf(PLY_SCR, "%d\n", std_score); FILE_END: fflush(PLY_SCR), fflush(ERR_RES); fclose(STD_INP), fclose(PLY_OUT), fclose(STD_OUT), fclose(PLY_SCR), fclose(ERR_RES); ; }
中间用了一个goto是为了关闭文件。
然后就是在这里面修改一下评测方式,框内填可执行文件路径
然后正常评测
然后如果评测出来写了“无效校验器”,那么请检查IO,如果全都答案错误,请留意得分那里的main传入的参数直接就是分数
然后目前介绍就到此位置,后续可能会继续补充,特别是交互器部分