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

Advent of code day 18

2024/livebooks/day-18.livemd

Advent of code day 18

Mix.install([
  {:kino, "~> 0.5.0"}
])

Setup input

example = Kino.Input.textarea("Please paste your input example:")
input = Kino.Input.textarea("Please paste your real input:")
lines = 70
cols = 70

grid =
  for l <- 0..lines, c <- 0..cols, into: %{} do
    {{l, c}, "."}
  end

coords =
  Kino.Input.read(example)
  |> String.split("\n", trim: true)
  |> Enum.map(fn
    coord ->
      [c, r] = String.split(coord, ",", trim: true)

      {String.to_integer(r), String.to_integer(c)}
  end)

grid =
  Enum.reduce(0..1024, grid, fn i, grid ->
    {c, r} = Enum.at(coords, i)
    Map.put(grid, {r, c}, "#")
  end)
defmodule Solver do
  @dirs [
    # up 
    {1, 0},
    # right
    {0, 1},
    # down 
    {-1, 0},
    # left 
    {0, -1}
  ]

  @dest {70, 70}

  def walk(grid) do
    0
    |> Stream.iterate(&amp;(&amp;1 + 1))
    |> Enum.reduce_while({MapSet.new(), [{{0, 0}, 0}], grid, 0}, fn _, {seen, queue, grid, _d} ->
      [{{r, c}, d} | tail] = queue

      new_positions =
        Enum.map(@dirs, fn {y, x} ->
          {r + y, c + x}
        end)

      go =
        Enum.reduce(new_positions, {seen, tail, grid, d}, fn
          _, {:ok, d, grid} ->
            {:ok, d, grid}

          {nr, nc}, {seen, tail, grid, d} ->
            cond do
              nr < 0 or nc < 0 or nr > 70 or nc > 70 ->
                {seen, tail, grid, d}

              grid[{nr, nc}] == "#" ->
                {seen, tail, grid, d}

              MapSet.member?(seen, {nr, nc}) ->
                {seen, tail, grid, d}

              {nr, nc} == @dest ->
                {:ok, d + 1, grid}

              true ->
                seen = MapSet.put(seen, {nr, nc})
                queue = tail ++ [{{nr, nc}, d + 1}]
                grid = Map.put(grid, {nr, nc}, "0")
                {seen, queue, grid, d}
            end
        end)

      case go do
        {_seen, [], _grid, _d} -> {:halt, {:error, :no_path}}
        {seen, queue, grid, d} -> {:cont, {seen, queue, grid, d}}
        {:ok, d, grid} -> {:halt, {:ok, d, grid}}
      end
    end)
  end
end

Part 01

{:ok, d, grid} = Solver.walk(grid)

d

Part 02

grid =
  Enum.reduce_while(1024..(length(coords) - 1), grid, fn i, grid ->
    {c, r} = Enum.at(coords, i)
    grid = Map.put(grid, {r, c}, "#")

    case Solver.walk(grid) do
      {:ok, _, _} ->
        {:cont, grid}

      {:error, _} ->
        {:halt, i}
    end
  end)
  |> then(fn x -> Enum.at(coords, x) end)