my advent of code solutions
1namespace Solutions._2021;
2
3/// <summary>
4/// Day 16: <a href="https://adventofcode.com/2021/day/16"/>
5/// </summary>
6public sealed class Day16PacketDecoder() : Day(2021, 16, "Packet Decoder")
7{
8 private Packet? _packet;
9
10 public override void ProcessInput()
11 {
12 var bits = Input.First()
13 .Select(c => Convert.ToString(Convert.ToInt32(c.ToString(), 16), 2).PadLeft(4, '0'))
14 .Join();
15 (_packet, _) = Packet.FromBinaryString(bits);
16 }
17
18 private record Packet(int Version, int TypeId, long Value, List<Packet> Packets)
19 {
20 public static (Packet packet, int offset) FromBinaryString(string input)
21 {
22 var index = 0;
23 var subPackets = new List<Packet>();
24 var version = Convert.ToInt32(input[..(index += 3)], 2);
25 var typeId = Convert.ToInt32(input[index..(index += 3)], 2);
26
27 if (typeId == 4)
28 {
29 // value packet, gather value bits and return right away
30 var literalBits = new StringBuilder();
31 foreach (var chunk in input.Skip(index).Chunk(5))
32 {
33 literalBits.Append(chunk[1..5]);
34 index += 5;
35 if (chunk[0] == '0') break;
36 }
37
38 return (new(version, typeId, Convert.ToInt64(literalBits.ToString(), 2), []), index);
39 }
40
41 switch (input[index++])
42 {
43 case '0':
44 var subPacketLength = Convert.ToInt32(input[index..(index += 15)], 2);
45 while (subPacketLength > 0)
46 {
47 var (packet, offset) = FromBinaryString(input[index..(index + subPacketLength)]);
48 subPackets.Add(packet);
49 subPacketLength -= offset;
50 index += offset;
51 }
52
53 break;
54 case '1':
55 foreach (var _ in Enumerable.Range(0, Convert.ToInt32(input[index..(index += 11)], 2)))
56 {
57 var (packet, offset) = FromBinaryString(input[index..]);
58 subPackets.Add(packet);
59 index += offset;
60 }
61
62 break;
63 }
64
65 return (new(version, typeId, 0, subPackets), index);
66 }
67
68 public long VersionTotal => Version + Packets.Sum(p => p.VersionTotal);
69
70 public long Eval =>
71 TypeId switch
72 {
73 0 => Packets.Sum(p => p.Eval),
74 1 => Packets.Aggregate(1L, (p, i) => p * i.Eval),
75 2 => Packets.Min(p => p.Eval),
76 3 => Packets.Max(p => p.Eval),
77 4 => Value,
78 5 => Packets[0].Eval > Packets[1].Eval ? 1 : 0,
79 6 => Packets[0].Eval < Packets[1].Eval ? 1 : 0,
80 7 => Packets[0].Eval == Packets[1].Eval ? 1 : 0,
81 _ => throw new ArgumentException("invalid packet type", nameof(TypeId)),
82 };
83 }
84
85 public override object Part1() => _packet!.VersionTotal;
86
87 public override object Part2() => _packet!.Eval;
88}