Skip to content

Commit a83ca75

Browse files
committed
[generate] Use input_visualizer/ instead of visualizer: key in generators.yaml
1 parent a0736af commit a83ca75

File tree

2 files changed

+25
-32
lines changed

2 files changed

+25
-32
lines changed

bin/generate.py

+22-30
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,14 @@ class VisualizerInvocation(Invocation):
191191
def __init__(self, problem, string):
192192
super().__init__(problem, string, allow_absolute=True, allow_relative=False)
193193

194-
# Run the visualizer, taking {name} as a command line argument.
195-
# Stdin and stdout are not used.
196-
# {name} is no longer used and hardcoded to `testcase` (see #273), and {seed} is also not used.
194+
# Run the visualizer, passing the test case input to stdin.
197195
def run(self, bar, cwd):
198196
assert isinstance(self.program, program.Visualizer), "Visualizer program must be built!"
199197

200-
result = self.program.run(cwd, args=self._sub_args())
198+
in_path = cwd / "testcase.in"
199+
200+
with in_path.open("rb") as in_file:
201+
result = self.program.run(cwd, args=self._sub_args(), stdin=in_file)
201202

202203
if result.status == ExecStatus.TIMEOUT:
203204
bar.debug(f"{Style.RESET_ALL}-> {shorten_path(self.problem, cwd)}")
@@ -325,7 +326,6 @@ def __init__(self, generator_config):
325326
"generate",
326327
"copy",
327328
"solution",
328-
"visualizer",
329329
"random_salt",
330330
"retries",
331331
"count",
@@ -337,7 +337,6 @@ def __init__(self, generator_config):
337337
"testdata.yaml",
338338
"include",
339339
"solution",
340-
"visualizer",
341340
"random_salt",
342341
"retries",
343342
]
@@ -348,7 +347,6 @@ def __init__(self, generator_config):
348347

349348
# Holds all inheritable configuration options. Currently:
350349
# - config.solution
351-
# - config.visualizer
352350
# - config.random_salt
353351
class Config:
354352
# Used at each directory or testcase level.
@@ -360,13 +358,6 @@ def parse_solution(p, x, path):
360358
return None
361359
return SolutionInvocation(p, x)
362360

363-
@staticmethod
364-
def parse_visualizer(p, x, path):
365-
assert_type("Visualizer", x, [type(None), str], path)
366-
if x is None:
367-
return None
368-
return VisualizerInvocation(p, x)
369-
370361
@staticmethod
371362
def parse_random_salt(p, x, path):
372363
assert_type("Random_salt", x, [type(None), str], path)
@@ -377,7 +368,6 @@ def parse_random_salt(p, x, path):
377368
INHERITABLE_KEYS: Final[Sequence] = [
378369
# True: use an AC submission by default when the solution: key is not present.
379370
("solution", True, parse_solution),
380-
("visualizer", None, parse_visualizer),
381371
("random_salt", "", parse_random_salt),
382372
# Non-portable keys only used by BAPCtools:
383373
# The number of retries to run a generator when it fails, each time incrementing the {seed}
@@ -386,7 +376,6 @@ def parse_random_salt(p, x, path):
386376
]
387377

388378
solution: SolutionInvocation
389-
visualizer: Optional[VisualizerInvocation]
390379
random_salt: str
391380
retries: int
392381

@@ -1053,21 +1042,21 @@ def generate_visualization():
10531042

10541043
if testcase.root in [*config.INVALID_CASE_DIRECTORIES, "valid_output"]:
10551044
return True
1056-
if not t.config.visualizer:
1057-
return True
10581045
if config.args.no_visualizer:
10591046
return True
1047+
if not generator_config.visualizer:
1048+
return True
10601049

10611050
visualizer_hash = {
1062-
"visualizer_hash": t.config.visualizer.hash(),
1063-
"visualizer": t.config.visualizer.cache_command(),
1051+
"visualizer_hash": generator_config.visualizer.hash(),
1052+
"visualizer": generator_config.visualizer.cache_command(),
10641053
}
10651054

