advent-of-code

Perserverance, or the lack thereof

git clone git://git.shimmy1996.com/advent-of-code.git

day-15.jl (6888B)

    1 function computer_step!(program, pc, relative_base, input)
    2     # Runs from given pc until completion or next request for input.
    3     # Modifies program and pc.
    4     # Returns if the program has terminated and the output.
    5     output = []
    6     function get_param(loc, mode)
    7         # Loc is gauranteed to exist.
    8         if mode == 0
    9             # Position mode.
   10             get(program, program[loc] + 1, 0)
   11         elseif mode == 1
   12             # Immediate mode.
   13             program[loc]
   14         else
   15             # Relative mode.
   16             get(program, relative_base + program[loc] + 1, 0)
   17         end
   18     end
   19     function get_res_loc(loc, mode)
   20         # Loc is gauranteed to exist.
   21         if mode == 0
   22             # Position mode.
   23             program[loc] + 1
   24         else
   25             # Relative mode.
   26             relative_base + program[loc] + 1
   27         end
   28     end
   29     while true
   30         op_code = program[pc] % 100
   31         mode_1 = (program[pc] % 1000) ÷ 100
   32         mode_2 = (program[pc] % 10000) ÷ 1000
   33         mode_3 = (program[pc] % 100000) ÷ 10000
   34         if op_code == 1
   35             program[get_res_loc(pc + 3, mode_3)] = (
   36                 get_param(pc + 1, mode_1)
   37                 + get_param(pc + 2, mode_2)
   38             )
   39             pc += 4
   40         elseif op_code == 2
   41             program[get_res_loc(pc + 3, mode_3)] = (
   42                 get_param(pc + 1, mode_1)
   43                 * get_param(pc + 2, mode_2)
   44             )
   45             pc += 4
   46         elseif op_code == 3
   47             if !isnothing(input)
   48                 program[get_res_loc(pc + 1, mode_1)] = input
   49                 input = nothing
   50             else
   51                 return (pc, relative_base, false, output)
   52             end
   53             pc += 2
   54         elseif op_code == 4
   55             push!(output, get_param(pc + 1, mode_1))
   56             pc += 2
   57         elseif op_code == 5
   58             if get_param(pc + 1, mode_1) != 0
   59                 pc = get_param(pc + 2, mode_2) + 1
   60             else
   61                 pc += 3
   62             end
   63         elseif op_code == 6
   64             if get_param(pc + 1, mode_1) == 0
   65                 pc = get_param(pc + 2, mode_2) + 1
   66             else
   67                 pc += 3
   68             end
   69         elseif op_code == 7
   70             program[get_res_loc(pc + 3, mode_3)] = (
   71                 get_param(pc + 1, mode_1)
   72                 < get_param(pc + 2, mode_2)
   73             ) ? 1 : 0
   74             pc += 4
   75         elseif op_code == 8
   76             program[get_res_loc(pc + 3, mode_3)] = (
   77                 get_param(pc + 1, mode_1)
   78                 == get_param(pc + 2, mode_2)
   79             ) ? 1 : 0
   80             pc += 4
   81         elseif op_code == 9
   82             relative_base += get_param(pc + 1, mode_1)
   83             pc += 2
   84         elseif op_code == 99
   85             break
   86         else
   87             println("Unknown op code: ", op_code)
   88             break
   89         end
   90     end
   91     (pc, relative_base, true, output)
   92 end
   93 
   94 function flip(cmd)
   95     if cmd == 1
   96         2
   97     elseif cmd == 2
   98         1
   99     elseif cmd == 3
  100         4
  101     else
  102         3
  103     end
  104 end
  105 
  106 function move_to_cmd(coord, cmd)
  107     if cmd == 1
  108         (coord[1], coord[2] + 1)
  109     elseif cmd == 2
  110         (coord[1], coord[2] - 1)
  111     elseif cmd == 3
  112         (coord[1] - 1, coord[2])
  113     else
  114         (coord[1] + 1, coord[2])
  115     end
  116 end
  117 
  118 function chart_maze(program)
  119     # Chart the maze via DFS
  120     program = Dict(enumerate(copy(program)))
  121     # Set program state.
  122     pc = 1
  123     relative_base = 0
  124     # DFS to chart the maze.
  125     curr_cmds = []
  126     curr_path = [(0, 0)]
  127     maze_map = Dict((0, 0) => 1)
  128     # Start with initial directions.
  129     queue = []
  130     for cmd in 1:4
  131         push!(queue, (1, cmd, move_to_cmd((0, 0), cmd)))
  132     end
  133     while !isempty(queue)
  134         path_len, cmd, coord = pop!(queue)
  135         # Check difference between current path and backtrack.
  136         if length(curr_path) > path_len
  137             # Backtrack
  138             for cmd in map(flip, reverse(curr_cmds[path_len:end]))
  139                 pc, relative_base, _, _ = computer_step!(
  140                     program, pc, relative_base, cmd
  141                 )
  142             end
  143             # Chean up path.
  144             resize!(curr_cmds, path_len - 1)
  145             # Keep the origin.
  146             resize!(curr_path, path_len)
  147         end
  148         # Move in specified direction.
  149         pc, relative_base, _, step_output = computer_step!(
  150             program, pc, relative_base, cmd
  151         )
  152         # Chart the result.
  153         maze_map[coord] = step_output[1]
  154         # Determine if we proceed in given direction.
  155         if step_output[1] != 0
  156             # Free space, record and plan next moves.
  157             # Otherwise, keep original path.
  158             push!(curr_cmds, cmd)
  159             push!(curr_path, coord)
  160             for next_cmd in 1:4
  161                 # Only visit uncharted location.
  162                 target = move_to_cmd(coord, next_cmd)
  163                 if !haskey(maze_map, target)
  164                     push!(queue, (path_len + 1, next_cmd, target))
  165                 end
  166             end
  167         end
  168     end
  169     maze_map
  170 end
  171 
  172 function find_min_path(maze_map)
  173     # Return shortest path from (0, 0) to space labeled 2.
  174     queue = []
  175     visited = Dict()
  176     push!(queue, (0, (0, 0)))
  177     # BFS to find location.
  178     while !isempty(queue)
  179         path_len, coord = popfirst!(queue)
  180         visited[coord] = true
  181         dest = get(maze_map, coord, 0)
  182         if dest == 1
  183             # Explore next stpes.
  184             for cmd in 1:4
  185                 next_coord = move_to_cmd(coord, cmd)
  186                 if !get(visited, next_coord, false)
  187                     push!(queue, (path_len + 1, next_coord))
  188                 end
  189             end
  190         elseif dest == 2
  191             return path_len
  192         end
  193     end
  194 end
  195 
  196 function calc_fill_time(maze_map)
  197     # Computes the fill time for the entire maze.
  198     # Find oxygen source.
  199     source_coord = collect(filter(v -> maze_map[v] == 2, keys(maze_map)))[1]
  200     queue = []
  201     filled = Dict()
  202     push!(queue, (0, source_coord))
  203     max_time = 0
  204     # BFS to fill the maze.
  205     while !isempty(queue)
  206         time, coord = popfirst!(queue)
  207         max_time = max(max_time, time)
  208         filled[coord] = true
  209         if get(maze_map, coord, 0) != 0
  210             # Walls can't be filled.
  211             # Fill the next area.
  212             for cmd in 1:4
  213                 next_coord = move_to_cmd(coord, cmd)
  214                 if !get(filled, next_coord, false)
  215                     push!(queue, (time + 1, next_coord))
  216                 end
  217             end
  218         end
  219     end
  220     max_time
  221 end
  222 
  223 function part_1(input)
  224     maze_map = chart_maze(input)
  225     find_min_path(maze_map)
  226 end
  227 
  228 function part_2(input)
  229     maze_map = chart_maze(input)
  230     calc_fill_time(maze_map)
  231 end
  232 
  233 input = map(x -> parse(Int128, x), split(readlines(open("input.txt"))[1], ','))
  234 
  235 println("Julia:")
  236 println("Part 1: ", part_1(input))
  237 println("Part 2: ", part_2(input))