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

Day 11: Cosmic Expansion – Advent of Code 2023

2023/11.livemd

Day 11: Cosmic Expansion – Advent of Code 2023

input =
  File.stream!("/Users/pw/src/weiland/adventofcode/2023/input/11.txt")
  |> Stream.map(&String.trim/1)

test_input = "...#......
.......#..
#.........
..........
......#...
.#........
.........#
..........
.......#..
#...#....." |> String.split("\n")

Parsing

duplicate_empty_rows = fn old_rows ->
  old_rows
  |> Enum.with_index()
  |> Enum.reduce({old_rows, 0}, fn {row, index}, {rows, offset} ->
    if Enum.all?(row, &(&1 == ".")) do
      {List.insert_at(rows, index + offset, row), offset + 1}
    else
      {rows, offset}
    end
  end)
  |> elem(0)
end

parse = fn input ->
  rows =
    input
    |> Enum.map(&String.graphemes/1)
    |> duplicate_empty_rows.()
    # transpose cols to rows
    |> Enum.zip_with(&Function.identity/1)
    |> duplicate_empty_rows.()
    # transpose back to rows
    |> Enum.zip_with(&Function.identity/1)
    |> Enum.map_reduce(0, fn row, row_count ->
      Enum.map_reduce(row, row_count, fn
        "#", count -> {count + 1, count + 1}
        ".", count -> {".", count}
      end)
    end)
    |> elem(0)

  # Enum.reduce(Enum.with_index(rows), Map.new(), fn {row, y}, map ->
  #   Enum.reduce(Enum.with_index(row), map, fn
  #     {".", _x}, acc -> acc
  #     {value, x}, acc -> Map.put(acc, {x, y}, value)
  #   end)
  # end)

  for(
    {row, y} <- Enum.with_index(rows),
    {value, x} <- Enum.with_index(row),
    into: %{},
    do: {{x, y}, value}
  )
  |> Map.reject(fn {_key, value} -> value == "." end)
end

parse.(test_input)

Part One

to_paths = fn map ->
  Enum.reduce(map, [], fn {{x1, y1}, a}, lst ->
    [
      Enum.map(map, fn {{x2, y2}, b} ->
        length = abs(x1 - x2) + abs(y1 - y2)
        {a, b, length}
      end)
      | lst
    ]
  end)
  |> List.flatten()
  |> Enum.reject(fn {_, _, v} -> v == 0 end)
end

part_one = fn input ->
  input |> parse.() |> to_paths.() |> Enum.map(&amp;elem(&amp;1, 2)) |> Enum.sum() |> div(2)
end

part_one.(test_input) == 374 &amp;&amp;
  part_one.(input)

Part Two

part_two = fn input ->
  mat = input |> Enum.map(&amp;String.graphemes/1)

  {empty_rows, empty_cols, galaxies} =
    mat
    |> Enum.with_index()
    |> Enum.reduce(
      {0..(Enum.count(mat) - 1), 0..(Enum.count(Enum.at(mat, 0)) - 1), []},
      fn {line, row}, {r, c, g} ->
        line
        |> Enum.with_index()
        |> Enum.reduce({r, c, g}, fn {ch, col}, {r, c, g} ->
          if ch == "#" do
            {Enum.reject(r, &amp;(&amp;1 == row)), Enum.reject(c, &amp;(&amp;1 == col)), [{row, col} | g]}
          else
            {r, c, g}
          end
        end)
      end
    )

  diff = 999_999

  expanded =
    galaxies
    |> Enum.map(fn {row, col} ->
      {empty_rows |> Enum.filter(&amp;(&amp;1 <= row)) |> Enum.count() |> then(&amp;(&amp;1 * diff + row)),
       empty_cols |> Enum.filter(&amp;(&amp;1 <= col)) |> Enum.count() |> then(&amp;(&amp;1 * diff + col))}
    end)

  expanded
  |> Enum.drop(-1)
  |> Enum.with_index(1)
  |> Enum.map(fn {a, i} ->
    expanded
    |> Enum.drop(i)
    |> Enum.reduce(0, fn b, acc ->
      acc + abs(elem(b, 0) - elem(a, 0)) + abs(elem(b, 1) - elem(a, 1))
    end)
  end)
  |> Enum.sum()
end

part_two.(test_input) &amp;&amp;
  part_two.(input)