Day 16
Input
Mix.install([
{:kino, github: "livebook-dev/kino"}
])
textarea = Kino.Input.textarea("Input:")
Common
defmodule Common do
def parse_input(raw_input) do
raw_input
|> String.trim()
|> Base.decode16!()
end
end
defmodule Packet.Operator do
@enforce_keys [:version, :type_id, :subpackets]
defstruct [:version, :type_id, :subpackets]
end
defmodule Packet.Literal do
@enforce_keys [:version, :value]
defstruct [:version, :value]
end
defmodule Packet do
import Bitwise
alias __MODULE__.{Operator, Literal}
def parse(<<>>), do: []
def parse(<>) do
{value, rest} = parse_literal_value(rest, 0)
packet = %Literal{version: version, value: value}
{packet, rest}
end
def parse(<>) do
{subpackets, rest} =
Enum.reduce(1..subpackets_count, {[], rest}, fn _, {acc, rest} ->
{packet, rest} = parse(rest)
{[packet | acc], rest}
end)
packet = %Operator{
version: version,
type_id: type_id,
subpackets: Enum.reverse(subpackets)
}
{packet, rest}
end
def parse(<<
version::3,
type_id::3,
0::1,
subpackets_length::15,
subpackets_bits::size(subpackets_length)-bits,
rest::bits
>>) do
packet = %Operator{
version: version,
type_id: type_id,
subpackets: parse_subpackets(subpackets_bits, [])
}
{packet, rest}
end
def values([]), do: []
def values([packet | rest]) do
[value(packet) | values(rest)]
end
def value(%Literal{value: value}), do: value
def value(%Operator{type_id: type_id, subpackets: subpackets}) do
values = values(subpackets)
operator_value(type_id, values)
end
def versions_sum([]), do: 0
def versions_sum([%Operator{version: version, subpackets: subpackets} | rest]),
do: version + versions_sum(subpackets) + versions_sum(rest)
def versions_sum([%Literal{version: version} | rest]),
do: version + versions_sum(rest)
# private
defp operator_value(type_id, values)
defp operator_value(0, values), do: Enum.sum(values)
defp operator_value(1, values), do: Enum.product(values)
defp operator_value(2, values), do: Enum.min(values)
defp operator_value(3, values), do: Enum.max(values)
defp operator_value(5, [lhs, rhs]) when lhs > rhs, do: 1
defp operator_value(5, _values), do: 0
defp operator_value(6, [lhs, rhs]) when lhs < rhs, do: 1
defp operator_value(6, _values), do: 0
defp operator_value(7, [same, same]), do: 1
defp operator_value(7, _values), do: 0
defp parse_subpackets("", acc), do: Enum.reverse(acc)
defp parse_subpackets(bits, acc) do
{packet, rest} = parse(bits)
parse_subpackets(rest, [packet | acc])
end
defp parse_literal_value(<<1::1, part::4, rest::bits>>, acc),
do: parse_literal_value(rest, increment_acc(acc, part))
defp parse_literal_value(<<0::1, part::4, rest::bits>>, acc),
do: {increment_acc(acc, part), rest}
defp increment_acc(acc, part), do: (acc <<< 4) + part
end
raw_input = Kino.Input.read(textarea)
input = Common.parse_input(raw_input)
Part 1
defmodule Part1 do
def run(input) do
{packet, _rest} = Packet.parse(input)
Packet.versions_sum([packet])
end
end
Part1.run(input)
Part 2
defmodule Part2 do
def run(input) do
{packet, _rest} = Packet.parse(input)
Packet.value(packet)
end
end
Part2.run(input)