2021-12-16 15:54:05 -05:00
/* Day 16: Packet Decoder */
package main
import (
"AoC2021/utils/args"
"AoC2021/utils/bits"
"AoC2021/utils/color"
"AoC2021/utils/files"
"AoC2021/utils/log"
2021-12-28 09:55:06 -05:00
"AoC2021/utils/numbers"
2021-12-16 15:54:05 -05:00
"AoC2021/utils/timer"
"bufio"
"fmt"
"math"
"os"
)
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 *" )
2021-12-28 09:55:06 -05:00
log . Println ( log . NORMAL , " Goal: Do the same thing but perform operations and stuff" )
log . Println ( log . NORMAL , " Answer: What do you get if you evaluate the expression represented by your hexadecimal-encoded BITS transmission?" )
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 + "\nCompleted reading from file with %d bytes remaining!\n" + color . Reset , bytesLeft )
}
log . Printf ( log . NORMAL , "\nTotal of all versions is %d\n" , p . value )
2021-12-16 15:54:05 -05:00
}
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
version int
subVersions int
pType int
subpacketCount int
bitCount int
subBitCount int
}
2021-12-28 09:55:06 -05:00
const (
SUM = 0
PRODUCT = 1
MINIMUM = 2
MAXIMUM = 3
LITERAL = 4
GREATERTHAN = 5
LESSTHAN = 6
EQUALTO = 7
)
2021-12-16 15:54:05 -05:00
var packetNum = 0
func parsePacket ( bitReader * BitReader , depth int ) ( Packet , error ) {
packetNum ++
2021-12-28 09:55:06 -05:00
p := Packet { packetNum , 0 , 0 , 0 , 0 , 0 , 0 , 0 }
2021-12-16 15:54:05 -05:00
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 )
2021-12-28 09:55:06 -05:00
// log.Printf(log.DIAGNOSTIC, "\nP: %3d %s", packetNum, strings.Repeat(" ", depth*2))
// log.Printf(log.DIAGNOSTIC, "Ver %d, Type %d", p.version, p.pType)
2021-12-16 15:54:05 -05:00
switch p . pType {
2021-12-28 09:55:06 -05:00
case LITERAL :
// log.Printf(log.DIAGNOSTIC, ": L")
2021-12-16 15:54:05 -05:00
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 )
2021-12-28 09:55:06 -05:00
log . Printf ( log . DIAGNOSTIC , "%d" , p . value )
2021-12-16 15:54:05 -05:00
return p , nil
2021-12-28 09:55:06 -05:00
case SUM , PRODUCT , MINIMUM , MAXIMUM , GREATERTHAN , LESSTHAN , EQUALTO :
// log.Printf(log.DIAGNOSTIC, ": O")
log . Printf ( log . DIAGNOSTIC , operString ( p . pType ))
2021-12-16 15:54:05 -05:00
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 )
2021-12-28 09:55:06 -05:00
// log.Printf(log.DIAGNOSTIC, " (count: %d) {", p.subpacketCount)
2021-12-16 15:54:05 -05:00
2021-12-28 09:55:06 -05:00
for subpacketTotal , accum := 0 , getAccum ( p . pType ); subpacketTotal < p . subpacketCount ; {
2021-12-16 15:54:05 -05:00
sp , err := parsePacket ( bitReader , depth + 1 )
2021-12-28 09:55:06 -05:00
// log.Printf(log.DIAGNOSTIC, " [S: %d]", subpacketTotal+1)
2021-12-16 15:54:05 -05:00
if err != nil {
return p , fmt . Errorf ( "%s for subpacket %d of id#%d" , err . Error (), subpacketTotal + 1 , p . id )
}
2021-12-28 09:55:06 -05:00
if subpacketTotal == 0 && ( p . pType == GREATERTHAN || p . pType == LESSTHAN || p . pType == EQUALTO ) {
accum = sp . value
} else {
p . value = mungeValue ( accum , p . pType , sp . value )
}
2021-12-16 15:54:05 -05:00
subpacketTotal ++
p . subVersions += sp . version + sp . subVersions
p . subBitCount += sp . bitCount + sp . subBitCount
2021-12-28 09:55:06 -05:00
if subpacketTotal < p . subpacketCount {
log . Printf ( log . DIAGNOSTIC , ", " )
}
2021-12-16 15:54:05 -05:00
}
} 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 )
2021-12-28 09:55:06 -05:00
//log.Printf(log.DIAGNOSTIC, " (length: %d) {", p.subBitCount)
2021-12-16 15:54:05 -05:00
subpacketBits := 0
2021-12-28 09:55:06 -05:00
for accum := getAccum ( p . pType ); subpacketBits < p . subBitCount ; {
2021-12-16 15:54:05 -05:00
sp , err := parsePacket ( bitReader , depth + 1 )
2021-12-28 09:55:06 -05:00
//log.Printf(log.DIAGNOSTIC, " [S: %d]", p.subpacketCount+1)
2021-12-16 15:54:05 -05:00
if err != nil {
return p , fmt . Errorf ( "%s for subpacket of id#%d" , err . Error (), p . id )
}
2021-12-28 09:55:06 -05:00
if subpacketBits == 0 && ( p . pType == GREATERTHAN || p . pType == LESSTHAN || p . pType == EQUALTO ) {
accum = sp . value
} else {
p . value = mungeValue ( accum , p . pType , sp . value )
}
2021-12-16 15:54:05 -05:00
subpacketBits += sp . bitCount + sp . subBitCount
p . subVersions += sp . version + sp . subVersions
p . subpacketCount += 1 + sp . subpacketCount
2021-12-28 09:55:06 -05:00
if subpacketBits < p . subBitCount {
log . Printf ( log . DIAGNOSTIC , ", " )
}
2021-12-16 15:54:05 -05:00
}
if subpacketBits != p . subBitCount {
return p , fmt . Errorf ( "packet #%d should parse %d bits but parsed %d" , p . id , p . subBitCount , subpacketBits )
}
}
2021-12-28 09:55:06 -05:00
log . Printf ( log . DIAGNOSTIC , ")" )
2021-12-16 15:54:05 -05:00
}
2021-12-28 09:55:06 -05:00
// if p.pType != 4 {
// log.Printf(log.DIAGNOSTIC, "\n%s}", strings.Repeat(" ", 6+depth*2))
// }
2021-12-16 15:54:05 -05:00
return p , nil
}
2021-12-28 09:55:06 -05:00
// get the starting accumulator for a given operation
func getAccum ( oper int ) int {
switch oper {
case SUM :
return 0
case PRODUCT :
return 1
case MINIMUM :
return - math . MaxInt
case MAXIMUM :
return math . MinInt
case GREATERTHAN :
return 1
case LESSTHAN :
return 1
case EQUALTO :
return 1
default :
panic ( fmt . Sprintf ( "Got invalid operation %d" , oper ))
}
}
// perform an operation on the given packet and return a new value
func mungeValue ( oldVal , oper , newVal int ) int {
switch oper {
case SUM :
return oldVal + newVal
case PRODUCT :
return oldVal * newVal
case MINIMUM :
return numbers . Min ( oldVal , newVal )
case MAXIMUM :
return numbers . Max ( oldVal , newVal )
case GREATERTHAN :
return numbers . BoolToInt ( oldVal > newVal )
case LESSTHAN :
return numbers . BoolToInt ( oldVal < newVal )
case EQUALTO :
return numbers . BoolToInt ( oldVal == newVal )
default :
panic ( fmt . Sprintf ( "Got invalid operation %d" , oper ))
}
}
func operString ( oper int ) string {
switch oper {
case SUM :
return "SUM("
case PRODUCT :
return "PROD("
case MINIMUM :
return "MIN("
case MAXIMUM :
return "MAX("
case GREATERTHAN :
return "GT("
case LESSTHAN :
return "LT("
case EQUALTO :
return "EQ("
default :
panic ( fmt . Sprintf ( "Got invalid operation %d" , oper ))
}
}
2021-12-16 15:54:05 -05:00
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
}