import matrix import shared import itertools import functools SAND_START = [0,500] def get_lines(rows): _rows = [] for row in rows: _row = [] for point in row.split(" -> "): _row.append(list(reversed(list(map(int, point.split(",")))))) _rows.append(_row) return _rows 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 = get_lines(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) def part2(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) def check_air(y,x): if y == floor: return False if (y,x) in walls: return False return True lines = get_lines(rows) (minY,maxY), (minX,maxX) = find_bounds(lines) walls = set() 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.add(c) for y,x in walls: walls.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, (dl, d, dr) = get_next(sand) if check_air(next_y, d): sand[0] = next_y elif check_air(next_y,dl): sand[0] = next_y sand[1] = dl elif check_air(next_y, dr): sand[0] = next_y sand[1] = dr else: walls.add(tuple(sand)) break #matrix.view_matrix(mx, 0,minX-1, maxY+3,maxX+2) 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()