diff --git a/day16/input.txt b/day16/input.txt new file mode 100644 index 0000000..859109a --- /dev/null +++ b/day16/input.txt @@ -0,0 +1 @@ +E20D41802B2984BD00540010F82D09E35880350D61A41D3004E5611E585F40159ED7AD7C90CF6BD6BE49C802DEB00525272CC1927752698693DA7C70029C0081002140096028C5400F6023C9C00D601ED88070070030005C2201448400E400F40400C400A50801E20004C1000809D14700B67676EE661137ADC64FF2BBAD745B3F2D69026335E92A0053533D78932A9DFE23AC7858C028920A973785338832CFA200F47C81D2BBBC7F9A9E1802FE00ACBA44F4D1E775DDC19C8054D93B7E72DBE7006AA200C41A8510980010D8731720CB80132918319804738AB3A8D3E773C4A4015A498E680292B1852E753E2B29D97F0DE6008CB3D4D031802D2853400D24DEAE0137AB8210051D24EB600844B95C56781B3004F002B99D8F635379EDE273AF26972D4A5610BA51004C12D1E25D802F32313239377B37100105343327E8031802B801AA00021D07231C2F10076184668693AC6600BCD83E8025231D752E5ADE311008A4EA092754596C6789727F069F99A4645008247D2579388DCF53558AE4B76B257200AAB80107947E94789FE76E36402868803F0D62743F00043A1646288800084C3F8971308032996A2BD8023292DF8BE467BB3790047F2572EF004A699E6164C013A007C62848DE91CC6DB459B6B40087E530AB31EE633BD23180393CBF36333038E011CBCE73C6FB098F4956112C98864EA1C2801D2D0F319802D60088002190620E479100622E4358952D84510074C0188CF0923410021F1CE1146E3006E3FC578EE600A4B6C4B002449C97E92449C97E92459796EB4FF874400A9A16100A26CEA6D0E5E5EC8841C9B8FE37109C99818023A00A4FD8BA531586BB8B1DC9AE080293B6972B7FA444285CC00AE492BC910C1697B5BDD8425409700562F471201186C0120004322B42489A200D4138A71AA796D00374978FE07B2314E99BFB6E909678A0 diff --git a/day16/p1.nim b/day16/p1.nim new file mode 100644 index 0000000..16bb129 --- /dev/null +++ b/day16/p1.nim @@ -0,0 +1,131 @@ +import os, bitops +import std/[strutils] + +type + ReadResult = + tuple[value: int, bitsRead: int] + + PacketType = enum + Literal = 4 + + +proc hexToBin(hex: char): int {.inline.} = + case hex: + of '0': 0b0000 + of '1': 0b0001 + of '2': 0b0010 + of '3': 0b0011 + of '4': 0b0100 + of '5': 0b0101 + of '6': 0b0110 + of '7': 0b0111 + of '8': 0b1000 + of '9': 0b1001 + of 'A', 'a': 0b1010 + of 'B', 'b': 0b1011 + of 'C', 'c': 0b1100 + of 'D', 'd': 0b1101 + of 'E', 'e': 0b1110 + of 'F', 'f': 0b1111 + else: 0 + +template readBits(packet: string, numBits: int): ReadResult = + let bytesToRead = (((bitPos mod 4) + numBits) div 4) + 1 + var skipBits = bitPos mod 4 + var numBitsToRead = numBits + var res: ReadResult + + var i = 0 + while numBitsToRead > 0 and i < bytesToRead and ((bitPos div 4) + i) < packet.len: + var bin = packet[(bitPos div 4) + i].hexToBin + + var currentBit = 4 + + case skipBits: + of 1: currentBit = 3 + of 2: currentBit = 2 + of 3: currentBit = 1 + else: discard + + skipBits = 0 + + while currentBit > 0 and numBitsToRead > 0: + let currentBVal = bin.testBit(currentBit - 1).int + res.value += currentBVal shl (numBitsToRead - 1) + res.bitsRead += 1 + currentBit -= 1 + numBitsToRead -= 1 + + i.inc + + bitPos += res.bitsRead + res + +template readLiteral(packet: string): ReadResult = + var + indicator = packet.readBits(1) + nextSection: ReadResult + res: ReadResult + + res.bitsRead += indicator.bitsRead + + while indicator.value == 1: + nextSection = packet.readBits(4) + + res.value = res.value shl 4 + res.value += nextSection.value + res.bitsRead += nextSection.bitsRead + + indicator = packet.readBits(1) + res.bitsRead += indicator.bitsRead + + # read the last line + nextSection = packet.readBits(4) + + res.value = res.value shl 4 + res.value += nextSection.value + res.bitsRead += nextSection.bitsRead + + res + +proc parsePacket(packet: string, bitPos: var int, verSum: var int) = + let + version = packet.readBits(3) + packetType = packet.readBits(3) + + verSum += version.value + + if packetType.value == Literal.int: + # Read Literal value + discard packet.readLiteral() + else: + let lengthTypeId = packet.readBits(1) + + if lengthTypeId.value == 0: + # 15 bit mode + let subPacketLen = packet.readBits(15) + var + bitsRead = 0 + initialBitPos = bitPos + while bitsRead < subPacketLen.value: + packet.parsePacket(bitPos, verSum) + bitsRead = bitPos - initialBitPos + else: + # 11 bit mode + let numSubPackets = packet.readBits(11).value + for i in 0 ..< numSubPackets: + packet.parsePacket(bitPos, verSum) + +proc main() = + let + fileName = paramStr(1) + packet = fileName.readFile().strip() + + var bitPos = 0 + var verSum = 0 + + packet.parsePacket(bitPos, verSum) + + echo "VER SUM: ", verSum + +main() diff --git a/day16/p2.nim b/day16/p2.nim new file mode 100644 index 0000000..675ae30 --- /dev/null +++ b/day16/p2.nim @@ -0,0 +1,164 @@ +import os, bitops, sugar +import std/[strutils, sequtils] + +type + ReadResult = + tuple[value: int, bitsRead: int] + + ParseResult = int + + PacketType = enum + Sum + Product + Minimum + Maximum + Literal + GreaterThan + LessThan + Equal + + +proc hexToBin(hex: char): int {.inline.} = + case hex: + of '0': 0b0000 + of '1': 0b0001 + of '2': 0b0010 + of '3': 0b0011 + of '4': 0b0100 + of '5': 0b0101 + of '6': 0b0110 + of '7': 0b0111 + of '8': 0b1000 + of '9': 0b1001 + of 'A', 'a': 0b1010 + of 'B', 'b': 0b1011 + of 'C', 'c': 0b1100 + of 'D', 'd': 0b1101 + of 'E', 'e': 0b1110 + of 'F', 'f': 0b1111 + else: 0 + +template readBits(packet: string, numBits: int): ReadResult = + let bytesToRead = (((bitPos mod 4) + numBits) div 4) + 1 + var skipBits = bitPos mod 4 + var numBitsToRead = numBits + var res: ReadResult + + var i = 0 + while numBitsToRead > 0 and i < bytesToRead and ((bitPos div 4) + i) < packet.len: + var bin = packet[(bitPos div 4) + i].hexToBin + + var currentBit = 4 + + case skipBits: + of 1: currentBit = 3 + of 2: currentBit = 2 + of 3: currentBit = 1 + else: discard + + skipBits = 0 + + while currentBit > 0 and numBitsToRead > 0: + let currentBVal = bin.testBit(currentBit - 1).int + res.value += currentBVal shl (numBitsToRead - 1) + res.bitsRead += 1 + currentBit -= 1 + numBitsToRead -= 1 + + i.inc + + bitPos += res.bitsRead + res + +template readLiteral(packet: string): ReadResult = + var + indicator = packet.readBits(1) + nextSection: ReadResult + res: ReadResult + + res.bitsRead += indicator.bitsRead + + while indicator.value == 1: + nextSection = packet.readBits(4) + + res.value = res.value shl 4 + res.value += nextSection.value + res.bitsRead += nextSection.bitsRead + + indicator = packet.readBits(1) + res.bitsRead += indicator.bitsRead + + # read the last line + nextSection = packet.readBits(4) + + res.value = res.value shl 4 + res.value += nextSection.value + res.bitsRead += nextSection.bitsRead + + res + +proc parsePacket(packet: string, bitPos: var int, verSum: var int): ParseResult = + let + version = packet.readBits(3) + packetType = packet.readBits(3) + + verSum += version.value + + var operation: (int, int) -> int + + case packetType.value.PacketType: + of Literal: + # Read Literal value + return packet.readLiteral().value + of Sum: + operation = (x, y: int) => x + y + of Product: + operation = (x, y: int) => x * y + of Minimum: + operation = (x, y: int) => min(x, y) + of Maximum: + operation = (x, y: int) => max(x, y) + of GreaterThan: + operation = (x, y: int) => (x > y).int + of LessThan: + operation = (x, y: int) => (x < y).int + of Equal: + operation = (x, y: int) => (x == y).int + + let lengthTypeId = packet.readBits(1) + var acc: seq[int] + + if lengthTypeId.value == 0: + # 15 bit mode + let subPacketLen = packet.readBits(15).value + var + bitsRead = 0 + initialBitPos = bitPos + + while bitsRead < subPacketLen: + acc.add packet.parsePacket(bitPos, verSum) + bitsRead = bitPos - initialBitPos + + else: + # 11 bit mode + let numSubPackets = packet.readBits(11).value + for i in 0 ..< numSubPackets: + acc.add packet.parsePacket(bitPos, verSum) + + return acc.foldl(operation(a, b)) + +proc main() = + let + fileName = paramStr(1) + var packet: string + try: + packet = fileName.readFile().strip() + except IOError: + packet = paramStr(1) + + var bitPos = 0 + var verSum = 0 + + echo packet.parsePacket(bitPos, verSum) + +main() diff --git a/day16/prob.md b/day16/prob.md new file mode 100644 index 0000000..7fe77fb --- /dev/null +++ b/day16/prob.md @@ -0,0 +1,109 @@ +--- Day 16: Packet Decoder --- +As you leave the cave and reach open waters, you receive a transmission from the Elves back on the ship. + +The transmission was sent using the Buoyancy Interchange Transmission System (BITS), a method of packing numeric expressions into a binary sequence. Your submarine's computer has saved the transmission in hexadecimal (your puzzle input). + +The first step of decoding the message is to convert the hexadecimal representation into binary. Each character of hexadecimal corresponds to four bits of binary data: + +0 = 0000 +1 = 0001 +2 = 0010 +3 = 0011 +4 = 0100 +5 = 0101 +6 = 0110 +7 = 0111 +8 = 1000 +9 = 1001 +A = 1010 +B = 1011 +C = 1100 +D = 1101 +E = 1110 +F = 1111 +The BITS transmission contains a single packet at its outermost layer which itself contains many other packets. The hexadecimal representation of this packet might encode a few extra 0 bits at the end; these are not part of the transmission and should be ignored. + +Every packet begins with a standard header: the first three bits encode the packet version, and the next three bits encode the packet type ID. These two values are numbers; all numbers encoded in any packet are represented as binary with the most significant bit first. For example, a version encoded as the binary sequence 100 represents the number 4. + +Packets with type ID 4 represent a literal value. Literal value packets encode a single binary number. To do this, the binary number is padded with leading zeroes until its length is a multiple of four bits, and then it is broken into groups of four bits. Each group is prefixed by a 1 bit except the last group, which is prefixed by a 0 bit. These groups of five bits immediately follow the packet header. For example, the hexadecimal string D2FE28 becomes: + +110100101111111000101000 +VVVTTTAAAAABBBBBCCCCC +Below each bit is a label indicating its purpose: + +The three bits labeled V (110) are the packet version, 6. +The three bits labeled T (100) are the packet type ID, 4, which means the packet is a literal value. +The five bits labeled A (10111) start with a 1 (not the last group, keep reading) and contain the first four bits of the number, 0111. +The five bits labeled B (11110) start with a 1 (not the last group, keep reading) and contain four more bits of the number, 1110. +The five bits labeled C (00101) start with a 0 (last group, end of packet) and contain the last four bits of the number, 0101. +The three unlabeled 0 bits at the end are extra due to the hexadecimal representation and should be ignored. +So, this packet represents a literal value with binary representation 011111100101, which is 2021 in decimal. + +Every other type of packet (any packet with a type ID other than 4) represent an operator that performs some calculation on one or more sub-packets contained within. Right now, the specific operations aren't important; focus on parsing the hierarchy of sub-packets. + +An operator packet contains one or more packets. To indicate which subsequent binary data represents its sub-packets, an operator packet can use one of two modes indicated by the bit immediately after the packet header; this is called the length type ID: + +If the length type ID is 0, then the next 15 bits are a number that represents the total length in bits of the sub-packets contained by this packet. +If the length type ID is 1, then the next 11 bits are a number that represents the number of sub-packets immediately contained by this packet. +Finally, after the length type ID bit and the 15-bit or 11-bit field, the sub-packets appear. + +For example, here is an operator packet (hexadecimal string 38006F45291200) with length type ID 0 that contains two sub-packets: + +00111000000000000110111101000101001010010001001000000000 +VVVTTTILLLLLLLLLLLLLLLAAAAAAAAAAABBBBBBBBBBBBBBBB +The three bits labeled V (001) are the packet version, 1. +The three bits labeled T (110) are the packet type ID, 6, which means the packet is an operator. +The bit labeled I (0) is the length type ID, which indicates that the length is a 15-bit number representing the number of bits in the sub-packets. +The 15 bits labeled L (000000000011011) contain the length of the sub-packets in bits, 27. +The 11 bits labeled A contain the first sub-packet, a literal value representing the number 10. +The 16 bits labeled B contain the second sub-packet, a literal value representing the number 20. +After reading 11 and 16 bits of sub-packet data, the total length indicated in L (27) is reached, and so parsing of this packet stops. + +As another example, here is an operator packet (hexadecimal string EE00D40C823060) with length type ID 1 that contains three sub-packets: + +11101110000000001101010000001100100000100011000001100000 +VVVTTTILLLLLLLLLLLAAAAAAAAAAABBBBBBBBBBBCCCCCCCCCCC +The three bits labeled V (111) are the packet version, 7. +The three bits labeled T (011) are the packet type ID, 3, which means the packet is an operator. +The bit labeled I (1) is the length type ID, which indicates that the length is a 11-bit number representing the number of sub-packets. +The 11 bits labeled L (00000000011) contain the number of sub-packets, 3. +The 11 bits labeled A contain the first sub-packet, a literal value representing the number 1. +The 11 bits labeled B contain the second sub-packet, a literal value representing the number 2. +The 11 bits labeled C contain the third sub-packet, a literal value representing the number 3. +After reading 3 complete sub-packets, the number of sub-packets indicated in L (3) is reached, and so parsing of this packet stops. + +For now, parse the hierarchy of the packets throughout the transmission and add up all of the version numbers. + +Here are a few more examples of hexadecimal-encoded transmissions: + +8A004A801A8002F478 represents an operator packet (version 4) which contains an operator packet (version 1) which contains an operator packet (version 5) which contains a literal value (version 6); this packet has a version sum of 16. +620080001611562C8802118E34 represents an operator packet (version 3) which contains two sub-packets; each sub-packet is an operator packet that contains two literal values. This packet has a version sum of 12. +C0015000016115A2E0802F182340 has the same structure as the previous example, but the outermost packet uses a different length type ID. This packet has a version sum of 23. +A0016C880162017C3686B18A3D4780 is an operator packet that contains an operator packet that contains an operator packet that contains five literal values; it has a version sum of 31. +Decode the structure of your hexadecimal-encoded BITS transmission; what do you get if you add up the version numbers in all packets? + +--- Part Two --- +Now that you have the structure of your transmission decoded, you can calculate the value of the expression it represents. + +Literal values (type ID 4) represent a single number as described above. The remaining type IDs are more interesting: + +Packets with type ID 0 are sum packets - their value is the sum of the values of their sub-packets. If they only have a single sub-packet, their value is the value of the sub-packet. +Packets with type ID 1 are product packets - their value is the result of multiplying together the values of their sub-packets. If they only have a single sub-packet, their value is the value of the sub-packet. +Packets with type ID 2 are minimum packets - their value is the minimum of the values of their sub-packets. +Packets with type ID 3 are maximum packets - their value is the maximum of the values of their sub-packets. +Packets with type ID 5 are greater than packets - their value is 1 if the value of the first sub-packet is greater than the value of the second sub-packet; otherwise, their value is 0. These packets always have exactly two sub-packets. +Packets with type ID 6 are less than packets - their value is 1 if the value of the first sub-packet is less than the value of the second sub-packet; otherwise, their value is 0. These packets always have exactly two sub-packets. +Packets with type ID 7 are equal to packets - their value is 1 if the value of the first sub-packet is equal to the value of the second sub-packet; otherwise, their value is 0. These packets always have exactly two sub-packets. +Using these rules, you can now work out the value of the outermost packet in your BITS transmission. + +For example: + +C200B40A82 finds the sum of 1 and 2, resulting in the value 3. +04005AC33890 finds the product of 6 and 9, resulting in the value 54. +880086C3E88112 finds the minimum of 7, 8, and 9, resulting in the value 7. +CE00C43D881120 finds the maximum of 7, 8, and 9, resulting in the value 9. +D8005AC2A8F0 produces 1, because 5 is less than 15. +F600BC2D8F produces 0, because 5 is not greater than 15. +9C005AC2F8F0 produces 0, because 5 is not equal to 15. +9C0141080250320F1802104A08 produces 1, because 1 + 3 = 2 * 2. +What do you get if you evaluate the expression represented by your hexadecimal-encoded BITS transmission?