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

--- Day 16: Packet Decoder ---

2021/day16.livemd

— Day 16: Packet Decoder —

Setup

defmodule Setup do
  def get_input(prompt) do
    case IO.gets(prompt) do
      :eof -> ""
      line -> line <> get_input(prompt)
    end
  end

  def parse(binary) do
    binary
    |> String.trim()
    |> :binary.decode_hex()
  end
end
defmodule Decoder do
  def read_packets(bits) do
    case read_packet(bits) do
      {:eof, _rest} ->
        []

      {packet, rest} ->
        [packet | read_packets(rest)]
    end
  end

  def read_n_packets(bits, 0), do: {[], bits}

  def read_n_packets(bits, n) do
    {packet, rest} = read_packet(bits)
    {packets, rest} = read_n_packets(rest, n - 1)
    {[packet | packets], rest}
  end

  def read_packet(bits) when bit_size(bits) < 8, do: {:eof, bits}
  def read_packet(<>), do: read_literal(rest, v)

  def read_packet(<>) do
    operants = read_packets(operants)

    {{:operator, v, t, operants}, rest}
  end

  def read_packet(<>) do
    {operants, rest} = read_n_packets(rest, l)

    {{:operator, v, t, operants}, rest}
  end

  def read_literal(bits, version, acc \\ <<>>, len \\ 4)

  def read_literal(<<1::1, literal::4, rest::bits>>, version, acc, len),
    do: read_literal(rest, version, <>, len + 4)

  def read_literal(<<0::1, literal::4, rest::bits>>, version, acc, len) do
    <> = <>

    {{:literal, version, number}, rest}
  end
end

defmodule Packets do
  def sum_versions(packets) do
    packets
    |> Enum.reduce(0, fn
      {:literal, v, _}, acc ->
        acc + v

      {:operator, v, _t, operants}, acc ->
        acc + v + sum_versions(operants)
    end)
  end

  def opcode(0), do: &amp;Enum.sum/1
  def opcode(1), do: &amp;Enum.product/1
  def opcode(2), do: &amp;Enum.min/1
  def opcode(3), do: &amp;Enum.max/1
  def opcode(5), do: fn [l, r] -> if(l > r, do: 1, else: 0) end
  def opcode(6), do: fn [l, r] -> if(l < r, do: 1, else: 0) end
  def opcode(7), do: fn [l, r] -> if(l == r, do: 1, else: 0) end

  def interpret({:literal, _, num}), do: num

  def interpret({:operator, _, opcode, operants}) do
    Enum.map(operants, &amp;interpret/1) |> opcode(opcode).()
  end
end

Part1

Setup.get_input("input")
|> Setup.parse()
|> Decoder.read_packets()
|> Packets.sum_versions()

Part2

Setup.get_input("input")
|> Setup.parse()
|> Decoder.read_packets()
|> List.first()
|> Packets.interpret()