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

Day11

2023/elixir/day11.livemd

Day11

Mix.install([
  {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"},
  {:benchee, "~> 1.0"},
  {:nimble_parsec, "~> 1.0"},
  {:libgraph, "~> 0.16.0"},
  {:math, "~> 0.7.0"}
])

Get Input

{:ok, data} = KinoAOC.download_puzzle("2023", "11", System.fetch_env!("LB_AOC_SECRET"))

Solve

defmodule Day11 do
  @dots MapSet.new(["."])
  def out(res, t), do: IO.puts("Res #{t}: #{res}")

  def run(data, part_dist) do
    data
    |> String.split("\n", trim: true)
    |> parse()
    |> expand(part_dist)
    |> get_galaxies()
    |> permutations()
    |> paths()
    |> Enum.sum()
  end

  def parse(rows) do
    Enum.map(rows, &String.split(&1, "", trim: true))
  end

  def expand(data, dist) do
    {data, rows} = get_expand_coords(data, dist)
    {data, cols} = data |> transpose() |> get_expand_coords(dist)
    {transpose(data), {rows, cols}}
  end

  def transpose(data) do
    data |> Enum.zip() |> Enum.map(&Tuple.to_list/1)
  end

  def get_expand_coords(rows, dist) do
    rows
    |> Enum.reduce({0, []}, fn row, {ind, acc} ->
      delta = (MapSet.new(row) == @dots && dist) || 1
      {ind + delta, [{row, ind + delta} | acc]}
    end)
    # taking data from {_tmp_ind, data}
    |> elem(1)
    |> Enum.reduce({[], []}, fn {row, ind}, {data, ind_acc} ->
      {[row | data], [ind | ind_acc]}
    end)
  end

  def get_galaxies({data, {rows, cols}}) do
    data
    |> Enum.with_index()
    |> Enum.flat_map(fn {row, r} ->
      row
      |> Enum.with_index()
      |> Enum.reduce([], fn elem, acc ->
        case elem do
          {"#", c} ->
            [{Enum.at(rows, r), Enum.at(cols, c)} | acc]

          _ ->
            acc
        end
      end)
    end)
  end

  def permutations(galaxies) do
    for a <- galaxies, b <- galaxies, a != b, into: MapSet.new(), do: MapSet.new([a, b])
  end

  def paths(set) do
    set
    |> Enum.to_list()
    |> Enum.map(&amp;Enum.to_list/1)
    |> Enum.map(&amp;manhattan_dist/1)
  end

  def manhattan_dist([{ra, ca}, {rb, cb}]) do
    abs(ra - rb) + abs(ca - cb)
  end
end

Check

ExUnit.start(autorun: false)

defmodule Day11.Test do
  use ExUnit.Case, async: false

  @td1 """
  ...#......
  .......#..
  #.........
  ..........
  ......#...
  .#........
  .........#
  ..........
  .......#..
  #...#.....
  """

  setup do
    {:ok, data} = KinoAOC.download_puzzle("2023", "11", System.fetch_env!("LB_AOC_SECRET"))
    %{data: data}
  end

  test "solves test cases" do
    assert Day11.run(@td1, 2) == 374
    assert Day11.run(@td1, 10) == 1030
    assert Day11.run(@td1, 100) == 8410
  end

  test "solves live cases", %{data: data} do
    assert Day11.run(data, 2) == 9_609_130
    assert Day11.run(data, 1_000_000) == 702_152_204_842
  end
end

ExUnit.run()

data |> Day11.run(2) |> Day11.out("t1")
data |> Day11.run(1_000_000) |> Day11.out("t2")