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 (p *packet) sum() int { sum := 0 for _, p := range p.subpackets { sum += p.value() } return sum } func (p *packet) product() int { product := 1 for _, p := range p.subpackets { product *= p.value() } return product } func (p *packet) min() int { min := 0 for i, p := range p.subpackets { if i == 0 || p.content < min { min = p.value() } } return min } func (p *packet) max() int { max := 0 for i, p := range p.subpackets { if i == 0 || p.content > max { max = p.value() } } return max } func (p *packet) greaterThan() int { result := 0 if p.subpackets[0].value() > p.subpackets[1].value() { result = 1 } return result } func (p *packet) lessThan() int { result := 0 if p.subpackets[0].value() < p.subpackets[1].value() { result = 1 } return result } func (p *packet) equalTo() int { result := 0 if p.subpackets[0].value() == p.subpackets[1].value() { result = 1 } return result } func (p *packet) value() int { switch p.typeID { case 0: return p.sum() case 1: return p.product() case 2: return p.min() case 3: return p.max() case 5: return p.greaterThan() case 6: return p.lessThan() case 7: return p.equalTo() } return p.content } 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:]) if len(remaining) == l+6 { remaining = "" } else { remaining = remaining[l+6:] } } 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, } parsedLength := 0 switch p.typeID { case literalType: value := 0 value, parsedLength = parseLiteral(remaining[6:]) p.content = value p.packet = remaining[:parsedLength+6] default: p.subpackets, parsedLength = parseOperator(remaining[6:]) } if len(remaining) == parsedLength+6 { remaining = "" } else { remaining = remaining[parsedLength+6:] } packets = append(packets, p) } return packets, length - len(remaining) } func parseOperator(input string) ([]packet, int) { length := 1 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 += 11 + 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 }