Files
advent-of-code-go/2021/day16/main.go
T

199 lines
4.7 KiB
Go

package main
import (
_ "embed"
"flag"
"fmt"
"strconv"
"strings"
"github.com/alexchao26/advent-of-code-go/mathy"
"github.com/alexchao26/advent-of-code-go/util"
)
//go:embed input.txt
var input string
func init() {
// do this in init (not main) so test file has same input
input = strings.TrimRight(input, "\n")
if len(input) == 0 {
panic("empty input.txt file")
}
}
func main() {
var part int
flag.IntVar(&part, "part", 1, "part 1 or 2")
flag.Parse()
fmt.Println("Running part", part)
if part == 1 {
ans := part1(input)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
} else {
ans := part2(input)
util.CopyToClipboard(fmt.Sprintf("%v", ans))
fmt.Println("Output:", ans)
}
}
func part1(input string) int64 {
bin := parseInput(input)
versionTotal, _, _ := handlePacket(bin)
return versionTotal
}
// bitsRead is often just called n in Go. See io.Read() for example
func handlePacket(pack string) (versionTotal int64, expressionValue int, bitsRead int) {
version, err := strconv.ParseInt(pack[0:3], 2, 64)
versionTotal += version
if err != nil {
panic("version strconv.ParseInt(): " + err.Error())
}
typeID, err := strconv.ParseInt(pack[3:6], 2, 64)
if err != nil {
panic("typeID strconv.ParseInt(): " + err.Error())
}
read := 6 // version and typeID
switch typeID {
case 4: // literal value
// parse 5 bits at a time
var bits string
for i := 6; i < len(pack); i += 5 {
fiveBits := pack[i : i+5]
read += 5
bits += fiveBits[1:]
if fiveBits[0] == '0' {
break
}
}
decimalVal, err := strconv.ParseInt(bits, 2, 64)
if err != nil {
panic("parsing packet bits: " + err.Error())
}
return version, int(decimalVal), read
default: // operator types?
// contains one or more packets
lengthTypeID := pack[6:7]
read++
var bitsToRead int
switch lengthTypeID {
case "0":
// next 15 bits == total length in bits for REST of subpackets
bitsToRead = 15
case "1":
// next 11 bits == NUMBER of subpackets
bitsToRead = 11
}
rawLength := pack[7 : 7+bitsToRead]
read += bitsToRead
length, err := strconv.ParseInt(rawLength, 2, 64)
if err != nil {
panic("parsing 0 lengthTypeID: " + err.Error())
}
// followed by the subpackets themselves
var subPacketExpressionValues []int
switch lengthTypeID {
case "0":
// next 15 bits == total length in bits for REST of subpackets
for length > 0 {
// continue reading until length of bits are read
ver, expVal, n := handlePacket(pack[read:])
read += n
length -= int64(n)
subPacketExpressionValues = append(subPacketExpressionValues, expVal)
versionTotal += ver
}
case "1":
// next 11 bits == NUMBER of subpackets
for length > 0 {
// continue reading until number of packets are read
ver, expVal, n := handlePacket(pack[read:])
read += n
length-- // reduce length by 1 (ie one packet read)
subPacketExpressionValues = append(subPacketExpressionValues, expVal)
versionTotal += ver
}
}
switch typeID {
// note: case 0 already handled above, literal value
case 0: // sum
return versionTotal, mathy.SumIntSlice(subPacketExpressionValues), read
case 1: // product
return versionTotal, mathy.MultiplyIntSlice(subPacketExpressionValues), read
case 2: // min
return versionTotal, mathy.MinInt(subPacketExpressionValues...), read
case 3: // max
return versionTotal, mathy.MaxInt(subPacketExpressionValues...), read
// 4 is literal...
case 5: // greater than (first subpacket > second, will always have exactly 2)
var ans int
if subPacketExpressionValues[0] > subPacketExpressionValues[1] {
ans = 1 // otherwise int zero val works
}
return versionTotal, ans, read
case 6: // less than (opposite)
var ans int
if subPacketExpressionValues[0] < subPacketExpressionValues[1] {
ans = 1 // otherwise int zero val works
}
return versionTotal, ans, read
case 7: // equal to
var ans int
if subPacketExpressionValues[0] == subPacketExpressionValues[1] {
ans = 1 // otherwise int zero val works
}
return versionTotal, ans, read
default:
panic(fmt.Sprintf("unknown typeID: %d", typeID))
}
}
}
func part2(input string) int {
bin := parseInput(input)
_, expVal, _ := handlePacket(bin)
return expVal
}
var hexToBin = map[string]string{
"0": "0000",
"1": "0001",
"2": "0010",
"3": "0011",
"4": "0100",
"5": "0101",
"6": "0110",
"7": "0111",
"8": "1000",
"9": "1001",
"A": "1010",
"B": "1011",
"C": "1100",
"D": "1101",
"E": "1110",
"F": "1111",
}
func parseInput(input string) string {
var binarySb strings.Builder
for _, char := range strings.Split(input, "") {
binarySb.WriteString(hexToBin[char])
}
return binarySb.String()
}