summaryrefslogtreecommitdiffstats
path: root/day9/__init__.py
blob: 696a6dc2665aabccbf872115f06a2007a0a59b14 (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
from abc import ABC
from collections import Counter
from copy import copy
from functools import reduce
from operator import mul
from typing import List, Tuple, Dict, FrozenSet, Iterator, Set, Callable

from aoc import BaseAssignment

Coordinate = Tuple[int, int]
Field = List[List[int]]


class Assignment(BaseAssignment, ABC):
    def parse_item(self, item: str) -> List[int]:
        return [int(i) for i in item]

    def read_input(self, example = False) -> Field:
        return list(super().read_input(example))

    @classmethod
    def neighbours(cls, field: Field, x: int, y: int) -> Iterator[Coordinate]:
        for ny in list(range(max(0, y - 1), min(len(field) - 1, y + 1) + 1)):
            if ny == y:
                continue
            yield (x, ny)

        for nx in list(range(max(0, x - 1), min(len(field[0]) - 1, x + 1) + 1)):
            if nx == x:
                continue
            yield (nx, y)

    @classmethod
    def lowest_points(cls, field: Field) -> Iterator[Coordinate]:
        for y, row in enumerate(field):
            for x, item in enumerate(row):
                if all(field[ny][nx] > item for nx, ny in cls.neighbours(field, x, y)):
                    yield x, y


class AssignmentOne(Assignment):
    example_result = 15

    def run(self, input: Field) -> int:
        lows = list(input[y][x] for x, y in self.lowest_points(input))
        return sum(lows) + len(lows)


def flatten(l: List[Set[Coordinate]]) -> List:
    flat_list = []
    for _ in l:
        flat_list += _

    return flat_list


class AssignmentTwo(Assignment):
    example_result = 1134

    @classmethod
    def find_basin_members(cls, field: Field, x: int, y: int) -> Set[Coordinate]:
        return {
            (x, y),
            *flatten([
                cls.find_basin_members(field, nx, ny)
                for nx, ny in cls.neighbours(field, x, y)
                if field[y][x] < field[ny][nx] < 9
            ])
        }

    def run(self, input: Field) -> int:
        return reduce(
            mul,
            sorted([
                len(self.find_basin_members(input, x, y))
                for x, y in self.lowest_points(input)
            ], reverse=True)[:3],
            1
        )