defmodule AOC.Day18 do use AOC.Day, day: 18 def parse_input(lines) do Enum.map(lines, fn line -> %{"direction" => direction, "length" => length, "color" => color} = Regex.named_captures( ~r/^(?[UDLR]) (?\d+) \(\#(?[[:alnum:]]+)\)$/, line ) {String.to_atom(direction), String.to_integer(length), color} end) end def dig_stretch({y, x}, direction, length) do Enum.map(1..length, fn n -> case direction do :U -> {y - n, x} :R -> {y, x + n} :D -> {y + n, x} :L -> {y, x - n} end end) end def execute_dig_plan(dig_plan) do Enum.reduce(dig_plan, {%{{0, 0} => true}, {0, 0}}, fn {direction, length, _color}, {terrain, position} -> stretch = dig_stretch(position, direction, length) terrain = Enum.reduce(stretch, terrain, fn new_position, acc -> Map.put(acc, new_position, true) end) new_position = stretch |> Enum.reverse() |> hd() {terrain, new_position} end) end def print_terrain(terrain) do max_y = terrain |> Enum.map(&elem(&1, 0)) |> Enum.map(&elem(&1, 0)) |> Enum.max() max_x = terrain |> Enum.map(&elem(&1, 0)) |> Enum.map(&elem(&1, 1)) |> Enum.max() min_y = terrain |> Enum.map(&elem(&1, 0)) |> Enum.map(&elem(&1, 0)) |> Enum.min() min_x = terrain |> Enum.map(&elem(&1, 0)) |> Enum.map(&elem(&1, 1)) |> Enum.min() Enum.each(min_y..max_y, fn y -> Enum.map(min_x..max_x, fn x -> case Map.fetch(terrain, {y, x}) do :error -> "." _ -> "#" end end) |> Enum.join() |> IO.puts() end) end def find_interior_space(terrain) do max_x = terrain |> Enum.map(&elem(&1, 0)) |> Enum.map(&elem(&1, 1)) |> Enum.max() min_x = terrain |> Enum.map(&elem(&1, 0)) |> Enum.map(&elem(&1, 1)) |> Enum.min() edge_x = Enum.find(min_x..max_x, fn x -> case Map.fetch(terrain, {2, x}) do :error -> false _ -> true end end) edge_x + 1 end def flood_fill_terrain(terrain) do interior_x = find_interior_space(terrain) flood_fill_terrain(terrain, {1, interior_x}) end def flood_fill_terrain(terrain, {y, x}) do [{y - 1, x}, {y, x + 1}, {y + 1, x}, {y, x - 1}] |> Enum.reduce(terrain, fn next_position, acc -> case Map.fetch(acc, next_position) do :error -> acc |> Map.put(next_position, true) |> flood_fill_terrain(next_position) _ -> acc end end) end def part1(dig_plan) do execute_dig_plan(dig_plan) |> elem(0) # |> tap(&print_terrain/1) |> flood_fill_terrain() # |> tap(&print_terrain/1) |> Enum.count() end def part2(_input) do "TODO" end end