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

216 lines
6.5 KiB
Elixir
Raw Normal View History

2023-12-14 09:20:50 +00:00
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