99 lines
2.8 KiB
Python
99 lines
2.8 KiB
Python
from __future__ import annotations
|
|
from dataclasses import dataclass
|
|
|
|
from typing import Iterator, Self
|
|
|
|
day_num = 8
|
|
|
|
|
|
def part1(lines: Iterator[str]) -> int:
|
|
return Forest.parse(lines).count_visible_trees()
|
|
|
|
|
|
def part2(lines: Iterator[str]) -> int:
|
|
return Forest.parse(lines).max_scenic_score()
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class Forest:
|
|
trees: list[list[int]]
|
|
width: int
|
|
height: int
|
|
|
|
@classmethod
|
|
def parse(cls, lines: Iterator[str]) -> Self:
|
|
trees = [[int(tree) for tree in line] for line in lines]
|
|
return cls(trees, len(trees[0]), len(trees))
|
|
|
|
def count_visible_trees(self) -> int:
|
|
visible: set[tuple[int, int]] = set()
|
|
|
|
# From Above
|
|
mx = self.trees[0].copy()
|
|
for y in range(1, self.height - 1):
|
|
for x in range(1, self.width - 1):
|
|
if self.trees[y][x] > mx[x]:
|
|
mx[x] = self.trees[y][x]
|
|
visible.add((x, y))
|
|
|
|
# From Below
|
|
mx = self.trees[-1].copy()
|
|
for y in range(self.height - 2, 0, -1):
|
|
for x in range(1, self.width - 1):
|
|
if self.trees[y][x] > mx[x]:
|
|
mx[x] = self.trees[y][x]
|
|
visible.add((x, y))
|
|
|
|
# From Left
|
|
mx = [row[0] for row in self.trees]
|
|
for x in range(1, self.width - 1):
|
|
for y in range(1, self.height - 1):
|
|
if self.trees[y][x] > mx[y]:
|
|
mx[y] = self.trees[y][x]
|
|
visible.add((x, y))
|
|
|
|
# From Right
|
|
mx = [row[-1] for row in self.trees]
|
|
for x in range(self.width - 2, 0, -1):
|
|
for y in range(1, self.height - 1):
|
|
if self.trees[y][x] > mx[y]:
|
|
mx[y] = self.trees[y][x]
|
|
visible.add((x, y))
|
|
|
|
return len(visible) + 2 * (self.width + self.height - 2)
|
|
|
|
def max_scenic_score(self) -> int:
|
|
max_score = 0
|
|
for y in range(1, self.height - 1):
|
|
for x in range(1, self.width - 1):
|
|
score = self.single_scenic_score(x, y)
|
|
if score > max_score:
|
|
max_score = score
|
|
return max_score
|
|
|
|
def single_scenic_score(self, x: int, y: int) -> int:
|
|
up = 0
|
|
for dy in range(y - 1, -1, -1):
|
|
up += 1
|
|
if self.trees[dy][x] >= self.trees[y][x]:
|
|
break
|
|
|
|
down = 0
|
|
for dy in range(y + 1, self.height):
|
|
down += 1
|
|
if self.trees[dy][x] >= self.trees[y][x]:
|
|
break
|
|
|
|
left = 0
|
|
for dx in range(x - 1, -1, -1):
|
|
left += 1
|
|
if self.trees[y][dx] >= self.trees[y][x]:
|
|
break
|
|
|
|
right = 0
|
|
for dx in range(x + 1, self.width):
|
|
right += 1
|
|
if self.trees[y][dx] >= self.trees[y][x]:
|
|
break
|
|
|
|
return up * down * left * right
|