Powered by AppSignal & Oban Pro

Day 13

2022/day-13.livemd

Day 13

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

example_input =
  Kino.Input.textarea("example input:")
  |> Kino.render()

real_input = Kino.Input.textarea("real input:")

Common

parse = fn input ->
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&Code.eval_string/1)
  |> Enum.map(&elem(&1, 0))
end
defmodule Pair do
  def compare(same, same), do: :eq

  def compare(left, right) when is_integer(left) and is_integer(right) do
    if left < right, do: :lt, else: :gt
  end

  def compare([], right) when is_list(right), do: :lt
  def compare(left, []) when is_list(left), do: :gt

  def compare(left, right) when is_integer(left) and is_list(right) do
    compare([left], right)
  end

  def compare(left, right) when is_list(left) and is_integer(right) do
    compare(left, [right])
  end

  def compare([l | left], [r | right]) do
    case compare(l, r) do
      :eq -> compare(left, right)
      val -> val
    end
  end
end

ExUnit.start(autorun: false)

defmodule PairTest do
  use ExUnit.Case, async: true

  test "first example" do
    assert :lt = Pair.compare([1, 1, 3, 1, 1], [1, 1, 5, 1, 1])
  end

  test "second example" do
    assert :lt = Pair.compare([[1], [2, 3, 4]], [[1], 4])
  end

  test "third example" do
    assert :gt = Pair.compare([9], [[8, 7, 6]])
  end

  test "fourth example" do
    assert :lt = Pair.compare([[4, 4], 4, 4], [[4, 4], 4, 4, 4])
  end

  test "fifth example" do
    assert :gt = Pair.compare([7, 7, 7, 7], [7, 7, 7])
  end

  test "sixth example" do
    assert :lt = Pair.compare([], [3])
  end

  test "seventh example" do
    assert :gt = Pair.compare([[[]]], [[]])
  end

  test "eighth example" do
    assert :gt =
             Pair.compare(
               [1, [2, [3, [4, [5, 6, 7]]]], 8, 9],
               [1, [2, [3, [4, [5, 6, 0]]]], 8, 9]
             )
  end
end

ExUnit.run()

Part 1

real_input
|> then(parse)
|> Enum.chunk_every(2)
|> Enum.with_index(1)
|> Enum.flat_map(fn {pair, index} -> if Enum.sort(pair, Pair) == pair, do: [index], else: [] end)
|> Enum.sum()

Part 2

divider_packets = [[[2]], [[6]]]

real_input
|> then(parse)
|> Enum.concat(divider_packets)
|> Enum.sort(Pair)
|> Enum.with_index(1)
|> Enum.flat_map(fn {packet, index} -> if packet in divider_packets, do: [index], else: [] end)
|> Enum.product()