2022-12-08 - Treetop Tree House
(original .ipynb)
Day 8 puzzle input is a grid of numbers representing tree heights (mine is here). Part 1 involves finding whether any tree is "visible" (taller than all trees either above, below, left or right of it). Part 2 involves calculating a "scenic score" based on the product of the number of trees smaller than it in all directions.
puzzle_input_str = open("./puzzle_input/day8.txt").read()
test_input_str = """30373
25512
65332
33549
35390"""
def visible_horizontal(grid, r, c):
if c == 0 or c == len(grid[r])-1:
return True
left = all(grid[r][c] > tree for tree in grid[r][:c])
right = all(grid[r][c] > tree for tree in grid[r][1+c:])
return right or left
def visible_vertical(grid, r, c):
if r == 0 or r == len(grid)-1:
return True
up = all(grid[r][c] > grid[xr][c] for xr in range(r))
down = all(grid[r][c] > grid[xr][c] for xr in range(r+1, len(grid)))
return up or down
def part_one(input_str):
grid = [
[int(s) for s in line]
for line
in input_str.splitlines()
]
return sum(
sum(1 for c, col in enumerate(row) if visible_horizontal(grid, r, c) or visible_vertical(grid, r, c))
for r, row in enumerate(grid)
)
assert 21 == part_one(test_input_str)
print("part one:", part_one(puzzle_input_str))
part one: 1698
from functools import reduce
def trees_above(grid, r, c):
count = 0
tree = grid[r][c]
r -= 1
while r >= 0:
count += 1
if tree <= grid[r][c]:
break
r -= 1
return count
def trees_below(grid, r, c):
count = 0
tree = grid[r][c]
r += 1
while r < len(grid):
count += 1
if tree <= grid[r][c]:
break
r += 1
return count
def trees_left(grid, r, c):
count = 0
tree = grid[r][c]
c -= 1
while c >= 0:
count += 1
if tree <= grid[r][c]:
break
c -= 1
return count
def trees_right(grid, r, c):
count = 0
tree = grid[r][c]
c += 1
while c < len(grid[0]):
count += 1
if tree <= grid[r][c]:
break
c += 1
return count
def scenic_score(grid, r, c):
return reduce(lambda acc, s: acc*s, [f(grid, r, c) for f in [trees_above, trees_below, trees_left, trees_right]])
def get_all_scores(grid):
for r, row in enumerate(grid):
for c, col in enumerate(row):
yield scenic_score(grid, r, c)
def part_two(input_str):
grid = [
[int(s) for s in line]
for line
in input_str.splitlines()
]
return max(get_all_scores(grid))
assert 8 == part_two(test_input_str)
print("part two:", part_two(puzzle_input_str))
part two: 672280
# Have I found a CPython bug!? I tightened up my implementation but weirdly
# the next_c/next_r calls never change. Need to come back to this one.
from functools import reduce
def count_trees(grid, r, c, next_r, next_c, not_done):
count = 0
tree = grid[r][c]
# note: this always prints the below, even though I use different next_c/next_r funcs
# next_r(10) 10
# next_c(10) 11
print("next_r(10)", next_r(10))
print("next_c(10)", next_c(10))
r = next_r(r)
c = next_c(c)
while not_done(grid, r, c):
count += 1
if tree <= grid[r][c]:
break
r = next_r(r)
c = next_c(c)
return count
directions = [
(lambda c: c, lambda r: r-1, lambda g,r,c: r >= 0),
(lambda c: c, lambda r: r+1, lambda g,r,c: r < len(g)),
(lambda c: c-1, lambda r: r, lambda g,r,c: c >= 0),
(lambda c: c+1, lambda r: r, lambda g,r,c: c < len(g[0])),
]
all_tree_ops = [
lambda grid, r, c: count_trees(grid, r, c, next_c, next_r, not_done)
for next_c, next_r, not_done
in directions
]
def get_all_scores(grid):
for r, row in enumerate(grid):
for c, col in enumerate(row):
yield reduce(lambda acc,s: acc*s, [f(grid, r, c) for f in all_tree_ops])
def part_two(input_str):
grid = [
[int(s) for s in line]
for line
in input_str.splitlines()
]
return max(get_all_scores(grid))
assert 8 == part_two(test_input_str)
print("part two:", part_two(puzzle_input_str))
next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10 next_r(10) 11 next_c(10) 10