競プロインタラクティブ問題のローカルテスト
インタラクティブ形式の問題とは、入力が最初の1回きりではなく、 こちらの質問(出力)に従ってジャッジが回答となる新たな入力を与えるのを繰り返し、 その情報を元に隠された何かを突き止めるタイプの問題。
こういう形式は、提出前に正しく動作するかテストがしにくい。 また最大ケースに対してクエリ数がどれくらいになるかも、ちゃんと調べたい。
ジャッジ側のプログラムは自分で書くしかないが、それが比較的簡単に書けるなら、
Pythonではたとえば以下のように書くと、提出コードとジャッジ側の応答をシミュレートし、その履歴が得られる。
クエリを「? XXX
」、解答を「! XXX
」で送るような問題に対する実装例とする。
動作や諸注意
まず実行すると、judge()
が呼ばれる。
judge()
では自身をsubprocessで起動する。
ここで起動した方は judge()
には進まず提出コードの方が実行されるよう、コマンドライン引数などで切り分ける。
プロセスを開始する Popen()
の引数を stdin=PIPE, stdout=PIPE
とすることにより、プロセス同士の対話が可能になる。
入出力のやりとりはstring型でなくbytes型なので、ジャッジ側が送ったり受け取ったりする文字列には decode, encode
が必要となる。
競プロではまずascii文字しか使わないので、デコードに用いる文字コードは utf8
や ascii
でよい。
(AtCoderではデフォルト文字コードが utf-8
なので無指定でもよいが、Codeforcesだと未定義なのでちゃんと指定しないとエラーになる)
また末尾の改行も、明示的に付与しないと提出コード側が入力待ちのまま動かなくなる。忘れないよう注意。
実装例
def judge(): import sys import subprocess sin = '3 1 4 1 5\n' # 最初の入力 ans = 'TheAnswerOfSampleCase' # 隠された答え query_count = 0 # クエリ回数カウント encoding = 'utf-8' with subprocess.Popen([sys.executable, __file__, 'DUMMY'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: # はじめに入力を与える処理 p.stdin.write(sin.encode(encoding)) p.flush() while True: query = p.stdout.readline().decode(encoding).strip() print(query) if query[0] == '?': # クエリへの返答を計算する処理 response = 'hogehoge' print(response) p.stdin.write(f'{response}\n'.encode(encoding)) p.stdin.flush() query_count += 1 elif query[0] == '!': # 解答があっているかチェックする処理 print(query[2:], ans, query[2:] == ans) print(query_count) break else: pass # ↓提出時はコメントアウト↓ import sys if len(sys.argv) == 1: judge() exit() # ↓提出コード↓ # ...
Anacondaやvenvなど仮想環境のpythonを使いたい場合、subprocessからactivateするのはどうするんだろう。