Skip to content

Instantly share code, notes, and snippets.

@almendar
Created December 16, 2021 11:50
Show Gist options
  • Save almendar/ed1402355b4cf88c7c43cb3ee45fa747 to your computer and use it in GitHub Desktop.
Save almendar/ed1402355b4cf88c7c43cb3ee45fa747 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"io/ioutil"
"log"
"math"
"strings"
)
var hexToBinaryMappings = map[rune]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 binaryToDec(binaryNumber string) int {
binaryChar := []rune(binaryNumber)
binaryLen := len(binaryChar)
counter := 0
for i := 0; i < binaryLen; i++ {
if binaryChar[i] == '1' {
counter += int(math.Pow(2, float64(binaryLen-i-1)))
}
}
return counter
}
func hexToBinary(hexString string) string {
b := strings.Builder{}
b.Grow(len(hexString) * 4)
for _, rune := range hexString {
b.WriteString(hexToBinaryMappings[rune])
}
return b.String()
}
type literal struct {
number int
}
type operator struct {
lengthTypeId int
subPackets []packet
}
type packet struct {
version int // 3bits
typeId int // 3bits
consumedBits int
literal literal
op operator
}
func value(p packet) int {
retVal := 0
switch p.typeId {
case 4:
retVal = p.literal.number
case 0: //sum
retVal = 0
for _, p2 := range p.op.subPackets {
retVal += value(p2)
}
case 1: //product
retVal = 1
for _, p2 := range p.op.subPackets {
retVal *= value(p2)
}
case 2: //min
min := math.MaxInt
for _, p2 := range p.op.subPackets {
newVal := value(p2)
if newVal < min {
min = newVal
}
}
retVal = min
case 3: //max
max := math.MinInt
for _, p2 := range p.op.subPackets {
newVal := value(p2)
if newVal > max {
max = newVal
}
}
retVal = max
case 5: //greater
if value(p.op.subPackets[0]) > value(p.op.subPackets[1]) {
retVal = 1
} else {
retVal = 0
}
case 6: //less than
if value(p.op.subPackets[0]) < value(p.op.subPackets[1]) {
retVal = 1
} else {
retVal = 0
}
case 7: //equal to
if value(p.op.subPackets[0]) == value(p.op.subPackets[1]) {
retVal = 1
} else {
retVal = 0
}
}
return retVal
}
func parsePacketHex(input string) packet {
binary := hexToBinary(input)
return parsePacket(binary)
}
func parsePacket(binary string) packet {
// fmt.Printf("binary: %v\n", binary)
p := packet{}
runes := []rune(binary)
p.version = binaryToDec(string(runes[0:3]))
p.consumedBits += 3
p.typeId = binaryToDec(string(runes[3:6]))
p.consumedBits += 3
if p.typeId == 4 {
// fmt.Printf("Literal version %d\n", p.version)
runes = runes[6:]
i := 0
b := strings.Builder{}
for {
instruction := runes[i]
number := runes[i+1 : i+5]
b.WriteString(string(number))
p.consumedBits += 5
if instruction == '1' {
i += 5
} else if instruction == '0' {
break
}
}
p.literal.number = binaryToDec(b.String())
} else {
lengthTypeId := string(runes[6])
// fmt.Printf("lengthTypeId: %v\n", lengthTypeId)
p.consumedBits += 1
runes = runes[7:]
if lengthTypeId == "0" {
p.op.lengthTypeId = 0
toParse := binaryToDec(string(runes[0:15]))
runes = runes[15:]
p.consumedBits += 15
for toParse > 0 {
// fmt.Printf("toParse: %v\n", toParse)
newPacket := parsePacket(string(runes))
p.consumedBits += newPacket.consumedBits
p.op.subPackets = append(p.op.subPackets, newPacket)
runes = runes[newPacket.consumedBits:]
toParse -= newPacket.consumedBits
}
} else if lengthTypeId == "1" {
// fmt.Printf("Operator version %d %s\n", p.version, "T2")
//11 bits number of sub-packets immediately contained
p.op.lengthTypeId = 1
numberOfSubPacktes := binaryToDec(string(runes[0:11]))
runes = runes[11:]
p.consumedBits += 11
// fmt.Printf("numberOfSubPacktes: %v\n", numberOfSubPacktes)
for i := 0; i < numberOfSubPacktes; i++ {
// fmt.Printf("subpackage: %v\n", i)
newPacket := parsePacket(string(runes))
p.consumedBits += newPacket.consumedBits
p.op.subPackets = append(p.op.subPackets, newPacket)
runes = runes[newPacket.consumedBits:]
}
}
}
return p
}
func sumVersions(p packet) int {
counter := 0
counter += p.version
if len(p.op.subPackets) > 0 {
for _, p2 := range p.op.subPackets {
counter += sumVersions(p2)
}
}
return counter
}
func task1Examples() {
fmt.Println("Day16 Task1 examples:")
examples := []struct {
given string
expected int
}{
{"8A004A801A8002F478", 16},
{"620080001611562C8802118E34", 12},
{"C0015000016115A2E0802F182340", 23},
{"A0016C880162017C3686B18A3D4780", 31},
}
for _, v := range examples {
got := sumVersions(parsePacketHex(v.given))
fmt.Printf("%s - expected: %d, got: %d\n", v.given, v.expected, got)
}
}
func task2Example() {
fmt.Println("Day16 Task1 examples:")
examples := []struct {
given string
expected int
}{
{"C200B40A82", 3},
{"04005AC33890", 54},
{"880086C3E88112", 7},
{"CE00C43D881120", 9},
{"D8005AC2A8F0", 1},
{"F600BC2D8F", 0},
{"9C005AC2F8F0", 0},
{"9C0141080250320F1802104A08", 1},
}
for _, v := range examples {
fmt.Printf("%s - expected: %d, got: %d\n", v.given, v.expected, value(parsePacketHex(v.given)))
}
}
func task1(data string) {
fmt.Printf("Day16 Task1 input: %v\n", sumVersions(parsePacketHex(data)))
}
func task2(data string) {
fmt.Printf("Day16 Task2 input: %v\n", value(parsePacketHex(data)))
}
func main() {
input, err := ioutil.ReadFile("input.txt")
if err != nil {
log.Fatal(err)
}
data := string(input)
task1Examples()
task1(data)
task2Example()
task2(data)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment