mirror of
https://github.com/RussellChamp/advent-of-code-2021.git
synced 2026-10-27 21:52:32 +00:00
268 lines
6.8 KiB
Go
268 lines
6.8 KiB
Go
/* Day 16: Packet Decoder */
|
|||
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"AoC2021/utils/args"
|
||
|
|
"AoC2021/utils/bits"
|
||
|
|
"AoC2021/utils/color"
|
||
|
|
"AoC2021/utils/files"
|
||
|
|
"AoC2021/utils/log"
|
||
|
|
"AoC2021/utils/timer"
|
||
|
|
"bufio"
|
||
|
|
"fmt"
|
||
|
|
"math"
|
||
|
|
"os"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
func check(e error) {
|
||
|
|
if e != nil {
|
||
|
|
log.Println(log.DEBUG, "Oh snap!")
|
||
|
|
panic(e)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func main() {
|
||
|
|
argMap := args.Parse(os.Args)
|
||
|
|
log.SetLogLevelFromArgs(argMap)
|
||
|
|
selectedPart, partSpecified := argMap["part"]
|
||
|
|
|
||
|
|
log.Println(log.NORMAL, "--- Day 16: Packet Decoder ---")
|
||
|
|
timer.Start()
|
||
|
|
|
||
|
|
if selectedPart == "parse" {
|
||
|
|
parseInputFile()
|
||
|
|
}
|
||
|
|
if !partSpecified || selectedPart == "1" {
|
||
|
|
part1()
|
||
|
|
timer.Tick()
|
||
|
|
log.Println(log.NORMAL)
|
||
|
|
}
|
||
|
|
|
||
|
|
if !partSpecified || selectedPart == "2" {
|
||
|
|
part2()
|
||
|
|
timer.Tick()
|
||
|
|
log.Println(log.NORMAL)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func part1() {
|
||
|
|
log.Println(log.NORMAL, "* Part 1 *")
|
||
|
|
log.Println(log.NORMAL, " Goal: For now, parse the hierarchy of the packets throughout the transmission. Decode the structure of your hexadecimal-encoded BITS transmission")
|
||
|
|
log.Println(log.NORMAL, " Answer: what do you get if you add up the version numbers in all packets?")
|
||
|
|
|
||
|
|
input, err := os.Open("./input.txt")
|
||
|
|
check(err)
|
||
|
|
defer input.Close()
|
||
|
|
|
||
|
|
bitReader := NewBitReader(input)
|
||
|
|
p, err := parsePacket(&bitReader, 0)
|
||
|
|
check(err)
|
||
|
|
|
||
|
|
scanner := bufio.NewScanner(bitReader.file)
|
||
|
|
if scanner.Scan() {
|
||
|
|
bytesLeft := len(scanner.Bytes())
|
||
|
|
log.Printf(log.NORMAL, color.Yellow+"Completed reading from file with %d bytes remaining!\n"+color.Reset, bytesLeft)
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Printf(log.NORMAL, "\nTotal of all versions is %d\n", p.version+p.subVersions)
|
||
|
|
}
|
||
|
|
|
||
|
|
func part2() {
|
||
|
|
log.Println(log.NORMAL, "* Part 2 *")
|
||
|
|
log.Println(log.NORMAL, " Goal: ")
|
||
|
|
log.Println(log.NORMAL, " Answer: ")
|
||
|
|
}
|
||
|
|
|
||
|
|
func parseInputFile() {
|
||
|
|
input, err := os.Open("./input.txt")
|
||
|
|
check(err)
|
||
|
|
defer input.Close()
|
||
|
|
|
||
|
|
output, err := files.CreateOrReplace("./output.txt")
|
||
|
|
check(err)
|
||
|
|
defer output.Close()
|
||
|
|
|
||
|
|
scanner := bufio.NewScanner(input)
|
||
|
|
for scanner.Scan() {
|
||
|
|
bytes := scanner.Bytes()
|
||
|
|
for _, b := range bytes {
|
||
|
|
outBits, err := bits.HexByteToBits(b)
|
||
|
|
if err != nil {
|
||
|
|
panic(fmt.Sprintf("Error parsing byte %s", string(b)))
|
||
|
|
}
|
||
|
|
for _, ob := range outBits {
|
||
|
|
if ob {
|
||
|
|
output.WriteString("1")
|
||
|
|
} else {
|
||
|
|
output.WriteString("0")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
type Packet struct {
|
||
|
|
id int
|
||
|
|
value int
|
||
|
|
subValues int
|
||
|
|
version int
|
||
|
|
subVersions int
|
||
|
|
pType int
|
||
|
|
subpacketCount int
|
||
|
|
bitCount int
|
||
|
|
subBitCount int
|
||
|
|
}
|
||
|
|
|
||
|
|
var packetNum = 0
|
||
|
|
|
||
|
|
func parsePacket(bitReader *BitReader, depth int) (Packet, error) {
|
||
|
|
packetNum++
|
||
|
|
p := Packet{packetNum, 0, 0, 0, 0, 0, 0, 0, 0}
|
||
|
|
versionBits, err := bitReader.ReadBits(3)
|
||
|
|
p.bitCount += 3
|
||
|
|
if err != nil {
|
||
|
|
return p, fmt.Errorf("%s for version bits", err.Error())
|
||
|
|
}
|
||
|
|
p.version = bits.ToInt(versionBits)
|
||
|
|
typeBits, err := bitReader.ReadBits(3)
|
||
|
|
p.bitCount += 3
|
||
|
|
if err != nil {
|
||
|
|
return p, fmt.Errorf("%s for type bits", err.Error())
|
||
|
|
}
|
||
|
|
p.pType = bits.ToInt(typeBits)
|
||
|
|
|
||
|
|
log.Printf(log.DIAGNOSTIC, "\nP: %3d %s", packetNum, strings.Repeat(" ", depth*2))
|
||
|
|
log.Printf(log.DIAGNOSTIC, "Ver %d, Type %d", p.version, p.pType)
|
||
|
|
|
||
|
|
switch p.pType {
|
||
|
|
case 4:
|
||
|
|
log.Printf(log.DIAGNOSTIC, ": L")
|
||
|
|
var litBits []bool
|
||
|
|
var bitVals []bool
|
||
|
|
// read sets of 5 bits until the first bit of 5 is not a '1'
|
||
|
|
for bitVals, err = bitReader.ReadBits(5); bitVals[0]; bitVals, err = bitReader.ReadBits(5) {
|
||
|
|
p.bitCount += 5
|
||
|
|
if err != nil {
|
||
|
|
return p, fmt.Errorf("%s for literal bits", err.Error())
|
||
|
|
}
|
||
|
|
litBits = append(litBits, bitVals[1:]...)
|
||
|
|
}
|
||
|
|
// add the last set to the literal value
|
||
|
|
p.bitCount += 5
|
||
|
|
litBits = append(litBits, bitVals[1:]...)
|
||
|
|
p.value = bits.ToInt(litBits)
|
||
|
|
log.Printf(log.DIAGNOSTIC, " (value %d)", p.value)
|
||
|
|
|
||
|
|
return p, nil
|
||
|
|
default:
|
||
|
|
log.Printf(log.DIAGNOSTIC, ": O")
|
||
|
|
hasSubpackets, err := bitReader.ReadBit()
|
||
|
|
p.bitCount += 1
|
||
|
|
if err != nil {
|
||
|
|
return p, fmt.Errorf("%s for subpacket bit", err.Error())
|
||
|
|
}
|
||
|
|
if hasSubpackets {
|
||
|
|
countBits, err := bitReader.ReadBits(11)
|
||
|
|
p.bitCount += 11
|
||
|
|
if err != nil {
|
||
|
|
return p, fmt.Errorf("%s for subpacket count", err.Error())
|
||
|
|
}
|
||
|
|
p.subpacketCount = bits.ToInt(countBits)
|
||
|
|
log.Printf(log.DIAGNOSTIC, " (count: %d) {", p.subpacketCount)
|
||
|
|
|
||
|
|
for subpacketTotal := 0; subpacketTotal < p.subpacketCount; {
|
||
|
|
sp, err := parsePacket(bitReader, depth+1)
|
||
|
|
log.Printf(log.DIAGNOSTIC, " [S: %d]", subpacketTotal+1)
|
||
|
|
if err != nil {
|
||
|
|
return p, fmt.Errorf("%s for subpacket %d of id#%d", err.Error(), subpacketTotal+1, p.id)
|
||
|
|
}
|
||
|
|
subpacketTotal++
|
||
|
|
p.subValues += sp.value + sp.subValues
|
||
|
|
p.subVersions += sp.version + sp.subVersions
|
||
|
|
p.subBitCount += sp.bitCount + sp.subBitCount
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
lengthBits, err := bitReader.ReadBits(15)
|
||
|
|
p.bitCount += 15
|
||
|
|
if err != nil {
|
||
|
|
return p, fmt.Errorf("%s for subpacket length", err.Error())
|
||
|
|
}
|
||
|
|
p.subBitCount = bits.ToInt(lengthBits)
|
||
|
|
log.Printf(log.DIAGNOSTIC, " (length: %d) {", p.subBitCount)
|
||
|
|
subpacketBits := 0
|
||
|
|
for subpacketBits < p.subBitCount {
|
||
|
|
sp, err := parsePacket(bitReader, depth+1)
|
||
|
|
log.Printf(log.DIAGNOSTIC, " [S: %d]", p.subpacketCount+1)
|
||
|
|
if err != nil {
|
||
|
|
return p, fmt.Errorf("%s for subpacket of id#%d", err.Error(), p.id)
|
||
|
|
}
|
||
|
|
subpacketBits += sp.bitCount + sp.subBitCount
|
||
|
|
p.subValues += sp.value + sp.subValues
|
||
|
|
p.subVersions += sp.version + sp.subVersions
|
||
|
|
p.subpacketCount += 1 + sp.subpacketCount
|
||
|
|
}
|
||
|
|
if subpacketBits != p.subBitCount {
|
||
|
|
return p, fmt.Errorf("packet #%d should parse %d bits but parsed %d", p.id, p.subBitCount, subpacketBits)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if p.pType != 4 {
|
||
|
|
log.Printf(log.DIAGNOSTIC, "\n%s}", strings.Repeat(" ", 6+depth*2))
|
||
|
|
}
|
||
|
|
return p, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
type BitReader struct {
|
||
|
|
file *os.File
|
||
|
|
values []bool
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewBitReader(file *os.File) BitReader {
|
||
|
|
return BitReader{file, []bool{}}
|
||
|
|
}
|
||
|
|
|
||
|
|
var totalBytesRead = 0
|
||
|
|
|
||
|
|
func (b *BitReader) ReadBits(num int) ([]bool, error) {
|
||
|
|
if len(b.values) < num {
|
||
|
|
// each char is worth 4 bits
|
||
|
|
byteCount := int(math.Ceil(float64(num-len(b.values)) / 4))
|
||
|
|
bytes := make([]byte, byteCount)
|
||
|
|
b.file.Read(bytes)
|
||
|
|
log.Printf(log.NEVER, "\n [Reading in %d more byte(s) to fulfill %d bit read]", byteCount, num)
|
||
|
|
totalBytesRead += byteCount
|
||
|
|
err := b.AddToValues(bytes)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("%s while reading %d bits", err.Error(), num)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// read the rest from input and set any remainder
|
||
|
|
bits := b.values[:num]
|
||
|
|
b.values = b.values[num:]
|
||
|
|
log.Printf(log.NEVER, " \n [Read %d bits from reader and left %d remainder values]", len(bits), len(b.values))
|
||
|
|
|
||
|
|
return bits, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (b *BitReader) ReadBit() (bool, error) {
|
||
|
|
bits, err := b.ReadBits(1)
|
||
|
|
if err == nil {
|
||
|
|
return bits[0], nil
|
||
|
|
} else {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (b *BitReader) AddToValues(bytes []byte) error {
|
||
|
|
for bIdx, byte := range bytes {
|
||
|
|
newBits, err := bits.HexByteToBits(byte)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("%s @ byte #%d", err.Error(), totalBytesRead-len(bytes)+bIdx+1)
|
||
|
|
}
|
||
|
|
b.values = append(b.values, newBits...)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|