自己实现一个程序评测器

背景

上学期在离期末还有接近一个月的时候,队领导说要在队里搞一次大学计算模拟考试,作为课代表我不得不接下这个任务。之后我去找大学计算老师问能不能安排一次考试,老师说只有一套题目,期末考试上机时的评测环境配置很麻烦不能拿来用,考试只有直接把题发下去让大家自己做一做就行了。作为负责任的课代表,我一寻思既然是考试怎么能这么水呢,我一定要想办法评测大家的程序,打出分数来。

考试形式

队里接近一百名同学,考试试题共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()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值