diff --git a/16/main.go b/16/main.go new file mode 100644 index 0000000..e89d830 --- /dev/null +++ b/16/main.go @@ -0,0 +1,154 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "time" +) + +func main() { + start := time.Now() + partOne() + duration := time.Since(start) + partTwo() + duration2 := time.Since(start) + fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration) +} + +var hexMap = map[rune][]uint8{ + '0': {0, 0, 0, 0}, + '1': {0, 0, 0, 1}, + '2': {0, 0, 1, 0}, + '3': {0, 0, 1, 1}, + '4': {0, 1, 0, 0}, + '5': {0, 1, 0, 1}, + '6': {0, 1, 1, 0}, + '7': {0, 1, 1, 1}, + '8': {1, 0, 0, 0}, + '9': {1, 0, 0, 1}, + 'A': {1, 0, 1, 0}, + 'B': {1, 0, 1, 1}, + 'C': {1, 1, 0, 0}, + 'D': {1, 1, 0, 1}, + 'E': {1, 1, 1, 0}, + 'F': {1, 1, 1, 1}, +} + +func makeScanner(test bool) *bufio.Scanner { + var f *os.File + if test { + f, _ = os.Open("inputs/testinput") + } else { + f, _ = os.Open("inputs/input") + } + reader := bufio.NewReader(f) + return bufio.NewScanner(reader) +} + +func parseNumber(stream []uint8, length int) int64 { + result := int64(0) + for i := 0; i < length; i++ { + result += int64(stream[i]) << (length - 1 - i) + } + return result +} + +type packet struct { + version int64 + typeID int64 + subPackets []packet + encodedNumber int64 +} + +func getPacketVersion(stream []uint8) int64 { + return parseNumber(stream, 3) +} + +func getPacketType(stream []uint8) int64 { + return parseNumber(stream, 3) +} + +func getSubpacketLength(t uint8, stream []uint8) (int, int64) { + if t == 0 { + return 15, parseNumber(stream, 15) + } + if t == 1 { + return 11, parseNumber(stream, 11) + } + return 0, 0 +} + +func parsePayload(stream []uint8) (int64, int) { + num := []uint8{} + pos := 0 + chars := 0 + for { + currentsegment := stream[pos : pos+5] + chars += 4 + num = append(num, currentsegment[1:5]...) + pos += 5 + if currentsegment[0] == 0 { + break + } + } + return parseNumber(num, chars), pos +} + +func parsePackets(stream []uint8) (int, []packet) { + if len(stream) <= 7 { + return 0, []packet{} + } + parsedPacket := packet{} + parsedPacket.version = getPacketVersion(stream[0:3]) + parsedPacket.typeID = getPacketType(stream[3:6]) + if parsedPacket.typeID == 4 { + encoded, consumed := parsePayload(stream[6:]) + parsedPacket.encodedNumber = encoded + return consumed + 6, []packet{parsedPacket} // 6 is packet header length + } else { + parsed := 0 + size, subpacketlength := getSubpacketLength(stream[6], stream[7:]) + for { + consumed, subs := parsePackets(stream[7+size+parsed:]) + parsedPacket.subPackets = append(parsedPacket.subPackets, subs...) + parsed += consumed + if size == 11 && int64(len(parsedPacket.subPackets)) == subpacketlength { + return parsed + 7 + size, []packet{parsedPacket} + + } else if size == 15 && int64(parsed) > subpacketlength || consumed == 0 { + return parsed + 7 + size, []packet{parsedPacket} + } + } + } +} + +func countVersions(packets []packet) int { + i := 0 + for _, p := range packets { + i += int(p.version) + i += countVersions(p.subPackets) + } + return i +} + +func partOne() { + scanner := makeScanner(false) + + scanner.Scan() + line := scanner.Text() + stream := []uint8{} + for _, hex := range line { + stream = append(stream, hexMap[hex]...) + } + _, parsedPacket := parsePackets(stream) + fmt.Println(countVersions(parsedPacket)) +} + +func partTwo() { + scanner := makeScanner(false) + + for scanner.Scan() { + // line := scanner.Text() + } +} diff --git a/16/main_test.go b/16/main_test.go new file mode 100644 index 0000000..cf060cb --- /dev/null +++ b/16/main_test.go @@ -0,0 +1,61 @@ +package main + +import ( + "testing" +) + +func TestVersionParse(t *testing.T) { + tests := map[int64][]uint8{ + 0: {0, 0, 0}, + 1: {0, 0, 1}, + 2: {0, 1, 0}, + 3: {0, 1, 1}, + 4: {1, 0, 0}, + 5: {1, 0, 1}, + 6: {1, 1, 0}, + 7: {1, 1, 1}, + } + for result, input := range tests { + if result != getPacketVersion(input) { + t.Fail() + } + if result != getPacketType(input) { + t.Fail() + } + } +} + +func TestParsePayload(t *testing.T) { + if n, q := parsePayload([]uint8{0, 1, 0, 1, 0}); n != 10 || q != 5 { + t.Log(n, q) + t.Fail() + } + if n, q := parsePayload([]uint8{1, 0, 0, 0, 1, 0, 0, 1, 0, 0}); n != 20 || q != 10 { + t.Log(n, q) + t.Fail() + } +} + +func TestCases(t *testing.T) { + inputs := map[string]int{ + "D2FE28": 6, + "38006F45291200": 9, + "EE00D40C823060": 14, + "8A004A801A8002F478": 16, + "620080001611562C8802118E34": 12, + "C0015000016115A2E0802F182340": 23, + "A0016C880162017C3686B18A3D4780": 31, + } + + for c, r := range inputs { + stream := []uint8{} + for _, hex := range c { + stream = append(stream, hexMap[hex]...) + } + _, parsedPacket := parsePackets(stream) + if r != countVersions(parsedPacket) { + t.Log(c, countVersions(parsedPacket)) + t.Fail() + } + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bd8ca58 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module adventofcode2021 + +go 1.17 diff --git a/run.sh b/run.sh index 2eeee27..dce9523 100755 --- a/run.sh +++ b/run.sh @@ -3,6 +3,6 @@ if [ "${1}" != "" ]; then padded=$(printf "%02g" ${1}) cd ${padded} - go run *.go + go run main.go cd - > /dev/null fi