from pathlib import Path from typing import Generator, TypeVar from collections.abc import Iterable T = TypeVar("T") def chunks(it: Iterable[T], n: int) -> Generator[list[T], None, None]: if len(it) % n != 0: raise ValueError() if not 0 < n <= len(it): raise ValueError() for idx in range(0, len(it), n): yield it[idx : idx + n] def priority(s: str) -> int: assert len(s) == 1 if "a" <= s <= "z": return ord(s) - ord("a") + 1 elif "A" <= s <= "Z": return ord(s) - ord("A") + 27 raise RuntimeError(f"Invalid char: {s}") def split(s: str) -> tuple[str, str]: mid = len(s) // 2 return s[:mid], s[mid:] def part1(data: str) -> int: result = 0 for line in data.splitlines(): left, right = split(line) both = {ch for ch in right}.intersection({ch for ch in left}) value = 0 for ch in both: value += priority(ch) print(f"{line}: {both}") result += value return result def part2(data: str) -> int: result = 0 lines = data.splitlines() for a, b, c in chunks(lines, 3): badge = set(a).intersection(set(b)).intersection(set(c)) value = 0 for ch in badge: value += priority(ch) result += value return result # Setup data = (Path(__file__).parents[1] / "input").read_text() # Problem solving - part 1 result = part1(data) print(f"Part 1 solution: {result}") # Problem solving - part 2 result = part2(data) print(f"Part 2 solution: {result}")