Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.

Commit fb191a0

Browse files
committed
Implemented terry format!
1 parent cec4c4a commit fb191a0

14 files changed

Lines changed: 889 additions & 369 deletions

python/config.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
from typing import List
44

5-
from task_maker.args import CacheMode, UIS, TaskFormat
6-
7-
# from task_maker.formats import Arch
5+
from task_maker.args import CacheMode, UIS, TaskFormat, Arch
86

97

108
class Config:
@@ -42,5 +40,5 @@ def __init__(self, args):
4240
self.detailed_checker = args.detailed_checker
4341

4442
# terry group
45-
# self.arch = args.arch # type: Arch
46-
# self.seed = args.seed # type: int
43+
self.arch = args.arch # type: Arch
44+
self.seed = args.seed # type: int

python/formats/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,20 @@ def __repr__(self):
154154
return "<Task name=%s title=%s>" % (self.name, self.title)
155155

156156

157+
class TerryTask:
158+
def __init__(self, name: str, title: str, max_score: float):
159+
self.name = name
160+
self.title = title
161+
self.max_score = max_score
162+
self.generator = None # type: SourceFile
163+
self.validator = None # type: Optional[SourceFile]
164+
self.official_solution = None # type: Optional[SourceFile]
165+
self.checker = None # type: SourceFile
166+
167+
def __repr__(self):
168+
return "<TerryTask name={} title={}>".format(self.name, self.title)
169+
170+
157171
def get_write_input_file(tc_num: int) -> str:
158172
return "input/input%d.txt" % tc_num
159173

python/formats/terry_format.py

Lines changed: 133 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
11
#!/usr/bin/env python3
2-
import argparse
32
import glob
43
import os.path
54
import platform
65
import random
6+
from typing import Optional, List
77

8-
from manager_pb2 import EvaluateTerryTaskRequest, TerrySolution
9-
from task_pb2 import TerryTask, DEFAULT, X86_64, I686
8+
from task_maker.args import Arch, CacheMode, UIS
9+
from task_maker.config import Config
10+
from task_maker.formats import get_options, TerryTask, list_files
11+
from task_maker.formats.ioi_format import parse_task_yaml
12+
from task_maker.source_file import SourceFile
13+
from task_maker.task_maker_frontend import Frontend
14+
from task_maker.uis.terry import TerryUIInterface
15+
from task_maker.uis.terry_curses_ui import TerryCursesUI
16+
from task_maker.uis.terry_finish_ui import TerryFinishUI
1017

11-
from task_maker.absolutize import absolutize_source_file, absolutize_path
12-
from task_maker.formats.ioi_format import parse_task_yaml, get_options, \
13-
list_files
14-
from task_maker.source_file import from_file
1518

16-
17-
def get_extension(target_arch=DEFAULT):
18-
if target_arch == DEFAULT:
19+
def get_extension(target_arch: Arch):
20+
if target_arch == Arch.DEFAULT:
1921
return "." + platform.system().lower() + "." + platform.machine()
20-
elif target_arch == X86_64:
22+
elif target_arch == Arch.X86_64:
2123
return "." + platform.system().lower() + ".x86_64"
22-
elif target_arch == I686:
24+
elif target_arch == Arch.I686:
2325
return "." + platform.system().lower() + ".i686"
2426
else:
2527
raise ValueError("Unsupported architecture")
2628

2729

28-
def create_task_from_yaml(data):
30+
def create_task_from_yaml(data) -> TerryTask:
2931
name = get_options(data, ["name", "nome_breve"])
3032
title = get_options(data, ["description", "nome"])
3133
max_score = get_options(data, ["max_score"])
@@ -34,14 +36,23 @@ def create_task_from_yaml(data):
3436
if title is None:
3537
raise ValueError("The title is not set in the yaml")
3638

37-
task = TerryTask()
38-
task.name = name
39-
task.title = title
40-
task.max_score = max_score
41-
return task
39+
return TerryTask(name, title, max_score)
40+
41+
42+
def get_solutions(solutions) -> List[str]:
43+
if solutions:
44+
solutions = list_files([
45+
sol + "*"
46+
if sol.startswith("solutions/") else "solutions/" + sol + "*"
47+
for sol in solutions
48+
])
49+
else:
50+
solutions = list_files(["solutions/*"], exclude=["sol/__init__.py"])
51+
return solutions
4252

4353

44-
def get_manager(manager, target_arch, optional=False):
54+
def get_manager(manager: str, target_arch: Arch,
55+
optional: bool = False) -> Optional[SourceFile]:
4556
managers = list_files(
4657
["managers/%s.*" % manager], exclude=["managers/%s.*.*" % manager])
4758
if len(managers) == 0:
@@ -50,70 +61,122 @@ def get_manager(manager, target_arch, optional=False):
5061
return None
5162
if len(managers) != 1:
5263
raise ValueError("Ambiguous manager: " + ", ".join(managers))
53-
return from_file(managers[0],
54-
"managers/%s%s" % (manager, get_extension(target_arch)),
55-
target_arch)
64+
return SourceFile.from_file(
65+
managers[0], manager, True,
66+
"managers/%s%s" % (manager, get_extension(target_arch)), target_arch,
67+
{})
5668

