# -*- coding: utf-8 -*- from typing import Iterator, Any, List, Tuple from aoc import BaseAssignment class Assignment(BaseAssignment): trees: List[str] def row_at(self, y: int) -> List[int]: return [int(i) for i in self.trees[y]] def col_at(self, x: int) -> List[int]: return [int(i[x]) for i in self.trees] def inner_trees(self) -> Iterator[Tuple[int, int]]: width = len(self.trees[0]) height = len(self.trees) for x in range(1, width - 1): for y in range(1, height - 1): yield x, y class AssignmentOne(Assignment): example_result = 21 def is_visible(self, trees: List[int], index: int) -> bool: highest_first_half = max(trees[:index]) highest_second_half = max(trees[index + 1 :]) value = trees[index] return highest_first_half < value or highest_second_half < value def tree_visible(self, x: int, y: int) -> bool: col = self.col_at(x) row = self.row_at(y) return self.is_visible(col, y) or self.is_visible(row, x) def run(self, input: Iterator) -> Any: self.trees = list(input) width = len(self.trees[0]) height = len(self.trees) visible_inside = 0 self.is_visible(self.col_at(1), 2) for x, y in self.inner_trees(): if self.tree_visible(x, y): visible_inside += 1 return visible_inside + ((width - 1) * 2) + ((height - 1) * 2) class AssignmentTwo(Assignment): example_result = 8 @classmethod def calculate_score(cls, value: int, values: List[int]) -> int: score = 0 for score, v in enumerate(values): if v >= value: break return score + 1 @classmethod def calculate_1d_scenic_score(cls, trees: List[int], index: int) -> int: first_half = list(reversed(trees[:index])) second_half = trees[index + 1 :] value = trees[index] return cls.calculate_score(value, first_half) * cls.calculate_score( value, second_half ) def calculate_scenic_score(self, x: int, y: int) -> int: col = self.col_at(x) row = self.row_at(y) return self.calculate_1d_scenic_score(col, y) * self.calculate_1d_scenic_score( row, x ) def run(self, input: Iterator) -> Any: self.trees = list(input) scenic_scores: List[int] = [] for x, y in self.inner_trees(): scenic_score = self.calculate_scenic_score(x, y) scenic_scores.append(scenic_score) return max(scenic_scores)