Powered by AppSignal & Oban Pro

Day 13

2022/day13.livemd

Day 13

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

Setup

input = Kino.Input.textarea("input file")

Common

defmodule Day13 do
  def parse_list(list) do
    [a, b] =
      list
      |> String.split("\n", trim: true)

    [elem(Code.eval_string(a), 0), elem(Code.eval_string(b), 0)]
    |> List.to_tuple()
  end

  def compare([], []), do: nil
  def compare([_ | _], []), do: false
  def compare([], [_ | _]), do: true

  def compare([x | left_rest], [y | right_rest]) when is_integer(x) and is_integer(y) do
    cond do
      x < y -> true
      x > y -> false
      x == y -> compare(left_rest, right_rest)
    end
  end

  def compare([x | left_rest], [y | right_rest]) when is_list(x) and is_list(y) do
    case compare(x, y) do
      nil -> compare(left_rest, right_rest)
      val -> val
    end
  end

  def compare([x | left_rest], [y | right_rest]) when is_list(x) and is_integer(y) do
    compare([x | left_rest], [[y] | right_rest])
  end

  def compare([x | left_rest], [y | right_rest]) when is_integer(x) and is_list(y) do
    compare([[x] | left_rest], [y | right_rest])
  end
end

Part 1

data =
  input
  |> Kino.Input.read()
  |> String.split("\n\n", trim: true)
  |> Enum.map(fn pair ->
    pair |> Day13.parse_list()
  end)
  |> Enum.with_index(1)

data
|> Enum.map(fn {{left, right}, idx} ->
  {idx, Day13.compare(left, right)}
end)
|> Enum.filter(fn {_, val} -> val end)
|> Enum.map(fn {idx, _} -> idx end)
|> Enum.sum()

Part 2

data =
  input
  |> Kino.Input.read()
  |> String.split("\n\n")
  |> Enum.flat_map(&String.split(&1, "\n"))
  |> Enum.map(&elem(Code.eval_string(&1), 0))

[[[2]], [[6]] | data]
|> Enum.sort_by(fn x -> x end, fn left, right ->
  Day13.compare(left, right)
end)
|> Enum.with_index(1)
|> Enum.filter(fn {element, _} -> element in [[[2]], [[6]]] end)
|> Enum.map(fn {_, idx} -> idx end)
|> Enum.product()