package sixteen import ( "bufio" "fmt" "os" "strconv" ) const ( literalType = 4 lengthType15 = 0 lengthType11 = 1 ) var hexToBin = map[string]string{ "0": "0000", "1": "0001", "2": "0010", "3": "0011", "4": "0100", "5": "0101", "6": "0110", "7": "0111", "8": "1000", "9": "1001", "A": "1010", "B": "1011", "C": "1100", "D": "1101", "E": "1110", "F": "1111", } type packet struct { packet string version int typeID int content int subpackets []packet } func (p *packet) versionSum() int { sum := p.version for _, s := range p.subpackets { sum += s.versionSum() } return sum } func hexToBinary(input string) string { output := "" for _, r := range input { output += hexToBin[string(r)] } return output } func parseHex(input string) packet { binary := hexToBinary(input) return parse(binary) } func parse(input string) packet { b := packet{} b.packet = input b.version, b.typeID = parseVersionType(input) content := input[6:] switch b.typeID { case literalType: b.content, _ = parseLiteral(content) default: b.subpackets, _ = parseOperator(content) } return b } func parseVersionType(input string) (int, int) { var version, typeID string if count, err := fmt.Sscanf(input, "%3s%3s", &version, &typeID); count != 2 || err != nil { fmt.Println("Unable to parse", input, err) return -1, -1 } v, err := strconv.ParseInt(version, 2, 64) if err != nil { fmt.Println("Unable to parse version from", version, err) return -1, -1 } t, err := strconv.ParseInt(typeID, 2, 64) if err != nil { fmt.Println("Unable to parse type from", typeID, err) return -1, -1 } return int(v), int(t) } func parsePackets(input string) []packet { packets := []packet{} remaining := input for remaining != "" { version, typeID := parseVersionType(remaining) p := packet{ version: version, typeID: typeID, } switch p.typeID { case literalType: value, length := parseLiteral(remaining[6:]) p.content = value p.packet = remaining[:length+6] remaining = remaining[length+6:] default: l := 0 p.subpackets, l = parseOperator(remaining[6:]) remaining = remaining[l+7:] } packets = append(packets, p) } return packets } func parseNPackets(input string, count int) ([]packet, int) { length := len(input) packets := []packet{} remaining := input for len(packets) != count { version, typeID := parseVersionType(remaining) p := packet{ version: version, typeID: typeID, } switch p.typeID { case literalType: value, length := parseLiteral(remaining[6:]) p.content = value p.packet = remaining[:length+6] remaining = remaining[length+6:] default: l := 0 p.subpackets, l = parseOperator(remaining[6:]) remaining = remaining[l+7:] } packets = append(packets, p) } return packets, length - len(remaining) } func parseOperator(input string) ([]packet, int) { length := 0 subPackets := []packet{} var lType int var remainder string if count, err := fmt.Sscanf(input, "%1d%s", &lType, &remainder); count != 2 || err != nil { fmt.Println("Unable to parse operator type", input) return subPackets, length } switch lType { case lengthType15: lengthBits, _ := strconv.ParseInt(string(remainder[:15]), 2, 64) subs := parsePackets(remainder[15 : 15+lengthBits]) subPackets = append(subPackets, subs...) length = 15 + int(lengthBits) case lengthType11: lengthBits, _ := strconv.ParseInt(string(remainder[:11]), 2, 64) subs, l := parseNPackets(remainder[11:], int(lengthBits)) subPackets = append(subPackets, subs...) length = l } return subPackets, length } func parseLiteral(input string) (int, int) { literal := "" length := 0 for i := 0; i < len(input); i += 5 { literal += string(input[i+1]) + string(input[i+2]) + string(input[i+3]) + string(input[i+4]) length += 5 if input[i] == '0' { break } } v, err := strconv.ParseInt(literal, 2, 64) if err != nil { fmt.Println("Unable to parse", literal, err) return 0, -1 } return int(v), length } func load(filename string) packet { b := packet{} file, err := os.Open(filename) if err != nil { return b } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { b = parseHex(scanner.Text()) } return b }