-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtesting_tool.py
More file actions
143 lines (128 loc) · 4.44 KB
/
Copy pathtesting_tool.py
File metadata and controls
143 lines (128 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/env python3
#
# Testing tool for the Going in Circles problem
#
# Usage:
#
# python3 testing_tool.py [-s sequence] <program>
#
# The sequence must consist of characters '0' and '1' and have length at least 3.
# If no initial sequence is specified, the sample (011) is used.
#
# You can compile and run your solution as follows.
# - You may have to replace 'python3' by just 'python'.
# - On Windows, you may have to replace '/' by '\'.
#
# If you have a Java solution that you would run using
# "java MyClass", you could invoke the testing tool with:
#
# python3 testing_tool.py java MyClass
#
# If you have a Python solution that you would run using
# "python solution.py", you could invoke the testing tool with:
#
# python3 testing_tool.py python solution.py
#
# If you have a C++ solution stored in a file called "sol.cpp",
# you must first compile using "g++ sol.cpp -o sol" and then
# invoke the testing tool with:
#
# python3 testing_tool.py ./sol
#
# The tool is provided as-is, and you should feel free to make
# whatever alterations or augmentations you like to it.
#
# The tool attempts to detect and report common errors, but it
# is not guaranteed that a program that passes the testing tool
# will be accepted.
#
import argparse
import subprocess
import sys
import traceback
def write(p, line):
assert p.poll() is None, "Program terminated early"
print("Write: {}".format(line), flush=True)
p.stdin.write("{}\n".format(line))
p.stdin.flush()
def read(p):
assert p.poll() is None, "Program terminated early"
line = p.stdout.readline().strip()
assert (
line != ""
), "Read empty line or closed output pipe. Make sure that your program started successfully."
print("Read: %s" % line, flush=True)
return line
def wrong_answer(p, reason):
sys.stdout.write("%s\n" % reason)
p.kill()
parser = argparse.ArgumentParser(
description="Testing tool for the Going in Circles problem"
)
parser.add_argument("-s", dest="sequence", metavar="sequence", default="011")
parser.add_argument("program", nargs="+", help="Invocation of your solution")
args = parser.parse_args()
sequence = list(args.sequence)
for c in sequence:
assert c in "01", f"Character {c} may not appear in the input sequence."
n = len(sequence)
assert n >= 3, f"Sequence must have length at least 3"
position = 0
queries = 0
queries_limit = 3 * n + 500
with subprocess.Popen(
" ".join(args.program),
shell=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
universal_newlines=True,
) as p:
try:
write(p, sequence[position])
while True:
response = read(p)
if response.startswith("? "):
if queries == 50000:
wrong_answer(p, "Program used too many queries, aborting")
break
queries += 1
action = response[2:]
if action == "right":
position = (position + 1) % n
elif action == "left":
position = (position - 1) % n
elif action == "flip":
sequence[position] = str(1 - int(sequence[position]))
else:
wrong_answer(p, "Program gave unrecognized action")
elif response.startswith("! "):
answer = response[2:]
assert (
answer.isnumeric()
), "Expected final guess to be a positive integer"
answer = int(answer)
if answer == n:
assert (
queries <= queries_limit
), "Program printed correct solution, but used too many queries"
assert (
p.stdout.readline() == ""
), "Printed extra data after finding solution"
assert p.wait() == 0, "Did not exit cleanly after finishing"
break
else:
wrong_answer(p, "Program printed incorrect solution")
break
else:
wrong_answer(p, "Program gave invalid response")
break
write(p, sequence[position])
except:
traceback.print_exc()
finally:
sys.stdout.flush()
sys.stderr.flush()
sys.stdout.write(
f"Used {queries} queries, limit is {queries_limit}.\nProgram exit code: {p.wait()}\n"
)
sys.stdout.flush()