defmodule AOC.Day10 do # use AOC.Day, day: 10, input: "example5" use AOC.Day, day: 10 def parse_input(lines) do pipe_map = lines |> Enum.with_index() |> Enum.flat_map(fn {row, y} -> row |> String.to_charlist() |> Enum.with_index() |> Enum.map(fn {tile, x} -> {{y, x}, tile} end) end) |> Enum.into(%{}) connection_map = Enum.map(pipe_map, fn {coords, tile} -> connections = case tile do ?| -> [:north, :south] ?- -> [:east, :west] ?L -> [:north, :east] ?J -> [:north, :west] ?7 -> [:south, :west] ?F -> [:south, :east] ?. -> [] ?S -> [] end {coords, connections} end) |> Enum.into(%{}) {start_location, start_connections} = find_start_connections(pipe_map) {pipe_map, connection_map, start_location, start_connections} end def find_start_connections(pipe_map) do {start_y, start_x} = start_location = pipe_map |> Enum.find(&(elem(&1, 1) == ?S)) |> elem(0) start_connections = [ {{start_y - 1, start_x}, ~c"|7F"}, {{start_y, start_x + 1}, ~c"-J7"}, {{start_y + 1, start_x}, ~c"|LJ"}, {{start_y, start_x - 1}, ~c"-LF"} ] |> Enum.filter(&Map.has_key?(pipe_map, elem(&1, 0))) |> Enum.filter(fn {coords, allowed_pipes} -> pipe = Map.fetch!(pipe_map, coords) Enum.member?(allowed_pipes, pipe) end) |> Enum.map(&elem(&1, 0)) {start_location, start_connections} end def find_furthest_pipe(connection_map, start_location, start_connections) do :ets.insert(:pipe_distances, {start_location, 0}) Enum.each(start_connections, &visit_pipe(connection_map, &1, 1)) :ets.tab2list(:pipe_distances) |> Enum.map(&elem(&1, 1)) |> Enum.max() end def visit_pipe(connection_map, {y, x} = location, step_count) do case Map.fetch(connection_map, location) do {:ok, directions} -> location_step_count = case :ets.lookup(:pipe_distances, location) do [{_, location_step_count}] -> location_step_count _ -> :infinity end if step_count < location_step_count do :ets.insert(:pipe_distances, {location, step_count}) Enum.each(directions, fn :north -> visit_pipe(connection_map, {y - 1, x}, step_count + 1) :east -> visit_pipe(connection_map, {y, x + 1}, step_count + 1) :south -> visit_pipe(connection_map, {y + 1, x}, step_count + 1) :west -> visit_pipe(connection_map, {y, x - 1}, step_count + 1) end) end :error -> :ok end end def part1({_, connection_map, start_location, start_connections}) do :ets.new(:pipe_distances, [:set, :protected, :named_table]) find_furthest_pipe(connection_map, start_location, start_connections) end def count_enclosed(pipe_map, connection_map, start_location, start_connections) do next_location = tl(start_connections) |> hd() # next_location = hd(start_connections) count_enclosed(pipe_map, connection_map, start_location, start_location, next_location) end def count_enclosed( pipe_map, connection_map, start_location, {prev_y, prev_x} = previous_location, {y, x} = current_location ) do if current_location != start_location do directions = Map.fetch!(connection_map, current_location) # Always take RIGHT hand side if Enum.member?(directions, :east) and Enum.member?(directions, :west) do if prev_x < x do # We came from the east flood_fill_ground(pipe_map, start_location, {y + 1, x}) else # We came from the west flood_fill_ground(pipe_map, start_location, {y - 1, x}) end end if Enum.member?(directions, :north) and Enum.member?(directions, :south) do if prev_y < y do # We came from the north flood_fill_ground(pipe_map, start_location, {y, x - 1}) else # We came from the south flood_fill_ground(pipe_map, start_location, {y, x + 1}) end end if Enum.member?(directions, :north) and Enum.member?(directions, :east) and prev_y < y do flood_fill_ground(pipe_map, start_location, {y, x - 1}) flood_fill_ground(pipe_map, start_location, {y + 1, x}) end if Enum.member?(directions, :east) and Enum.member?(directions, :south) and prev_x > x do flood_fill_ground(pipe_map, start_location, {y - 1, x}) flood_fill_ground(pipe_map, start_location, {y, x - 1}) end if Enum.member?(directions, :south) and Enum.member?(directions, :west) and prev_y > y do flood_fill_ground(pipe_map, start_location, {y, x + 1}) flood_fill_ground(pipe_map, start_location, {y - 1, x}) end if Enum.member?(directions, :west) and Enum.member?(directions, :north) and prev_x < x do flood_fill_ground(pipe_map, start_location, {y + 1, x}) flood_fill_ground(pipe_map, start_location, {y, x + 1}) end next_location = Enum.map(directions, fn :north -> {y - 1, x} :east -> {y, x + 1} :south -> {y + 1, x} :west -> {y, x - 1} end) |> Enum.reject(&(&1 == previous_location)) |> hd() count_enclosed(pipe_map, connection_map, start_location, current_location, next_location) end end def flood_fill_ground(pipe_map, start_location, {y, x} = location) do if location != start_location do case Map.fetch(pipe_map, location) do {:ok, _} -> case :ets.lookup(:pipe_distances, location) do [] -> case :ets.lookup(:enclosed_pipes, location) do [] -> :ets.insert(:enclosed_pipes, {location, 1}) [{y - 1, x}, {y, x + 1}, {y + 1, x}, {y, x - 1}] |> Enum.each(&flood_fill_ground(pipe_map, start_location, &1)) _ -> :ok end _ -> :ok end _ -> :ok end end end def part2({pipe_map, connection_map, start_location, start_connections}) do :ets.new(:enclosed_pipes, [:set, :protected, :named_table]) count_enclosed(pipe_map, connection_map, start_location, start_connections) :ets.tab2list(:enclosed_pipes) |> Enum.count() |> IO.inspect() "TODO" end end