Sean McLemon | Advent of Code

Home | Czech | Blog | GitHub | Advent Of Code | Notes


2018-12-17 - Reservoir Research

(original .ipynb)
import itertools
import math
from advent import grid_to_file

def parse_single_coord(coord):
    axis, value = coord.split("=")
    return (axis, int(value))

def parse_range_coord(coord):
    axis, range_values_str = coord.split("=")
    range_start, range_end = range_values_str.split("..")
    return (axis, range(int(range_start), 1 + int(range_end)))
    
def parse_line(line):
    left_coord_str, right_coord_str = line.strip().split(",")
    
    left_coord = parse_single_coord(left_coord_str)
    right_coords = parse_range_coord(right_coord_str)
    
    if left_coord[0] == "x":
        cols = right_coords[1]
        row = left_coord[1]
        return zip(cols, [row] * len(cols))
    else:
        rows = right_coords[1]
        col = left_coord[1]
        return zip([col] * len(rows), rows)   
    
def parse_coords(lines):
    parsed_lines = [ parse_line(line) for line in lines ]
    return list(itertools.chain.from_iterable(parsed_lines))    

def grid_dimensions(coords):
    min_row = math.inf
    max_row = 0
    min_col = math.inf
    max_col = 0
    
    for r, c in coords:
        if r > max_row:
            max_row = r
        if r < min_row:
            min_row = r
        if c > max_col:
            max_col = c
        elif c < min_col:
            min_col = c
            
    return (min_row, max_row, max_col, min_col)


def build_row(max_col, min_col):
    return ["."] * (1 + max_col - min_col)

def build_grid(lines):
    coords = parse_coords(lines)
    min_row, max_row, max_col, min_col = grid_dimensions(coords)
    grid = [ build_row(max_col, min_col) for n in range(max_row  + 1)]
    
    for row, col in coords:
        grid[row][col - min_col] = "#"
        
    grid[0][500 - min_col] = "+"
    
    return min_row, grid

def drip_down(grid, r, c):
    if r+1 < len(grid):
        grid[r+1][c] = "|"
    
def cell_below(grid, r, c):
    if (r+1 >= len(grid)):
        return "-"    
    return grid[r+1][c]
    
def cell_left(grid, r, c):
    return grid[r][c-1]
    
def cell_right(grid, r, c):
    return grid[r][c+1]    
    
def fill_left(grid, r, c):
    to_fill = []
    spill = False
    
    while True:
        to_fill.append((r, c))
        if cell_left(grid, r, c) == "#":
            spill = False
            break
        elif cell_below(grid, r, c) == ".":
            spill = True
            break
        c -= 1
        
    return (spill, to_fill)
        
def fill_right(grid, r, c):
    to_fill = []
    spill = False
    
    while True:
        to_fill.append((r, c))
        if cell_right(grid, r, c) == "#":
            spill = False
            break
        elif cell_below(grid, r, c) == ".":
            spill = True
            break
        c += 1
    
    return (spill, to_fill)
    

def fill(grid, r, c):
    spill_left, to_fill_left = fill_left(grid, r, c)
    spill_right, to_fill_right = fill_right(grid, r, c)                           
    fill_str = "|" if spill_left or spill_right else "~"
    grid[r][c] = fill_str
    
    to_fill = to_fill_left + to_fill_right
    
    for (fill_r, fill_c) in to_fill:
        grid[fill_r][fill_c] = fill_str
        
def step_grid(grid):
    
    change_made = False
    
    for r, row in enumerate(grid):
        for c, col in enumerate(row):
            if col == "+" and grid[r+1][c] != "|":
                grid[r+1][c] = "|"
                change_made = True
                    
            if (col == "|"):
                if cell_below(grid, r, c) == ".":
                    drip_down(grid, r, c)
                    change_made = True
                    
                elif cell_below(grid, r, c) == "#" and cell_right(grid, r, c) != "|" and cell_left(grid, r, c) != "|":
                    fill(grid, r, c)
                    change_made = True
                    
                elif cell_below(grid, r, c) == "~" and cell_right(grid, r, c) != "|" and cell_left(grid, r, c) != "|":
                    fill(grid, r, c)
                    change_made = True
                    
                elif cell_below(grid, r, c) == "~" and cell_left(grid, r, c) == ".":
                    fill(grid, r, c)
                    change_made = True            

                elif cell_below(grid, r, c) == "~" and cell_right(grid, r, c) == ".":
                    fill(grid, r, c)
                    change_made = True                       

    return change_made

def count_water_tiles(grid, min_row):
    count = 0
    for r, row in enumerate(grid):
        for col in row:
            if r >= min_row and (col == "|" or col == "~"):
                count += 1
    return count

def count_standing_water_tiles(grid):
    count = 0
    for row in grid:
        for col in row:
            if col == "~":
                count += 1
    return count
()
('a',)
('b',)
('c',)
('a', 'b')
('a', 'c')
('b', 'c')
('a', 'b', 'c')
test_input_str = """x=495, y=2..7
y=7, x=495..501
x=501, y=3..7
x=498, y=2..4
x=506, y=1..2
x=498, y=10..13
x=504, y=10..13
y=13, x=498..504"""

palette = {
    ".": "white",
    "#": "orange",
    "|": "blue",
    "~": "lightblue",
    "+": "red"
}

def solve(input_lines, filename, big=False):
    min_row, grid = build_grid(input_lines)
    
    changed = True
    while changed:
        changed = step_grid(grid)

    if big:
        grid_to_file(grid, palette, filename, show=True, cell_size=1)
    else:
        grid_to_file(grid, palette, filename, show=True)
    
    total_water_tiles = count_water_tiles(grid, min_row)
    print("part 1:", total_water_tiles)
    
    standing_water_tiles = count_standing_water_tiles(grid)
    print("part 2:", standing_water_tiles)

solve(test_input_str.split("\n"), "day-17.png")
part 1: 57
part 2: 29
problem = open("day17.txt", "r").readlines()

solve(problem, "day17.png", True)
part 1: 38021
part 2: 32069