Files
aoc2021/sixteen/bits.go

228 lines
4.2 KiB
Go

package sixteen
import (
"bufio"
"fmt"
"os"
"strconv"
)
const (
literalType = 4
lengthType15 = 0
lengthType11 = 1
)
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",
}
type packet struct {
packet string
version int
typeID int
content int
subpackets []packet
}
func (p *packet) versionSum() int {
sum := p.version
for _, s := range p.subpackets {
sum += s.versionSum()
}
return sum
}
func hexToBinary(input string) string {
output := ""
for _, r := range input {
output += hexToBin[string(r)]
}
return output
}
func parseHex(input string) packet {
binary := hexToBinary(input)
return parse(binary)
}
func parse(input string) packet {
b := packet{}
b.packet = input
b.version, b.typeID = parseVersionType(input)
content := input[6:]
switch b.typeID {
case literalType:
b.content, _ = parseLiteral(content)
default:
b.subpackets, _ = parseOperator(content)
}
return b
}
func parseVersionType(input string) (int, int) {
var version, typeID string
if count, err := fmt.Sscanf(input, "%3s%3s", &version, &typeID); count != 2 || err != nil {
fmt.Println("Unable to parse", input, err)
return -1, -1
}
v, err := strconv.ParseInt(version, 2, 64)
if err != nil {
fmt.Println("Unable to parse version from", version, err)
return -1, -1
}
t, err := strconv.ParseInt(typeID, 2, 64)
if err != nil {
fmt.Println("Unable to parse type from", typeID, err)
return -1, -1
}
return int(v), int(t)
}
func parsePackets(input string) []packet {
packets := []packet{}
remaining := input
for remaining != "" {
version, typeID := parseVersionType(remaining)
p := packet{
version: version,
typeID: typeID,
}
switch p.typeID {
case literalType:
value, length := parseLiteral(remaining[6:])
p.content = value
p.packet = remaining[:length+6]
remaining = remaining[length+6:]
default:
l := 0
p.subpackets, l = parseOperator(remaining[6:])
remaining = remaining[l+7:]
}
packets = append(packets, p)
}
return packets
}
func parseNPackets(input string, count int) ([]packet, int) {
length := len(input)
packets := []packet{}
remaining := input
for len(packets) != count {
version, typeID := parseVersionType(remaining)
p := packet{
version: version,
typeID: typeID,
}
switch p.typeID {
case literalType:
value, length := parseLiteral(remaining[6:])
p.content = value
p.packet = remaining[:length+6]
remaining = remaining[length+6:]
default:
l := 0
p.subpackets, l = parseOperator(remaining[6:])
remaining = remaining[l+7:]
}
packets = append(packets, p)
}
return packets, length - len(remaining)
}
func parseOperator(input string) ([]packet, int) {
length := 0
subPackets := []packet{}
var lType int
var remainder string
if count, err := fmt.Sscanf(input, "%1d%s", &lType, &remainder); count != 2 || err != nil {
fmt.Println("Unable to parse operator type", input)
return subPackets, length
}
switch lType {
case lengthType15:
lengthBits, _ := strconv.ParseInt(string(remainder[:15]), 2, 64)
subs := parsePackets(remainder[15 : 15+lengthBits])
subPackets = append(subPackets, subs...)
length = 15 + int(lengthBits)
case lengthType11:
lengthBits, _ := strconv.ParseInt(string(remainder[:11]), 2, 64)
subs, l := parseNPackets(remainder[11:], int(lengthBits))
subPackets = append(subPackets, subs...)
length = l
}
return subPackets, length
}
func parseLiteral(input string) (int, int) {
literal := ""
length := 0
for i := 0; i < len(input); i += 5 {
literal += string(input[i+1]) + string(input[i+2]) + string(input[i+3]) + string(input[i+4])
length += 5
if input[i] == '0' {
break
}
}
v, err := strconv.ParseInt(literal, 2, 64)
if err != nil {
fmt.Println("Unable to parse", literal, err)
return 0, -1
}
return int(v), length
}
func load(filename string) packet {
b := packet{}
file, err := os.Open(filename)
if err != nil {
return b
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
b = parseHex(scanner.Text())
}
return b
}