5769

58-
def get_request(args: argparse.Namespace):
70+
def get_request(config: Config) -> (TerryTask, List[SourceFile]):
5971
data = parse_task_yaml()
6072
if not data:
6173
raise RuntimeError("The task.yaml is not valid")
6274

6375
task = create_task_from_yaml(data)
6476

65-
task.generator.CopyFrom(get_manager("generator", args.arch.value))
66-
absolutize_source_file(task.generator)
77+
task.generator = get_manager("generator", config.arch)
78+
task.validator = get_manager("validator", config.arch, optional=True)
79+
task.official_solution = get_manager(
80+
"solution", config.arch, optional=True)
81+
task.checker = get_manager("checker", config.arch)
82+
83+
solutions = get_solutions(config.solutions)
84+
sols = [] # type: List[SourceFile]
85+
for solution in solutions:
86+
path, ext = os.path.splitext(os.path.basename(solution))
87+
source = SourceFile.from_file(solution, task.name, config.copy_exe,
88+
"bin/" + path + "_" + ext[1:],
89+
Arch.DEFAULT, {})
90+
sols.append(source)
91+
92+
return task, sols
93+
94+
95+
def evaluate_task(frontend: Frontend, task: TerryTask,
96+
solutions: List[SourceFile], config: Config):
97+
ui_interface = TerryUIInterface(task, config.ui == UIS.PRINT)
98+
if config.ui == UIS.CURSES:
99+
curses_ui = TerryCursesUI(ui_interface)
100+
curses_ui.start()
101+
102+
task.generator.prepare(frontend, config)
103+
ui_interface.add_non_solution(task.generator)
104+
if task.validator:
105+
task.validator.prepare(frontend, config)
106+
ui_interface.add_non_solution(task.validator)
107+
if task.official_solution:
108+
task.official_solution.prepare(frontend, config)
109+
ui_interface.add_non_solution(task.official_solution)
110+
task.checker.prepare(frontend, config)
111+
ui_interface.add_non_solution(task.checker)
112+
113+
for solution in solutions:
114+
solution.prepare(frontend, config)
115+
ui_interface.add_solution(solution)
116+
evaluate_solution(frontend, task, solution, config, ui_interface)
67117

68-
validator = get_manager("validator", args.arch.value, optional=True)
69-
if validator:
70-
task.validator.CopyFrom(validator)
71-
absolutize_source_file(task.validator)
118+
frontend.evaluate()
72119

73-
task.checker.CopyFrom(get_manager("checker", args.arch.value))
74-
absolutize_source_file(task.checker)
120+
if config.ui == UIS.CURSES:
121+
curses_ui.stop()
122+
if config.ui != UIS.SILENT:
123+
finish_ui = TerryFinishUI(config, task, ui_interface)
124+
finish_ui.print()
125+
return ui_interface
75126

76-
solution = get_manager("solution", args.arch.value, optional=True)
77-
if solution:
78-
task.solution.CopyFrom(solution)
79-
absolutize_source_file(task.solution)
80127

