Skip to content

Instantly share code, notes, and snippets.

@sarahzrf
Created December 16, 2021 21:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sarahzrf/eb172c3efd50fda7867cb956dac35fa0 to your computer and use it in GitHub Desktop.
Save sarahzrf/eb172c3efd50fda7867cb956dac35fa0 to your computer and use it in GitHub Desktop.
def bits(n)
[(n & 0b1000) >> 3,
(n & 0b0100) >> 2,
(n & 0b0010) >> 1,
n & 0b0001]
end
def from_bits(bs)
bs.each_with_index.sum {|b, i| b * (1 << (bs.length - i - 1))}
end
class Bitstream
include Enumerable
def initialize(io)
@io = io
@buffer = []
@count = 0
end
def each
return to_enum unless block_given?
loop {yield read_bit}
end
def read(n)
@count += n
if n <= @buffer.length
@buffer.shift(n)
else
bits = @buffer
n -= bits.length
nybbles = n / 4
nybbles += 1 unless n % 4 == 0
dat = @io.read(nybbles)
@buffer = dat.chars.flat_map {|c| bits(c.to_i(16))}
bits.concat @buffer.shift(n)
end
end
def read_bit
read(1)[0]
end
def read_int(n)
from_bits(read(n))
end
def tell
@count
end
end
class Packet < Struct.new(:ver, :ty, :dat)
def value
case ty
when 0
dat.sum(&:value)
when 1
dat.map(&:value).reduce(:*)
when 2
dat.map(&:value).min
when 3
dat.map(&:value).max
when 4
dat
when 5
dat[0].value > dat[1].value ? 1 : 0
when 6
dat[0].value < dat[1].value ? 1 : 0
when 7
dat[0].value == dat[1].value ? 1 : 0
end
end
end
def decode_packet(str)
ver = str.read_int(3)
ty = str.read_int(3)
case ty
when 4
bs = []
more = 1
until more == 0
more = str.read_bit
bs.concat(str.read(4))
end
dat = from_bits(bs)
else
if str.read_bit == 0
len = str.read_int(15)
dat = []
start = str.tell
while str.tell - start < len
dat << decode_packet(str)
end
else
ct = str.read_int(11)
dat = ct.times.map {decode_packet(str)}
end
end
Packet.new(ver, ty, dat)
end
puts decode_packet(Bitstream.new(STDIN)).value
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment