135 lines
4.0 KiB
Python
135 lines
4.0 KiB
Python
import matrix
|
|
import shared
|
|
import itertools
|
|
import functools
|
|
|
|
|
|
SAND_START = [0,500]
|
|
|
|
|
|
def find_bounds(lines):
|
|
x = []
|
|
y = []
|
|
for line in lines:
|
|
x.extend(matrix.split_x_out(line))
|
|
y.extend(matrix.split_y_out(line))
|
|
return shared.minmax(y),shared.minmax(x)
|
|
|
|
|
|
def part1(rows):
|
|
def get_next(sand):
|
|
next_y = sand[0]+1
|
|
dl = sand[1]-1
|
|
d = sand[1]
|
|
dr = sand[1]+1
|
|
return next_y, (dl,d,dr)
|
|
lines = [[list(reversed(list(map(int, point.split(","))))) for point in row.split(" -> ")] for row in rows]
|
|
(minY,maxY), (minX,maxX) = find_bounds(lines)
|
|
mx = matrix.matrix_of_size(maxX+2,maxY+2)
|
|
walls = []
|
|
for line in lines:
|
|
for x in range(len(line)-1):
|
|
coords = matrix.coords_between_points(line[x], line[x+1])
|
|
walls.extend(coords)
|
|
for y,x in walls:
|
|
mx[y][x] = "#"
|
|
mx[SAND_START[0]][SAND_START[1]] = "+"
|
|
|
|
sands = []
|
|
|
|
try:
|
|
for sand_grains in range(900): # produce Sand one unit at a time
|
|
last_grain = []
|
|
sand = SAND_START[:]
|
|
#print("starting sand", sand_grains+1, sand)
|
|
while True:
|
|
last_grain.append(sand[:])
|
|
next_y, (dl, d, dr) = get_next(sand)
|
|
if mx[next_y][d] == 0:
|
|
#print(mx[next_y][d],"moving_down", end=' ')
|
|
#print("moving down")
|
|
sand[0] = next_y
|
|
elif mx[next_y][dl] == 0:
|
|
#print(mx[next_y][dl], "moving left", end=' ')
|
|
#print("moving left")
|
|
sand[0] = next_y
|
|
sand[1] = dl
|
|
elif mx[next_y][dr] == 0:
|
|
#print(mx[next_y][dr], "moving right", end=' ')
|
|
#print("moving right")
|
|
sand[0] = next_y
|
|
sand[1] = dr
|
|
else:
|
|
mx[sand[0]][sand[1]] = "o"
|
|
#print(mx[next_y][dl], mx[next_y][d], mx[next_y][dr])
|
|
#print("STOPPING", end=' ')
|
|
break
|
|
#matrix.view_matrix(mx, 0,minX-1, maxY+1,maxX+2)
|
|
except IndexError:
|
|
sands.append(last_grain)
|
|
for y,x in sands[-1]:
|
|
mx[y][x] = "~"
|
|
#matrix.view_matrix(mx, 0,minX-1, maxY+1,maxX+2)
|
|
print("done", sand_grains)
|
|
|
|
|
|
@shared.profile
|
|
def part2(rows):
|
|
lines = [[list(reversed(list(map(int, point.split(","))))) for point in row.split(" -> ")] for row in rows]
|
|
(minY,maxY), (minX,maxX) = find_bounds(lines)
|
|
walls = {}
|
|
_between_points = matrix.coords_between_points
|
|
for line in lines:
|
|
for x in range(len(line)-1):
|
|
coords = matrix.coords_between_points(line[x], line[x+1])
|
|
for c in coords:
|
|
walls[c] = "#"
|
|
for y,x in walls:
|
|
walls[(y,x)] = "#"# .add((y,x))
|
|
floor = maxY + 2
|
|
|
|
sand_grains = -1
|
|
while True:
|
|
sand_grains += 1
|
|
sand = [0,500]
|
|
|
|
if tuple(sand) in walls:
|
|
print("done", sand_grains)
|
|
break
|
|
|
|
while True:
|
|
next_y = sand[0]+1
|
|
dl = sand[1]-1
|
|
dr = sand[1]+1
|
|
if next_y == floor:
|
|
# hit the floor
|
|
walls[tuple(sand)] = "#" # Draw stop
|
|
break
|
|
|
|
elif (next_y, sand[1]) not in walls:
|
|
sand[0] = next_y
|
|
elif (next_y, dl) not in walls:
|
|
sand[0] = next_y
|
|
sand[1] = dl
|
|
elif (next_y, dr) not in walls:
|
|
sand[0] = next_y
|
|
sand[1] = dr
|
|
|
|
else:
|
|
# Hit something flat
|
|
walls[tuple(sand)] = "#"
|
|
break
|
|
|
|
def main():
|
|
rows = [row for row in shared.load_rows(14)]
|
|
with shared.elapsed_timer() as elapsed:
|
|
part1(rows)
|
|
print("🕒", elapsed())
|
|
with shared.elapsed_timer() as elapsed:
|
|
part2(rows)
|
|
print("🕒", elapsed())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|