241 lines
4.5 KiB
Go
241 lines
4.5 KiB
Go
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
|
|
}
|