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()