diff options
Diffstat (limited to 'day5/__init__.py')
| -rw-r--r-- | day5/__init__.py | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/day5/__init__.py b/day5/__init__.py new file mode 100644 index 0000000..399aedc --- /dev/null +++ b/day5/__init__.py | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | from abc import ABC | ||
| 3 | from typing import Iterator | ||
| 4 | |||
| 5 | from aoc import BaseAssignment, I, T | ||
| 6 | |||
| 7 | |||
| 8 | class Assignment(BaseAssignment, ABC): | ||
| 9 | def parse_item(cls, item: str) -> Iterator[range | None | int]: | ||
| 10 | try: | ||
| 11 | [start, end] = item.split("-") | ||
| 12 | yield range(int(start), int(end) + 1) | ||
| 13 | except ValueError: | ||
| 14 | if item == "": | ||
| 15 | yield None | ||
| 16 | else: | ||
| 17 | yield int(item) | ||
| 18 | |||
| 19 | def read_input(self, example=False) -> Iterator[tuple[list[range], list[int]]]: | ||
| 20 | ranges = [] | ||
| 21 | ids = [] | ||
| 22 | |||
| 23 | read_ranges = True | ||
| 24 | for item in super().read_input(example): | ||
| 25 | if item is None: | ||
| 26 | read_ranges = False | ||
| 27 | continue | ||
| 28 | |||
| 29 | if read_ranges: | ||
| 30 | ranges.append(item) | ||
| 31 | else: | ||
| 32 | ids.append(item) | ||
| 33 | |||
| 34 | yield (ranges, ids) | ||
| 35 | |||
| 36 | @classmethod | ||
| 37 | def find_fresh_ranges_for_id(cls, id: int, ranges: list[range]) -> Iterator[range]: | ||
| 38 | for r in ranges: | ||
| 39 | if id in r: | ||
| 40 | yield r | ||
| 41 | |||
| 42 | |||
| 43 | class AssignmentOne(Assignment): | ||
| 44 | example_result = 3 | ||
| 45 | |||
| 46 | def run(self, input: Iterator[tuple[list[range], list[int]]]) -> T: | ||
| 47 | ranges, ids = next(input) | ||
| 48 | |||
| 49 | fresh_ids = [] | ||
| 50 | for id in ids: | ||
| 51 | if len(list(self.find_fresh_ranges_for_id(id, ranges))) > 0: | ||
| 52 | fresh_ids.append(id) | ||
| 53 | |||
| 54 | return len(fresh_ids) | ||
| 55 | |||
| 56 | |||
| 57 | class AssignmentTwo(Assignment): | ||
| 58 | example_result = 14 | ||
| 59 | |||
| 60 | @classmethod | ||
| 61 | def merge_ranges(cls, ranges: list[range]) -> list[range]: | ||
| 62 | if not ranges: | ||
| 63 | return [] | ||
| 64 | |||
| 65 | ranges = sorted(ranges, key=lambda r: r.start) | ||
| 66 | merged = [ranges[0]] | ||
| 67 | for r in ranges[1:]: | ||
| 68 | last = merged[-1] | ||
| 69 | if not (last.stop < r.start): | ||
| 70 | merged[-1] = range(last.start, max(last.stop, r.stop)) | ||
| 71 | else: | ||
| 72 | merged.append(r) | ||
| 73 | return merged | ||
| 74 | |||
| 75 | def run(self, input: Iterator[tuple[list[range], list[int]]]) -> T: | ||
| 76 | ranges, ids = next(input) | ||
| 77 | |||
| 78 | fresh_ranges = set() | ||
| 79 | |||
| 80 | for id in ids: | ||
| 81 | for r in self.find_fresh_ranges_for_id(id, ranges): | ||
| 82 | fresh_ranges.add(r) | ||
| 83 | |||
| 84 | fresh_ranges = self.merge_ranges(list(fresh_ranges)) | ||
| 85 | |||
| 86 | return sum([r.stop - r.start for r in fresh_ranges]) | ||
