目录
前言
前几天刚刚写完了c++编写评测机(1),今天又来更2了。这更新速度,必须来个三连啊。
评测机1.0.0版本中还有很多可以优化的部分,比如:刚打开就进行评测,评测完要再次运行才能继续评测,无法选择忽略空格与换行,没有标程的情况下无法评测。v1.0.1对其中一部分进行了完善。
v1.0.1版本的评测机主要更新了以下内容:
- 优化了软件界面,可以多次重复评测。
- 可以选择对拍时忽略与不忽略空格,忽略与不忽略换行。
- 将一些杂七杂八的函数写到一个头文件里。
完善过程
1.针对评测机刚运行就进行评测,评测完要再次运行才能继续第二次评测的问题,我们可以在程序最外层加上while(1)死循环,并且只有输入1才进行评测,评测结束后输入0回到主页可以再次评测。这是优化后的部分代码:
while(1){
Clear_Qiuckedit();
hide();
gotoxy(1, 1);
printf("1:评测 2:设置\n");
char choose = getch();
if(choose == '1'){
system("cls");
if(!initialize()){
printf("0:返回\n");
Mypause('0');
continue;
}
start_judge();
printf("0:返回\n");
Mypause('0');
continue;
}
}
2.针对评测时的对拍方式,我们可以在主页增加一个选项,就是输入2即可进入设置。设置也有两种,一种是选择是否忽略空格,一种是选择是否忽略换行。并且后面还配有“√”与“-”进行图形化展示选项。在评测时,只要注意对ans.out和std.out中的字符串进行按照评测室的选项特殊处理就好了。代码如下:
else if(choose == '2'){
while(1){
system("cls");
printf("0:返回\n1:评测方式\n2:比较方式\n");
char choosea = getch();
if(choosea == '1'){
system("cls");
printf("暂不支持\n0:返回\n");
Mypause('0');
}else if(choosea == '2'){
while(1){
system("cls");
printf("0:返回\n1:忽略空格");
if(ifblank){
printf("-\n");
}else{
printf("√\n");
}
printf("2:忽略换行");
if(ifenter){
printf("-\n");
}else{
printf("√\n");
}
char choose2 = getch();
if(choose2 == '1') ifblank = !ifblank;
else if(choose2 == '2') ifenter = !ifenter;
else if(choose2 == '0'){
system("cls");
break;
}
}
}else if(choosea == '0'){
system("cls");
break;
}
}
}
可以看到,这里又用到了while(1),这样可以避免选择完后立即回到主页,想要选择就得再次点击”“设置”的尴尬情况。
对于后续的字符串处理,我们可以通过删除的方式忽略空格或换行符,具体看代码:
void enter_deal(const char* filename, bool ifblank = 1, bool ifenter = 1){
int i, j = 0;
char s[10000];
FILE *p;
p = fopen(filename, "r");
fscanf(p, "%[^$]", s);
fclose(p);
for(int i = 0; i < strlen(s); i++){
if((!ifblank && s[i] == ' ') || (!ifenter && s[i] == '\n'))
continue;
s[j++] = s[i];
}
s[j] = 0;
p = fopen(filename, "w");
fprintf(p, "%s", s);
fclose(p);
return;
}
其中ifblank变量指的是是否忽略空格,ifenter变量指的是是否忽略换行。特别注明:换行符是‘\n',占一个字符,和‘a’、‘b'等一样可以操作,只不过电脑输出时是以换行的方式呈现的。
3.可以看到,现在的代码函数已经非常多了,为了方便后续的更改,我们把这些函数放入一个头文件‘tool.h’里。把自定义的变量放入自己的名字空间内。下面介绍如何自定义头文件与名字空间。
如何自定义头文件与名字空间
博主写这个评测机的目的就是为了学习嘛,所以与评测机无关的东西可能有点多。
1.如何自定义与使用头文件。
我们只需要新建一个文件,然后在里面放入如下代码
#ifndef TOOLS_H
#define TOOLS_H
#endif
其中TOOLS_H中的TOOLS是头文件名称,大小写随意。
然后在中间编写正常的程序,一般只在头文件中写函数,变量与宏最好不要写进去。不过博主为了代码美观,将变量放到一个名字空间中放入头文件。变量与宏不写进头文件的主要原因是可能在引用头文件的程序中使用了重复的变量名或宏,但由于评测机.cpp中没有任何变量,所以可以放心写进头文件中。
那么自定义头文件该怎么用呢?有静态和动态链接两种方式,本文只讲静态。将头文件命名为XXX.h,注意:一定是.h,然后引用时写成include"XXX.h",例如本文就写成include"tools.h"。这样就可以在程序中引用头文件中的函数啦。
2.如何自定义名字空间。
大家最常用的名字空间就是std了,不过它也可以自己定义。
定义方法如下:namespace 名字空间的名称。用时写成:using namespace 名字空间的名称。并且多个名字空间可以同时引用。例如:
namespace mname{
char textpoint[10000];
struct node{
int time_limit, point;
}sample[1005];
int cnt, mysco, totlesco;
int outcheck_std, outcheck_ans;
const int maxn = 100005;
char ANS[maxn], STD[maxn] = "";
clock_t start, end;
double elapsed_secs;
const char *name1 = "test.in";
string samplename = "sample";
bool ifblank = 1, ifenter = 1;
}
using namespace mname;
完整代码
知道了如何完善后,大家可以自己动手完善。不过我还是会附上源码,毕竟(有白嫖怪)这是个开源项目嘛。
评测机.cpp
//v1.0.2
#include"tools.h"
int main(){
color(7);
while(1){
Clear_Qiuckedit();
hide();
gotoxy(1, 1);
printf("1:评测 2:设置\n");
char choose = getch();
if(choose == '1'){
system("cls");
if(!initialize()){
printf("0:返回\n");
Mypause('0');
continue;
}
start_judge();
printf("0:返回\n");
Mypause('0');
continue;
}
else if(choose == '2'){
while(1){
system("cls");
printf("0:返回\n1:评测方式\n2:比较方式\n");
char choosea = getch();
if(choosea == '1'){
system("cls");
printf("暂不支持\n0:返回\n");
Mypause('0');
}else if(choosea == '2'){
while(1){
system("cls");
printf("0:返回\n1:忽略空格");
if(ifblank){
printf("-\n");
}else{
printf("√\n");
}
printf("2:忽略换行");
if(ifenter){
printf("-\n");
}else{
printf("√\n");
}
char choose2 = getch();
if(choose2 == '1') ifblank = !ifblank;
else if(choose2 == '2') ifenter = !ifenter;
else if(choose2 == '0'){
system("cls");
break;
}
}
}else if(choosea == '0'){
system("cls");
break;
}
}
}
}
return 0;
}
tools.h
#ifndef TOOLS_H
#define TOOLS_H
#include<bits/stdc++.h>
#include<windows.h>
#include<conio.h>
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
#define WIFEXITED(status) ((status & 0x7f) == 0)
using namespace std;
namespace mname{
char textpoint[10000];
struct node{
int time_limit, point;
}sample[1005];
int cnt, mysco, totlesco;
int outcheck_std, outcheck_ans;
const int maxn = 100005;
char ANS[maxn], STD[maxn] = "";
clock_t start, end;
double elapsed_secs;
const char *name1 = "test.in";
string samplename = "sample";
bool ifblank = 1, ifenter = 1;
}
using namespace mname;
void Clear_Qiuckedit(){
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
GetConsoleMode(hStdin, &mode);
mode &= ~ENABLE_QUICK_EDIT_MODE;
SetConsoleMode(hStdin, mode);
}
void hide(int hide_type = 0){
CONSOLE_CURSOR_INFO cursor_info = {1, hide_type};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
void color(int x, bool intensity = true){
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
x | (intensity << 3));
}
void gotoxy(int x, int y){
COORD pos = {y - 1, x - 1};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
void print(string s, int n){
gotoxy(1, 1);
cout << "状态:";
color(n);
cout << s << " " << endl;
color(7);
}
bool findfile(const char * find_filename){
FILE *fp;
fp = fopen(find_filename, "r");
fclose(fp);
return fp;
}
void enter_deal(const char* filename, bool ifblank = 1, bool ifenter = 1){
int i, j = 0;
char s[10000];
FILE *p;
p = fopen(filename, "r");
fscanf(p, "%[^$]", s);
fclose(p);
for(int i = 0; i < strlen(s); i++){
if((!ifblank && s[i] == ' ') || (!ifenter && s[i] == '\n'))
continue;
s[j++] = s[i];
}
s[j] = 0;
p = fopen(filename, "w");
fprintf(p, "%s", s);
fclose(p);
return;
}
bool compare(const char* filename1, const char* filename2){
int i, j = 0;
char s1[10000];
FILE *p1;
p1 = fopen(filename1, "r");
fscanf(p1, "%[^$]", s1);
fclose(p1);
char s2[10000];
FILE *p2;
p2 = fopen(filename2, "r");
fscanf(p2, "%[^$]", s2);
fclose(p2);
if(strlen(s1) != strlen(s2)) return false;
for(int i = 0; i < strlen(s1); i++){
s1[j++] = s1[i];
}
j = 0;
for(int i = 0; i < strlen(s1); i++){
s2[j++] = s2[i];
}
for(int i = 0; i < strlen(s1); i++){
if(s1[i] != s2[i]) return false;
}
return true;
}
void getpoint(const char *filename){
int i, j = 0;
FILE *p;
p = fopen(filename, "r");
fscanf(p, "%[^$]", textpoint);
fclose(p);
for(int i = 0; i < strlen(textpoint); i++){
textpoint[j++] = textpoint[i];
}
textpoint[j] = 0;
int f;
for(int i = 0; i < strlen(textpoint); i++){//获得测试点个数
if(textpoint[i] == '#'){
f = i;
break;
}
if(textpoint[i] >= '0' && textpoint[i] <= '9')
cnt = cnt * 10 + textpoint[i] - '0';
}
int now = 0;
for(int i = f; i <= strlen(textpoint); i++){//获取测试点时间限制及分数
if(textpoint[i] == '#'){
now++;
}
else if(textpoint[i] == ':'){
int nowlimit = 0;
for(int j = i; j <= strlen(textpoint); j++){
if(textpoint[j] == ','){
int nowpoint = 0;
for(int k = j; k <= strlen(textpoint); k++){
if(textpoint[k] == '#'){
break;
}
if(textpoint[k] <= '9' && textpoint[k] >= '0'){
nowpoint = nowpoint * 10 + textpoint[k] - '0';
}
}
sample[now].point = nowpoint;
break;
}
if(textpoint[j] >= '0' && textpoint[j] <= '9') nowlimit = nowlimit * 10 + textpoint[j] - '0';
}
sample[now].time_limit = nowlimit;
}
}
}
void printsample(const char *filename, const char *filename2){
int i, j = 0;
char s[10000];
FILE *p;
p = fopen(filename, "r");
fscanf(p, "%[^$]", s);
fclose(p);
for(int i = 0; i < strlen(s); i++){
s[j++] = s[i];
}
s[j] = 0;
p = fopen(filename2, "w");
fprintf(p, "%s", s);
fclose(p);
return;
}
void judge(){
for(int i = 1; i <= cnt; i++){
totlesco += sample[i].point;
cout << "#" << i << ": ";
char temp[10000];
sprintf(temp, "%d", i);
string toname = samplename + temp + ".in";
const char *name2 = toname.c_str();
if(!findfile(name2)){
cout << "未找到" << name2 << "!\n";
continue;
}
printsample(name2, name1);
outcheck_std = system("std.exe");
start = clock();//
outcheck_ans = system("ans.exe");
end = clock();//
elapsed_secs = static_cast<double>(end - start);//
if(outcheck_std == -1 || outcheck_ans == -1 || WIFEXITED(outcheck_std) == false || WIFEXITED(outcheck_ans) == false || 0 != WEXITSTATUS(outcheck_std) || 0 != WEXITSTATUS(outcheck_ans)){
color(5);
cout << "RE";
color(7);
cout << "(" << elapsed_secs << "ms)\n";
continue;
}
if(elapsed_secs > sample[i].time_limit){
color(3);
cout << "TLE";
color(7);
cout << "(" << elapsed_secs << "ms)\n";
continue;
}
bool ifcontinue = 1;
if(!findfile("ans.out")){
cout << "未找到输出!\n";
ifcontinue = 0;
}
if(!findfile("std.out")){
cout << "未找到输出!\n";
ifcontinue = 0;
}
if(!ifcontinue) continue;
enter_deal("ans.out", ifblank, ifenter);
enter_deal("std.out", ifblank, ifenter);
int count_ans = 0, count_std = 0;
freopen("std.out", "r", stdin);
scanf("%s", STD);
freopen("ans.out", "r", stdin);
scanf("%s", ANS);
if(compare("ans.out", "std.out")){
color(2);
cout << "AC";
color(7);
cout << "(" << elapsed_secs << "ms)\n";
mysco += sample[i].point;
}
else{
color(4);
cout << "WA";
color(7);
cout << "(" << elapsed_secs << "ms)\n";
}
// printf("Your answer:\n%s\nCorrect answer:\n%s\n", ANS, STD);//输出用户答案以及正确答案
}
}
void start_judge(){
getpoint("textpoint.txt");
print("提交中", 7);
int i = 0, j = 0;
// freopen("report.out","w",stdout);
// system("del ans.out");
// system("del std.out");
// system("del report.out");
// system("del ans.exe");
// system("del std.exe");
gotoxy(1, 1);
print("编译中", 7);
if(sizeof(char*) == 4){
int status_std32 = 0, status_ans32 = 0;
status_std32 = system("g++ -o std.exe -m32 std.cpp");
status_ans32 = system("g++ -o ans.exe -m32 ans.cpp");
if(status_ans32 == -1 || status_std32 == -1 || WIFEXITED(status_ans32) == false || WIFEXITED(status_std32) == false || 0 != WEXITSTATUS(status_ans32) || 0 != WEXITSTATUS(status_std32)){
print("CE", 6);
return ;
}
}
if(sizeof(char*) == 8){
int status_std = 0, status_ans = 0;
status_std = system("g++ -o std.exe std.cpp");
status_ans = system("g++ -o ans.exe ans.cpp");
if(status_ans == -1 || status_std == -1 || WIFEXITED(status_ans) == false || WIFEXITED(status_std) == false || 0 != WEXITSTATUS(status_ans) || 0 != WEXITSTATUS(status_std)){
print("CE", 6);
return ;
}
}
print("评测中", 7);
judge();
print("评测结束", 7);
gotoxy(cnt + 2, 1);
cout << "分数:" << mysco << "(/" << totlesco << ")" << endl;
printf("结果:");
if(totlesco == mysco){
color(2);
printf("AC\n");
color(7);
}
else{
color(4);
printf("WA\n");
color(7);
}
}
//void deletefile(){
// system("del ans.exe");
// system("del std.exe");
// system("del ans.out");
// system("del std.out");
// system("del test.in");
//}
bool initialize(){
bool back = 1;;
if(!findfile("std.cpp")){
printf("未找到std.cpp!\n");
back = 0;
}
if(!findfile("ans.cpp")){
printf("未找到源程序!\n");
back = 0;
}
if(!findfile("textpoint.txt")){
printf("请创建textpoin.txt并将测试点数据存入\n");
back = 0;
}
cnt = 0;
totlesco = 0;
mysco = 0;
return back;
}
void Mypause(char to){
while(1){
if(kbhit()){
char ch = getch();
if(ch == to){
system("cls");
break;
}
}
}
}
使用方法除了多了一个头文件,其他和v1.0.0版本一样。
后续透露
下一个版本的改动比较大,主要是针对访问指定目录下文件进行性优化。给个三连,让我有更下去的动力~