adventofcode/2021/day16/main.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
}