# -*- coding: utf-8 -*- from abc import ABC from typing import Iterator from aoc import BaseAssignment, I, T class Assignment(BaseAssignment, ABC): def parse_item(cls, item: str) -> Iterator[range | None | int]: try: [start, end] = item.split("-") yield range(int(start), int(end) + 1) except ValueError: if item == "": yield None else: yield int(item) def read_input(self, example=False) -> Iterator[tuple[list[range], list[int]]]: ranges = [] ids = [] read_ranges = True for item in super().read_input(example): if item is None: read_ranges = False continue if read_ranges: ranges.append(item) else: ids.append(item) yield (ranges, ids) @classmethod def find_fresh_ranges_for_id(cls, id: int, ranges: list[range]) -> Iterator[range]: for r in ranges: if id in r: yield r class AssignmentOne(Assignment): example_result = 3 def run(self, input: Iterator[tuple[list[range], list[int]]]) -> T: ranges, ids = next(input) fresh_ids = [] for id in ids: if len(list(self.find_fresh_ranges_for_id(id, ranges))) > 0: fresh_ids.append(id) return len(fresh_ids) class AssignmentTwo(Assignment): example_result = 14 @classmethod def merge_ranges(cls, ranges: list[range]) -> list[range]: if not ranges: return [] ranges = sorted(ranges, key=lambda r: r.start) merged = [ranges[0]] for r in ranges[1:]: last = merged[-1] if not (last.stop < r.start): merged[-1] = range(last.start, max(last.stop, r.stop)) else: merged.append(r) return merged def run(self, input: Iterator[tuple[list[range], list[int]]]) -> T: ranges, ids = next(input) fresh_ranges = set() for id in ids: for r in self.find_fresh_ranges_for_id(id, ranges): fresh_ranges.add(r) fresh_ranges = self.merge_ranges(list(fresh_ranges)) return sum([r.stop - r.start for r in fresh_ranges])