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.
102 lines
2.5 KiB
102 lines
2.5 KiB
1 year ago
|
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)
|