@@ -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
@@ -1007,21 +996,21 @@ def needed(ext):
1007
996
def generate_visualization ():
1008
997
nonlocal meta_yaml
1009
998
1010
- if not t .config .visualizer :
1011
- return True
1012
999
if config .args .no_visualizer :
1013
1000
return True
1001
+ if not generator_config .visualizer :
1002
+ return True
1014
1003
1015
1004
visualizer_hash = {
1016
- "visualizer_hash" : t . config .visualizer .hash (),
1017
- "visualizer" : t . config .visualizer .cache_command (),
1005
+ "visualizer_hash" : generator_config .visualizer .hash (),
1006
+ "visualizer" : generator_config .visualizer .cache_command (),
1018
1007
}
1019
1008
1020
1009
if meta_yaml .get ("visualizer_hash" ) == visualizer_hash :
1021
1010
return True
1022
1011
1023
1012
# Generate visualization
1024
- t . config .visualizer .run (bar , cwd )
1013
+ generator_config .visualizer .run (bar , cwd )
1025
1014
1026
1015
meta_yaml ["visualizer_hash" ] = visualizer_hash
1027
1016
write_yaml (meta_yaml , meta_path , allow_yamllib = True )
@@ -1456,6 +1445,13 @@ def __init__(self, problem, restriction=None):
1456
1445
# Files that should be processed
1457
1446
self .restriction = restriction
1458
1447
1448
+ # The input visualizer is shared between all test cases.
1449
+ self .visualizer : Optional [VisualizerInvocation ] = (
1450
+ VisualizerInvocation (problem , "/input_visualizer" )
1451
+ if (problem .path / "input_visualizer" ).is_dir ()
1452
+ else None
1453
+ )
1454
+
1459
1455
if yaml_path .is_file ():
1460
1456
self .yaml = read_yaml (yaml_path )
1461
1457
self .has_yaml = True
@@ -1780,7 +1776,6 @@ def add_included_case(t: TestcaseRule):
1780
1776
def build (self , build_visualizers = True , skip_double_build_warning = False ):
1781
1777
generators_used : set [Path ] = set ()
1782
1778
solutions_used : set [Path ] = set ()
1783
- visualizers_used : set [Path ] = set ()
1784
1779
1785
1780
# Collect all programs that need building.
1786
1781
# Also, convert the default submission into an actual Invocation.
@@ -1801,14 +1796,12 @@ def collect_programs(t):
1801
1796
default_solution = DefaultSolutionInvocation (self )
1802
1797
t .config .solution = default_solution
1803
1798
solutions_used .add (t .config .solution .program_path )
1804
- if build_visualizers and t .config .visualizer :
1805
- visualizers_used .add (t .config .visualizer .program_path )
1806
1799
1807
1800
self .root_dir .walk (collect_programs , dir_f = None )
1808
1801
1809
1802
def build_programs (
1810
1803
program_type : type [program .Generator | program .Visualizer | run .Submission ],
1811
- program_paths : set [Path ],
1804
+ program_paths : Iterable [Path ],
1812
1805
):
1813
1806
programs = list [program .Generator | program .Visualizer | run .Submission ]()
1814
1807
for program_path in program_paths :
@@ -1844,7 +1837,10 @@ def build_program(p):
1844
1837
# TODO: Consider building all types of programs in parallel as well.
1845
1838
build_programs (program .Generator , generators_used )
1846
1839
build_programs (run .Submission , solutions_used )
1847
- build_programs (program .Visualizer , visualizers_used )
1840
+ build_programs (
1841
+ program .Visualizer ,
1842
+ [self .visualizer .program_path ] if build_visualizers and self .visualizer else [],
1843
+ )
1848
1844
1849
1845
self .problem .validators (validate .InputValidator )
1850
1846
if not self .problem .interactive and not self .problem .multi_pass :
@@ -1854,10 +1850,6 @@ def build_program(p):
1854
1850
def cleanup_build_failures (t ):
1855
1851
if t .config .solution and t .config .solution .program is None :
1856
1852
t .config .solution = None
1857
- if not build_visualizers or (
1858
- t .config .visualizer and t .config .visualizer .program is None
1859
- ):
1860
- t .config .visualizer = None
1861
1853
1862
1854
self .root_dir .walk (cleanup_build_failures , dir_f = None )
1863
1855
0 commit comments