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(&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()