using Pkg Pkg.add("Match") using Match include("utils.jl") function parse_input(filename) reverse(collect(collect(non_empty_lines(filename))[1])) end mutable struct BinaryReader input rest BinaryReader(input) = new(input, []) end function fill_rest(reader) char = pop!(reader.input) code = codepoint(char) number = code < 0x3A ? code - 0x30 : code - (0x41 - 10) reader.rest = reverse(parse.(Int, collect(string(number, base=2, pad=4)))) end function read_bits(reader, count) result = [] while length(result) < count if length(reader.rest) == 0 fill_rest(reader) end push!(result, pop!(reader.rest)) end result end function reader_length(reader) length(reader.rest) + length(reader.input) * 4 end function to_int(bits) parse(Int, reduce(*, string.(bits)), base=2) end function parse_literal(reader, version) result = [] cont = 1 while cont == 1 cont = read_bits(reader, 1)[1] result = [result; read_bits(reader, 4)] end (version, 4, to_int(result)) end function parse_operator(reader, version, type) length_type = read_bits(reader, 1)[1] subpackets = [] if length_type == 0 subpacket_bits = to_int(read_bits(reader, 15)) start_remaining = reader_length(reader) while reader_length(reader) + subpacket_bits > start_remaining push!(subpackets, parse_packet(reader)) end else subpacket_count = to_int(read_bits(reader, 11)) for _ in 1:subpacket_count push!(subpackets, parse_packet(reader)) end end (version, type, subpackets) end function parse_packet(reader) version = to_int(read_bits(reader, 3)) type = to_int(read_bits(reader, 3)) if type == 4 parse_literal(reader, version) else parse_operator(reader, version, type) end end function sum_versions(packet) result = packet[1] if packet[2] != 4 result += reduce(+, sum_versions.(packet[3])) end result end function solution16_1() input = parse_input("16.data") reader = BinaryReader(input) packet = parse_packet(reader) sum_versions(packet) end function value(packet) @match packet[2] begin 0 => reduce(+, value.(packet[3])) 1 => reduce(*, value.(packet[3])) 2 => reduce(min, value.(packet[3])) 3 => reduce(max, value.(packet[3])) 4 => packet[3] 5 => value(packet[3][1]) > value(packet[3][2]) ? 1 : 0 6 => value(packet[3][1]) < value(packet[3][2]) ? 1 : 0 7 => value(packet[3][1]) == value(packet[3][2]) ? 1 : 0 end end function solution16_2() input = parse_input("16.data") reader = BinaryReader(input) packet = parse_packet(reader) value(packet) end solution16_2()