import os import re import sys from pathlib import Path from solution import process_inputs, solve USAGE = """\ usage: python main.py [--tests=DIR] [--output=DIR] [args] commands: single path/to/test.in run a single test file range START [END] run tests START..END (inclusive) flags: --tests=DIR directory containing test inputs (default: tests) --output=DIR directory for outputs (default: output) """ config = { "input_name": re.compile(r"in_level-[0-9]_([0-9])-(?:small|large)\.txt"), "tests_dir": Path("tests"), "output_dir": Path("output") } class FillingList(list): def __init__(self, iterable=(), fill=None): super().__init__(iterable) self.fill = fill def __setitem__(self, index, value): if isinstance(index, int) and index >= len(self): self.extend([self.fill] * (index - len(self) + 1)) super().__setitem__(index, value) def run_single_test(input_path: str, output_path: str): print(f"Input path: {input_path}") print(f"Output path: {output_path}") with open(input_path, 'r') as f: data = f.read() testcases = process_inputs(data) solution = solve(testcases) with open(output_path, 'w') as f: f.write(solution) def run_test_range(start_test: int, end_test: int | None = None): print(f"Running test range {start_test} {end_test}") input_files = FillingList() # Find all input files for filename in os.listdir(config['tests_dir']): path = os.path.join(config['tests_dir'], filename) if not os.path.isfile(path): continue m = config['input_name'].fullmatch(filename) if m: input_files[int(m.group(1))] = { "path": path, "filename": filename, } else: print(f"{filename} does not match, skipping") if not end_test: end_test = len(input_files) # Run solution for each discovered input file for i, input_file in enumerate(input_files[start_test:(end_test + 1)]): if not input_file: print(f'WARNING: Could not find test #{i}') print(f'running test {i} looking at path {input_file['path']}') output_name = input_file['filename'].replace('in', 'out') output_path = config['output_dir'] / output_name run_single_test(input_file['path'], output_path) def parse_argv(argv): """Split argv into ({flag: value}, [positional]). Supports --k=v and --k v.""" flags, positional = {}, [] i = 0 while i < len(argv): arg = argv[i] if arg.startswith("--"): if "=" in arg: key, value = arg[2:].split("=", 1) else: key = arg[2:] i += 1 if i >= len(argv): raise SystemExit(f"error: flag --{key} requires a value\n\n{USAGE}") value = argv[i] flags[key] = value else: positional.append(arg) i += 1 return flags, positional def main(argv=None): argv = sys.argv[1:] if argv is None else argv flags, positional = parse_argv(argv) unknown = set(flags) - {"tests", "output"} if unknown: raise SystemExit(f"error: unknown flag(s): {', '.join(sorted(unknown))}\n\n{USAGE}") config["tests_dir"] = Path(flags.get("tests", config["tests_dir"])) config["output_dir"] = Path(flags.get("output", config["output_dir"])) os.makedirs(config["output_dir"], exist_ok=True) if not positional: raise SystemExit(USAGE) command, *args = positional if command == "single": if len(args) != 1: raise SystemExit(f"error: 'single' takes exactly one path\n\n{USAGE}") input_path = Path(args[0]) if not input_path.is_file(): raise SystemExit(f"error: no such file: {input_path}") output_path = Path(config["output_dir"]) / input_path.with_suffix(".out").name run_single_test(str(input_path), str(output_path)) elif command == "range": if len(args) not in (1, 2): raise SystemExit(f"error: 'range' takes START [END]\n\n{USAGE}") try: start_test = int(args[0]) end_test = int(args[1]) if len(args) == 2 else None except ValueError: raise SystemExit(f"error: range bounds must be integers\n\n{USAGE}") run_test_range(start_test, end_test) else: raise SystemExit(f"error: unknown command {command!r}\n\n{USAGE}") if __name__ == "__main__": main() # Path: solution.py # def process_inputs(inputs): # lines = inputs.split('\n') # lines = iter(lines) # testcases_num = int(next(lines)) # testcases = [] # for i in range(testcases_num): # line = next(lines) # testcase = {} # return [{'testcase': 'testcase1'}] # def solve(testcases): # res = '' # for i, testcase in enumerate(testcases): # print('Solving testcase #', i + 1) # res += process_testcase(**testcase) + '\n' # return res # def process_testcase(arg1, args): # return 'solution'