67 lines
1.5 KiB
Python
67 lines
1.5 KiB
Python
from collections.abc import Iterable
|
|
from pathlib import Path
|
|
from typing import Generator, TypeVar
|
|
|
|
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}")
|