@@ -191,13 +191,14 @@ class VisualizerInvocation(Invocation):
191
191
def __init__ (self , problem , string ):
192
192
super ().__init__ (problem , string , allow_absolute = True , allow_relative = False )
193
193
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.
197
195
def run (self , bar , cwd ):
198
196
assert isinstance (self .program , program .Visualizer ), "Visualizer program must be built!"
199
197
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 )
201
202
202
203
if result .status == ExecStatus .TIMEOUT :
203
204
bar .debug (f"{ Style .RESET_ALL } -> { shorten_path (self .problem , cwd )} " )
@@ -325,7 +326,6 @@ def __init__(self, generator_config):
325
326
"generate" ,
326
327
"copy" ,
327
328
"solution" ,
328
- "visualizer" ,
329
329
"random_salt" ,
330
330
"retries" ,
331
331
"count" ,
@@ -337,7 +337,6 @@ def __init__(self, generator_config):
337
337
"testdata.yaml" ,
338
338
"include" ,
339
339
"solution" ,
340
- "visualizer" ,
341
340
"random_salt" ,
342
341
"retries" ,
343
342
]
@@ -348,7 +347,6 @@ def __init__(self, generator_config):
348
347
349
348
# Holds all inheritable configuration options. Currently:
350
349
# - config.solution
351
- # - config.visualizer
352
350
# - config.random_salt
353
351
class Config :
354
352
# Used at each directory or testcase level.
@@ -360,13 +358,6 @@ def parse_solution(p, x, path):
360
358
return None
361
359
return SolutionInvocation (p , x )
362
360
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
-
370
361
@staticmethod
371
362
def parse_random_salt (p , x , path ):
372
363
assert_type ("Random_salt" , x , [type (None ), str ], path )
@@ -377,7 +368,6 @@ def parse_random_salt(p, x, path):
377
368
INHERITABLE_KEYS : Final [Sequence ] = [
378
369
# True: use an AC submission by default when the solution: key is not present.
379
370
("solution" , True , parse_solution ),
380
- ("visualizer" , None , parse_visualizer ),
381
371
("random_salt" , "" , parse_random_salt ),
382
372
# Non-portable keys only used by BAPCtools:
383
373
# 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):
386
376
]
387
377
388
378
solution : SolutionInvocation
389
- visualizer : Optional [VisualizerInvocation ]
390
379
random_salt : str
391
380
retries : int
392
381
@@ -1053,21 +1042,21 @@ def generate_visualization():
1053
1042
1054
1043
if testcase .root in [* config .INVALID_CASE_DIRECTORIES , "valid_output" ]:
1055
1044
return True
1056
- if not t .config .visualizer :
1057
- return True
1058
1045
if config .args .no_visualizer :
1059
1046
return True
1047
+ if not generator_config .visualizer :
1048
+ return True
1060
1049
1061
1050
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 (),
1064
1053
}
1065
1054
1066
1055
if meta_yaml .get ("visualizer_hash" ) == visualizer_hash :
1067
1056
return True
1068
1057
1069
1058
# Generate visualization
1070
- t . config .visualizer .run (bar , cwd )
1059
+ generator_config .visualizer .run (bar , cwd )
1071
1060
1072
1061
meta_yaml ["visualizer_hash" ] = visualizer_hash
1073
1062
write_yaml (meta_yaml , meta_path , allow_yamllib = True )
@@ -1510,6 +1499,13 @@ def __init__(self, problem, restriction=None):
1510
1499
# Files that should be processed
1511
1500
self .restriction = restriction
1512
1501
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
+
1513
1509
if yaml_path .is_file ():
1514
1510
self .yaml = read_yaml (yaml_path )
1515
1511
self .has_yaml = True
@@ -1834,7 +1830,6 @@ def add_included_case(t: TestcaseRule):
1834
1830
def build (self , build_visualizers = True , skip_double_build_warning = False ):
1835
1831
generators_used : set [Path ] = set ()
1836
1832
solutions_used : set [Path ] = set ()
1837
- visualizers_used : set [Path ] = set ()
1838
1833
1839
1834
# Collect all programs that need building.
1840
1835
# Also, convert the default submission into an actual Invocation.
@@ -1855,14 +1850,12 @@ def collect_programs(t):
1855
1850
default_solution = DefaultSolutionInvocation (self )
1856
1851
t .config .solution = default_solution
1857
1852
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 )
1860
1853
1861
1854
self .root_dir .walk (collect_programs , dir_f = None )
1862
1855
1863
1856
def build_programs (
1864
1857
program_type : type [program .Generator | program .Visualizer | run .Submission ],
1865
- program_paths : set [Path ],
1858
+ program_paths : Iterable [Path ],
1866
1859
):
1867
1860
programs = list [program .Generator | program .Visualizer | run .Submission ]()
1868
1861
for program_path in program_paths :
@@ -1898,7 +1891,10 @@ def build_program(p):
1898
1891
# TODO: Consider building all types of programs in parallel as well.
1899
1892
build_programs (program .Generator , generators_used )
1900
1893
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
+ )
1902
1898
1903
1899
self .problem .validators (validate .InputValidator )
1904
1900
self .problem .validators (validate .AnswerValidator )
@@ -1907,10 +1903,6 @@ def build_program(p):
1907
1903
def cleanup_build_failures (t ):
1908
1904
if t .config .solution and t .config .solution .program is None :
1909
1905
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
1914
1906
1915
1907
self .root_dir .walk (cleanup_build_failures , dir_f = None )
1916
1908
0 commit comments