可以看这个题目

快速配置一个turtle题目

打开右侧文件链接

下载全部文件

checker.py.py3turtle
compile.sh
config.yaml
controller.py
execute.sh
turtlecompare.py

上传到想要配置的题目中

修改checker.py.py3turtle中间部分代码,改为画出正确图形的代码

import turtle as t 
import turtlecompare
​
t.speed(0)
​
# 这里开始是根据题目获取
# 如果为其他题目写checker,只需要改这一部分
​
# TODO 在这里开始写画出正确图形的turtle代码
​
## 注意, 由于是图片比较, 需要根据题面决定是否hideturtle
## 也要明确告知学生没有根据要求hideturtle就是WA
t.hideturtle()
# 需要修改的到这里结束
​
# 正确图案的画法已经转备好了
# 调用turtlecompare去判断是否一致
turtlecompare.compare()

这时候应该ok了,可以写一个AC的代码和一个错误的代码测试一下(注意是评测而不是自测)

技术方案详解

具体的做法就是用testlib自己些checker,去写代码进行测试用例的正确性判断

testlib checker

Github

Codeforces上的说明,几个状态主要在这里

codeforces来的产物,judge系统会将[用户输入(出题人配置的输入数据)、用户输出(用户提交的数据)、答案数据(出题人配置的答案数据)] 三个流调用运行checker程序,HydroJudge根据stderr的结果来判定本次测试的结果(看代码)

如果stderr读到 "ok \n" 表示AC,其他则为WA,并将stderr的内容作为WA的信息进行输出(所以如果需要给出错误的提示,可以写在这个地方,比如 return: 999 expected: -1,表示正确答案是-1,但程序返回了999 )

设置checker类型的评测机制

评测文件中的config.yaml可以对评测进行配置,这个文件的文档

time: 5000ms
checker_type: testlib
checker: checker.py.py3turtle
user_extra_files:
  - execute.sh
  - compile.sh
  - controller.py
judge_extra_files:
  - turtlecompare.py # 评测需要用到这个文件里面的比较程序
cases:
- input: /dev/null # 本题无输入,当然你也可以设计成 input 一个数字,绘制对应边长的图形,等等
  output: /dev/null
langs:
  - py.py3turtle

注意:大部份的题目里面,只需要对checker.py.py3turtle进行修改即可

程序运行的配置

我们如何评测用户写好的代码能否画出对应的图形呢?当前的思路是

利用execute.sh去无头运行xvfb-run

由于turtle、tkinter需要有显示,judge其实是个后台跑的程序,需要用xvfb-run来运行

将用户写好的turtle程序转成输出

传统的judge系统就是根据用户程序的stdout和正确答案进行比对来判断程序正确性,沿用这个思路,我们将turtle的画布转成一个postscript串,再print()到stdout上,就可以将用户程序的结果流到checker中。

比较正确答案和用户输出

上一步让我们能在checker中拿到画好图像的postscript串。接下来我们只需要基于这个串生成pillow Image (user_out_image) ,在生成一个正确答案的pillow Image(answer_image)。将两者的尺寸与逐个像素进行比较,相差的像素数量小于0.5%(千分之五),就认为用户的程序结果是正确的。

以下是turtlecompare的实现

import turtle
import io
import sys
from PIL import Image, ImageChops
​
​
def get_size(img):
    return img.size
​
def compare_by_pixel(img1, img2, w, h):
    cnt = 0
    diff_cnt = 0
    for i in range(w):
        for j in range(h):
            p1 = img1.getpixel((i, j)) 
            p2 = img2.getpixel((i, j)) 
            if p1 == (255,255,255) and p2 == (255,255,255) :
                continue
​
            cnt += 1;
            if p1 != p2:
                diff_cnt += 1
    return diff_cnt / cnt
            
​
def compare():
    ## 将checker里面当前的canvas str拿出来
    answer_ps = turtle.getcanvas().postscript(colormode='color')
    answer_canvas_image = Image.open(io.BytesIO(answer_ps.encode('utf-8')))
    answer_im = answer_canvas_image
    ## 将foo里面用户画好的当前canvas str拿出来
    #sys.stderr.write(open("user_out").read())
​
    user_im = Image.open("user_out")
​
    answer_img_size = get_size(answer_im)
    user_img_size = get_size(user_im)
​
​
    if answer_img_size != user_img_size:
        sys.stderr.write("size is different: " + str(answer_img_size[0]) + ", " + str(answer_img_size[1]) + ", " 
        + str(user_img_size[0]) + ", "+ str(user_img_size[1]))
    else:
        diff = compare_by_pixel(answer_im, user_im, answer_img_size[0], answer_img_size[1])
        if diff < 0.005: # 正确性阈值 0.5%
            sys.stderr.write('ok \n')
        else:
            sys.stderr.write("img is different:" + str(diff))
​

Turtle 题目checker

checker的逻辑很简单,先用turtle画出正确的图形,再调用turtlecompare文件去进行图片大小和像素的匹配

import turtle as t 
import turtlecompare
​
t.speed(0)
​
# 这里开始是根据题目获取
# 如果为其他题目写checker,只需要改这一部分
t.penup()
t.forward(50)
t.lt(90)
t.bk(50)
t.pendown()
​
for i in range(4):
    t.fd(100)
    t.rt(90)
​
## 注意, 由于是图片比较, 需要根据题面决定是否hideturtle
## 也要明确告知学生没有根据要求hideturtle就是WA
t.hideturtle()
# 需要修改的到这里结束
​
# 正确图案的画法已经转备好了
# 调用turtlecompare去判断是否一致
turtlecompare.compare()

环境配置

这个主要是再次搭建hydrooj的时候用到,可以不看

配置py.py3turtle语言

在hydrooj网页版 - 控制面板 - 系统设置 - langs里面添加语言设置(这样judge系统才知道后缀是py3.py3turtle的checker文件要怎么运行)

py.py3turtle:
  compile: >-
    /usr/bin/python3 -c "import py_compile; py_compile.compile('/w/foo.py',
    '/w/foo', doraise=True)"
  execute: xvfb-run /usr/bin/python3 foo # checker也用了turtle去画出正确图形,所以需要用xvfb-run运行。如果是直接准备好ps图像文件,就不需要了
  display: Python 3 Turtle

配置运行环境的包

上述的技术方案中需要使用xvfb-run / ghostscript / python-tkinter / python-pillow

由于hydrooj用nix搭建运行环境,所以上述的安装需要在nix search里面找到对应的包进行安装

nix-env -iA nixpkgs.xvfb-run
nix-env -iA nixpkgs.ghostscript
nix-env -iA nixpkgs.python310Packages.tkinter
nix-env -iA nixpkgs.python310Packages.pillow

此外,在hydrooj的系统设置中,还需要配置pythonlib的位置

PYTHONPATH=/lib/python3.10/site-packages

最后通过重启hydrooj / hydro-sandbox两个应用,环境就可以用了

pm2 restart hydrooj
pm2 restart hydro-sandbox
​

0 条评论

目前还没有评论...