110 lines
2.8 KiB
Elixir
110 lines
2.8 KiB
Elixir
|
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/^(?<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
|