aoc2023/aoc.py

128 lines
3.6 KiB
Python

from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any, Generic, TypeVar
from urllib.request import Request, urlopen
from html.parser import HTMLParser
T1 = TypeVar("T1")
T2 = TypeVar("T2")
class CodeExtractor(HTMLParser):
def __init__(self) -> None:
super().__init__()
self.inpre = False
self.incode = False
self.code: list[str] = []
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
if tag == "code":
self.incode = True
elif tag == "pre":
self.inpre = True
def handle_endtag(self, tag: str) -> None:
if tag == "code":
self.incode = False
elif tag == "pre":
self.inpre = False
def handle_data(self, data: str) -> None:
if self.incode and self.inpre:
self.code.append(data)
class Aoc2(ABC, Generic[T1, T2]):
def __init__(self, year: int, day: int):
self._year = year
self._day = day
with open(".session", encoding="utf8") as f:
self._session = f.read().strip()
@abstractmethod
def part1(self, inpt: T1) -> Any:
...
@abstractmethod
def part2(self, inpt: T2) -> Any:
...
@abstractmethod
def parseinput1(self, inpt: str) -> T1:
...
@abstractmethod
def parseinput2(self, inpt: str) -> T2:
...
def dl_with_cache(self, url: str, cachefilepath: Path) -> str:
cachefilepath.parent.mkdir(parents=True, exist_ok=True)
if cachefilepath.exists():
return cachefilepath.read_text()
request = Request(url, headers={"Cookie": f"session={self._session}"})
with urlopen(request) as response:
inpt: str = response.read().decode()
cachefilepath.write_text(inpt)
return inpt
def get_data_for_day(self) -> str:
return self.dl_with_cache(
f"https://adventofcode.com/{self._year}/day/{self._day}/input",
Path("aoc_cache") / str(self._year) / f"day{self._day}_input",
)
def get_examples_for_day(self) -> list[str]:
site = self.dl_with_cache(
f"https://adventofcode.com/{self._year}/day/{self._day}",
Path("aoc_cache") / str(self._year) / f"day{self._day}_site",
)
code_extractor = CodeExtractor()
code_extractor.feed(site)
return code_extractor.code
def run_example_1(self, ex_nr: int = 0) -> None:
inpt = self.get_examples_for_day()[ex_nr]
print(self.part1(self.parseinput1(inpt)))
def run_example_2(self, ex_nr: int = -1) -> None:
inpt = self.get_examples_for_day()[ex_nr]
print(self.part2(self.parseinput2(inpt)))
def run_1(self) -> None:
inpt = self.get_data_for_day()
print(self.part1(self.parseinput1(inpt)))
def run_2(self) -> None:
inpt = self.get_data_for_day()
print(self.part2(self.parseinput2(inpt)))
def run(self) -> None:
print("Examples:")
print("Part 1: ", end="")
self.run_example_1()
print("Part 2: ", end="")
self.run_example_2()
print("\nPuzzle:")
print("Part 1: ", end="")
self.run_1()
print("Part 2: ", end="")
self.run_2()
class Aoc(Generic[T1], Aoc2[T1, T1]):
def parseinput(self, inpt: str) -> T1:
raise NotImplementedError
def parseinput1(self, inpt: str) -> T1:
return self.parseinput(inpt)
def parseinput2(self, inpt: str) -> T1:
return self.parseinput(inpt)
def part2(self, inpt: T1) -> Any:
return self.part1(inpt)