mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-06-01 01:38:28 +02:00
199 lines
4.7 KiB
Go
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()
|
|
}
|