216 lines
6.5 KiB
Elixir
216 lines
6.5 KiB
Elixir
|
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
|