如何设计Codeforces交互式评分器?

sga*_*a01 7 python

我在 Codeforces 中遇到了一个交互问题。我想知道分级器或交互器(根据 Codeforces 的条款)是如何设计的。

假设我想为这个问题创建一个评分器:1. 猜数字

我对上述问题的解决方案存储在1_Guess_the_Number.py文件中。这是一个正确的解决方案,并被 CF 分级机接受。

#!/usr/bin/env python3

l, r = 1, 1000000
while l != r:
    mid = (l + r + 1) // 2
    print(mid, flush=True)
    response = input()
    if response == "<":
        r = mid - 1
    else:
        l = mid

print("!", l)
Run Code Online (Sandbox Code Playgroud)

我创建了以下grader.py文件:

#!/usr/bin/env python3

import sys

INP = 12


def interactor(n):
    if n > INP:
        return "<"
    return ">="


while True:
    guess = input()
    if guess.startswith("!"):
        print(int(guess.split()[1]) == INP, flush=True)
        sys.exit()
    print(interactor(int(guess)), flush=True)
Run Code Online (Sandbox Code Playgroud)

因此,当我运行时./1_Guess_the_Number.py | ./grader_1.py,我希望它能够正常工作。但在终端中,上述命令无限运行,仅输出以下内容:

<
Run Code Online (Sandbox Code Playgroud)

我不知道出了什么问题。另外,如果有人可以提供任何其他方式,这将非常有帮助。

use*_*_na 3

@user2357112 的评论正确地描述了它不起作用的原因。当您的管道将第一个脚本的输出发送到评分器时,您不会将“grader.py”的响应发送到第一个脚本。

所以我们需要做的是建立双向沟通。

这是一种方法。在评分器中,调用要测试的脚本subprocess并通过管道与其进行通信。我在代码中添加了额外的解释。

1_Guess_the_Number.py和你的一样:

#!/usr/bin/env python3
l, r = 1, 1000000
while l != r:
    mid = (l + r + 1) // 2
    print(mid, flush=True)
    response = input()
    if response == "<":
        r = mid - 1
    else:
        l = mid

print("!", l)
Run Code Online (Sandbox Code Playgroud)

grader.py将测试文件的名称作为输入并使用以下命令执行它subprocess.Popen

# !/usr/bin/env python3
import sys
import subprocess

INP = 12

def interactor(n):
    if n > INP:
        return "<"
    return ">="

def decodeBytes(message):
    # Helperfunction to decode the message from stdin
    # guess has the format b'12345\r\n' 
    # rstrip() removes the \r\n, then we decode it as ascii
    return message.rstrip().decode('ascii')

if __name__ == '__main__':
    print(f'The secret answer is: {INP}')

    # get script name:
    name = sys.argv[1]
    print(f'calling script {name}')

    # start script as a subprocess
    p = subprocess.Popen(["python3", name], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
        
    # create iterator to read subsequent guesses
    stdout_iterator = iter(p.stdout.readline, b"")

    # loop over all guesses
    for msg_in in stdout_iterator:

        #msg_in is a byte array, we need to decode it
        guess = decodeBytes(msg_in)
        print(f'got msg: {guess}')

        if guess.startswith("!"):
            final_answer = int(guess.split()[1])
            print(f'The final answer is: {final_answer}')
            if final_answer == INP:
                print('Answer was correct!')
                break
            print('Answer is wrong!')
            break
        
        # we determine the message to send out
        msg_out = interactor(int(guess))
        print(f'send msg: {msg_out}')
        
        # sending it to the scripts stdin. 
        p.stdin.write((msg_out + '\n').encode())
        # we need to flush stdin, to make sure the message is not stuck in a buffer
        p.stdin.flush()
Run Code Online (Sandbox Code Playgroud)

从命令行您可以通过以下方式调用评分器

python grader.py 1_Guess_the_Number.py
Run Code Online (Sandbox Code Playgroud)

打印输出是:

The secret answer is: 12
calling script 1_Guess_the_Number.py
got msg: 500001
send msg: <
got msg: 250001
send msg: <
got msg: 125001
send msg: <
got msg: 62501
send msg: <
got msg: 31251
send msg: <
got msg: 15626
send msg: <
got msg: 7813
send msg: <
got msg: 3907
send msg: <
got msg: 1954
send msg: <
got msg: 977
send msg: <
got msg: 489
send msg: <
got msg: 245
send msg: <
got msg: 123
send msg: <
got msg: 62
send msg: <
got msg: 31
send msg: <
got msg: 16
send msg: <
got msg: 8
send msg: >=
got msg: 12
send msg: >=
got msg: 14
send msg: <
got msg: 13
send msg: <
got msg: ! 12
The final answer is: 12
Answer was correct!
Run Code Online (Sandbox Code Playgroud)