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

aoc 2021 day 16

2021/elixir/day-16.livemd

aoc 2021 day 16

Setup

Mix.install([{:kino, "~> 0.5.0"}])
input = Kino.Input.text("input")

Part 1

defmodule Translator do
  def translate([v1, v2, v3, ?1, ?0, ?0 | rest] = _packets) do
    version = [v1, v2, v3] |> List.to_integer(2)

    {number, rest} = literal(rest)

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

  def translate([v1, v2, v3, t1, t2, t3, i | rest] = _packets) do
    # IO.inspect(packets)
    version = [v1, v2, v3] |> List.to_integer(2)
    type = [t1, t2, t3] |> List.to_integer(2)

    case i do
      ?0 ->
        {len_of_sub_bin, rest} = Enum.split(rest, 15)
        len_of_sub = List.to_integer(len_of_sub_bin, 2)
        {sub_packets, rest} = Enum.split(rest, len_of_sub)
        {sp, _} = sub_packets(sub_packets)
        {%{version: version, type: type, length_type: 0, sub_packets: sp}, rest}

      ?1 ->
        {cnt_of_sub_bin, rest} = Enum.split(rest, 11)
        cnt_of_sub = List.to_integer(cnt_of_sub_bin, 2)
        {sp, rest} = sub_packets(rest, cnt_of_sub)
        {%{version: version, type: type, length_type: 1, sub_packets: sp}, rest}
    end
  end

  defp sub_packets(packets, cnt \\ nil) do
    Stream.iterate(0, &(&1 + 1))
    |> Enum.reduce_while({[], packets}, fn
      _, {acc, []} ->
        {:halt, {acc |> Enum.reverse(), []}}

      ^cnt, {acc, remain} ->
        {:halt, {acc |> Enum.reverse(), remain}}

      _idx, {acc, remains} ->
        # IO.inspect(remains, label: idx)
        {data, rest} = translate(remains)
        {:cont, {[data | acc], rest}}
    end)
  end

  def literal(packets, bits \\ [])

  def literal([?0 | _] = packets, bits) do
    {last_group, rest} = Enum.split(packets, 5)
    number = last_group |> tl() |> then(&Enum.concat(bits, &1)) |> List.to_integer(2)
    # {number, Enum.drop(rest, rem(bit_count + 5, 4) - 1)}
    {number, rest}
  end

  def literal([?1 | _] = packets, bits) do
    {group, rest} = Enum.split(packets, 5)
    acc = group |> tl() |> then(&Enum.concat(bits, &1))
    literal(rest, acc)
  end
end

defmodule VersionParser do
  def versions(%{sub_packets: sub_packets, version: version}) do
    sub_versions = sub_packets |> Enum.map(&versions/1) |> Enum.sum()
    sub_versions + version
  end

  def versions(%{version: version}), do: version
end

defmodule Operator do
  def calculate(%{sub_packets: sub_packets, type: type}) do
    case type do
      0 ->
        sub_packets |> Enum.map(&calculate/1) |> Enum.sum()

      1 ->
        sub_packets |> Enum.map(&calculate/1) |> Enum.product()

      2 ->
        sub_packets |> Enum.map(&calculate/1) |> Enum.min()

      3 ->
        sub_packets |> Enum.map(&calculate/1) |> Enum.max()

      5 ->
        [p1, p2] = sub_packets |> Enum.map(&calculate/1)
        if p1 > p2, do: 1, else: 0

      6 ->
        [p1, p2] = sub_packets |> Enum.map(&calculate/1)
        if p1 < p2, do: 1, else: 0

      7 ->
        [p1, p2] = sub_packets |> Enum.map(&amp;calculate/1)
        if p1 == p2, do: 1, else: 0
    end
  end

  def calculate(%{number: number}), do: number
end
# input = "D2FE28"
# input = "38006F45291200"
# input = "EE00D40C823060"

packets =
  input
  |> Kino.Input.read()
  |> Base.decode16!()
  |> :binary.bin_to_list()
  |> Enum.flat_map(fn num ->
    x = Integer.to_charlist(num, 2)
    l = length(x)
    List.duplicate(?0, max(0, 8 - l)) ++ x
  end)

# |> IO.inspect()

# length(packets)
{data, _} = Translator.translate(packets)

IO.inspect(data)

VersionParser.versions(data)

Part 2

Operator.calculate(data)

After watching jose’s streaming

defmodule Parser do
  import Bitwise

  def decode(packet) do
    {_, [result]} =
      packet
      |> Base.decode16!()
      |> decode_type([])

    result
  end

  defp decode_type(<<_version::3, 4::3, literals::bits>>, values),
    do: decode_literal(literals, 0, values)

  defp decode_type(
         <<_version::3, type::3, 0::1, len::15, sub_packets::size(len)-bits, _rest::bits>>,
         values
       ) do
    values = decode_len_type(sub_packets, values)
    {"", [handle_operator(type, values)]}
  end

  defp decode_type(<<_version::3, type::3, 1::1, count::11, rest::bits>>, values) do
    {rest, values} = decode_count_type(rest, count, values)
    {rest, [handle_operator(type, values)]}
  end

  defp decode_literal(<<1::1, part::4, rest::bits>>, acc, values) do
    decode_literal(rest, (acc <<< 4) + part, values)
  end

  defp decode_literal(<<0::1, part::4, rest::bits>>, acc, values) do
    {rest, [(acc <<< 4) + part | values]}
  end

  defp decode_len_type("", values), do: values

  defp decode_len_type(sub_packets, values) do
    {rest, values} = decode_type(sub_packets, values)
    decode_len_type(rest, values)
  end

  defp decode_count_type(rest, 0, values), do: {rest, values}

  defp decode_count_type(sub_packets, count, values) do
    {rest, values} = decode_type(sub_packets, values)
    decode_count_type(rest, count - 1, values)
  end

  defp handle_operator(0, values), do: Enum.sum(values)
  defp handle_operator(1, values), do: Enum.product(values)
  defp handle_operator(2, values), do: Enum.min(values)
  defp handle_operator(3, values), do: Enum.max(values)
  defp handle_operator(5, [p1, p2]) when p1 > p2, do: 1
  defp handle_operator(6, [p1, p2]) when p1 < p2, do: 1
  defp handle_operator(7, [p1, p2]) when p1 == p2, do: 1
  defp handle_operator(_, _), do: 0
end
Parser.decode("D2FE28") |> IO.inspect()
Parser.decode("38006F45291200") |> IO.inspect()
Parser.decode("C200B40A82") |> IO.inspect()
Parser.decode("04005AC33890") |> IO.inspect()
Parser.decode("CE00C43D881120") |> IO.inspect()
Parser.decode("D8005AC2A8F0") |> IO.inspect()