about summary refs log tree commit diff
path: root/aoc2021/Day16.cs
blob: 7e956f38b023e2010ac26ce6ec331100f6e62124 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
namespace aoc2021;

/// <summary>
/// Day 16: <see href="https://adventofcode.com/2021/day/16"/>
/// </summary>
public sealed class Day16 : Day
{
    private readonly Packet _packet;

    public Day16() : base(16, "Packet Decoder")
    {
        var bits = string.Join(string.Empty,
            Input.First().Select(c => Convert.ToString(Convert.ToInt32(c.ToString(), 16), 2).PadLeft(4, '0')));
        (_packet, _) = Packet.FromBinaryString(bits);
    }

    private record Packet(int Version, int TypeId, long Value, List<Packet> Packets)
    {
        public static (Packet packet, int offset) FromBinaryString(string input)
        {
            var index = 0;
            var subPackets = new List<Packet>();
            var version = Convert.ToInt32(input[..(index += 3)], 2);
            var typeId = Convert.ToInt32(input[index..(index += 3)], 2);

            if (typeId == 4)
            {
                // value packet, gather value bits and return right away
                var literalBits = new StringBuilder();
                foreach (var chunk in input.Skip(index).Chunk(5))
                {
                    literalBits.Append(chunk[1..5]);
                    index += 5;
                    if (chunk[0] == '0') break;
                }

                return (new(version, typeId, Convert.ToInt64(literalBits.ToString(), 2), new()), index);
            }

            switch (input[index++])
            {
                case '0':
                    var subPacketLength = Convert.ToInt32(input[index..(index += 15)], 2);
                    while (subPacketLength > 0)
                    {
                        var (packet, offset) = FromBinaryString(input[index..(index + subPacketLength)]);
                        subPackets.Add(packet);
                        subPacketLength -= offset;
                        index += offset;
                    }

                    break;
                case '1':
                    foreach (var _ in Enumerable.Range(0, Convert.ToInt32(input[index..(index += 11)], 2)))
                    {
                        var (packet, offset) = FromBinaryString(input[index..]);
                        subPackets.Add(packet);
                        index += offset;
                    }

                    break;
            }

            return (new(version, typeId, 0, subPackets), index);
        }

        public long VersionTotal => Version + Packets.Sum(p => p.VersionTotal);

        public long Eval =>
            TypeId switch
            {
                0 => Packets.Sum(p => p.Eval),
                1 => Packets.Aggregate(1L, (p, i) => p * i.Eval),
                2 => Packets.Min(p => p.Eval),
                3 => Packets.Max(p => p.Eval),
                4 => Value,
                5 => Packets[0].Eval > Packets[1].Eval ? 1 : 0,
                6 => Packets[0].Eval < Packets[1].Eval ? 1 : 0,
                7 => Packets[0].Eval == Packets[1].Eval ? 1 : 0,
                _ => throw new ArgumentException("invalid packet type", nameof(TypeId))
            };
    }

    public override object Part1() => _packet.VersionTotal;

    public override object Part2() => _packet.Eval;
}