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