Count how many grains fit in a reservoir!
Code: Latest – Get It Done
Once again, I ran into a small bug in my code that took ages to debug. In Python, indentation matters, and I had an else
attached to the wrong level.
Indentation Bug
# What I meant to do:
while can_move and cur.imag < max_y:
for d in directions:
if cur + d not in rocks and (cur + d).imag < max_y:
cur += d
break
else:
can_move = False
# What I did do:
while can_move and cur.imag < max_y:
for d in directions:
if cur + d not in rocks and (cur + d).imag < max_y:
cur += d
break
else:
can_move = False
Python Solver (one function, both parts)
def solver(self, rocks: InputType, first_to_the_floor: bool) -> int:
"""Simulate sand filling a reservoir. Return which grain passes the floor or stops falling."""
# Sand moves in these directions, in this order of preference.
movement_directions = (complex(0, 1), complex(-1, 1), complex(1, 1))
starting_point = complex(500, 0)
# Use the input to build a set of rock points and compute the lowest points.
rocks_and_sand = set()
lowest_rock = 0
for points in rocks:
lowest_rock = max(lowest_rock, max(p.imag for p in points))
for start, end in zip(points[:-1], points[1:]):
for point in aoc.Line.from_complex(start, end).points():
rocks_and_sand.add(complex(point))
# The lowest position a grain may occupy.
lowest_position = lowest_rock + 1
# Simulate the grains.
for grain in itertools.count():
cur = starting_point
# Grains can drop until they hit the lowest_position.
while cur.imag < lowest_position:
for d in movement_directions:
if cur + d not in rocks_and_sand:
cur += d
break
else:
break
rocks_and_sand.add(cur)
if first_to_the_floor and cur.imag == lowest_position:
# Part 1: return the grain prior to the first to pass the lowest rock.
return grain
elif cur == starting_point:
# Part 2: return the grain which stops at the starting_point.
return grain + 1