81-
if args.solutions:
82-
solutions = list_files([
83-
sol + "*"
84-
if sol.startswith("solutions/") else "solutions/" + sol + "*"
85-
for sol in args.solutions
86-
])
128+
def evaluate_solution(frontend: Frontend, task: TerryTask,
129+
solution: SourceFile, config: Config,
130+
interface: TerryUIInterface):
131+
if config.seed:
132+
seed = config.seed
87133
else:
88-
solutions = list_files(
89-
["solutions/*"], exclude=["solutions/__init__.py"])
90-
91-
request = EvaluateTerryTaskRequest()
92-
request.task.CopyFrom(task)
93-
copy_compiled = args.copy_exe
94-
for solution in solutions:
95-
path, ext = os.path.splitext(os.path.basename(solution))
96-
bin_file = copy_compiled and "bin/" + path + "_" + ext[1:]
97-
source_file = from_file(solution, bin_file)
98-
absolutize_source_file(source_file)
99-
terry_solution = TerrySolution()
100-
terry_solution.solution.CopyFrom(source_file)
101-
if args.seed:
102-
terry_solution.seed = args.seed
103-
else:
104-
terry_solution.seed = random.randint(0, 2**32 - 1)
105-
request.solutions.extend([terry_solution])
106-
request.store_dir = absolutize_path(args.store_dir)
107-
request.temp_dir = absolutize_path(args.temp_dir)
108-
request.cache_mode = args.cache.value
109-
if args.num_cores:
110-
request.num_cores = args.num_cores
111-
request.dry_run = args.dry_run
112-
if args.evaluate_on:
113-
request.evaluate_on = args.evaluate_on
114-
request.keep_sandbox = args.keep_sandbox
115-
request.exclusive = args.exclusive
116-
return request
134+
seed = random.randint(0, 2**31 - 1)
135+
name = solution.name
136+
137+
generation = task.generator.execute(
138+
frontend, "Generation of input for solution {} with seed {}".format(
139+
name, seed), [str(seed), "0"])
140+
if config.cache == CacheMode.NOTHING:
141+
generation.disableCache()
142+
input = generation.stdout(False)
143+
if task.official_solution:
144+
generation.addInput(task.official_solution.exe_name,
145+
task.official_solution.executable)
146+
interface.add_generation(name, seed, generation)
147+
148+
if task.validator:
149+
validation = task.validator.execute(
150+
frontend, "Validation of input for solution {}".format(name),
151+
["0"])
152+
if config.cache == CacheMode.NOTHING:
153+
validation.disableCache()
154+
validation.setStdin(input)
155+
if task.official_solution:
156+
validation.addInput(task.official_solution.exe_name,
157+
task.official_solution.executable)
158+
interface.add_validation(name, validation)
159+
160+
solving = solution.execute(frontend, "Running solution {}".format(name),
161+
[])
162+
if config.cache != CacheMode.ALL:
163+
solving.disableCache()
164+
solving.setStdin(input)
165+
if task.validator:
166+
solving.addInput("wait_for_validation", validation.stdout(False))
167+
output = solving.stdout(False)
168+
interface.add_solving(name, solving)
169+
170+
checker = task.checker.execute(
171+
frontend, "Checking solution {}".format(name), ["input", "output"])
172+
if config.cache == CacheMode.NOTHING:
173+
checker.disableCache()
174+
checker.addInput("input", input)
175+
checker.addInput("output", output)
176+
if task.official_solution:
177+
checker.addInput(task.official_solution.exe_name,
178+
task.official_solution.executable)
179+
interface.add_checking(name, checker)
117180

118181

119182
def clean():

python/languages/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ def get_dependencies(self, filename: str) -> List[Dependency]:
118118
"""
119119
return []
120120

121+
def __repr__(self):
122+
return "<Language {}>".format(self.name)
123+
121124
def __eq__(self, other):
122125
return self.name == other.name
123126

python/source_file.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ def from_file(path: str, unit_name: str, copy_executable: bool,
4848
language = LanguageManager.from_file(path)
4949
dependencies = language.get_dependencies(path)
5050
grader = grader_map.get(language)
51-
exe_name = os.path.splitext(os.path.basename(write_to or path))[0]
51+
if write_to:
52+
exe_name = os.path.basename(write_to)
53+
else:
54+
exe_name = os.path.splitext(os.path.basename(path))[0]
5255
source_file = SourceFile(path, unit_name, exe_name, dependencies,
5356
language, copy_executable and write_to,
5457
target_arch, grader)

python/task_maker.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from task_maker.args import get_parser, TaskFormat
1414
from task_maker.config import Config
1515
from task_maker.detect_format import find_task_dir
16-
from task_maker.formats import ioi_format, tm_format
16+
from task_maker.formats import ioi_format, tm_format, terry_format
1717
from task_maker.languages import LanguageManager
1818
from task_maker.manager import get_frontend, spawn_server, spawn_worker
1919

@@ -38,8 +38,8 @@ def run(config: Config) -> Union[None, IOIUIInterface]:
3838
ioi_format.clean()
3939
elif format == TaskFormat.TM:
4040
tm_format.clean()
41-
# elif format == "terry":
42-
# terry_format_clean(args)
41+
elif format == "terry":
42+
terry_format.clean()
4343
else:
4444
raise ValueError("Format %s not supported" % format)
4545
return None
@@ -58,11 +58,9 @@ def stop_server(signum: int, _: Any) -> None:
5858
elif format == TaskFormat.TM:
5959
task, solutions = tm_format.get_request(config)
6060
return ioi_format.evaluate_task(frontend, task, solutions, config)
61-
# elif format == "terry":
62-
# request = terry_format.get_request(args)
63-
# solutions = [
64-
# os.path.basename(sol.solution.path) for sol in request.solutions
65-
# ]
61+
elif format == TaskFormat.TERRY:
62+
task, solutions = terry_format.get_request(config)
63+
return terry_format.evaluate_task(frontend, task, solutions, config)
6664
else:
6765
raise ValueError("Format %s not supported" % format)
6866

python/tests/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ bin/
66

77
!task_without_gen/input
88
!task_without_gen/output
9+
10+
*.linux.x86_64
11+
*.linux.i686

0 commit comments

Comments
 (0)