67 lines
2.0 KiB
Python
67 lines
2.0 KiB
Python
|
from helpers import *
|
||
|
lines = read_day(day).split('\n')
|
||
|
sample_lines = '''
|
||
|
498,4 -> 498,6 -> 496,6
|
||
|
503,4 -> 502,4 -> 502,9 -> 494,9'''.strip().split('\n')
|
||
|
|
||
|
AIR = ord('.')
|
||
|
ROCK = ord('#')
|
||
|
SAND_SOURCE = ord('+')
|
||
|
SAND_REST = ord('o')
|
||
|
SAND_ORIGIN = (500, 0)
|
||
|
FALL_DIRS = np.array([[0,1], [-1,1], [1,1]], dtype=dtype)
|
||
|
|
||
|
def apply_sand_fall(map: ArrayLike) -> bool:
|
||
|
falling_sand_pos = np.array(SAND_ORIGIN, dtype=dtype)
|
||
|
MAX_Y = map.shape[1] - 1
|
||
|
while falling_sand_pos[1] < MAX_Y:
|
||
|
for dir in FALL_DIRS:
|
||
|
if map[*(falling_sand_pos + dir)] == AIR:
|
||
|
falling_sand_pos += dir
|
||
|
break
|
||
|
else:
|
||
|
if falling_sand_pos[1] == 0:
|
||
|
return False
|
||
|
# resting place
|
||
|
# print('Resting at', falling_sand_pos)
|
||
|
map[*falling_sand_pos] = SAND_REST
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def sim(lines: list[str], solid_floor=False) -> ArrayLike:
|
||
|
nums = [[int(x) for x in numbers_pattern.findall(line)] for line in lines]
|
||
|
max_x = max([max(n[::2]) for n in nums])
|
||
|
max_y = max([max(n[1::2]) for n in nums])
|
||
|
print(f'Max X: {max_x}, Max Y: {max_y}')
|
||
|
dim_x = max_x + max_y # assume it can pile out diagonally all the way
|
||
|
map = np.full((dim_x,max_y+4), AIR, dtype=np.uint8)
|
||
|
for n in nums:
|
||
|
for x0,y0,x1,y1 in zip(n[::2], n[1::2], n[2::2], n[3::2]):
|
||
|
map[min(x0,x1):max(x0,x1)+1, min(y0,y1):max(y0,y1)+1] = ROCK
|
||
|
if solid_floor:
|
||
|
map[:,max_y+2] = ROCK
|
||
|
map[SAND_ORIGIN] = SAND_SOURCE
|
||
|
while apply_sand_fall(map):
|
||
|
pass
|
||
|
return map
|
||
|
|
||
|
def visualize_map(map: ArrayLike):
|
||
|
X, Y = np.nonzero(map == SAND_REST)
|
||
|
x0 = max(min(X) - 2, 0)
|
||
|
x1 = min(max(X) + 3, map.shape[0])
|
||
|
y0 = max(min(Y) - 2, 0)
|
||
|
y1 = min(max(Y) + 3, map.shape[1])
|
||
|
for row in map[x0:x1, y0:y1].T:
|
||
|
print(''.join((chr(x) for x in row)))
|
||
|
|
||
|
|
||
|
map_1 = sim(lines) # 885
|
||
|
map_2 = sim(lines, True) # 28690 (+1)
|
||
|
print(f'Part 1: {(map_1==SAND_REST).sum()}')
|
||
|
print(f'Part 2: {(map_2==SAND_REST).sum()+(map_2==SAND_SOURCE).sum()}')
|
||
|
if input('Print Part 1 visualization? [y/N]: ').lower() == 'y':
|
||
|
visualize_map(map_1)
|
||
|
print()
|
||
|
if input('Print Part 2 visualization? [y/N]: ').lower() == 'y':
|
||
|
visualize_map(map_2)
|