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

Day 11

2023/day_11.livemd

Day 11

Mix.install([:kino])

Get input

input =
  Kino.Input.textarea("Paste input here",
    default: """
    ...#......
    .......#..
    #.........
    ..........
    ......#...
    .#........
    .........#
    ..........
    .......#..
    #...#.....
    """
  )
universe =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Stream.map(fn line -> String.split(line, "", trim: true) end)
  |> Stream.map(&Enum.with_index/1)
  |> Stream.with_index()
  |> Stream.flat_map(fn {line, y} -> Stream.map(line, fn {item, x} -> {{x, y}, item} end) end)
  |> Enum.into(%{})
import Kino.Shorts

defmodule CosmicExpansion do
  def draw(universe) do
    {max_x, max_y} = get_bounds(universe)

    text =
      Enum.map(0..max_y, fn y ->
        Enum.map(0..max_x, fn x -> universe[{x, y}] end)
        |> Enum.join("")
      end)
      |> Enum.join("\n")

    Kino.Shorts.markdown("""
    ```
    #{text}
    ```
    """)
  end

  def expand(universe, by \\ 1) do
    {empty_rows, empty_cols} = get_empties(universe)

    universe
    |> Enum.map(fn {{x, y}, c} ->
      shift_x = Enum.count(empty_cols, fn ex -> x > ex end)
      shift_y = Enum.count(empty_rows, fn ey -> y > ey end)

      {{x + shift_x * by, y + shift_y * by}, c}
    end)
    |> then(fn u ->
      {max_x, max_y} = get_bounds(universe)

      new_cols =
        empty_cols
        |> Stream.with_index()
        |> Enum.flat_map(fn {i, x} ->
          Enum.map(0..(max_y + length(empty_rows)), &{{x + 1 + i, &1}, "."})
        end)

      new_rows =
        empty_rows
        |> Stream.with_index()
        |> Enum.flat_map(fn {i, y} ->
          Enum.map(0..(max_x + length(empty_cols)), &{{&1, y + 1 + i}, "."})
        end)

      List.flatten([u, new_cols, new_rows])
    end)
    |> Enum.into(%{})
  end

  def get_bounds(universe) do
    Enum.reduce(universe, {0, 0}, fn {{x, y}, _}, {max_x, max_y} ->
      {max(max_x, x), max(max_y, y)}
    end)
  end

  def get_empties(universe) do
    {max_x, max_y} = get_bounds(universe)

    empty_rows =
      Enum.reduce(0..max_y, [], fn y, acc ->
        if Enum.all?(0..max_x, fn x -> universe[{x, y}] === "." end) do
          acc ++ [y]
        else
          acc
        end
      end)

    empty_cols =
      Enum.reduce(0..max_x, [], fn x, acc ->
        if Enum.all?(0..max_y, fn y -> universe[{x, y}] === "." end) do
          acc ++ [x]
        else
          acc
        end
      end)

    {empty_rows, empty_cols}
  end
end
defmodule RC do
  def comb(0, _), do: [[]]
  def comb(_, []), do: []

  def comb(m, [h | t]) do
    for(l <- comb(m - 1, t), do: [h | l]) ++ comb(m, t)
  end
end
defmodule CosmicExpansion.Math do
  def manhattan_dist({{x1, y1}, {x2, y2}}) do
    abs(x1 - x2) + abs(y1 - y2)
  end
end
# CosmicExpansion.expand(universe)
# |> CosmicExpansion.draw()

Part 1

expanded_universe = CosmicExpansion.expand(universe)

galaxies =
  Enum.filter(expanded_universe, fn
    {_, "#"} -> true
    _ -> false
  end)

combinations = RC.comb(2, galaxies)

combinations
|> Stream.map(fn [{c1, _}, {c2, _}] -> CosmicExpansion.Math.manhattan_dist({c1, c2}) end)
|> Enum.sum()

Part 2

expanded_universe = CosmicExpansion.expand(universe, 999_999)

galaxies =
  Enum.filter(expanded_universe, fn
    {_, "#"} -> true
    _ -> false
  end)

combinations = RC.comb(2, galaxies)

combinations
|> Stream.map(fn [{c1, _}, {c2, _}] -> CosmicExpansion.Math.manhattan_dist({c1, c2}) end)
|> Enum.sum()