Skip to content

Instantly share code, notes, and snippets.

@sarahzrf
Created December 16, 2021 21:28
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/e3f9949c4a23641890fa5bfcf0cf3ca3 to your computer and use it in GitHub Desktop.
Save sarahzrf/e3f9949c4a23641890fa5bfcf0cf3ca3 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); 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
def ver_sum(pkt)
if pkt.ty == 4
pkt.ver
else
pkt.ver + pkt.dat.sum {|p| ver_sum(p)}
end
end
puts ver_sum(decode_packet(Bitstream.new(STDIN)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment