summaryrefslogtreecommitdiffstats
path: root/day5/__init__.py
blob: 399aedc68dd13a8192bda1524f8ce4fdfe6e8c55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# -*- 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])