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" )