Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Advent of Code 2023 Day 10

2023/day-10.livemd

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, &amp;Enum.any?(&amp;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(&amp;elem(&amp;1, 1)) |> elem(1)
j_max = loop |> Enum.max_by(&amp;elem(&amp;1, 1)) |> elem(1)
loop = MapSet.new(loop)
Graph.neighbors(graph, start) |> Enum.map(&amp;Graph.neighbors(graph, &amp;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? &amp;&amp; {i, j} not in loop ->
            {count + 1, true, prev}

          !inside? &amp;&amp; {i, j} not in loop ->
            {count, false, prev}

          grid[{i, j}] in [?|, ?S] &amp;&amp; prev == nil ->
            {count, !inside?, nil}

          grid[{i, j}] in [?J, ?L] &amp;&amp; prev == nil ->
            {count, inside?, :up}

          grid[{i, j}] in [?J, ?L] &amp;&amp; prev == :up ->
            {count, inside?, nil}

          grid[{i, j}] in [?J, ?L] &amp;&amp; prev == :down ->
            {count, !inside?, nil}

          grid[{i, j}] in [?7, ?F] &amp;&amp; prev == nil ->
            {count, inside?, :down}

          grid[{i, j}] in [?7, ?F] &amp;&amp; prev == :up ->
            {count, !inside?, nil}

          grid[{i, j}] in [?7, ?F] &amp;&amp; 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)