kwando

kwando

Advent Of Code 2022 - Day 12

Phew, this one took a while to get right. My naive attempts was way to slow so I reached for Dijkstras shortest path algorithm… and that payed off pretty well for part 2 :slight_smile:
~1s on my machine, I have not bothered to cleanup and optimize it yet… you have been warned :slight_smile:

Most Liked

code-shoily

code-shoily

Took it as an opportunity to learn me some Vegalite too.

Thank you @kwando for inspiring me to do so.

Basic stuff, will dive deeper at night.

Update: I could add animation to the first part. The second part graph looks ugly though. I need to play more with this stuff. This is amazing stuff!

Here’s the code for Livemd version: https://github.com/code-shoily/advent_of_code/blob/master/lib/2022/day_12.livemd

And the regular one (This one has cache based part 2 since I don’t need to worry about any graphs) advent_of_code/lib/2022/day_12.ex at master · code-shoily/advent_of_code · GitHub

maennchen

maennchen

I initially went with :digraph, since that is already part of the BEAM:

I felt that that was cheating since that did 90% of the given problem. Therefore, I also implemented Dijkstra naively based on the Wikipedia description of the algorithm.

kwando

kwando

:digraph was the thing I was looking for, I knew there were something like that already bundled but didn’t remember the name :sweat_smile:

Is there a way to get “all the distances” (djikstra table) from :digraph? It was quick enough to :digraph.get_short_path(g, <a>, goal) for every a though…

stevensonmt

stevensonmt

Like most I used :digraph. Felt like cheating actually. Though after Day 11 I wasn’t above taking the easy path.

Pun intended.

AOCDay12
defmodule Day12 do
  defmodule Input do
    def sample_data() do
      """
      Sabqponm
      abcryxxl
      accszExk
      acctuvwj
      abdefghi
      """
    end
    @neighbors [{-1, 0}, {1, 0}, {0, -1}, {0, 1}]

    def parse(input) do
      rows =
        input
        |> String.split("\n", trim: true)

      {row_len, col_len} = {length(rows), String.length(hd(rows))}

      rows
      |> populate_vertices()
      |> populate_edges(row_len - 1, col_len - 1)
    end

    defp populate_vertices(input_rows) do
      input_rows
      |> Enum.with_index()
      |> Enum.reduce({:digraph.new(), nil, nil}, fn {line, r}, {graph, start, stop} ->
        String.codepoints(line)
        |> Enum.with_index()
        |> Enum.reduce({graph, start, stop}, fn {<<val::utf8>>, c}, {g, begin, final} ->
          case val do
            83 ->
              :digraph.add_vertex(g, {c, r}, ?a)
              {g, {c, r}, final}

            69 ->
              :digraph.add_vertex(g, {c, r}, ?z)
              {g, begin, {c, r}}

            _ ->
              :digraph.add_vertex(g, {c, r}, val)
              {g, begin, final}
          end
        end)
      end)
    end

    defp populate_edges({graph, start, stop}, rows, cols) do
      edged_graph =
        for r <- 0..rows,
            c <- 0..cols,
            reduce: graph do
          graff ->
            {emanating_vertex, v1} = :digraph.vertex(graff, {c, r})

            @neighbors
            |> Enum.reduce(graff, fn {x, y}, gr ->
              incident = :digraph.vertex(graff, {c + x, r + y})

              case incident do
                {incident_vertex, v2} when v2 - v1 == 1 ->
                  :digraph.add_edge(gr, emanating_vertex, incident_vertex)
                  gr

                {incident_vertex, v2} when v2 - v1 <= 0 ->
                  :digraph.add_edge(gr, emanating_vertex, incident_vertex)
                  gr

                _ ->
                  gr
              end
            end)
        end

      {edged_graph, start, stop}
    end
  end

  def part1({graph, start, stop}) do
    :digraph.get_short_path(graph, start, stop) |> Enum.count() |> Kernel.-(1)
  end

  def part2({graph, _start, stop}) do
    graph
    |> :digraph.vertices()
    |> Enum.filter(fn vertex -> {vertex, ?a} == :digraph.vertex(graph, vertex) end)
    |> Enum.map(fn starter -> :digraph.get_short_path(graph, starter, stop) end)
    |> Enum.filter(& &1)
    |> Enum.map(fn path -> Enum.count(path) - 1 end)
    |> Enum.min()
  end
milli

milli

I don’t know if the algorithm I’ve used has a name, but part 2 took ~20 ms on my machine :slightly_smiling_face:

defp walk([{position, distance} | rest], map) do
  if map[position].distance == :unknown do
    new_map = put_in(map, [position, :distance], distance)
    possible_ways = where_can_i_go(position, map) |> Enum.map(fn p -> {p, distance + 1} end)
    walk(rest ++ possible_ways, new_map)
  else
    walk(rest, map)
  end
end

Full solution

Where Next?

Popular in Challenges Top

Aetherus
This topic is about Day 7 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
bjorng
Note: This topic is to talk about Day 1 of the Advent of Code 2019.
New
bjorng
Note: This topic is to talk about Day 16 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
Aetherus
This topic is about Day 15 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/l...
New
Aetherus
Don’t know why the regex ~r/[\W && [^\.]]/x does not work in Elixir. It works pretty well in Ruby. Anyway, here is my solution:
New
lud
At first I was scared but I found is a simple way to compute the sides. defmodule AdventOfCode.Solutions.Y24.Day12 do alias AdventOfCo...
New
Aetherus
This topic is about Day 5 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
New
stevensonmt
Reasonably pleased with my solution. The bitstring packet problems are so well suited to Erlang/Elixir it’s almost not fair. defmodule D...
New
bjorng
Note: This topic is to talk about Day 18 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New

Other popular topics Top

TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement