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

Day 13

d13.livemd

Day 13

Solution

defmodule D13 do
  def parse_input(path) do
    File.read!(path)
    |> String.trim_trailing()
    |> String.split("\n")
    |> Enum.filter(fn x -> x != "" end)
    |> Enum.map(fn x ->
      {res, _} = Code.eval_string(x)
      res
    end)
    |> Enum.chunk_every(2)
  end

  def cmp(same, same) when is_integer(same), do: nil
  def cmp(left, right) when is_integer(left) and is_integer(right), do: left < right
  def cmp(left, right) when is_integer(left) and is_list(right), do: cmp([left], right)
  def cmp(left, right) when is_list(left) and is_integer(right), do: cmp(left, [right])

  def cmp(left, right) when is_list(left) and is_list(right) do
    elems_comp =
      Enum.zip(left, right)
      |> Enum.reduce_while(nil, fn {left, right}, acc ->
        case cmp(left, right) do
          nil -> {:cont, acc}
          true -> {:halt, true}
          false -> {:halt, false}
        end
      end)

    case elems_comp do
      nil when length(left) < length(right) -> true
      nil when length(right) < length(left) -> false
      nil -> nil
      decided when is_boolean(decided) -> decided
    end
  end

  def p1(parts_list) do
    parts_list
    |> Enum.with_index(1)
    |> Enum.filter(fn {[left, right], _id} -> cmp(left, right) end)
    |> Enum.map(fn {_, id} -> id end)
    |> Enum.sum()
  end

  @dividers [[[2]], [[6]]]

  def p2(parts_list) do
    [@dividers | parts_list]
    |> Enum.concat()
    |> Enum.sort_by(fn x -> x end, &amp;cmp/2)
    |> Enum.with_index(1)
    |> Enum.filter(fn {elem, _idx} -> elem in @dividers end)
    |> Enum.map(fn {_elem, idx} -> idx end)
    |> Enum.product()
  end
end
input = D13.parse_input("inputs/d13")
{time_mcs, ans} = :timer.tc(fn -> D13.p1(input) end)
IO.puts("P1 ans = #{inspect(ans)}. [#{time_mcs / 1_000} ms.]")
input = D13.parse_input("inputs/d13")
{time_mcs, ans} = :timer.tc(fn -> D13.p2(input) end)
IO.puts("P2 ans = #{inspect(ans)}. [#{time_mcs / 1_000} ms.]")

Tests

ExUnit.start(autorun: false)

defmodule D11Tests do
  use ExUnit.Case, async: true

  setup_all _context do
    %{
      input: D13.parse_input("inputs/d13"),
      test_input: D13.parse_input("inputs/d13_test")
    }
  end

  test "p1", %{input: input, test_input: test_input} do
    assert D13.p1(test_input) == 13
    assert D13.p1(input) == 5882
  end

  test "p2", %{input: input, test_input: test_input} do
    assert D13.p2(test_input) == 140
    assert D13.p2(input) == 24948
  end
end

ExUnit.run()