package main import ( "fmt" "os" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) func main() { inp := h.StdinToString() var arg string if len(os.Args) > 1 { arg = os.Args[1] } p := ParsePacket(inp) if arg == "--json" { fmt.Println(p.Json()) } else { fmt.Println("# Part 1") fmt.Println("Version Sum:", CalculateVersionSum(p)) fmt.Println() fmt.Println("# Part 2") fmt.Println("Value:", p.value) } } func CalculateVersionSum(p *Packet) int { sum := p.version for s := range p.packets { sum += CalculateVersionSum(p.packets[s]) } return sum } const ( PacketTypeSum = iota PacketTypeProduct PacketTypeMin PacketTypeMax PacketTypeLiteral PacketTypeGreater PacketTypeLess PacketTypeEqual LengthTypeBits = 0 LengthTypeSubPackets = 1 ) type Packet struct { version int packetType int value int // Operator lengthTypeId int subLength int // Sub-packets packets []*Packet binaryString string } func ParsePacket(inp string) *Packet { var bin string for i := 0; i < len(inp); i += 2 { if len(inp) >= i+2 { bin += fmt.Sprintf("%08b", HexToByte(inp[i:i+2])) } } return ParseBinaryPacket(bin) } func ParseBinaryPacket(inp string) *Packet { p := Packet{ binaryString: inp, } // Parse the version for i := 0; i < 3; i++ { p.version = p.version << 1 if p.binaryString[i] == '1' { p.version |= 1 } } for i := 3; i < 6; i++ { p.packetType = p.packetType << 1 if p.binaryString[i] == '1' { p.packetType |= 1 } } if p.packetType == PacketTypeLiteral { p.parseLiteral() } else { p.parseOperator() } return &p } func (p *Packet) parseLiteral() { // The bits for the literal start at bit 7 // Every 5 bits is a chunk of the literal ptr := 6 more := true for more { more = (p.binaryString[ptr] == '1') ptr++ for i := 0; i < 4; i++ { p.value = p.value << 1 if p.binaryString[ptr] == '1' { p.value |= 1 } ptr++ } } p.binaryString = p.binaryString[:ptr] } func (p *Packet) parseOperator() { ptr := 6 p.lengthTypeId = int(p.binaryString[ptr] - '0') ptr++ if p.lengthTypeId == LengthTypeBits { for i := 0; i < 15; i++ { p.subLength = p.subLength << 1 if p.binaryString[ptr] == '1' { p.subLength |= 1 } ptr++ } length := p.subLength for length > 0 { sp := ParseBinaryPacket(p.binaryString[ptr:]) p.packets = append(p.packets, sp) ptr += len(sp.binaryString) length -= len(sp.binaryString) } p.binaryString = p.binaryString[:ptr] } else if p.lengthTypeId == LengthTypeSubPackets { for i := 0; i < 11; i++ { p.subLength = p.subLength << 1 if p.binaryString[ptr] == '1' { p.subLength |= 1 } ptr++ } length := p.subLength for length > 0 { sp := ParseBinaryPacket(p.binaryString[ptr:]) p.packets = append(p.packets, sp) ptr += len(sp.binaryString) length-- } p.binaryString = p.binaryString[:ptr] } switch p.packetType { case PacketTypeSum: for _, sp := range p.packets { p.value += sp.value } case PacketTypeProduct: p.value = 1 for _, sp := range p.packets { p.value *= sp.value } case PacketTypeMin: p.value = h.MAX_INT for _, sp := range p.packets { if sp.value < p.value { p.value = sp.value } } case PacketTypeMax: p.value = h.MIN_INT for _, sp := range p.packets { if sp.value > p.value { p.value = sp.value } } case PacketTypeGreater: if len(p.packets) == 2 && (p.packets[0].value > p.packets[1].value) { p.value = 1 } else { p.value = 0 } case PacketTypeLess: if len(p.packets) == 2 && (p.packets[0].value < p.packets[1].value) { p.value = 1 } else { p.value = 0 } case PacketTypeEqual: if len(p.packets) == 2 && (p.packets[0].value == p.packets[1].value) { p.value = 1 } else { p.value = 0 } } } func (p Packet) Json() string { ret := fmt.Sprintf("{\"version\":%d, \"type\":%d, \"value\":%d,", p.version, p.packetType, p.value) ret += "\"packets\":[" for i := range p.packets { ret += fmt.Sprintf("%s", p.packets[i].String()) if i < len(p.packets)-1 { ret += "," } } ret += fmt.Sprintf("],\"binary\":\"%s\"}", p.binaryString) return ret } func (p Packet) String() string { if p.packetType == PacketTypeLiteral { return fmt.Sprintf("%d", p.value) } else { return p.Json() } } func HexToByte(h string) byte { var b byte for i := range h { var wrk byte if h[i] >= '0' && h[i] <= '9' { wrk = byte(h[i] - '0') } else if h[i] >= 'A' && h[i] <= 'F' { wrk = byte(h[i]-'A') + 10 } if i == 0 { b = wrk << 4 } else { b |= wrk } } return b }