My Advent of Code solutions in Python. kevinyap.ca/2019/12/going-fast-in-advent-of-code/
advent-of-code python
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add 2021/16

+153
+153
2021/day16.py
··· 1 + import fileinput 2 + from utils import mul 3 + 4 + 5 + HEX_MAPPING = { 6 + '0': '0000', 7 + '1': '0001', 8 + '2': '0010', 9 + '3': '0011', 10 + '4': '0100', 11 + '5': '0101', 12 + '6': '0110', 13 + '7': '0111', 14 + '8': '1000', 15 + '9': '1001', 16 + 'A': '1010', 17 + 'B': '1011', 18 + 'C': '1100', 19 + 'D': '1101', 20 + 'E': '1110', 21 + 'F': '1111', 22 + } 23 + 24 + OPERATORS = { 25 + 0: '+', 26 + 1: '*', 27 + 2: 'min', 28 + 3: 'max', 29 + 5: '>', 30 + 6: '<', 31 + 7: '==' 32 + } 33 + 34 + 35 + def parse_packets(bits, num_subpackets=None, bit_length=None, depth=0, quiet=False): 36 + """ 37 + Given a bitstring `bits`, parses `num_subpackets` subpackets, and returns ([packets], bit_length, version). 38 + 39 + If the argument `num_subpackets` is given, this dictates the number of packets 40 + to parse from `bits` before returning. 41 + 42 + If the argument `bit_length` is given, this dictates how many bits to process 43 + before returning however many packets the parser got through. 44 + 45 + If `quiet` is False, the parser will print the operator and values processed, 46 + with various nesting levels based on `depth` (incremented per recursive call). 47 + """ 48 + 49 + i = 0 # current pointer into `bits` 50 + subs = [] # list of subpackets currently parsed 51 + version = 0 # the version so far (?) 52 + 53 + try: 54 + while True: 55 + # Standard Packet Header 56 + 57 + # Read 3-bit version 58 + version += int(bits[i:i+3], 2) 59 + i += 3 60 + 61 + # Read 3-bit type ID 62 + ttype = int(bits[i:i+3], 2) 63 + i += 3 64 + 65 + # Packets with type ID 4 represent a literal value. 66 + if ttype == 4: 67 + lit = '' 68 + 69 + # Read groups of 5 bits until the final group 70 + # (prefixed 0) is seen. Then stop reading. 71 + while True: 72 + group = bits[i:i+5] 73 + i += 5 74 + lit += group[1:] 75 + if group[0] == '0': 76 + break 77 + 78 + try: 79 + lit = int(lit, 2) 80 + subs.append(lit) 81 + except Exception: 82 + print "bad literal, moving on" 83 + 84 + # Otherwise, process an operator. 85 + else: 86 + # Operator packet mode is based on its 1-bit length type ID. 87 + len_type_id = bits[i] 88 + i += 1 89 + 90 + # Specifies the bit length of contained subpackets. 91 + if len_type_id == '0': 92 + sub_len = int(bits[i:i+15], 2) 93 + i += 15 94 + subpackets, new_i, new_version = parse_packets(bits[i:i+sub_len], bit_length=sub_len, depth=depth + 1, quiet=quiet) 95 + 96 + i += new_i 97 + version += new_version 98 + 99 + # Specifies total number of subpackets. 100 + elif len_type_id == '1': 101 + sub_packets = int(bits[i:i+11], 2) 102 + i += 11 103 + subpackets, new_i, new_version = parse_packets(bits[i:], num_subpackets=sub_packets, depth=depth+1, quiet=quiet) 104 + 105 + i += new_i 106 + version += new_version 107 + else: 108 + print "Unknown length type ID" 109 + 110 + # Process operators 111 + if not quiet: 112 + print " " * depth, OPERATORS[ttype], subpackets 113 + 114 + if ttype == 0: 115 + subs.append(sum(subpackets)) 116 + elif ttype == 1: 117 + subs.append(mul(subpackets)) 118 + elif ttype == 2: 119 + subs.append(min(subpackets)) 120 + elif ttype == 3: 121 + subs.append(max(subpackets)) 122 + elif ttype == 5: 123 + subs.append(1 if subpackets[0] > subpackets[1] else 0) 124 + elif ttype == 6: 125 + subs.append(1 if subpackets[0] < subpackets[1] else 0) 126 + elif ttype == 7: 127 + subs.append(1 if subpackets[0] == subpackets[1] else 0) 128 + 129 + # Check exit conditions for loop. 130 + if num_subpackets is not None and len(subs) == num_subpackets: 131 + # IMPORTANT: return i and not len(i); it is not an invariant 132 + # that we read the full `bits`, since there may have been 133 + # some trailing data that we ignore since it falls out of 134 + # the range of `num_subpackets` subpackets. 135 + return subs, i, version 136 + elif bit_length is not None and i > bit_length: 137 + return subs, len(bits), version 138 + elif i >= len(bits): 139 + return subs, len(bits), version 140 + 141 + except Exception as e: 142 + print "Unexpected parsing error:", e 143 + return subs, i, version 144 + 145 + 146 + # Read problem input. 147 + BITS = ''.join(HEX_MAPPING[byte] for byte in fileinput.input()[0].strip()) 148 + 149 + # Problem guarantees the input consists of precisely 1 outer packet. 150 + packets, _, total_version = parse_packets(BITS, num_subpackets=1, quiet=True) 151 + 152 + print "Part 1:", total_version 153 + print "Part 2:", packets[0]