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])
|