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

Day 12

priv/livebooks/2022/day_12.livemd

Day 12

Mix.install([
  {:vega_lite, "~> 0.1.6"},
  {:kino_vega_lite, "~> 0.1.7"}
])

alias VegaLite, as: Vl

Part 1

kino_input = Kino.Input.textarea("Enter the input")
raw_input = Kino.Input.read(kino_input)
defmodule Solution do
  def grid2d(data, tx \\ &Function.identity/1) do
    for {row, row_idx} <- Enum.with_index(data),
        {cell, col_idx} <- Enum.with_index(row),
        into: %{} do
      {{row_idx, col_idx}, tx.(cell)}
    end
  end

  def parse(data) do
    map =
      data
      |> String.split("\n", trim: true)
      |> Enum.map(&amp;String.graphemes/1)
      |> grid2d(fn char -> :binary.first(char) end)

    source = elem(Enum.find(map, fn {_, v} -> v == ?S end), 0)
    destination = elem(Enum.find(map, fn {_, v} -> v == ?E end), 0)
    map = %{map | source => ?a, destination => ?z}

    {map, source, destination}
  end

  def to_digraph(grid) do
    graph = :digraph.new()

    Enum.reduce(grid, graph, fn {{x, y}, _}, _ ->
      [{x - 1, y}, {x + 1, y}, {x, y - 1}, {x, y + 1}]
      |> Enum.filter(&amp;Map.has_key?(grid, &amp;1))
      |> Enum.map(fn point ->
        unless grid[point] - grid[{x, y}] > 1 do
          {{x, y}, point}
        end
      end)
      |> Enum.reject(&amp;is_nil/1)
      |> Enum.each(fn {v1, v2} ->
        :digraph.add_vertex(graph, v1)
        :digraph.add_vertex(graph, v2)
        :digraph.add_edge(graph, v1, v2)
      end)
    end)

    graph
  end
end
{map, source, destination} = Solution.parse(raw_input)
graph = Solution.to_digraph(map)
path = :digraph.get_short_path(graph, source, destination)

:ok
path_chart =
  Vl.new(width: 800, height: 200)
  |> Vl.layers([
    Vl.new()
    |> Vl.data_from_values(
      Enum.map(
        map,
        fn {{x, y}, h} ->
          case {x, y} do
            ^destination -> %{"x" => y, "y" => x, "h" => 30}
            _ -> %{"x" => y, "y" => x, "h" => ?z - h}
          end
        end
      )
    )
    |> Vl.mark(:rect, opacity: 0.9)
    |> Vl.encode_field(:x, "x", type: :nominal, axis: nil)
    |> Vl.encode_field(:y, "y", type: :nominal, axis: nil)
    |> Vl.encode(:color, field: "h", type: :quantitative),
    Vl.new()
    |> Vl.mark(:point, opacity: 1, size: 1, color: :red)
    |> Vl.encode_field(:x, "x", type: :nominal, axis: nil)
    |> Vl.encode_field(:y, "y", type: :nominal, axis: nil)
    |> Vl.encode(:color, field: "h", type: :quantitative)
  ])
  |> Kino.VegaLite.new()
  |> Kino.render()

for {x, y} <- path do
  Kino.VegaLite.push(path_chart, %{"x" => y, "y" => x, "h" => -25})
  Process.sleep(10)
end

:ok

Solution Part 1

solution_1 = length(path) - 1

Part 2

sources = Enum.map(Enum.filter(map, fn {_, v} -> v == ?a end), &amp;elem(&amp;1, 0))

paths =
  sources
  |> Enum.map(fn source ->
    :digraph.get_short_path(graph, source, destination)
  end)
  |> Enum.filter(&amp; &amp;1)

:ok
Vl.new(width: 800, height: 200)
|> Vl.layers(
  for path <- paths do
    Vl.new()
    |> Vl.data_from_values(
      path
      |> Enum.with_index()
      |> Enum.map(fn {{x, y}, h} -> %{"x" => y, "y" => x, "h" => h} end)
    )
    |> Vl.mark(:point, opacity: 1, size: 2)
    |> Vl.encode_field(:x, "x", type: :nominal, axis: nil)
    |> Vl.encode_field(:y, "y", type: :nominal, axis: nil)
    |> Vl.encode(:color, field: "h", type: :quantitative)
  end
)

Solution Part II

solution_2 = (paths |> Enum.map(&amp;length/1) |> Enum.min()) - 1