Restructure solver modules with classes and unittest suites

This commit is contained in:
2024-12-09 20:47:50 -08:00
parent 7c021ab87f
commit bee7f5e59e
11 changed files with 995 additions and 698 deletions

View File

@@ -1,80 +1,60 @@
import sys
from importlib import import_module
from inspect import getmembers, isclass
from time import time
from unittest import TextTestRunner, defaultTestLoader
from advent.errors import InvalidSessionIDError, PuzzleNotFoundError
from advent.functions import get_puzzle_input
def usage():
print("Usage: python solve.py <day>")
from puzzles._solver import Solver
def main():
if len(sys.argv) != 2:
usage()
sys.exit(1)
raise RuntimeError("Usage: python solve.py <day>")
try:
day = int(sys.argv[1])
except ValueError:
print("Day must be an integer")
usage()
sys.exit(1)
raise RuntimeError("Day must be an integer")
print(f"Loading 2024, day {day} solver...")
try:
print(f"Loading 2024, day {day} solver")
mod = import_module(f"puzzles.{day}")
except ModuleNotFoundError:
print(f"Solver for day {day} not found")
sys.exit(1)
try:
print("Testing part 1 solution...")
test_input_p1 = getattr(mod, "test_input_p1", getattr(mod, "test_input", None))
start = time()
assert mod.solve_p1(test_input_p1) == mod.test_solution_p1
print(f"Test passed in {time() - start:.3f} seconds")
solvers = getmembers(mod, lambda m: isclass(m) and issubclass(m, Solver))
if len(solvers) == 0:
raise RuntimeError("No solver found")
solver_class: type[Solver] = solvers[0][1]
print("Testing part 2 solution...")
test_input_p2 = getattr(mod, "test_input_p2", getattr(mod, "test_input", None))
start = time()
assert mod.solve_p2(test_input_p2) == mod.test_solution_p2
print(f"Test passed in {time() - start:.3f} seconds")
test_suite = defaultTestLoader.loadTestsFromModule(mod)
test_runner = TextTestRunner(verbosity=0)
test_result = test_runner.run(test_suite)
if not test_result.wasSuccessful():
raise RuntimeError("Tests failed")
except AssertionError:
print("Test failed")
sys.exit(1)
print("Fetching puzzle input...")
puzzle_input = get_puzzle_input(2024, int(day))
except Exception as exc:
print("Error:", exc)
sys.exit(1)
solver = solver_class(puzzle_input)
try:
print("Fetching puzzle input...")
puzzle_input = get_puzzle_input(2024, int(day))
except (PuzzleNotFoundError, InvalidSessionIDError) as exc:
print(exc)
sys.exit(1)
print("Solving part 1...")
start = time()
solution_p1 = solver.solve_p1()
print(f"Part 1 solution: {solution_p1} ({time() - start:.3f} seconds)")
try:
print("Solving part 1...")
start = time()
solution_p1 = mod.solve_p1(puzzle_input)
print(f"Part 1 solution: {solution_p1} ({time() - start:.3f} seconds)")
print("Solving part 2...")
solution_p2 = mod.solve_p2(puzzle_input)
print(f"Part 2 solution: {solution_p2} ({time() - start:.3f} seconds)")
except NotImplementedError:
print("Puzzle solver is incomplete. Exiting")
sys.exit(1)
except Exception as exc:
print("Error:", exc)
sys.exit(1)
print("Solving part 2...")
start = time()
solution_p2 = solver.solve_p2()
print(f"Part 2 solution: {solution_p2} ({time() - start:.3f} seconds)")
if __name__ == "__main__":
main()
try:
main()
except Exception as exc:
print(exc)
sys.exit(1)