Created
December 16, 2021 11:50
-
-
Save almendar/ed1402355b4cf88c7c43cb3ee45fa747 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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