summaryrefslogtreecommitdiffstats
path: root/day13/__init__.py
blob: 41b81dc6d3f1c5300466edad08f63fef0bb0daec (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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import re
from abc import ABC
from copy import copy
from dataclasses import dataclass
from typing import List, Tuple, Union, TypedDict

from aoc import BaseAssignment

Coordinate = Tuple[int, int]

def mirrored_index(index: int, fold: int) -> int:
    return fold + (fold - index)

@dataclass
class Sheet:
    dots: List[Coordinate]
    width: int = 0
    height: int = 0

    def __post_init__(self):
        x_coordinates, y_coordinates = zip(*self.dots)

        self.width = max(x_coordinates) + 1
        self.height = max(y_coordinates) + 1


    def fold_x(self, position: int):
        for x, y in copy(self.dots):
            mirrored_x = mirrored_index(x, position)
            if mirrored_x < position:
                self.dots.append((mirrored_x, y))
                self.dots.remove((x, y))

        self.width = self.width // 2
        self.dots = list(set(self.dots))

    def fold_y(self, position: int):
        for x, y in copy(self.dots):
            mirrored_y = mirrored_index(y, position)
            if mirrored_y < position:
                self.dots.append((x, mirrored_y))
                self.dots.remove((x, y))

        self.height = self.height // 2
        self.dots = list(set(self.dots))

    def __repr__(self):
        dots = set(self.dots)
        return '\n'.join([
            '',
            *[
                ''.join([
                    '█' if (x, y) in dots else ' '
                    for x in range(self.width)
                ])
                for y in range(self.height)
            ],
            '',
        ])

class Instruction(TypedDict):
    direction: str
    position: int

coordinate_regex = re.compile('(\d+),(\d+)')
instruction_regex = re.compile('fold along (?P<direction>[xy])=(?P<position>\d+)')

class Assignment(BaseAssignment, ABC):
    def parse_item(self, item: str) -> Union[Coordinate,Instruction]:
        if coordinate := coordinate_regex.match(item):
            return tuple(int(i) for i in coordinate.groups())

        if instruction := instruction_regex.match(item):
            return {
                **instruction.groupdict(),
                'position': int(instruction.groupdict()['position'])
            }

    def read_input(self, example = False) -> Tuple[Sheet, List[Instruction]]:
        input = super().read_input(example)

        coordinates: List[Coordinate] = []

        while True:
            coordinate = next(input)
            if coordinate is None:
                break

            coordinates.append(coordinate)

        sheet = Sheet(dots=coordinates)

        return sheet, list(input)


class AssignmentOne(Assignment):
    example_result = 17

    def run(self, input: Tuple[Sheet, List[Instruction]]) -> int:
        sheet, instructions = input
        getattr(sheet, f'fold_{instructions[0]["direction"]}')(instructions[0]["position"])
        return len(sheet.dots)

class AssignmentTwo(Assignment):
    def run(self, input: Tuple[Sheet, List[Instruction]]) -> Sheet:
        sheet, instructions = input
        for instruction in instructions:
            getattr(sheet, f'fold_{instruction["direction"]}')(instruction["position"])
        return sheet