84 lines
2.5 KiB
Python
84 lines
2.5 KiB
Python
import importlib.util
|
|
import pathlib
|
|
from aoc import exceptions
|
|
from typing import Any, Callable
|
|
import time
|
|
|
|
|
|
_ANSI_ITALIC: str = "\x1b[3m"
|
|
_ANSI_BOLD: str = "\x1b[1m"
|
|
_ANSI_RESET: str = "\x1b[0m"
|
|
|
|
|
|
def _pprint_ns(ns: int) -> str:
|
|
if ns >= 1_000_000_000:
|
|
s = ns / 1_000_000_000
|
|
return f"{s:.2f}s"
|
|
elif ns >= 1_000_000:
|
|
ms = ns / 1_000_000
|
|
return f"{ms:.2f}ms"
|
|
elif ns >= 1_000:
|
|
us = ns / 1_000
|
|
return f"{us:.2f}µs"
|
|
else:
|
|
return f"{ns}ns"
|
|
|
|
|
|
def _pprint_result(
|
|
year: int, day: int, part: int | str, result: Any, duration_ns: int
|
|
) -> None:
|
|
part = f"{_ANSI_BOLD}{year}/{day:02}/{part}{_ANSI_RESET}"
|
|
|
|
if result is None:
|
|
solved = f"{_ANSI_ITALIC}(unsolved){_ANSI_RESET}"
|
|
print(f"{part}: {solved}")
|
|
else:
|
|
if isinstance(result, list) or isinstance(result, tuple):
|
|
result = "\n".join(str(r) for r in result)
|
|
solved = f"{_ANSI_ITALIC}(elapsed: {_pprint_ns(duration_ns)}){_ANSI_RESET}"
|
|
print(f"{part}: {solved}\n{result}")
|
|
|
|
|
|
def _run_func(
|
|
f: Callable[[str], Any], year: int, day: int, part: int | str, input_data: str
|
|
) -> None:
|
|
t_1 = time.perf_counter_ns()
|
|
result = f(input_data)
|
|
t_2 = time.perf_counter_ns()
|
|
|
|
_pprint_result(year, day, part, result, t_2 - t_1)
|
|
|
|
|
|
def run_day(year: int, day: int, input_data: str, path_base: pathlib.Path) -> None:
|
|
try:
|
|
module_path = path_base / f"year_{year}" / f"day_{day:02}.py"
|
|
spec = importlib.util.spec_from_file_location(
|
|
f"year_{year}.day_{day:02}", module_path
|
|
)
|
|
if spec is None or spec.loader is None:
|
|
raise exceptions.AocError()
|
|
solution_module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(solution_module)
|
|
except (ModuleNotFoundError, exceptions.AocError) as e:
|
|
raise exceptions.AocError(
|
|
f"solution module for {year}/{day:02} not found: run 'python main.py create --year {year} {day}'"
|
|
) from e
|
|
|
|
p_1 = getattr(solution_module, "part_1", None)
|
|
p_2 = getattr(solution_module, "part_2", None)
|
|
|
|
if p_1 is not None and p_2 is not None:
|
|
_run_func(p_1, year, day, 1, input_data)
|
|
_run_func(p_2, year, day, 2, input_data)
|
|
return
|
|
|
|
p_1_2 = getattr(solution_module, "part_1_2", None)
|
|
|
|
if p_1_2 is not None:
|
|
_run_func(p_1_2, year, day, "1&2", input_data)
|
|
return
|
|
|
|
raise exceptions.AocError(
|
|
f"{year}/{day:02} must define part_1 and part_2 or part_1_2"
|
|
)
|