aoc/23/elixir/lib/days/day18.ex

110 lines
2.9 KiB
Elixir

defmodule AOC.Day18 do
# use AOC.Day, day: 18, input: "example"
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/^(?<direction>[UDLR]) (?<length>\d+) \(\#(?<color>[[: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