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
 | 
						|
}
 |