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

Day 11

2023/day11.livemd

Day 11

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

Section

input = Kino.Input.textarea("Input", monospace: true)
defmodule Constellation do
  def parse(input) do
    input
    |> String.split("\n")
    |> Enum.with_index()
    |> Enum.reduce(MapSet.new(), fn {line, y}, galaxies ->
      line
      |> String.graphemes()
      |> Enum.with_index()
      |> Enum.reduce(galaxies, fn
        {"#", x}, galaxies -> MapSet.put(galaxies, {x, y})
        {_, _}, galaxies -> galaxies
      end)
    end)
  end

  def distance(constellation) do
    constellation
    |> Enum.reduce({0, Enum.to_list(constellation)}, fn {x1, y1}, {dist, [_ | galaxies]} ->
      {Enum.reduce(galaxies, dist, fn {x2, y2}, dist ->
         dist + abs(x1 - x2) + abs(y1 - y2)
       end), galaxies}
    end)
    |> elem(0)
  end

  def expand(constellation, factor \\ 0) do
    constellation
    |> expand_over(0, factor)
    |> expand_over(1, factor)
  end

  defp expand_over(constellation, dim, factor) do
    constellation_groups = Enum.group_by(constellation, &elem(&1, dim))

    Enum.reduce(0..elem(Enum.max_by(constellation, &elem(&1, dim)), dim), {0, MapSet.new()}, fn i,
                                                                                                {offset,
                                                                                                 galaxies} ->
      case constellation_groups[i] do
        nil ->
          {offset + factor + 1, galaxies}

        group ->
          {offset,
           MapSet.union(
             galaxies,
             MapSet.new(group, fn galaxy ->
               update_in(galaxy, [Access.elem(dim)], &(&1 + offset))
             end)
           )}
      end
    end)
    |> elem(1)
  end
end
input
|> Kino.Input.read()
|> Constellation.parse()
|> Constellation.expand()
|> Constellation.distance()
input
|> Kino.Input.read()
|> Constellation.parse()
|> Constellation.expand(1_000_000)
|> Constellation.distance()