diff --git a/2021/README.md b/2021/README.md index 0733edf..2dd4e77 100644 --- a/2021/README.md +++ b/2021/README.md @@ -16,7 +16,7 @@ | :-: | :-: | :-: | :-: | :-: | :-: | :-: | | | | | [1] | [2] | [3] | [4] | | [5] | [6] | [7] | [8] | [9] | [10] | [11]| -| [12]| [13]| [14]| [15] | 16 | 17 | 18 | +| [12]| [13]| [14]| [15] | [16] | 17 | 18 | | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @@ -36,3 +36,4 @@ [13]: ./lib/2021/13.ex [14]: ./lib/2021/14.ex [15]: ./lib/2021/15.ex +[16]: ./lib/2021/16.ex diff --git a/2021/lib/2021/16.ex b/2021/lib/2021/16.ex new file mode 100644 index 0000000..97b59c3 --- /dev/null +++ b/2021/lib/2021/16.ex @@ -0,0 +1,101 @@ +import AOC + +aoc 2021, 16 do + def to_binary_digits(?0), do: [0, 0, 0, 0] + def to_binary_digits(?1), do: [0, 0, 0, 1] + def to_binary_digits(?2), do: [0, 0, 1, 0] + def to_binary_digits(?3), do: [0, 0, 1, 1] + def to_binary_digits(n) when n in ?4..?7, do: [0 | Integer.digits(n - ?0, 2)] + def to_binary_digits(n) when n in ?8..?9, do: Integer.digits(n - ?0, 2) + def to_binary_digits(n) when n in ?A..?F, do: Integer.digits(n - ?A + 10, 2) + + def input_bitstring(input_string \\ input_string()) do + input_string + |> String.trim() + |> String.to_charlist() + |> Enum.flat_map(&to_binary_digits/1) + |> Enum.into(<<>>, &<<&1::1>>) + end + + def decode_packets(bits) do + case decode_packet(bits) do + {:ok, packet, rest} -> [packet | decode_packets(rest)] + _ -> [] + end + end + + def decode_packets(bits, 0) do + {[], bits} + end + + def decode_packets(bits, n) do + {:ok, packet, rest} = decode_packet(bits) + {packets, rest} = decode_packets(rest, n - 1) + {[packet | packets], rest} + end + + def decode_packet(<>) do + {value, rest} = decode_value(rest) + + {:ok, [version, 4, Integer.undigits(value, 16)], rest} + end + + def decode_packet(<>) do + <> = rest + + {:ok, [version, id, decode_packets(<>)], rest} + end + + def decode_packet(<>) do + {packets, rest} = decode_packets(<>, number_of_subpackets) + + {:ok, [version, id, packets], rest} + end + + def decode_packet(_), do: :error + + def decode_value(<<0::1, chunk::4, rest::bits>>), do: {[chunk], rest} + + def decode_value(<<1::1, chunk::4, rest::bits>>) do + {bits, rest} = decode_value(rest) + {[chunk | bits], rest} + end + + def versions([version, _, subpackets]) when is_list(subpackets) do + [version | Enum.map(subpackets, &versions/1)] + end + def versions([version, _, _]), do: [version] + + + def eval([_, 4, v]), do: v + def eval([_, op, args]) do + case {op, Enum.map(args, &eval/1)} do + {0 = _sum, args} -> Enum.sum(args) + {1 = _prod, args} -> Enum.product(args) + {2 = _min, args} -> Enum.min(args) + {3 = _max, args} -> Enum.max(args) + {5 = _gt, [a, b]} -> if a > b, do: 1, else: 0 + {6 = _lt, [a, b]} -> if a < b, do: 1, else: 0 + {7 = _eq, [a, b]} -> if a == b, do: 1, else: 0 + end + end + + def p1 do + {:ok, packet, _rest} = + input_bitstring() + |> decode_packet() + + versions(packet) + |> List.flatten() + |> Enum.sum() + end + + def p2 do + {:ok, packet, _rest} = + input_bitstring() + |> decode_packet() + + + eval(packet) + end +end