Advent of Code 2023 Day 10
Mix.install([
{:kino_aoc, "~> 0.1.5"},
{:libgraph, "~> 0.16.0"},
{:kino_vega_lite, "~> 0.1.10"}
])
Prep
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2023", "10", System.fetch_env!("LB_AOC_SESSION"))
IO.puts(puzzle_input)
chars =
puzzle_input
|> String.split("\n")
|> Enum.map(&String.to_charlist/1)
grid =
for {row, i} <- Stream.with_index(chars),
{val, j} <- Stream.with_index(row),
into: %{},
do: {{i * 2 + 1, j * 2 + 1}, val}
graph =
for {{i, j}, val} <- grid, reduce: Graph.new(type: :undirected) do
graph ->
case val do
?. ->
graph
?- ->
graph |> Graph.add_edge({i, j}, {i, j - 1}) |> Graph.add_edge({i, j}, {i, j + 1})
?| ->
graph |> Graph.add_edge({i, j}, {i - 1, j}) |> Graph.add_edge({i, j}, {i + 1, j})
?7 ->
graph |> Graph.add_edge({i, j}, {i, j - 1}) |> Graph.add_edge({i, j}, {i + 1, j})
?F ->
graph |> Graph.add_edge({i, j}, {i, j + 1}) |> Graph.add_edge({i, j}, {i + 1, j})
?L ->
graph |> Graph.add_edge({i, j}, {i, j + 1}) |> Graph.add_edge({i, j}, {i - 1, j})
?J ->
graph |> Graph.add_edge({i, j}, {i, j - 1}) |> Graph.add_edge({i, j}, {i - 1, j})
?S ->
graph
|> Graph.add_edge({i, j}, {i, j - 1})
|> Graph.add_edge({i, j}, {i, j + 1})
|> Graph.add_edge({i, j}, {i - 1, j})
|> Graph.add_edge({i, j}, {i + 1, j})
end
end
{start, _} = Enum.find(grid, fn {_, val} -> val == ?S end)
components = Graph.components(graph)
loop = Enum.find(components, &Enum.any?(&1, fn v -> v == start end))
Part 1
loop |> length() |> div(4)
Part 2
i_min = Enum.min(loop) |> elem(0)
i_max = Enum.max(loop) |> elem(0)
j_min = loop |> Enum.min_by(&elem(&1, 1)) |> elem(1)
j_max = loop |> Enum.max_by(&elem(&1, 1)) |> elem(1)
loop = MapSet.new(loop)
Graph.neighbors(graph, start) |> Enum.map(&Graph.neighbors(graph, &1))
for i <- i_min..i_max//2, reduce: 0 do
count ->
for j <- (j_min - 2)..(j_max + 2)//2, reduce: {count, false, nil} do
{count, inside?, prev} ->
cond do
inside? && {i, j} not in loop ->
{count + 1, true, prev}
!inside? && {i, j} not in loop ->
{count, false, prev}
grid[{i, j}] in [?|, ?S] && prev == nil ->
{count, !inside?, nil}
grid[{i, j}] in [?J, ?L] && prev == nil ->
{count, inside?, :up}
grid[{i, j}] in [?J, ?L] && prev == :up ->
{count, inside?, nil}
grid[{i, j}] in [?J, ?L] && prev == :down ->
{count, !inside?, nil}
grid[{i, j}] in [?7, ?F] && prev == nil ->
{count, inside?, :down}
grid[{i, j}] in [?7, ?F] && prev == :up ->
{count, !inside?, nil}
grid[{i, j}] in [?7, ?F] && prev == :down ->
{count, inside?, nil}
grid[{i, j}] == ?- ->
{count, inside?, prev}
true ->
IO.inspect({i, j}, label: "Coord")
IO.inspect(inside?, label: "Inside")
IO.inspect({i, j} in loop, label: "Boarder")
IO.inspect(prev, label: "Previous half vertical edge")
IO.inspect(<>, label: "Current")
raise "Unknown Situation"
end
end
|> elem(0)
end
Part 2 Alternative
defmodule Flood do
def flood(grid, border, {i, j}, seen) do
grid = Map.put(grid, {i, j}, ?~)
seen = MapSet.put(seen, {i, j})
[
{i, j - 1},
{i, j + 1},
{i - 1, j},
{i + 1, j}
]
|> Enum.reduce({grid, seen}, fn neighbor, {grid, seen} ->
cond do
neighbor in seen -> {grid, seen}
neighbor in border -> {grid, seen}
true -> flood(grid, border, neighbor, seen)
end
end)
end
end
{i, j} = Enum.min(loop)
{flooded, _} = Flood.flood(grid, loop, {i + 1, j + 1}, MapSet.new())
import Integer
Enum.count(flooded, fn {{i, j}, v} ->
is_odd(i) and is_odd(j) and v == ?~
end)