背景
上学期在离期末还有接近一个月的时候,队领导说要在队里搞一次大学计算模拟考试,作为课代表我不得不接下这个任务。之后我去找大学计算老师问能不能安排一次考试,老师说只有一套题目,期末考试上机时的评测环境配置很麻烦不能拿来用,考试只有直接把题发下去让大家自己做一做就行了。作为负责任的课代表,我一寻思既然是考试怎么能这么水呢,我一定要想办法评测大家的程序,打出分数来。
考试形式
队里接近一百名同学,考试试题共7道,每道题设置成20分,十个测试点,每个测试点2分,满分140,题目是现成的,数据标程什么的都得我自己写,这一部分很简单,题目很水,数据标程分分钟搞定。
首先,在./scr/std中放上标程,然后把标程中核心函数内容抠掉,剩下的作为模板框架放进下发文件夹里,当然题目也得放在下发文件夹里。之后再把每道题标程放进data文件夹下对应每道题的文件夹中,对每道题写好makedata.py,然后利用标程生成.in和.ans文件作为输入和标准输出。
之后的部分就是评测器,我一开始想到的是用cena,但是这玩意儿好像不能评测python,无奈之下,我打算自己搞一个脚本来评测。于是我找到cycleke,让他帮我写了一个大致的框架,然后我再自己实现想要的功能。其中最麻烦的是判断超时,如果不判超时评测过程中很有可能遇到死循环,我百度到一种方法,把程序放入子线程中跑,超时就直接kill掉,我尝试了一下这种做法,发现只在linux下有用,在win下仍然会崩溃。不过这都无所谓了,只要能够评测出来分数就够了。
评测完毕之后再将每个人的成绩保存下来就大功告成了。
评测代码如下
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# __ _,--="=--,_ __
# / \." .-. "./ \
# / ,/ _ : : _ \/` \
# \ `| /o\ :_: /o\ |\__/
# `-'| :="~` _ `~"=: |
# \` (_) `/
# .-"-. \ | / .-"-.
# .---{ }--| /,.-'-.,\ |--{ }---.
# ) (_)_)_) \_/`~-===-~`\_/ (_(_(_) (
# ( Coding like a dog )
# ) (
# '---------------------------------------'
import os
import signal
import time
import threading
import pandas as pd
def copy_file(a, b):
fa = open(a, "rb")
fb = open(b, "wb")
fb.write(fa.read())
fa.close()
fb.close()
def check_ignore_extra_space(file_a, file_b):
str_a = open(file_a, "r").read()
str_b = open(file_b, "r").read()
if str_a == str_b:
return 2, True
list_a = str_a.split("\n")
list_b = str_b.split("\n")
while list_a and list_a[-1] == "":
list_a.pop()
while list_b and list_b[-1] == "":
list_b.pop()
if len(list_a) != len(list_b):
return 0, False
for i in range(len(list_a)):
a = list_a[i].split()
b = list_a[i].split()
while a and a[-1] == "":
a.pop()
while b and b[-1] == "":
b.pop()
if a != b:
return 0, False
return 2, True
class Checker:
"""
Checker is a class to check the standard answer
and user's answer.
"""
def __init__(
self,
problem_name,
check_fn=check_ignore_extra_space,
input_extension=".in",
output_extension=".out",
ans_extension=".ans",
src_folder="src/",
data_folder="data/",
id_range=range(0, 20),
):
"""
check_fn is the function that checks the answers.
The default check_fn will igore the extra space
at the end of each line.
"""
self.problem_name = problem_name
self.check_fn = check_fn
self.input_extension = input_extension
self.output_extension = output_extension
self.ans_extension = ans_extension
self.src_folder = src_folder
self.data_folder = data_folder
self.in_file = "tmp/" + problem_name + input_extension
self.out_file = "tmp/" + problem_name + output_extension
self.id_range = id_range
def get_score(self, usr_name):
#@set_timeout(1, after_timeout)
score = []
def runprog():
os.system("python tmp/main.py < %s > %s" % (self.in_file, self.out_file))
total_score = 0
for self.problem_name in range(7):
self.problem_name = str(self.problem_name)
src = self.src_folder + usr_name + "/" + self.problem_name + ".py"
if not os.path.exists(src):
cur_score=0
print(self.problem_name+':File_not_found')
else:
copy_file(src, "tmp/main.py")
for idx in self.id_range:
input_file = (
self.data_folder + self.problem_name + '/' + self.problem_name + '_' + str(idx) + self.input_extension
)
std_ans_file = (
self.data_folder + self.problem_name + '/' + self.problem_name + '_' + str(idx) + self.ans_extension
)
copy_file(input_file, self.in_file)
t = threading.Thread(target=runprog)
t.setDaemon(True)
t.start()
t.join(3) #调整每组数据测试时间
cur_score, is_correct = self.check_fn(self.out_file, std_ans_file)
if is_correct:
result = self.problem_name + "_" + str(idx) + " :Accepted"
print(result)
score.append(result)
else:
result = self.problem_name + "_" + str(idx) + " :Wrong Answer"
print(result)
score.append(result)
total_score += cur_score
score.append('total_score = ' + str(total_score))
file_ = self.src_folder + usr_name + '/' + 'score.txt'
f = open(file_,'w')
for res in score:
f.write(res)
f.write('\n')
f.close()
return total_score
def get_number(x):
return int(filter(str.isdigit,str(x)))
def main():
checker = Checker("pluse",id_range=range(10))
#score = checker.get_score("std")
path = "./src"
files = os.listdir(path)
files.sort()
name = []
sco = []
for file_ in files:
print(file_ + ':')
name.append(file_)
score = checker.get_score(file_)
sco.append(score)
print(score)
output = pd.DataFrame({'Name':name,'score':sco})
output.to_csv('all_score.csv',index=False)
if __name__ == "__main__":
main()