You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
101 lines
2.5 KiB
101 lines
2.5 KiB
import aoclib |
|
|
|
|
|
inp = aoclib.get_input(8, parser=aoclib.parse_lines_with_func(lambda line: [int(c) for c in line])) |
|
|
|
|
|
class Forest: |
|
class Directions: |
|
UP = (-1, 0) |
|
DOWN = (1, 0) |
|
LEFT = (0, -1) |
|
RIGHT = (0, 1) |
|
ALL = (UP, DOWN, LEFT, RIGHT) |
|
|
|
def __init__(self, grid): # assumes rect grid |
|
self.grid = grid |
|
self.max = (len(grid[0]), len(grid)) |
|
|
|
def is_tree_visible(self, x: int, y: int): |
|
return any([self._tree_visible_from(direction, x, y) for direction in self.Directions.ALL]) |
|
|
|
def _tree_visible_from(self, direction, x, y): |
|
main = self._get_tree(x, y) |
|
others = self._get_all_trees_in_dir(direction, x, y) |
|
return all([o < main for o in others]) |
|
|
|
def _get_all_trees_in_dir(self, direction, x, y): |
|
trees = [] |
|
dx, dy = direction |
|
while True: |
|
x += dx |
|
y += dy |
|
tree = self._get_tree(x, y) |
|
if tree is not None: |
|
trees.append(tree) |
|
else: |
|
return trees |
|
|
|
def scenic_score_for_pos(self, x, y): |
|
prod = 1 |
|
for d in self.Directions.ALL: |
|
prod *= self._get_scenic_score_in_dir(d, x, y) |
|
if prod == 0: |
|
break |
|
return prod |
|
|
|
def _get_scenic_score_in_dir(self, direction, x, y): |
|
house = self._get_tree(x, y) |
|
count = 0 |
|
for tree in self._get_trees_in_dir_iter(direction, x, y): |
|
count += 1 |
|
if tree >= house: |
|
break |
|
|
|
return count |
|
|
|
def _get_trees_in_dir_iter(self, direction, x, y): |
|
dx, dy = direction |
|
while True: |
|
x += dx |
|
y += dy |
|
tree = self._get_tree(x, y) |
|
if tree is not None: |
|
yield tree |
|
else: |
|
break |
|
|
|
def _get_tree(self, x, y): |
|
if x < 0 or y < 0: |
|
return None |
|
try: |
|
return self.grid[y][x] |
|
except IndexError: |
|
return None |
|
|
|
|
|
def part1(): |
|
f = Forest(inp) |
|
xmax, ymax = f.max |
|
count = 0 |
|
for x in range(xmax): |
|
for y in range(ymax): |
|
if f.is_tree_visible(x, y): |
|
count += 1 |
|
|
|
return count |
|
|
|
|
|
def part2(): |
|
f = Forest(inp) |
|
xmax, ymax = f.max |
|
highest = 0 |
|
for x in range(xmax): |
|
for y in range(ymax): |
|
if (current := f.scenic_score_for_pos(x, y)) > highest: |
|
highest = current |
|
|
|
return highest |
|
|
|
|
|
aoclib.main(part1, part2)
|
|
|