79 lines
2.2 KiB
Python
79 lines
2.2 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
import sys
|
||
|
|
import dataclasses
|
||
|
|
import re
|
||
|
|
from typing import Sequence, Set, Tuple
|
||
|
|
from copy import deepcopy
|
||
|
|
|
||
|
|
|
||
|
|
FOLD_RE = re.compile(r"fold along (?P<dir>[xy])=(?P<off>[0-9]+)")
|
||
|
|
Point = Tuple[int, int]
|
||
|
|
|
||
|
|
|
||
|
|
@dataclasses.dataclass
|
||
|
|
class Fold:
|
||
|
|
direction: str
|
||
|
|
offset: int
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def from_str(s: str) -> "Fold":
|
||
|
|
match = FOLD_RE.match(s)
|
||
|
|
assert match
|
||
|
|
return Fold(match["dir"], int(match["off"]))
|
||
|
|
|
||
|
|
|
||
|
|
class Grid:
|
||
|
|
def __init__(self, points: Set[Point]):
|
||
|
|
self.rows = max(y for x, y in points) + 1
|
||
|
|
self.cols = max(x for x, y in points) + 1
|
||
|
|
self.points = points
|
||
|
|
|
||
|
|
def apply_fold(self, fold: Fold):
|
||
|
|
if fold.direction == "y":
|
||
|
|
# Find all points that are below this line
|
||
|
|
below = {(x, y) for x, y in self.points if y > fold.offset}
|
||
|
|
# Find their new positions and then remove the old ones
|
||
|
|
self.points -= below
|
||
|
|
self.points |= {(x, self.rows - y - 1) for (x, y) in below}
|
||
|
|
self.rows //= 2
|
||
|
|
else:
|
||
|
|
# Find all points that are to the right of this line
|
||
|
|
right = {(x, y) for x, y in self.points if x > fold.offset}
|
||
|
|
# Find their new positions and then remove the old ones
|
||
|
|
self.points -= right
|
||
|
|
self.points |= {(self.cols - x - 1, y) for (x, y) in right}
|
||
|
|
self.cols //= 2
|
||
|
|
|
||
|
|
def display(self) -> Sequence[str]:
|
||
|
|
grid = [["." for _ in range(self.cols)] for _ in range(self.rows)]
|
||
|
|
for x, y in self.points:
|
||
|
|
grid[y][x] = "#"
|
||
|
|
return ["".join(line) for line in grid]
|
||
|
|
|
||
|
|
|
||
|
|
def part1(grid: Grid, folds: Sequence[Fold]):
|
||
|
|
grid = deepcopy(grid)
|
||
|
|
grid.apply_fold(folds[0])
|
||
|
|
print(len(grid.points))
|
||
|
|
|
||
|
|
|
||
|
|
def part2(grid: Grid, folds: Sequence[Fold]):
|
||
|
|
for fold in folds:
|
||
|
|
grid.apply_fold(fold)
|
||
|
|
print("\n".join(grid.display()))
|
||
|
|
|
||
|
|
|
||
|
|
points_str, folds_str = sys.stdin.read().split("\n\n")
|
||
|
|
points = {
|
||
|
|
(int(a), int(b))
|
||
|
|
for a, b in map(lambda line: line.split(","), points_str.split("\n"))
|
||
|
|
}
|
||
|
|
|
||
|
|
grid = Grid(points)
|
||
|
|
folds = [Fold.from_str(line) for line in folds_str.split("\n")]
|
||
|
|
|
||
|
|
print("Part 1")
|
||
|
|
part1(grid, folds)
|
||
|
|
print("Part 2")
|
||
|
|
part2(grid, folds)
|