10661055
if meta_yaml.get("visualizer_hash") == visualizer_hash:
10671056
return True
10681057

10691058
# Generate visualization
1070-
t.config.visualizer.run(bar, cwd)
1059+
generator_config.visualizer.run(bar, cwd)
10711060

10721061
meta_yaml["visualizer_hash"] = visualizer_hash
10731062
write_yaml(meta_yaml, meta_path, allow_yamllib=True)
@@ -1510,6 +1499,13 @@ def __init__(self, problem, restriction=None):
15101499
# Files that should be processed
15111500
self.restriction = restriction
15121501

1502+
# The input visualizer is shared between all test cases.
1503+
self.visualizer: Optional[VisualizerInvocation] = (
1504+
VisualizerInvocation(problem, "/input_visualizer")
1505+
if (problem.path / "input_visualizer").is_dir()
1506+
else None
1507+
)
1508+
15131509
if yaml_path.is_file():
15141510
self.yaml = read_yaml(yaml_path)
15151511
self.has_yaml = True
@@ -1834,7 +1830,6 @@ def add_included_case(t: TestcaseRule):
18341830
def build(self, build_visualizers=True, skip_double_build_warning=False):
18351831
generators_used: set[Path] = set()
18361832
solutions_used: set[Path] = set()
1837-
visualizers_used: set[Path] = set()
18381833

18391834
# Collect all programs that need building.
18401835
# Also, convert the default submission into an actual Invocation.
@@ -1855,14 +1850,12 @@ def collect_programs(t):
18551850
default_solution = DefaultSolutionInvocation(self)
18561851
t.config.solution = default_solution
18571852
solutions_used.add(t.config.solution.program_path)
1858-
if build_visualizers and t.config.visualizer:
1859-
visualizers_used.add(t.config.visualizer.program_path)
18601853

18611854
self.root_dir.walk(collect_programs, dir_f=None)
18621855

18631856
def build_programs(
18641857
program_type: type[program.Generator | program.Visualizer | run.Submission],
1865-
program_paths: set[Path],
1858+
program_paths: Iterable[Path],
18661859
):
18671860
programs = list[program.Generator | program.Visualizer | run.Submission]()
18681861
for program_path in program_paths:
@@ -1898,7 +1891,10 @@ def build_program(p):
18981891
# TODO: Consider building all types of programs in parallel as well.
18991892
build_programs(program.Generator, generators_used)
19001893
build_programs(run.Submission, solutions_used)
1901-
build_programs(program.Visualizer, visualizers_used)
1894+
build_programs(
1895+
program.Visualizer,
1896+
[self.visualizer.program_path] if build_visualizers and self.visualizer else [],
1897+
)
19021898

19031899
self.problem.validators(validate.InputValidator)
19041900
self.problem.validators(validate.AnswerValidator)
@@ -1907,10 +1903,6 @@ def build_program(p):
19071903
def cleanup_build_failures(t):
19081904
if t.config.solution and t.config.solution.program is None:
19091905
t.config.solution = None
1910-
if not build_visualizers or (
1911-
t.config.visualizer and t.config.visualizer.program is None
1912-
):
1913-
t.config.visualizer = None
19141906

19151907
self.root_dir.walk(cleanup_build_failures, dir_f=None)
19161908

bin/program.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -604,10 +604,11 @@ def __init__(self, problem: "Problem", path: Path, **kwargs):
604604
)
605605

606606
# Run the visualizer.
607-
# Stdin and stdout are not used.
608-
def run(self, cwd, args=[]):
607+
# Stdout is not used.
608+
def run(self, cwd, stdin, args=[]):
609609
assert self.run_command is not None
610610
return self._exec_command(
611611
self.run_command + args,
612612
cwd=cwd,
613+
stdin=stdin,
613614
)

0 commit comments

Comments
 (0)