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

Day16

2021/day16.livemd

Day16

Untitled

data =
  "input"
  |> IO.getn(1_000_000)
  |> String.trim()
  |> String.split("", trim: true)
  |> Enum.flat_map(
    &(&1
      |> String.to_integer(16)
      |> Integer.digits(2)
      |> then(fn l -> [0, 0, 0] ++ l end)
      |> Enum.take(-4))
  )
defmodule D16 do
  def parse(data) do
    {data, %{}}
    |> parse_meta(:version)
    |> parse_meta(:type)
    |> parse_by_type()
  end

  def parse_meta({[a, b, c | rest], meta}, meta_key) do
    {rest, Map.put(meta, meta_key, Integer.undigits([a, b, c], 2))}
  end

  def parse_by_type({data, %{type: 4} = meta}) do
    {literal, rest} = parse_literal(data)
    {{Integer.undigits(literal, 2), rest}, meta}
  end

  def parse_by_type({data, meta}) do
    {parse_operator(data), meta}
  end

  def parse_literal([1, a, b, c, d | rest]) do
    {data, rest} = parse_literal(rest)
    {[a, b, c, d] ++ data, rest}
  end

  def parse_literal([0, a, b, c, d | rest]) do
    {[a, b, c, d], rest}
  end

  def parse_operator([0 | rest]) do
    {length, rest} = Enum.split(rest, 15)
    {payload, rest} = Enum.split(rest, Integer.undigits(length, 2))
    {parse_sub(payload), rest}
  end

  def parse_operator([1 | rest]) do
    {length, payload} = Enum.split(rest, 11)
    {data, [rest]} = Enum.split(parse_sub(payload, Integer.undigits(length, 2)), -1)
    {data, rest}
  end

  def parse_sub([]), do: []

  def parse_sub(data) do
    {{data, rest}, meta} = parse(data)
    [{{data, []}, meta} | parse_sub(rest)]
  end

  def parse_sub(rest, 0), do: [rest]

  def parse_sub(data, n) do
    {{data, rest}, meta} = parse(data)
    [{{data, []}, meta} | parse_sub(rest, n - 1)]
  end

  def solve1({{data, _rest}, meta}) when is_list(data) do
    meta.version + (data |> Enum.map(&solve1/1) |> Enum.sum())
  end

  def solve1({{_data, _rest}, meta}) do
    meta.version
  end

  def solve2({{data, _}, %{type: 0}}) do
    data |> Enum.map(&solve2/1) |> Enum.sum()
  end

  def solve2({{data, _}, %{type: 1}}) do
    data |> Enum.map(&solve2/1) |> Enum.product()
  end

  def solve2({{data, _}, %{type: 2}}) do
    data |> Enum.map(&solve2/1) |> Enum.min()
  end

  def solve2({{data, _}, %{type: 3}}) do
    data |> Enum.map(&solve2/1) |> Enum.max()
  end

  def solve2({{data, _}, %{type: 4}}) do
    data
  end

  def solve2({{data, _}, %{type: 5}}) do
    data
    |> Enum.map(&solve2/1)
    |> then(fn [a, b] -> (a > b && 1) || 0 end)
  end

  def solve2({{data, _}, %{type: 6}}) do
    data
    |> Enum.map(&solve2/1)
    |> then(fn [a, b] -> (a < b &amp;&amp; 1) || 0 end)
  end

  def solve2({{data, _}, %{type: 7}}) do
    data
    |> Enum.map(&amp;solve2/1)
    |> then(fn [a, b] -> (a == b &amp;&amp; 1) || 0 end)
  end
end
data
|> D16.parse()
|> D16.solve1()
data
|> D16.parse()
|> D16.solve2()