Skip to content

Instantly share code, notes, and snippets.

@albertein
Created December 16, 2021 18:17
Show Gist options
  • Save albertein/1d737f44814d7d6efc66609b0907682e to your computer and use it in GitHub Desktop.
Save albertein/1d737f44814d7d6efc66609b0907682e to your computer and use it in GitHub Desktop.
TAG_LITERAL = 4
TAG_SUM = 0
TAG_PRODUCT = 1
TAG_MIN = 2
TAG_MAX = 3
TAG_GT = 5
TAG_LT = 6
TAG_EQ = 7
TYPE_BITS = 0
class Stream:
def __init__(self, data):
self.data = data
self.ready_carry = 0
self.carry_size = 0
self.carry = 0
self.nibbles = self._init_read()
def get(self, size):
data = self.carry
data_size = self.carry_size
while data_size < size:
data = data << 4
data = data | next(self.nibbles)
data_size += 4
if data_size > size:
self.carry_size = data_size - size
self.carry = data & (2 ** self.carry_size - 1)
data = data >> self.carry_size
else:
self.carry = 0
self.carry_size = 0
return data
def _init_read(self):
for char in self.data:
yield int(char, 16)
def get_literal_value(stream):
value = 0
bits_read = 6 # Tag + Version
while True:
has_more_data = stream.get(1)
value = value << 4
value = value | stream.get(4)
bits_read += 5
if not has_more_data:
break
return value, bits_read
def decode(input_data):
stream = Stream(input_data)
return decode_packet(stream)
def decode_packet(stream):
version = stream.get(3)
tag = stream.get(3)
value = 0
if tag == TAG_LITERAL:
value, bits_read = get_literal_value(stream)
return (bits_read, version, tag, value)
length_type = stream.get(1)
subpackages = []
if length_type == TYPE_BITS:
bits_length = stream.get(15)
bits_read = 0
while bits_read < bits_length:
subpackages.append(decode_packet(stream))
bits_read += subpackages[-1][0]
bits_read += 22 # 3 for version, 3 for tag, 1 length type + 15 for length
return (bits_read, version, tag, subpackages)
else: # TYPE_SUBPACKAGES
package_length = stream.get(11)
subpackages = []
bits_read = 0
while len(subpackages) < package_length:
subpackages.append(decode_packet(stream))
bits_read += subpackages[-1][0]
bits_read += 18 # 3 for version, 3 for tag, 1 length type + 11 package count
return (bits_read, version, tag, subpackages)
def eval(package):
length, version, tag, value = package
if tag == TAG_LITERAL:
return value
child_values = [eval(child) for child in value]
if tag == TAG_SUM:
return sum(child_values)
elif tag == TAG_PRODUCT:
product = 1
for item in child_values:
product *= item
return product
elif tag == TAG_MIN:
return min(child_values)
elif tag == TAG_MAX:
return max(child_values)
elif tag == TAG_GT:
if child_values[0] > child_values[1]:
return 1
return 0
elif tag == TAG_LT:
if child_values[0] < child_values[1]:
return 1
return 0
elif tag == TAG_EQ:
if child_values[0] == child_values[1]:
return 1
return 0
if __name__ == '__main__':
with open('input.txt') as data:
package = decode(data.readline().strip())
print(package, eval(package))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment