From 5bb33c70e8aeb32cca22ce1036496540042824da Mon Sep 17 00:00:00 2001 From: Tibor Bizjak Date: Sat, 4 Mar 2023 15:03:21 +0100 Subject: [PATCH] Refactoring some old code. Porting to python3 and cleaning up. Added solver.py, the main program. --- solver.py | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 solver.py diff --git a/solver.py b/solver.py new file mode 100644 index 0000000..45119f2 --- /dev/null +++ b/solver.py @@ -0,0 +1,143 @@ +import importlib +import __main__ +import os +import re +import pathlib +import traceback + +from argparse import ArgumentParser + +PATH = pathlib.Path(__file__).parent +INPUT_PATH = PATH / "input" + +class Tests(list): + def add(self, test_input, partI=None, partII=None): + self.append((str(test_input), partI, partII)) + +def testf(tests, f, part): + tests = [(t[0], t[part]) for t in tests if t[part] != None] + if tests == []: + return "No tests" + for i, (case, sol) in enumerate(tests): + if sol == None: + continue + r = f(case) + if r == sol: + continue + return "Failed on test {}. Expected {}. Got {}".format(i+1, sol, r) + return "OK" + + + +class Solver: + def __init__(self, mod): + def unpack(name, default): + return getattr(mod, name) if name in dir(mod) else default + + dn_pattern = re.compile("\d\d?") + self.preproc = unpack('preproc', NotImplemented) + self.tests = unpack('tests', []) + self.partI = getattr(mod, 'partI') + self.partII = getattr(mod, 'partII') + + def format_result(f): + def aux(*args): + solver = args[0] + ptI, ptII = f(*args) + print("Part One : {}".format(ptI)) + print("Part Two : {}".format(ptII)) + return aux + + @format_result + def solve(self, puzzle_input): + puzzle_input = puzzle_input.rstrip() + if self.preproc != NotImplemented: + puzzle_input = self.preproc(puzzle_input) + ptI = self.partI(puzzle_input) + ptII = self.partII(puzzle_input) + return ptI, ptII + + @format_result + def test(self): + tests = self.tests + if self.preproc != NotImplemented: + tests = [(self.preproc(str(t)), s1, s2) + for (t, s1, s2) in tests] + ptI = testf(tests, self.partI, 1) + ptII = testf(tests, self.partII, 2) + return ptI, ptII + + + +def read_input(mod_name): + name = mod_name + ".txt" + with open(INPUT_PATH / name) as f: + r = f.read() + return r + + +parser = ArgumentParser() + +parser.description = \ +"""Solves Advent of Code 2019 puzzle of day {}. + The puzzle is available at https://adventofcode.com/2019/day/{}""" + +parser.add_argument('-d', '--day', + action='store', + type=str, + metavar="DAY[-DAY]", + help="solve puzzle of day DAY or of range DAY-DAY") + +parser.add_argument('-t', '--test', + action='store_true', + help="run tests") + +def parse_day_arg(s): + split = s.split('-') + if len(split) == 1: + return [int(split[0])] + elif len(split) == 2: + d, D = map(int, split) + return list(range(d, D+1)) + raise ValueError + + + +if __name__ == '__main__': + solver_pattern = re.compile("day(\d\d?).py") + matches = map(solver_pattern.fullmatch, os.listdir(PATH)) + solvers = {int(s.group(1)) : s.group(0) for s in matches if s != None} + + days = list(range(1, 25+1)) + + args = parser.parse_args() + if args.day != None: + try: + days = parse_day_arg(args.day) + except ValueError: + print("error : invalid format of argument -d/--day") + quit() + + for d in days: + print("=" * 6 + " Day {} ".format(d) + "=" * 6) + if d not in solvers: + print("error : no solver for day {}".format(d)) + continue + mod_name = solvers[d][:-3] + try: + mod = importlib.import_module(mod_name) + solver = Solver(mod) + except: + print("error : bad solver") + print(traceback.format_exc()) + continue + try: + if args.test: + solver.test() + else: + puzzle_input = read_input(mod_name) + solver.solve(puzzle_input) + except: + print("error : error in solver") + print(traceback.format_exc()) +